marqov-mcp
An MCP server for the Marqov quantum orchestration platform, so Claude Code — or any MCP client, or your own agent — can discover backends, price runs, execute on a free simulator, and inspect jobs directly.
Preview. Tool contract version
1. The tools are stable enough to build against. Browser authorization and stored credentials are supported (see Authentication).You need a Marqov account. The platform is in an invite-only beta. Without an account,
list_backendsandcheck_backend_availabilitystill work; every other tool needs a key from a Marqov team. Full guide: AI Assistants (MCP Server).
What it is, and what it is not
This wraps the Marqov control plane: backends, cost estimates, jobs, traces, budget, playground.
It is not a Qiskit MCP. A model writes Qiskit competently from its own weights, and wrapping circuit construction in a tool call spends context to buy nothing. What a model cannot know from its weights is your live fleet, your prices, your job IDs and your team's budget — which is exactly where tools earn their place.
Authentication
You supply your own Marqov API key. The server holds no user model, no session store and no state; it forwards your key and the platform decides, per request.
Authorise this machine in a browser:
marqov-mcp login # shows a code, opens the consent page, stores the key it is granted
marqov-mcp status --check # confirms the platform accepts it
You get a short code, a team owner or admin approves it on the Marqov site, and
the key is issued to this machine — scoped, capped and revocable. The approver
can grant less than was asked for, so check what login reports at the end.
--level asks for a permission level (default simulate, the lowest that can
run simulations), --spend-limit asks for a daily cap in USD, and
--client-name sets the name shown on the approval screen.
No browser, or no admin to hand? marqov-mcp login --paste still takes a key
you minted yourself.
Or supply it per-process:
export MARQOV_API_KEY="marqey_..." # Team -> Marqov API Keys in the Marqov UI
export MARQOV_BASE_URL="https://app.marqov.ai" # optional
The environment wins if both are present, so an exported key always overrides a
stored one. If MARQOV_API_KEY is not set, the server also reads MARQOV_PLATFORM_KEY,
the variable the marqov Python SDK uses, so one exported key serves both. marqov-mcp logout removes the stored credential.
Only team owners and admins can authorise agent access today. If you are neither, ask one of them to approve the code
loginshows you.
Mint the key with the lowest permission level you need and a daily spend
limit. The key carries its own permission tier (browse / simulate / qpu /
seal), team scope and spend ceiling, all enforced server-side — the server can
do nothing your key cannot.
Scope is your team, not you personally. A key is team-scoped, so list_jobs
will show work submitted by anyone on the team, not only your own. That is
correct behaviour.
list_backends needs no key at all, so you can discover the fleet before you
have an account.
Install
Requires uv.
uvx marqov-mcp login
claude mcp add --scope user marqov \
-e MARQOV_BASE_URL=https://app.marqov.ai \
-- uvx marqov-mcp
Or copy .mcp.json.example to .mcp.json in a project after logging in. The server
uses the stored credential, or MARQOV_API_KEY if explicitly set in its environment.
Desktop clients may not inherit shell variables. Browser login stores the credential where the server can resolve it without putting a literal key into client configuration.
Tools
| Tool | Needs | What it does |
|---|---|---|
list_backends |
— | The fleet: slug, provider, type, qubits, availability |
check_backend_availability |
— | Only what is up, with queue times and pricing |
estimate_cost |
browse |
Price a run without running it |
run_on_simulator |
simulate |
Run a program on a free simulator, get counts. Currently failing on the platform side; use the Local Simulator on the Run page meanwhile |
list_jobs |
browse |
This team's jobs, filterable by status and date |
get_job_status |
browse |
Where a job got to |
get_job_result |
browse |
The full result payload |
get_job_trace |
browse |
Execution trace — the debugging tool |
get_team_usage |
browse |
The team's recorded spend for a month, by provider, backend and day |
save_script |
simulate |
Save source to the team's script library; idempotent on a caller-supplied key |
get_execution_options |
browse |
Where a saved script can run, why not elsewhere, and the team's available balance |
analyse_script |
simulate |
The platform's pre-run analysis of a saved script for one backend: cost, checks, can_run |
submit_managed_run |
simulate |
Submit a saved task/workflow script as a tracked job on a free managed simulator, under a required cap; dry-run by default |
get_team_usage reports spend, not headroom: the usage API does not expose the team's
spending limit or a key's daily cap, and the tool says so rather than inventing a budget.
It also reports pending_settlement_jobs, completed paid jobs whose charge has not settled
and so are not yet in the totals.
get_execution_options does report the team's available balance, because the platform
returns it with the per-script preflight.
Preparing a run (milestone 2). save_script, get_execution_options and
analyse_script take a user to the doorstep of a tracked or paid run with every check the
platform offers. The only submission this server makes is the free managed run described
under milestone 3; paid runs are submitted in the web app or the SDK. analyse_script returns the
analysis_id a paid submission requires, which is single-use and expires after fifteen
minutes; the user submits it with the script_id in the web app or the SDK. Analysing a
saved script persists an analysis row on the platform, which is why it needs simulate.
save_script sends one request with an Idempotency-Key (pass idempotency_key to make
a retry identical) and returns the key with every outcome.
Submitting a tracked run (milestone 3). submit_managed_run is the one tool that can
create a job. It speaks the platform's managed contract (marqov.public-submission/v1),
which admits a run atomically against a mandatory per-job cap, and it is restricted on
purpose:
- Free managed simulators only. Today that is
marqov-sim, the one managed destination the platform also vouches as free of provider cost. Paid hardware stays unreachable from this server until an API key can cancel a job. - A cap is required and bounded twice. The caller must pass
cap_cents, the most the platform may reserve of the team's funding for the job. The platform caps a job at $100; this server refuses anything above 500 cents by default. An operator may raise that withMARQOV_MCP_MAX_CAP_CENTS, never past 2500. "Free of provider cost" does not mean the platform reserves nothing: the cap covers compilation and tasks, and an insufficient balance or spending limit is refused with HTTP 402. - Dry run by default. The default call checks the key's team, the runtimes enabled for
it, the cap against the runtime's range, and the saved source, then returns the exact
request without sending it.
dry_run=falsesends it.MARQOV_MCP_SUBMIT_DRY_RUN=1forces dry run whatever the caller passes. - One key, identical bytes. The platform deduplicates on the UUID
Idempotency-Keyplus the body and returns the original receipt for an identical retry, so the client's bounded retry of a connection error or a 503 is a replay, not a second job. A read timeout is never retried. Every outcome returns the key, and the advice is always to retry with the same key, never a fresh one. - Source is pinned. The submission carries the SHA-256 of the saved script as read during preflight; if the script changes before admission the platform refuses with HTTP 409, and a receipt describing different source is reported as an error that names the admitted job.
Admission means funded and queued, not finished: follow the returned job_id with
get_job_status.
Projects tools (opt-in, default off)
Thirteen more tools read and write a team's Projects (living lab
notebooks) and can seal one into an immutable Capsule. They are not registered unless
you set MARQOV_MCP_ENABLE_PROJECTS=1, because they are a step past this server's
read-and-simulate posture and whether they stay is an open decision. With the flag off
the surface is exactly the thirteen tools above.
| Tool | Needs | What it does |
|---|---|---|
list_projects |
browse |
The team's Projects |
read_project_workspace |
browse |
The Workspace's blocks, paged and trimmed; one block in full by id |
list_scripts |
browse |
Discover saved team scripts with pagination |
read_script |
browse |
Read current source and requirements in pages, with content digests |
create_project_from_job |
simulate |
Seed a new Project from an existing completed job; dry-run by default |
add_markdown_block |
simulate |
Append a markdown block |
add_script_block |
simulate |
Append a block referencing a saved script, optionally associated with the completed job that ran it |
add_result_block |
simulate |
Append a result block pinned to a completed job |
add_image_block |
simulate |
Upload a local PNG/JPEG/GIF/WebP and append it |
add_dated_update |
simulate |
Append a dated update, optionally with a first note |
get_seal_readiness |
browse |
Blockers and warnings a Capsule seal would hit; read-only |
prepare_capsule |
browse |
Check readiness, metadata and version conflicts; return a proposed seal request |
seal_project |
seal |
Seal into an immutable Capsule (dry-run by default, needs certify=true) |
A Project can have several Workspaces. These tools use the one the platform names as primary, never an archived one, and refuse rather than guess if several are active and none is primary. The platform seals one Workspace, so readiness blocks while several are active.
Design rules: writes are append-only (no edit or delete of an existing block) and target the Workspace only, never the Report. Block content is user-authored data and is described to the model as such.
Update notes are new blocks; even the empty markdown block seeded by the platform
is left untouched. Projects writes are sent once. Ordinary appends and seals do not
deduplicate retries. After an ambiguous failure, inspect the Workspace or Capsule versions before
retrying. If a dated update succeeds but its note fails, the error includes the new
update_group_id so the caller can append the note without recreating the update.
The from-job creation route deduplicates on the Idempotency-Key plus the request body.
Its two replay outcomes are named: a key still being processed (HTTP 409) says to wait
and retry with the same key and identical arguments; a key reused with different
arguments (HTTP 422) says to inspect existing state before choosing a new key. Other
conflicts (HTTP 409, such as an existing Capsule version) and rejected writes (HTTP 400)
state that nothing new was created.
All failed tool operations return the standard MCP isError: true alongside an
actionable text explanation, including missing credentials, permission failures,
HTTP errors and transport failures. Readiness checks fail when a dependency cannot
be checked, so unavailable data cannot produce a false readiness result.
Tools also advertise MCP read-only, destructive and idempotency hints. These describe effects for downstream clients; platform authorization remains the enforcement point.
Bounded reads and pagination. list_projects returns next_offset; pass it as
offset to continue until null. read_project_workspace pages blocks with next_offset.
To read a long block, supply block_id, then pass its next_content_offset as
content_offset until null. max_chars for workspace previews is limited to 0–4000.
Each page of a block carries content_sha256 for the whole block; if it changes between
pages, a colleague edited the block mid-read and the pages must not be joined.
get_job_result keeps small results as their original JSON. Large results return a
json-text-page object with content, total_chars, content_sha256 and next_offset.
Concatenate the content pages in order and parse the combined JSON; pass next_offset as
offset until null. max_chars is 1–4000. Offsets count characters, including Unicode.
No available result is an explicit error; inspect get_job_status for the job state.
Every tool response is redacted and capped at 32,000 characters. Oversized responses without dedicated pagination return an explicit truncated preview. Invalid JSON and platform error envelopes never become successful tool results.
Dry run. Every write takes dry_run. A dry run does the reads it needs, records the
exact request it would have sent, and sends nothing. MARQOV_MCP_PROJECTS_DRY_RUN=1
forces this for every write whatever the model passes; MARQOV_MCP_DRY_RUN_LOG=/path.jsonl
appends each record as a JSON line. seal_project defaults to dry run, and a real seal
needs dry_run=false and certify=true, because the certification is a statement
about accuracy that belongs to the user.
The in-memory dry-run history retains the latest 100 records. The optional JSONL log is the durable record.
get_seal_readiness checks the platform's structural requirements and additionally
blocks empty source, unavailable result payloads, empty narrative and placeholder-only
narrative. The seal endpoint remains the authority. As on the platform, having no
result blocks at all is a warning, permitting reference-only Capsules.
Readiness also returns evidence: one entry per script and result block saying what it
pins and whether a seal would carry a submitted-source snapshot (executed_source_sealed)
or the current saved revision. The list is bounded to 60 entries, with evidence_truncated
set when a Project has more. capsule_source_policy states the platform's rule in words,
and workspace_fingerprint digests the blocks a seal would read.
Existing results to Capsule preparation
- Find a completed run with
list_jobs, then inspectget_job_statusandget_job_result. Keep the exact job ID with the observations; a free synchronous simulator run has no job record and cannot become a result-block reference. - Choose an existing Project with
list_projects, or previewcreate_project_from_job. To create the new notebook, repeat withdry_run=falseand the preview'sidempotency_key. This route supports idempotent recovery: retain the key and identical arguments after an uncertain response. The tool verifies that the new Workspace actually contains the expected result block; partial failures preserve the project ID so the caller can recover without creating another notebook. - Read the Workspace. A notebook created from a job returns
result_block_idsand anyscript_ids. Inspect saved code and dependencies withread_script; uselist_scriptsto discover existing library references. Follownext_offsetand checkcontent_sha256stays constant across script pages. - For an existing Project, append the intended result with
add_result_blockand, when verified, the saved-script reference withadd_script_block. Result appends check that the job completed and has a payload; script appends reject empty or deleted source. A library match alone does not establish an execution relationship. When the job ran that exact saved script, pass itsjob_run_idtoadd_script_blockso the platform records the association and seals the submitted-source snapshot. The tool reads the association back from the receipt: a platform that does not record it returns an error naming the appended block as reference-only, so the caller never describes current source as executed code and never appends the block twice. - Append narrative with
add_markdown_blockoradd_dated_update, citing exact job and result-block IDs. Distinguish observations, interpretation, and provenance limitations. Seeded narrative placeholders are preserved; readiness warns that they will appear in the Capsule and blocks preparation until substantive narrative exists. - Call
prepare_capsulewith the intended name, new version, and contributor details. It uses read-only requests, checks metadata and existing versions, and returnsprepared_for_seal, blockers, warnings, and the proposed request. It never records certification or seals.seal_projectwith its default dry-run can record that same request. An ordinary dry run records intent; it does not validate readiness. - Preparation and sealing are tied together by
workspace_fingerprint. Pass it toseal_projectasexpected_workspace_fingerprint: the seal re-reads the Workspace and refuses, sending nothing, if any block was added, reordered or edited since the user reviewed the prepared request. A dry run with the fingerprint reportsworkspace_fingerprint_matcheswithout sealing.
Provenance limit: the platform snapshots the submitted source and its SHA-256 on
every job at submission, and its seal route uses that snapshot for a script block that
is associated with the job. But the public job-status API exposes neither the snapshot,
its hash nor script_id, so this server cannot verify an association itself; it reports
what the platform recorded. A script block with no association is sealed from the
current saved revision, which can change after a run. read_script explicitly returns
the current saved revision, and reference-only script blocks must not be described as
verified execution snapshots. An inline-code job may have no saved script; do not attach
an unrelated library entry to bypass a readiness blocker. Recording an association from
add_script_block or create_project_from_job needs a platform that accepts
job_run_id on script blocks, which the platform does not yet do; on the current
platform both tools report the reference as unassociated.
Actual sealing still requires explicit user instruction and certification plus a
seal-tier key; preparation is an advisory check, not a guarantee of platform acceptance.
Submitting to real hardware is not available through this server. It can show
you QPUs and price them, but not run on them — use the web UI or the marqov SDK.
Develop
uv run --extra dev pytest # offline suite
uv run --extra dev ruff check .
MARQOV_API_KEY=marqey_... uv run --extra dev pytest -m live # real platform
The live suite is opt-in and hits app.marqov.ai. It uses MARQOV_API_KEY if set,
otherwise the credential stored by marqov-mcp login. It is worth running before any
release: both bugs found in this server's first live run — a playground cold start
outrunning the client timeout, and a 404 blaming team scoping for a job that simply
had no trace — were invisible to the mocked tests. Note that it includes one free
simulator execution (run_on_simulator), which creates no job record and costs nothing.
Releasing
Releases are published to PyPI by .github/workflows/publish.yml through PyPI trusted
publishing, so no token lives in the repository. To cut one:
- Set
versioninpyproject.tomland__version__insrc/marqov_mcp/__init__.py, move theCHANGELOG.mdheading from "unreleased" to the date, and merge. - Create a GitHub release tagged
vX.Y.Z(the workflow refuses a tag that does not match the package version). - The workflow runs Ruff and the offline suite, builds, and publishes.
uvx marqov-mcpthen resolves for everyone.
One-time setup is described at the top of the workflow file: a pending trusted publisher
on pypi.org for marqov-mcp pointing at this repository and workflow, and a GitHub
environment named pypi.
Status
Preview. The default surface covers watching, preparing, and free managed runs; see
CHANGELOG.md. Report bugs in Issues
and security problems as described in SECURITY.md.
Licence
Apache-2.0.
Metadata
Release files for marqov-mcp 0.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| marqov_mcp-0.3.2.tar.gz | 176.7 kB | With provenance | Details for marqov_mcp-0.3.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| marqov_mcp-0.3.2-py3-none-any.whl | Python 3 | none | any | Details for marqov_mcp-0.3.2-py3-none-any.whl |
Total release size: 242.8 kB
Release files / marqov_mcp-0.3.2.tar.gz
| Download URL | marqov_mcp-0.3.2.tar.gz |
|---|---|
| Size | 176.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8f4f56f4ede7606eb8df762dc45cfa771143a517f9ce812998f3d6442c83d479
|
|
BLAKE2b-256 checksum How to use checksums |
a563a11e3c6f737e5fd6b500ad59d39ea016b6a3da84af50a50adee30a175f9b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.
Transparency logRelease files / marqov_mcp-0.3.2-py3-none-any.whl
| Download URL | marqov_mcp-0.3.2-py3-none-any.whl |
|---|---|
| Size | 66.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5d96ae7c11834cb4b4ce2c599fd15903f926ea15ab3029b0eb711846f09818c7
|
|
BLAKE2b-256 checksum How to use checksums |
9eb5a0f06f0277931527189c0f7e1e3795e154c297205035ab344a6b3a4df77c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.
Transparency logSecurity for marqov-mcp 0.3.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for marqov_mcp-0.3.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.