
FlagQuantum MCP Server
io.github.FlagQuantum由 GitHub 账号 FlagQuantum 发布v0.3.2更新于 Oct 29, 2026
Build, compile, run and export FlagQuantum circuits locally. No credentials or outbound network.
概览
本地 MCP 服务器,让助手在用户机器上构建、编译、路由、导出并模拟 FlagQuantum 量子线路。
- 可以做什么
- 构建与检查线路:查询门定义、统计门数与深度、查看分层、序列化与反序列化 IR
- 处理参数:识别 ansatz 中的符号参数并将其绑定为数值
- 编译与路由:优化、执行完整编译流程、在直线/环形/网格/自定义拓扑上路由并比较拓扑代价
- 导出与呈现:输出 OpenQASM 2.0/3.0 或 QCIS 文本、导入 OpenQASM 文本、绘制 ASCII 线路图、规划执行与 Lindblad 演化
- 本地运行:状态向量模拟(计数、采样、边缘概率、期望值)、连续时间 Lindblad 演化、针对 Pauli 和哈密顿量的参数训练
- 研究流程:检索与描述本地能力、估算与校验请求、执行请求并校验返回的 Evidence Bundle
- 适用场景
- 适合希望助手在本地起草、检查、优化或模拟量子线路、且不需要云账号或硬件访问的场景,例如比较不同拓扑下的路由开销,或在导出为 OpenQASM 前检查线路。
- 运行要求
- 以本地进程通过 stdio 运行,可从 PyPI 用 uvx 或 pip 安装;会拉取 flagquantum 包,而该包依赖 torch。可通过命令行参数启用可选的 HTTP 传输。无需任何凭据或 API 密钥。可用 FLAGQUANTUM_MCP_MAX_QUBITS、FLAGQUANTUM_MCP_MAX_GATES、FLAGQUANTUM_MCP_MAX_IR_BYTES 等环境变量收紧规模上限。
安装
在 SourceWeft 中
- 打开 控制台中的 FlagQuantum MCP Server,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
FlagQuantum MCP Server
An MCP server that gives any MCP-compatible agent local access to the FlagQuantum SDK: build, compile, route, serialize and plan quantum circuits — and run them locally — with no credentials, no outbound network access and no hardware submission.
"Outbound" is the load-bearing word. Every tool here runs in this process and reaches nothing; the server never calls a provider, reads a token or submits a job. It can also listen, so a gateway that connects to its clients rather than launching them can reach it — see Serving over HTTP. An inbound listener is not egress.
Part of FlagQuantum/mcp-servers.
The mcp-name comment above is not decoration: the MCP Registry reads it from
this README to verify that whoever publishes the registry entry also controls
the PyPI package. Removing it breaks registry publishing.
What it does
Twenty-seven tools over stdio or Streamable HTTP. The research tools use the installed FlagQuantum service contract directly; the MCP layer does not reimplement request decoding, estimation, provider routing or scientific validation.
Build and inspect
Parameters
Compile and route
Export and present
Run
Research capability workflow
Four concrete resources include flagquantum://version,
flagquantum://gate-set, flagquantum://ir-schema, and the installed research
catalog at flagquantum://capabilities/index. Two resource templates expose
exact upstream documents without copying them:
flagquantum://capabilities/{capability_id}/{version} and
flagquantum://schemas/{schema_id}.
Three prompts: build_and_analyze_circuit, compile_for_topology,
export_circuit.
These are recipes for the clients that present them to a person — Claude
Desktop, the MCP Inspector, a host that turns them into slash commands. An
autonomous agent may receive none of them: several recorded sessions could not
enumerate them at all, and reported that they could not tell whether prompts
existed. So nothing an agent has to know lives only here. Every constraint the
prompts state is also in a tool summary, or in the server instructions, or in a
resource — the QCIS refusal and the "say what you assumed" rule were the two
exceptions, and they are in emit_qcis_tool and bind_parameters_tool now.
PROMPT_CONSTRAINTS in tests/test_server_contract.py keeps that true.
Install
This pulls flagquantum, which depends on torch.
Claude Code
Claude Desktop / Cline
MCP Inspector
Serving over HTTP
A client that launches a process — Claude Code, Claude Desktop, the Inspector — uses the default stdio transport and needs nothing below. A client that instead connects to servers, such as a gateway holding several of them, needs the server to listen:
The endpoint is /mcp on that host and port, and it speaks Streamable HTTP.
Binding a non-loopback interface requires --allowed-host. Without it the
server refuses to start rather than listen on every interface with nothing
checking who reaches it:
Passing any --allowed-host or --allowed-origin turns host validation on; a
request carrying an unlisted Host is answered 421 Misdirected Request.
Loopback hosts are always answered, so enabling validation cannot lock out a
local client.
Two things worth knowing if you are wiring this up:
- Configure validation through the settings object, not
http_app( allowed_hosts=...). That keyword appends to a list which is only consulted once validation is on, so on its own it reads as a security setting and enforces nothing. Measured on fastmcp 3.4.7: a bogusHostis answered200with the keyword and421through the settings path. --transport httpadds an inbound socket, not an outbound one. No tool gains a network path; rule 4 in AGENTS.md is unchanged.
Circuit formats
Two input formats are accepted, both of them FlagQuantum's own serialization.
ir (canonical) — FlagQuantum IR JSON, as produced by
CircuitIR.to_json(). Versioned, hashable, and rejected if it carries unknown
fields:
qir (convenience) — the compact gate list from Circuit.to_qir(), easier
to write by hand:
A gate carries its arguments under parameters, which the example above
never shows because neither gate takes one:
The two formats use different key names, and this is the most common
mistake: the gate list calls a gate name and its wires index, while serialized
IR calls them opcode and wires. Sending IR keys as qir is rejected with a
message that says so by name. An empty gate list is also rejected, because the
wire count is inferred from the highest index — an empty list describes no
circuit. A bare integer is accepted for a single-wire gate
("index": 0 means "index": [0]).
Either format can be passed to any tool; serialize_circuit_tool converts
qir into canonical ir.
One more naming trap: an IR payload's version field is spelled version,
and sending ir_version instead is rejected as an unknown key — but tool
results report that same field as ir_version. The payload key and the reported
key are not spelled the same way.
circuit_format is a closed set — the JSON schema publishes
"enum": ["ir", "qir"], so a wrong value is rejected before any tool body runs.
OpenQASM text is not a supported input. FlagQuantum ships emitters but no
QASM parser, so there is nothing to convert it with; a caller holding OpenQASM
has to load it into FlagQuantum itself and send the resulting IR.
On parameters
A gate parameter is either a number or a symbol, and a symbol is written as a one-key object naming it:
This works in both input formats. A symbol is what makes a circuit an ansatz:
inspect_parameters_tool reports it, and bind_parameters_tool substitutes a
number for it.
A bare string is not a symbol. {"theta": "theta"} is a value, not a
reference; the SDK stores it as one, so the circuit reports itself as
unparameterized. Nothing else complains: draw_circuit_tool prints the value it
was handed, and a string prints as RY(theta) — indistinguishable from a real
symbol — while plan_execution_tool plans the circuit for execution. So the
input boundary rejects it, along with a misspelled marker ({"$unknown": ...})
and a non-string symbol name, naming the gate and the parameter. The other
encodings a parameter may carry are $expression, $complex and $tensor; a
mapping carrying none of those four keys is rejected for the same reason. The
flagquantum://ir-schema resource carries a worked parameter_example.
One rough edge, inherited from the SDK: a real symbol is rendered as the SDK's
repr of it, so RY(theta) appears as RY(Parameter(name='theta')).
FlagQuantum's Parameter defines no __str__, so str() falls through to
__repr__. The symbol is intact — only the diagram's spelling is clumsy — and
it affects both input formats equally.
Binding is needed to export: the emitters write numbers, and refuse an
unbound circuit naming bind_parameters. Nothing else needs it. analyze,
optimize, route, draw, describe_layers and plan_execution_tool all
accept a parameterized circuit as it stands, because none of them reads a
parameter value — a plan is built from the payload's shape and dtype. Bind when
you want the emitted text or a concrete circuit to run, not before.
On an explicit unitary
A gate's matrix travels under a key that differs by format: the gate list
calls it gate, serialized IR calls it matrix.
Two things are rejected rather than half-honoured.
The wrong key for the format. Circuit.from_qir reads gate and ignores
matrix entirely, so a gate list spelling it the IR way would be built with no
matrix at all — silently. For a built-in name that means a different circuit
than the caller wrote, so the key mismatch is named.
A matrix on a built-in opcode. A matrix belongs to any, the opcode
FlagQuantum reserves for it. On a built-in name the SDK keeps the matrix in the
payload but lets the built-in's own definition win, which splits the tools:
analyze_circuit_tool reports the gate under the built-in's name while the
emitters refuse to lower it. Name the gate any instead.
On a wire number
Wire numbers must be integers in both formats, and the wires list must be a
list. Both are worth stating because the SDK accepts more: it iterates whatever
it is handed and calls int() on each element, so "1", true, 1.7 and
0.9 are wires, and "01" is wires 0 and 1 while {"0": 1} is wire 0. The last
cases are why this is an error rather than a convenience — a value that means
nothing turns into a circuit that looks fine.
On the envelope's own fields
The same rule, applied to the fields outside instructions. n_wires and the
entries of shape are read with int(), so "2", 2.7 and true are all a
width; dtype is looked up as an attribute on torch, so a value that is not a
string reaches an attribute lookup inside the SDK.
The test for what is refused is whether information is lost, not whether the
JSON type matches the schema exactly. That is why version: 1.0 is accepted —
str(1.0) reproduces it — while n_wires: 2.7 is not: it silently becomes 2,
and n_wires: true silently becomes 1.
dtype is checked for being a string here. Which strings are legal stays with
the SDK, whose own message names the rule (complex_dtype must be complex64 or complex128), so the two cannot drift apart.
On observables and measurements
Both are lists of objects, empty when the circuit has none, and an entry is short — the SDK fills in the rest on load:
What the SDK stores adds the optional keys and lowercases the observable name,
so "ZZ" comes back as "zz" carrying "coefficient": 1.0. coefficient is
the term's weight, and it may be a symbol exactly as a gate angle may
({"$parameter": "w"}), which is how a Hamiltonian term carries a variational
weight. shots is null when omitted.
An observable is what a VQE ansatz exists to measure, so it is worth knowing
that the shape is reachable without being wrong first — flagquantum://ir-schema
publishes both forms side by side as observable_example.
On emitted text
emit_openqasm_tool measures every wire unless result_wires names the ones you
want, so the emitted program normally carries measurements the source circuit
does not — a Bell circuit with no measurements still emits c = measure q;.
Declaring measurements in the source IR changes nothing: the emitted text is
chosen by result_wires alone. The content_hash in the result identifies the
source circuit, not the emitted text. emit_qcis_tool appends nothing.
On imported text
import_openqasm_tool reads the canonical subset FlagQuantum's own emitters
write, and refuses the rest with UNSUPPORTED_FORMAT and the importer's own
message naming the statement and line. It is not a general OpenQASM front end:
text from another toolchain may be refused rather than approximated, and a
program is never silently rewritten to fit.
The round trip is faithful to the text, not to the source circuit. Because
emit_openqasm_tool adds a measurement the source circuit never had, importing
its output yields a circuit that measures. Its content_hash therefore differs
from the emitter's, which describes the source. Import twice and the hash is
stable; import what you emitted and the gate sequence matches, the measurement
is new.
On content_hash
Every payload carries the SDK's content_hash, which is the SHA-256 of the
canonical JSON including the metadata object. An IR payload that omits
metadata is legal — it defaults to {} — but it hashes differently from the
same circuit carrying its runtime metadata. Treat the hash as identifying the
payload, not the gate sequence alone. The flagquantum://ir-schema resource
says the same thing where an agent will read it.
Limits
Every bound is overridable by environment variable, so a deployment can tighten them without a code change:
The last one is the only bound on what this server returns rather than what it
accepts. A result travels into a model's context rather than into memory, and a
sampled result grows with shots — 200,000 draws is legal by every other bound
here and far more than an answer.
Errors
There are two layers, and which one answers depends on whether the schema could describe the mistake.
Schema violations are caught by the MCP layer before any tool body runs and
come back as a protocol error. That covers a circuit_format outside the enum,
a missing required argument, and an argument of the wrong JSON type.
Everything else comes back as a structured envelope, so a caller can branch on the code instead of parsing prose:
Codes: INVALID_INPUT, LIMIT_EXCEEDED, UNSUPPORTED_FORMAT,
SDK_UNAVAILABLE, INTERNAL_ERROR.
Both layers reach the client as an error it can read; only the layer differs.
Nothing escapes as an unhandled exception that would break the transport — a
failed call leaves the session usable for the next one, which
tests/test_server_process.py asserts over a real stdio connection.
What this server deliberately does not do
- No hardware, no credentials, no outbound network. FlagQuantum's own release 0.2.0 ships no remote-submission entry point, and this adapter adds none. The HTTP transport opens a listening socket and changes nothing about what a tool may do once a request arrives.
- No instruction-level
NoiseModelobjects. Those are live SDK objects rather than serializable values. Continuous-time evolution instead accepts closed JSON collapse-operator descriptors with physical rates. - No in-tree coupling. This package must never be imported by the
FlagQuantum repository. That project's long-horizon architecture contract
names "the main repository has no production MCP transport dependency" as a
retirement condition, and its
tests/team/services/test_service_boundaries.pyfails ifmcporfastmcpbecomes importable on the core path. Keeping the gateway out of tree is what that contract asks for.
What it runs, and what that does not prove
plan_evolution_tool and evolve_dynamics_tool use a separate continuous-time
contract. A Hamiltonian and observables are Pauli-term lists, the initial state
is a basis bitstring or dense state, times are explicit and strictly increasing,
and amplitude damping is a physical rate in the Lindblad equation—not a channel
probability. The execution tool returns one population row per requested time,
trace drift, a bounded-population check, and numerical method/order/step-size/
precision provenance. Both tools are deterministic, local CPU operations with
no provider, credentials, hardware, or outbound network access.
simulate_circuit_tool executes circuits in this process: a statevector
simulation on CPU. That is the whole of it — no remote target, no provider, no
token. It was not always here. Until it landed the server planned and never ran,
and the line above used to say so; a real task showed the cost, so the line
moved rather than the tool being smuggled in underneath it.
The result says where it ran, in the SDK's own fields rather than in a summary:
accuracy.metric is not_measured, and plan_execution_tool reports
release_gate_allowed: false for the same circuit. That second field is the
plan's, in its summary — the simulation result above carries execution,
outputs and status, and its accuracy block has five fields (version,
metric, value, tolerance, passed). An ideal simulation of a circuit is a
statement about that circuit, not about any device that would run it — the SDK
declines to license a stronger reading, and this server does not add one.
Three things are refused rather than reinterpreted, each because accepting would produce a plausible-looking answer:
The width where a full probability distribution stops being returnable is the
SDK's own contraction limit, and its message points at a setting this server
does not expose. That refusal keeps the SDK's text and adds what a caller can
actually do — name a few wires, or ask for counts, which report only the
outcomes that occurred.
Measuring an energy
{"kind": "expectation", "pauli": "ZZ"} evaluates one term. A Hamiltonian is a
weighted sum, so it takes terms instead:
Each term comes back as its own row, carrying its coefficient and its
value, because that is what the SDK computes — it evaluates the sum term by term
and does not total it. So ⟨H⟩ is the caller's arithmetic:
That is deliberate rather than convenient. A total computed here would be a
number the SDK never produced and this server could not attribute, and the
per-term rows are what make the result auditable — you can see which term
dominated. coefficient appears only on rows the SDK put one on, which means
the terms of a weighted expectation.
A coefficient must be a real number. A symbol is refused: Z(0) * Parameter
builds a parameter expression rather than an observable, so there is nothing to
evaluate — bind the circuit's gates first instead. A single unweighted term is
still spelled "pauli": "ZZ", and the two spellings are one code path, so they
cannot drift apart.
Lowering an energy
simulate_circuit_tool evaluates an energy. train_parameters_tool improves
one, using the exact gradients a statevector simulation reports:
It returns the loss after each step, the parameters it ended on, and the
parameters it started from. Call it again with values set to the parameters
it returned to continue the run rather than restart it.
The hamiltonian argument is the same term list an expectation output takes,
and it is required: without an objective the number being minimised is not an
energy. One letter per wire — the circuit above is one wire, so each term is
one letter. A two-wire circuit takes "ZZ", and a term whose length does not
match the circuit is refused before anything runs.
Two limits are worth knowing before you call it. Training is far more expensive
than simulating — measured, a 16-qubit step costs 85 ms against a simulation's
few milliseconds — so a run is refused up front when its predicted cost exceeds
FLAGQUANTUM_MCP_MAX_TRAIN_SECONDS, with the prediction, the width and the step
count in the message. The cost that dominates at the top of the width range is
holding the state: the model predicts 25.2 s per step at 24 wires before a single
gate is applied, so no 24-wire circuit gets more than two steps, and the layered
ansatz measured here — 71 instructions — is predicted at 119 s per step and gets
none. That is a statement about the circuit, not the width: one gate at 24 wires
is still under the budget, and the budget refuses by prediction rather than by
width, so a narrow circuit with many layers is bounded for the same reason — a
2500-instruction four-wire circuit is predicted at 0.5 s per step, which the
default budget admits 119 steps at a time where a one-gate circuit at that width
gets three thousand.
And the result is what it is: a loss curve and a set of angles. Whether the run
converged is your reading, not this tool's claim, and the SDK's
accuracy.metric == "not_measured" travels with the execution exactly as it
does for a simulation.
Which contracts this rests on
FlagQuantum publishes a frozen stable_exports snapshot (31 names, each with a
named verification test), describes flagquantum.compiler as its "stable expert
compiler interface", and lets each package declare its own __all__. This
server uses all three tiers, and tests/test_api_contract.py pins the members
of each:
Tier 3 is the weakest, and it is not decoration. Gate validation needs each
gate's wire count and parameter names, and the SDK's operator manifest is the
only authority for that — a caller cannot infer it from the circuit format, and
reimplementing it here would create a second source of truth that drifts. The
manifest is reached through a helper that turns a relocation into a named error
rather than an AttributeError inside a tool call, and every tier-3 name is
pinned by a test so a move upstream fails the build instead of failing a user.
The failures that guard against are quiet ones: a weaker gate check accepts a
misspelled parameter and drops it.
The training tool is the one place where the tiers are not the whole story. Its
objective needs two names that are not frozen — Hamiltonian and
pauli_term, both from flagquantum.algorithms — and one that is:
RuntimePolicy is in the frozen snapshot, but the snapshot fixes only the name.
Whether the Hamiltonian reaches the run is decided by
RuntimePolicy().observable, whose default is "z", so
tests/test_api_contract.py pins that default too rather than trusting the
snapshot to carry it.
The dependency is pinned to flagquantum>=0.3.1,<0.4. It is a version range,
never a git URL: a URL in the dependency table makes every environment that
installs a different upstream revision unresolvable.
Capability coverage decisions
contracts/sdk-capability-coverage-v1.json
records a decision for every production-supported or release-certified
capability in the pinned post-#632 FlagQuantum source manifest. FlagQuantum
0.3.1 is the first published compatible core wheel. A decision either
names this server's bounded tool surface or explains why no tool is exposed.
The one existing drawing tool is recorded separately as a legacy experimental
exception; the ledger does not authorize adding other experimental tools.
The CI checker compares these decisions to the pinned upstream
capability-maturity.toml and to the server's declared tool surface. A new
eligible capability or MCP tool without a decision fails the check.
This ledger is an exposure audit, not a claim that every Python function is
available over MCP. It does not replace installed-package compatibility tests:
the current flagquantum>=0.3.1,<0.4 dependency is verified separately by the
test matrix and clean installed-wheel MCP handshake. A source mapping alone
does not authorize a new tool or prove a future FlagQuantum release works.
To inspect the pinned source locally, check out commit
5c78e1d3b3fad31af5bce9e785ca8a1954229cf7 of FlagQuantum and run:
Development
See the repository README and CONTRIBUTING.md.
License
Apache-2.0.
来源:flagquantum-mcp-server/README.md,提交 ba0e8f7
工具
0版本历史
5- v0.3.2最新Oct 29, 2026
- v0.3.1Oct 16, 2026
- v0.3.0Sep 19, 2026
- v0.2.0Sep 18, 2026
- v0.1.5Sep 18, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分