leona-mcp 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- leona_mcp-0.3.0/.gitignore +10 -0
- leona_mcp-0.3.0/LICENSE +21 -0
- leona_mcp-0.3.0/PKG-INFO +508 -0
- leona_mcp-0.3.0/README.md +495 -0
- leona_mcp-0.3.0/pyproject.toml +61 -0
- leona_mcp-0.3.0/src/leona_mcp/__init__.py +10 -0
- leona_mcp-0.3.0/src/leona_mcp/plan_catalog.json +17 -0
- leona_mcp-0.3.0/src/leona_mcp/server.py +1359 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# This package is published (owner ruling ai-ops#432), and hatchling copies the NEAREST
|
|
2
|
+
# .gitignore into the source distribution and builds with its patterns alone. Without
|
|
3
|
+
# this file that was the repository root's, commentary and all, inside a public sdist.
|
|
4
|
+
# Keep the patterns a build must never ship; leona-client's tests/test_client_distribution.py
|
|
5
|
+
# checks the sdist.
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
*.egg-info/
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
leona_mcp-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eshaan Mistry
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
leona_mcp-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: leona-mcp
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: An MCP server over the public Quantum Atlas at leonaqt.com, and, with a token, verified runs
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Requires-Dist: anyio<5,>=4.15.1
|
|
9
|
+
Requires-Dist: leona-client>=0.3.0
|
|
10
|
+
Requires-Dist: mcp<2,>=1.27
|
|
11
|
+
Requires-Dist: pydantic<3,>=2.11
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# leona-mcp
|
|
15
|
+
|
|
16
|
+
<!-- mcp-name: io.github.Leona-Quantum/leona-mcp -->
|
|
17
|
+
|
|
18
|
+
An MCP server that lets an AI assistant look things up in the
|
|
19
|
+
[Quantum Atlas](https://leonaqt.com/repository), Leona Quantum's public catalog of
|
|
20
|
+
quantum algorithms, gates, states and benchmark circuits — and, with a personal
|
|
21
|
+
access token, start a verified run, read its result, plan an algorithm and cost it from
|
|
22
|
+
its sources, estimate physical resources, call a published Qapp, have a circuit
|
|
23
|
+
checked, and list, read, edit and re-run your own Leona notebooks, all as the token's
|
|
24
|
+
own account.
|
|
25
|
+
|
|
26
|
+
Three tools need nothing at all: they read one public API endpoint, the same
|
|
27
|
+
anonymous catalog listing the website uses. You need no account or key, and they
|
|
28
|
+
cannot spend money. Twelve more need a personal access token (minted on leonaqt.com,
|
|
29
|
+
Account → Access tokens) in the `LEONA_API_TOKEN` environment variable — never as a
|
|
30
|
+
tool argument, and it is never logged or echoed in an error. Without one, those twelve
|
|
31
|
+
tools answer with a message telling you to set it; the three read-only tools are
|
|
32
|
+
unaffected. The server runs on your machine and talks to your MCP client over stdio.
|
|
33
|
+
|
|
34
|
+
## Tools
|
|
35
|
+
|
|
36
|
+
| Tool | Needs a token | What it does |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `search_methods` | No | Filters the Atlas by free text, problem area, a qubit limit, a depth limit, and NISQ or fault-tolerant, using the same rules as the site's method finder. Results are unranked candidates, sorted by slug. |
|
|
39
|
+
| `get_method` | No | One record in full: description, explanation, the cost it states, its speedup class and whether that was checked against the primary paper, its verification tier, the literature it cites, and the OpenQASM 3 of its circuit when it has one. |
|
|
40
|
+
| `list_problem_areas` | No | The problem areas the Atlas uses, with a definition and a record count for each. |
|
|
41
|
+
| `run_verified` | Yes (`run` scope) | Starts a Nala run — the same route the website's Run box calls — as your account, and polls for up to `wait_s` seconds for a verified result. |
|
|
42
|
+
| `get_run` | Yes | Reads one of your own runs by id, including its verification record. |
|
|
43
|
+
| `list_my_runs` | Yes | Lists your own runs, most recent first. |
|
|
44
|
+
| `plan_workflow` | Yes (any scope) | Plans an algorithm the way [leonaqt.com/repository/plan](https://leonaqt.com/repository/plan) does and returns the same numbers: the pipeline of Atlas blocks for a problem (factoring, search, ground-state energy, linear systems, MaxCut, amplitude and phase estimation, Hamiltonian simulation, elliptic-curve keys, differential equations), each stage's alternatives, and the cost lines evaluated from published formulas at your sizes. Every line carries its kind (exact, a bound, leading order, a paper's numerical estimate, a scaling with no constant, ...) and its source, and `summary` quotes each number with both. `estimate_point` is ready to pass to `estimate_resources`. Arithmetic only: nothing is run or stored. |
|
|
45
|
+
| `estimate_resources` | Yes | Turns a logical cost you state into physical qubits and runtime under a named assumption set. |
|
|
46
|
+
| `run_qapp` | Yes (`run` scope) | Calls a published Qapp with input values, through the same route and the same sandboxed execution as the Qapp's own page, and polls for its result. |
|
|
47
|
+
| `check_circuit` | Yes (`run` scope) | Checks a small OpenQASM 3 circuit (the tool description gives the widest per kind, from the contract's own limits) against one property: an output state, a unitary, an ideal measured distribution, or an energy. Returns pass, fail or inconclusive with a diagnosis, a plain `passed` boolean, a one-paragraph `summary`, and its teeth: how many deliberately broken copies of the circuit the check caught. An optional `structure` says how the circuit must be built (gate budgets, gates it may, may not or must use, measurements only at the end), so a circuit that loads its answer with `prepare_state` instead of building it fails. To check a circuit family rather than one circuit, send `circuits` (the circuit built at each of up to 8 sizes) with a reference written with `n`, such as `ghz(n)`: each size is judged with its own teeth, and the check passes only if every size does. Judged by Leona's own code, one circuit at a time (a busy server answers 503; call again in a few seconds); nothing is stored. |
|
|
48
|
+
| `list_notebooks` | Yes | Lists your own notebooks (id, title, current version, latest status, updated), newest first, paginated with `cursor`. |
|
|
49
|
+
| `pull_notebook` | Yes | Reads one notebook version's `.nb.py` SOURCE — jupytext's percent-format authoring text, not compiled `.ipynb` JSON — the form a model edits far more reliably. Omit `version` for the current one. |
|
|
50
|
+
| `push_notebook` | Yes (`run` scope) | Saves edited `.nb.py` source as a new version of an existing notebook — the same route the web editor and `leona-notebooks push --to` use. Runs it in Leona's sandbox by default and waits for a per-cell report; `execute=false` saves a draft with no run. |
|
|
51
|
+
| `notebook_status` | Yes | Reads a notebook's latest version: status, a plain `ok`, and each cell's outcome (including, on error, the exception and a traceback tail). Poll this after `push_notebook`/`run_notebook` if `wait_s` ran out. |
|
|
52
|
+
| `run_notebook` | Yes (`run` scope) | Re-runs a notebook's current version with no edits, and waits for a per-cell report. To run edited source, use `push_notebook` instead. |
|
|
53
|
+
|
|
54
|
+
Everything the first three tools return comes from the published Atlas record, with
|
|
55
|
+
a link back to its page on leonaqt.com. When a record does not carry a field, the
|
|
56
|
+
answer says "not stated in the record" instead of guessing. A record is a claim from
|
|
57
|
+
the sources it cites, not a result this server checked.
|
|
58
|
+
|
|
59
|
+
**A run's `status` can be `"succeeded"` while its verification did not pass.** Read
|
|
60
|
+
`verifier_decision` and `verification_summary` — every run tool's response also
|
|
61
|
+
carries a plain `verified` boolean, true only when `verifier_decision` is `"pass"` —
|
|
62
|
+
before telling anyone a result is verified.
|
|
63
|
+
|
|
64
|
+
**A `check_circuit` pass means "checked against" what `checked_against` names, and
|
|
65
|
+
nothing more.** Quote it that way, never as "verified": the reference can itself be the
|
|
66
|
+
wrong target. Its teeth say whether the check could have failed at all; a check that
|
|
67
|
+
caught none of the broken copies could not tell a broken circuit from yours.
|
|
68
|
+
|
|
69
|
+
**A notebook version's `status` can be `"ready"` while `ok` is `false`.** `ok` comes
|
|
70
|
+
from the stored run report, not from `status`: a version is `ready` the moment
|
|
71
|
+
execution finishes, whether or not a cell raised. Read `ok` and `cells` — never just
|
|
72
|
+
`status` — before telling anyone a notebook ran clean.
|
|
73
|
+
|
|
74
|
+
**Editing a notebook never needs `.ipynb`.** `pull_notebook` returns the `.nb.py`
|
|
75
|
+
percent-format source — a plain-text file of a YAML header, markdown cells and code
|
|
76
|
+
cells under `# %%` markers — and `push_notebook` takes that same text back. Neither
|
|
77
|
+
tool touches compiled `.ipynb` JSON, which is why they exist beside `leona-notebooks
|
|
78
|
+
pull`/`push` rather than duplicating them: a model edits Python far more reliably than
|
|
79
|
+
JSON with escaped newlines inside string literals.
|
|
80
|
+
|
|
81
|
+
## Example: "write a 3-qubit QFT and check it with Leona"
|
|
82
|
+
|
|
83
|
+
A Claude Code user with the server connected and a `run`-scope token set
|
|
84
|
+
(`claude mcp add --transport stdio --env LEONA_API_TOKEN=lq_pat_... leona-atlas -- uvx
|
|
85
|
+
--from "leona-mcp==0.3.0"
|
|
86
|
+
leona-mcp`) asks their agent to write a 3-qubit QFT and check it with Leona. Bitstrings
|
|
87
|
+
and Pauli strings use Qiskit's order, q0 rightmost, and `qft(3)` is Qiskit's QFT
|
|
88
|
+
including its final swaps. The requests and answers below come from a real run:
|
|
89
|
+
`services/api/tests/test_check_circuit_route.py` produces them through the MCP tool,
|
|
90
|
+
`leona_client`, the API route and the check engine, and fails if this README drifts from
|
|
91
|
+
what they answer.
|
|
92
|
+
|
|
93
|
+
<!-- check-circuit-example:start (generated by test_check_circuit_route.py) -->
|
|
94
|
+
|
|
95
|
+
The agent's first attempt leaves out the final swap. It calls:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"tool": "check_circuit",
|
|
100
|
+
"arguments": {
|
|
101
|
+
"qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[2];\ncp(pi/2) q[1], q[2];\ncp(pi/4) q[0], q[2];\nh q[1];\ncp(pi/2) q[0], q[1];\nh q[0];\n",
|
|
102
|
+
"kind": "unitary",
|
|
103
|
+
"reference": "qft(3)"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Leona answers:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"status": "fail",
|
|
113
|
+
"basis": "circuit",
|
|
114
|
+
"checked_against": "Qiskit's QFT on 3 qubits, exact unitary up to global phase",
|
|
115
|
+
"measure": "largest entry of the difference, after removing global phase, 7.07e-01 (needs ≤ 1.00e-06)",
|
|
116
|
+
"detail": "It matches up to a reversal of the qubit order on one side. A QFT written without its final swaps does exactly this.",
|
|
117
|
+
"qubits": 3,
|
|
118
|
+
"subject_fingerprint": "c5fcd4e70621fe09aca5a0421af4d7be5f9e7f4ef3da09d3ae784bcbc4d66880",
|
|
119
|
+
"subject_qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[2];\ncp(pi/2) q[1], q[2];\ncp(pi/4) q[0], q[2];\nh q[1];\ncp(pi/2) q[0], q[1];\nh q[0];\n",
|
|
120
|
+
"teeth": {
|
|
121
|
+
"status": "not_measured",
|
|
122
|
+
"reason": "Broken copies are tried only on a check that passes. This one failed, so there was nothing to test.",
|
|
123
|
+
"mutants": 0,
|
|
124
|
+
"equivalent": 0,
|
|
125
|
+
"caught": 0,
|
|
126
|
+
"survivors": [],
|
|
127
|
+
"could_not_run": 0,
|
|
128
|
+
"shortcut": null
|
|
129
|
+
},
|
|
130
|
+
"per_size": null,
|
|
131
|
+
"counts": null,
|
|
132
|
+
"structure": null,
|
|
133
|
+
"behaviour_status": null,
|
|
134
|
+
"behaviour_detail": "",
|
|
135
|
+
"passed": false,
|
|
136
|
+
"summary": "Checked against Qiskit's QFT on 3 qubits, exact unitary up to global phase: fail (largest entry of the difference, after removing global phase, 7.07e-01 (needs ≤ 1.00e-06)). It matches up to a reversal of the qubit order on one side. A QFT written without its final swaps does exactly this. Whether this check can catch a broken circuit was not measured: Broken copies are tried only on a check that passes. This one failed, so there was nothing to test.",
|
|
137
|
+
"teeth_note": "Whether this check can catch a broken circuit was not measured: Broken copies are tried only on a check that passes. This one failed, so there was nothing to test."
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The agent adds `swap q[0], q[2];` at the end and calls again:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"tool": "check_circuit",
|
|
146
|
+
"arguments": {
|
|
147
|
+
"qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[2];\ncp(pi/2) q[1], q[2];\ncp(pi/4) q[0], q[2];\nh q[1];\ncp(pi/2) q[0], q[1];\nh q[0];\nswap q[0], q[2];\n",
|
|
148
|
+
"kind": "unitary",
|
|
149
|
+
"reference": "qft(3)"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Leona answers:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"status": "pass",
|
|
159
|
+
"basis": "circuit",
|
|
160
|
+
"checked_against": "Qiskit's QFT on 3 qubits, exact unitary up to global phase",
|
|
161
|
+
"measure": "largest entry of the difference, after removing global phase, 1.28e-15 (needs ≤ 1.00e-06)",
|
|
162
|
+
"detail": "",
|
|
163
|
+
"qubits": 3,
|
|
164
|
+
"subject_fingerprint": "077aef414f965daa2894271ba1cdea0a7c6090f70779a8f91a3c94ef0ab53509",
|
|
165
|
+
"subject_qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[2];\ncp(pi/2) q[1], q[2];\ncp(pi/4) q[0], q[2];\nh q[1];\ncp(pi/2) q[0], q[1];\nh q[0];\nswap q[0], q[2];\n",
|
|
166
|
+
"teeth": {
|
|
167
|
+
"status": "measured",
|
|
168
|
+
"reason": "",
|
|
169
|
+
"mutants": 11,
|
|
170
|
+
"equivalent": 0,
|
|
171
|
+
"caught": 11,
|
|
172
|
+
"survivors": [],
|
|
173
|
+
"could_not_run": 0,
|
|
174
|
+
"shortcut": null
|
|
175
|
+
},
|
|
176
|
+
"per_size": null,
|
|
177
|
+
"counts": null,
|
|
178
|
+
"structure": null,
|
|
179
|
+
"behaviour_status": null,
|
|
180
|
+
"behaviour_detail": "",
|
|
181
|
+
"passed": true,
|
|
182
|
+
"summary": "Checked against Qiskit's QFT on 3 qubits, exact unitary up to global phase: pass (largest entry of the difference, after removing global phase, 1.28e-15 (needs ≤ 1.00e-06)). Leona also made 11 deliberately broken copies of the circuit that change its output, and the check caught all 11."
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
<!-- check-circuit-example:end -->
|
|
187
|
+
|
|
188
|
+
## Example: "how big a machine does RSA-2048 need, by Leona's sources?"
|
|
189
|
+
|
|
190
|
+
The same user asks their agent what factoring a 2048-bit RSA modulus costs. Any token
|
|
191
|
+
will do here; planning and estimating are arithmetic. The requests and answers below come
|
|
192
|
+
from a real run: `services/api/tests/test_plan_route.py` makes both calls through the MCP
|
|
193
|
+
tools, `leona_client`, the API routes, the planner port and the estimator, and fails if
|
|
194
|
+
this README drifts from what they answer. The planner's numbers are the ones
|
|
195
|
+
`/repository/plan` shows for the same input: the port is held equal to the TypeScript
|
|
196
|
+
planner on about 900 inputs (`packages/py/planner/tests/test_planner_parity.py`).
|
|
197
|
+
|
|
198
|
+
<!-- plan-workflow-example:start (generated by test_plan_route.py) -->
|
|
199
|
+
|
|
200
|
+
The agent calls:
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"tool": "plan_workflow",
|
|
205
|
+
"arguments": {
|
|
206
|
+
"problem": "factoring",
|
|
207
|
+
"params": {
|
|
208
|
+
"bits": 2048
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Leona answers (the summary, the cost lines, and the point to cost; the full answer also carries every stage, its alternatives and the methods' stated costs, the sources with their quotes, and the published whole-machine figures):
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"summary": "Leona's workflow planner, for Factor an integer (RSA) at bits = 2,048 (given): Recover the period of a periodic function by Period finding in a finite cyclic group (the planner's default). Costs: Logical qubits (Gidney–Ekerå 2019): 6,190 logical qubits [leading-order; Gidney & Ekerå 2019 (arxiv:1905.09749), abstract]; Toffoli gates (Gidney–Ekerå 2019): 2.62e9 Toffoli gates [leading-order; Gidney & Ekerå 2019 (arxiv:1905.09749), abstract]; Measurement depth (Gidney–Ekerå 2019): 2.14e9 measurement layers [leading-order; Gidney & Ekerå 2019 (arxiv:1905.09749), abstract]; Logical qubits (Gidney 2025): 1,399 logical qubits [published; Gidney 2025 (arxiv:2505.15917), Table 5]; Toffoli gates per factoring (Gidney 2025): 6.5e9 Toffoli gates [published; Gidney 2025 (arxiv:2505.15917), Table 5]. Published at this size: Noisy qubits, 8 hours (Gidney–Ekerå 2019): 2e7 physical qubits [published; Gidney & Ekerå 2019 (arxiv:1905.09749), title and abstract]; Noisy qubits, under a week (Gidney 2025): < 1,000,000 physical qubits [published; Gidney 2025 (arxiv:2505.15917), abstract]. Figures here are rounded for reading; the lines carry exact values. For physical qubits and runtime, pass estimate_point to estimate_resources.",
|
|
219
|
+
"lines": [
|
|
220
|
+
{
|
|
221
|
+
"id": "ge2021-qubits",
|
|
222
|
+
"label": "Logical qubits (Gidney–Ekerå 2019)",
|
|
223
|
+
"value": 6190,
|
|
224
|
+
"unit": "logical qubits",
|
|
225
|
+
"kind": "leading-order",
|
|
226
|
+
"source": "ge2021-logical",
|
|
227
|
+
"formula": "3n + 0.002·n·lg n"
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"id": "ge2021-toffolis",
|
|
231
|
+
"label": "Toffoli gates (Gidney–Ekerå 2019)",
|
|
232
|
+
"value": 2624225017.856,
|
|
233
|
+
"unit": "Toffoli gates",
|
|
234
|
+
"kind": "leading-order",
|
|
235
|
+
"source": "ge2021-logical",
|
|
236
|
+
"formula": "0.3n³ + 0.0005·n³·lg n"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"id": "ge2021-depth",
|
|
240
|
+
"label": "Measurement depth (Gidney–Ekerå 2019)",
|
|
241
|
+
"value": 2143289344,
|
|
242
|
+
"unit": "measurement layers",
|
|
243
|
+
"kind": "leading-order",
|
|
244
|
+
"source": "ge2021-logical",
|
|
245
|
+
"formula": "500n² + n²·lg n"
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"id": "g2025-qubits",
|
|
249
|
+
"label": "Logical qubits (Gidney 2025)",
|
|
250
|
+
"value": 1399,
|
|
251
|
+
"unit": "logical qubits",
|
|
252
|
+
"kind": "published",
|
|
253
|
+
"source": "gidney2025-table5",
|
|
254
|
+
"formula": "Table 5, n = 2048"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"id": "g2025-toffolis",
|
|
258
|
+
"label": "Toffoli gates per factoring (Gidney 2025)",
|
|
259
|
+
"value": 6500000000,
|
|
260
|
+
"unit": "Toffoli gates",
|
|
261
|
+
"kind": "published",
|
|
262
|
+
"source": "gidney2025-table5",
|
|
263
|
+
"formula": "Table 5, n = 2048"
|
|
264
|
+
}
|
|
265
|
+
],
|
|
266
|
+
"logical": {
|
|
267
|
+
"logical_qubits": "ge2021-qubits",
|
|
268
|
+
"toffolis": "ge2021-toffolis",
|
|
269
|
+
"t_gates": null,
|
|
270
|
+
"queries": null,
|
|
271
|
+
"serial_depth": "ge2021-depth"
|
|
272
|
+
},
|
|
273
|
+
"estimate_point": {
|
|
274
|
+
"label": "factoring (Leona planner)",
|
|
275
|
+
"parameter_value": 2048,
|
|
276
|
+
"logical_qubits": 6190,
|
|
277
|
+
"toffoli_count": 2624225018,
|
|
278
|
+
"t_count": 0,
|
|
279
|
+
"non_clifford_depth": 2143289344
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The agent passes `estimate_point` on:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"tool": "estimate_resources",
|
|
289
|
+
"arguments": {
|
|
290
|
+
"points": [
|
|
291
|
+
{
|
|
292
|
+
"label": "factoring (Leona planner)",
|
|
293
|
+
"parameter_value": 2048,
|
|
294
|
+
"logical_qubits": 6190,
|
|
295
|
+
"toffoli_count": 2624225018,
|
|
296
|
+
"t_count": 0,
|
|
297
|
+
"non_clifford_depth": 2143289344
|
|
298
|
+
}
|
|
299
|
+
]
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Leona answers (the costed point; the full answer adds the frontier and each assumption set's citation):
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"assumptions": "gidney-2025@v2",
|
|
309
|
+
"points": [
|
|
310
|
+
{
|
|
311
|
+
"label": "factoring (Leona planner)",
|
|
312
|
+
"refused": null,
|
|
313
|
+
"distance": {
|
|
314
|
+
"code_distance": 31,
|
|
315
|
+
"logical_operations": 132690604193744,
|
|
316
|
+
"required_error_per_operation": 7.536328635144969e-17,
|
|
317
|
+
"achieved_error_per_operation": 1.0000000000000008e-17,
|
|
318
|
+
"physical_per_logical": 2048
|
|
319
|
+
},
|
|
320
|
+
"fastest": {
|
|
321
|
+
"footprint": {
|
|
322
|
+
"data_patch_qubits": 12677120,
|
|
323
|
+
"routing_qubits": 12677120,
|
|
324
|
+
"factory_qubits": 417792,
|
|
325
|
+
"total_physical_qubits": 25772032
|
|
326
|
+
},
|
|
327
|
+
"runtime": {
|
|
328
|
+
"magic_states": 20993800144,
|
|
329
|
+
"factory_count": 17,
|
|
330
|
+
"throughput_seconds": 21410.58882332941,
|
|
331
|
+
"reaction_limited_seconds": 21432.893440000003,
|
|
332
|
+
"seconds": 21432.893440000003,
|
|
333
|
+
"binding_term": "reaction",
|
|
334
|
+
"factory_crossover": 17
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
"smallest": {
|
|
338
|
+
"footprint": {
|
|
339
|
+
"data_patch_qubits": 12677120,
|
|
340
|
+
"routing_qubits": 12677120,
|
|
341
|
+
"factory_qubits": 24576,
|
|
342
|
+
"total_physical_qubits": 25378816
|
|
343
|
+
},
|
|
344
|
+
"runtime": {
|
|
345
|
+
"magic_states": 20993800144,
|
|
346
|
+
"factory_count": 1,
|
|
347
|
+
"throughput_seconds": 363980.0099965999,
|
|
348
|
+
"reaction_limited_seconds": 21432.893440000003,
|
|
349
|
+
"seconds": 363980.0099965999,
|
|
350
|
+
"binding_term": "throughput",
|
|
351
|
+
"factory_crossover": 17
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
]
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
<!-- plan-workflow-example:end -->
|
|
360
|
+
|
|
361
|
+
No tool here submits a hardware job. Submitting to hardware needs a token with the
|
|
362
|
+
separate `hardware` scope (ai-ops 376), which the `run` scope does not include, and even
|
|
363
|
+
with that scope this server has no tool that submits.
|
|
364
|
+
|
|
365
|
+
## Example: "pull my coin-flip notebook, fix the bug, and run it"
|
|
366
|
+
|
|
367
|
+
Connect the server with a `run`-scope token (below), then just ask, in your own words:
|
|
368
|
+
a Claude Code user with the server connected asks their agent to fix a bug in one of
|
|
369
|
+
their notebooks. The agent finds it (`list_notebooks`), reads its source
|
|
370
|
+
(`pull_notebook`, `version: null` — the current version), edits the `.nb.py` text
|
|
371
|
+
directly, and saves the fix as a new version, run and reported in one call
|
|
372
|
+
(`push_notebook`):
|
|
373
|
+
|
|
374
|
+
```json
|
|
375
|
+
{"tool": "list_notebooks", "arguments": {"limit": 5}}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
```json
|
|
379
|
+
{"tool": "pull_notebook", "arguments": {"notebook_id": "01996f3a-...-b21c"}}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
```json
|
|
383
|
+
{
|
|
384
|
+
"tool": "push_notebook",
|
|
385
|
+
"arguments": {
|
|
386
|
+
"notebook_id": "01996f3a-...-b21c",
|
|
387
|
+
"source": "# ---\n# title: Coin flip\n# kind: lesson\n# ---\n\n# %% role=run\nfrom qiskit import QuantumCircuit\nqc = QuantumCircuit(1, 1)\nqc.h(0)\nqc.measure(0, 0)\n",
|
|
388
|
+
"message": "measure the qubit before reading it out"
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
`push_notebook` waits for the run and answers with the per-cell report directly — no
|
|
394
|
+
separate poll needed unless `wait_s` runs out, in which case `notebook_status` reads
|
|
395
|
+
the same shape once it finishes.
|
|
396
|
+
|
|
397
|
+
## Install
|
|
398
|
+
|
|
399
|
+
Requires Python 3.12 or later and [uv](https://docs.astral.sh/uv/). `uvx` fetches
|
|
400
|
+
the supported 0.3.0 server and its public dependencies from PyPI; Git access is not
|
|
401
|
+
required.
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
uvx --from "leona-mcp==0.3.0" leona-mcp --version
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
For a regular Python environment, use `python -m pip install "leona-mcp==0.3.0"`,
|
|
408
|
+
then run `leona-mcp`. The server depends on the public `leona-client` package;
|
|
409
|
+
Leona's check engine, contracts, sandbox and planner stay on the service.
|
|
410
|
+
|
|
411
|
+
## Connect a client
|
|
412
|
+
|
|
413
|
+
The stdio examples below launch the server directly. Atlas tools need no token;
|
|
414
|
+
acting tools need `LEONA_API_TOKEN` with the scope noted above.
|
|
415
|
+
|
|
416
|
+
The examples name the server `leona-atlas`. Any name works.
|
|
417
|
+
|
|
418
|
+
### Claude Code
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
claude mcp add --transport stdio leona-atlas -- \
|
|
422
|
+
uvx --from "leona-mcp==0.3.0" leona-mcp
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Options go before the name and everything after `--` is the command. Add
|
|
426
|
+
`--scope user` to make it available in every project, or `--scope project` to write it
|
|
427
|
+
to the project's `.mcp.json` for everyone who clones it. The default scope is `local`.
|
|
428
|
+
|
|
429
|
+
### Cursor
|
|
430
|
+
|
|
431
|
+
Add this to `~/.cursor/mcp.json`, or to `.cursor/mcp.json` in a project:
|
|
432
|
+
|
|
433
|
+
```json
|
|
434
|
+
{
|
|
435
|
+
"mcpServers": {
|
|
436
|
+
"leona-atlas": {
|
|
437
|
+
"type": "stdio",
|
|
438
|
+
"command": "uvx",
|
|
439
|
+
"args": [
|
|
440
|
+
"--from",
|
|
441
|
+
"leona-mcp==0.3.0",
|
|
442
|
+
"leona-mcp"
|
|
443
|
+
]
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Claude Desktop
|
|
450
|
+
|
|
451
|
+
Open the config file (on macOS
|
|
452
|
+
`~/Library/Application Support/Claude/claude_desktop_config.json`, on Windows
|
|
453
|
+
`%APPDATA%\Claude\claude_desktop_config.json`), add the server, and restart the app:
|
|
454
|
+
|
|
455
|
+
```json
|
|
456
|
+
{
|
|
457
|
+
"mcpServers": {
|
|
458
|
+
"leona-atlas": {
|
|
459
|
+
"command": "uvx",
|
|
460
|
+
"args": [
|
|
461
|
+
"--from",
|
|
462
|
+
"leona-mcp==0.3.0",
|
|
463
|
+
"leona-mcp"
|
|
464
|
+
]
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Desktop apps often start without your shell's `PATH`. If the server does not start,
|
|
471
|
+
replace `"uvx"` with the full path that `which uvx` prints (for example
|
|
472
|
+
`/Users/you/.local/bin/uvx`).
|
|
473
|
+
|
|
474
|
+
## Settings
|
|
475
|
+
|
|
476
|
+
| Variable | Default | Meaning |
|
|
477
|
+
|---|---|---|
|
|
478
|
+
| `LEONA_API_URL` | `https://majorana-api-nikekeixtq-uw.a.run.app` | The Leona API to read. Point it at `http://localhost:8000` to read a local API. |
|
|
479
|
+
| `LEONA_API_TOKEN` | unset | A personal access token, for `run_verified`/`get_run`/`list_my_runs`/`plan_workflow`/`estimate_resources`/`run_qapp`/`check_circuit`/`list_notebooks`/`pull_notebook`/`push_notebook`/`notebook_status`/`run_notebook`. Leave unset to use only the three read-only Atlas tools. |
|
|
480
|
+
|
|
481
|
+
In Claude Code, pass either with `--env NAME=value` before the server name. In the
|
|
482
|
+
JSON configs, add `"env": {"LEONA_API_URL": "...", "LEONA_API_TOKEN": "..."}` beside
|
|
483
|
+
`args`. Personal access tokens are a Leona feature still being rolled out
|
|
484
|
+
(proposal 7 Phase B); if `run_verified` answers that tokens are not available yet,
|
|
485
|
+
that is the account-wide switch, not this server.
|
|
486
|
+
|
|
487
|
+
The server reads the whole Atlas once (a few megabytes, in pages of 100) and reuses it
|
|
488
|
+
for ten minutes. HTTP calls time out after 30 seconds. If the listing it gets back is
|
|
489
|
+
incomplete or changes while it is being read, the tool call fails with a message
|
|
490
|
+
saying so, rather than answering from part of the Atlas.
|
|
491
|
+
|
|
492
|
+
## Develop
|
|
493
|
+
|
|
494
|
+
From the repository root:
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
uv sync --all-packages
|
|
498
|
+
uv run pytest packages/py/mcp -q
|
|
499
|
+
uv run leona-mcp # speaks MCP on stdin and stdout; Ctrl-C to stop
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
The Atlas filtering rules and the HTTP client live in `leona_client`
|
|
503
|
+
(`packages/py/client`), shared with the `%nala` Jupyter magic and the
|
|
504
|
+
`leona-notebooks` CLI — this package now holds only the MCP protocol layer. The
|
|
505
|
+
filtering rules are themselves copies of TypeScript in `apps/web/lib/repository/`,
|
|
506
|
+
which is the source of truth; `packages/py/client/tests/test_mirrors.py` fails when a
|
|
507
|
+
copied vocabulary stops matching it. See `AGENTS.md` in this directory, and in
|
|
508
|
+
`packages/py/client`, before changing a rule.
|