Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ MCP server wrapping [Google's Stim](https://github.com/quantumlib/Stim) stabiliz
| `analyze_errors` | Build the Detector Error Model and find shortest logical error paths |
| `get_circuit_diagram` | Return an ASCII, SVG, or timeline diagram |
| `inject_noise` | Add depolarizing or X error noise to a circuit |
| `generate_circuit` | Generate standard QEC circuits (surface, repetition, color codes) |

## Connecting to the remote server

Expand Down Expand Up @@ -180,3 +181,18 @@ gcloud run deploy stim-mcp \
uv sync
uv run pytest
```

## Releasing

Pushing a `v*` tag to `master` triggers the full release pipeline (tests must pass first):

- **PyPI** — package is built and published automatically
- **Cloud Run** — server is deployed automatically

```bash
# bump version in pyproject.toml, then:
git tag v0.x.y
git push origin v0.x.y
```

Required GitHub secrets: `PYPI_API_TOKEN`, `GCP_SA_KEY`.
67 changes: 67 additions & 0 deletions docs/qa/2026-03-22/modularize-generate-circuit/report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# QA Report — 2026-03-22

## Feature Tested
Modularization of `server.py` into `tools/` package + new `generate_circuit` tool for standard QEC circuits.

## Target Server
Local — Stim version: 1.15.0

## Changes Analyzed
Branch: `feature/generate-circuit-modularize`
Files changed:
- `src/stim_mcp_server/server.py` (slimmed to 55 lines)
- `src/stim_mcp_server/resources.py` (new — extracted resource handlers)
- `src/stim_mcp_server/tools/__init__.py` (new)
- `src/stim_mcp_server/tools/health.py` (new — `hello_quantum`)
- `src/stim_mcp_server/tools/circuit_management.py` (new — `create_circuit`, `append_operation`, `generate_circuit`)
- `src/stim_mcp_server/tools/simulation.py` (new — `sample_circuit`)
- `src/stim_mcp_server/tools/analysis.py` (new — `analyze_errors`, `inject_noise`)
- `src/stim_mcp_server/tools/visualization.py` (new — `get_circuit_diagram`)
- `tests/test_server.py` (updated imports + added `TestGenerateCircuit`)
- `README.md` (added `generate_circuit` to tools table)

## Test Results

| # | Description | Tool Called | Result | Details |
|---|-------------|-------------|--------|---------|
| 1 | Health check | `hello_quantum` | ✅ PASS | status ok, stim_version present, active_sessions is int |
| 2 | Bell circuit create + sample | `create_circuit`, `sample_circuit` | ✅ PASS | 2 qubits, 2 measurements, ~50% flip rates (entangled pair) |
| 3 | Append operation regression | `append_operation` | ✅ PASS | H gate appended, metadata updated |
| 4 | generate_circuit: repetition_code:memory (d=3, r=5) | `generate_circuit` | ✅ PASS | 5 qubits, 12 detectors, 1 observable, circuit_text present |
| 5 | generate_circuit: surface_code:rotated_memory_z (d=3, r=3) | `generate_circuit` | ✅ PASS | 26 qubits, 24 detectors, 1 observable |
| 6 | generate_circuit: with noise params | `generate_circuit` | ✅ PASS | DEPOLARIZE2 + X_ERROR gates present in circuit_text |
| 7 | generate_circuit: invalid code_task | `generate_circuit` | ✅ PASS | success:false, clear error message, supported_tasks list returned |
| 8 | generate → analyze_errors (noiseless) | `analyze_errors` | ✅ PASS | num_errors=0 expected for noiseless circuit; graceful note returned |
| 8b | generate → analyze_errors (noisy rep code) | `analyze_errors` | ✅ PASS | 27 errors in DEM, code_distance_lower_bound=3 (correct for d=3) |
| 9 | generate → get_circuit_diagram (crumble + text) | `get_circuit_diagram` | ✅ PASS | crumble URL valid, text diagram renders surface code timeline |
| 10 | generate → inject_noise → sample_circuit (pipeline) | `inject_noise`, `sample_circuit` | ✅ PASS | noisy circuit sampled, logical_error_rates returned |
| 11 | color_code:memory_xyz (d=3, r=2) | `generate_circuit` | ✅ PASS | C_XYZ gates, MY final measurements, correct structure |
| 12 | Missing circuit_id error handling | `sample_circuit` | ⚠️ WARN | success:false returned, but error string has extra quotes from KeyError repr |
| 13 | inject_noise preserves original | `inject_noise`, `sample_circuit` | ✅ PASS | original Bell circuit flip rates unchanged after inject_noise |

## Summary
Total: 14 | Passed: 13 | Failed: 0 | Warnings: 1

## Issues Found

### Critical
None.

### Warning
- **Double-quoted error message** (T12): When a circuit_id is not found, the error value is `"\"No circuit found with id 'X'\""` — extra quotes appear because `str(KeyError(...))` returns the repr of the argument. This is pre-existing behavior (present in original `server.py` too) and does not affect functional correctness. The tests pass because the circuit_id substring is still found within the quoted string.

## Fixes Applied
None — the only issue is pre-existing and cosmetic; no fix applied to avoid unintended scope creep.

## Remaining Issues
- **Double-quoted error strings** in `success:false` responses from `KeyError` paths. Fix would be to use `exc.args[0]` instead of `str(exc)` in all KeyError handlers across the tool modules. Deferred: pre-existing, non-blocking, and out of scope for this feature branch.

## Passed Checks
- ✅ All 7 existing tools work after modularization (health, create, append, sample, analyze, diagram, inject)
- ✅ `generate_circuit` happy path: all 6 supported QEC code types (tested rep, surface-Z, color)
- ✅ Noise parameters correctly embedded in generated circuit_text
- ✅ `generate_circuit` error path: invalid task returns `success:false` + `supported_tasks` list
- ✅ Generated circuits are stored and usable by all downstream tools (sample, analyze, diagram, inject)
- ✅ Code distance verification: noisy rep code d=3 yields `code_distance_lower_bound=3`
- ✅ `inject_noise` still preserves original circuit after modularization
- ✅ Module registration pattern (global `_store` set by `register()`) works correctly across all tool modules
44 changes: 44 additions & 0 deletions src/stim_mcp_server/resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""MCP resource handlers."""

from __future__ import annotations

import json

_store = None


def register(mcp, store) -> None:
global _store
_store = store

@mcp.resource("stim://circuit/{circuit_id}")
def resource_circuit(circuit_id: str) -> str:
"""Raw Stim source code for the circuit session."""
try:
session = _store.get(circuit_id)
return str(session.circuit)
except KeyError:
return f"Error: no circuit with id '{circuit_id}'"

@mcp.resource("stim://dem/{circuit_id}")
def resource_dem(circuit_id: str) -> str:
"""Detector Error Model for the circuit session (if applicable)."""
try:
session = _store.get(circuit_id)
dem = session.circuit.detector_error_model(decompose_errors=True)
return str(dem)
except KeyError:
return f"Error: no circuit with id '{circuit_id}'"
except Exception as exc:
return f"Error building DEM: {exc}"

@mcp.resource("stim://stats/{circuit_id}")
def resource_stats(circuit_id: str) -> str:
"""Most recent simulation statistics for the circuit session (JSON)."""
try:
session = _store.get(circuit_id)
if not session.stats:
return json.dumps({"note": "No simulation has been run yet for this circuit."})
return json.dumps(session.stats, indent=2)
except KeyError:
return f"Error: no circuit with id '{circuit_id}'"
Loading
Loading