Metadata-Version: 2.5
Name: softqcos-mcp
Version: 3.13.1
Summary: QCOS MCP Server — typed quantum computing tools for AI agents via Model Context Protocol
Author-email: SoftQuantus innovative OÜ <support@softquantus.com>
Maintainer-email: SoftQuantus innovative OÜ <support@softquantus.com>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: mcp<2.0.0,>=1.7.0
Requires-Dist: softqcos-sdk<4,>=3.13.1
Requires-Dist: starlette>=0.37
Requires-Dist: uvicorn>=0.30
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Description-Content-Type: text/markdown

# SoftQCOS MCP

[![PyPI version](https://img.shields.io/pypi/v/softqcos-mcp.svg)](https://pypi.org/project/softqcos-mcp/)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)

A [Model Context Protocol](https://modelcontextprotocol.io) server that gives
an AI agent typed tools over the QCOS REST API at `https://api.softquantus.com`.
Each tool is one call to that API, made with the caller's own API key. The
server has no privileges of its own and runs nothing the API does not serve.

The MCP server, the Python SDK (`softqcos-sdk`) and the CLI (`softqcos`) are
released together and always carry the same version number. The table at the
end of this page lists which tools each version added.

## Which MCP server

There are two MCP servers in the SoftQuantus family, and an agent host should
connect to the one that owns what it needs:

| Server | Owns | Package / endpoint |
| --- | --- | --- |
| **SoftQCOS MCP** (this one) | Quantum execution over the QCOS REST API: backends, estimates, `/api/v1/runtime` jobs, artifacts, evidence, calibration, pulse, chemistry, QEC, science runtime, hybrid workflows (experimental) | `pip install softqcos-mcp`, run it yourself (a hosted endpoint is planned, not yet public) |
| **SynapseX Labs MCP** | Workspaces, experiments, datasets, reports and the collaboration layer around them | published with the SynapseX platform |

The SynapseX MCP Router federates both, so a host that connects to the router
sees one tool list. Connect directly to this server when the agent only needs
to run and inspect quantum jobs with its own QCOS API key. Do not connect both
this server and the router to the same host: the tools would appear twice.

Every tool here is a thin call to `https://api.softquantus.com`; the SDK
(`softqcos-sdk`) and the CLI (`softqcos`) reach the same routes, so a job
submitted through one can be read through any of the others with the same key.

## Install

```bash
pip install softqcos-mcp
```

Python 3.11 or newer. The package installs one command, `softqcos-mcp`, which
starts the server. It installs `softqcos-sdk` as a dependency.

## Run it

```bash
softqcos-mcp
```

The server speaks **Streamable HTTP** (stateless) and listens on
`http://127.0.0.1:8100/mcp`, this machine only. Stop it with Ctrl-C.
`softqcos-mcp --help` and `softqcos-mcp --version` print and exit without
starting a server.

Check that it is up:

```bash
curl -s http://127.0.0.1:8100/health
```

It answers `{"status":"ok","service":"softqcos-mcp"}`.

For a client that starts its MCP servers itself, over stdin/stdout, run it as
`softqcos-mcp --transport stdio` (see Claude Desktop below). Over stdio there is
no header to carry a key, so the tools act for the key in `QCOS_API_KEY`.

| Flag | Environment variable | Default | Meaning |
|---|---|---|---|
| `--transport` | | `http` | `http` (Streamable HTTP on `/mcp`) or `stdio` |
| `--host` | `SOFTQCOS_MCP_HOST` | `127.0.0.1` | HTTP address to bind |
| `--port` | `SOFTQCOS_MCP_PORT` | `8100` | HTTP port to bind |
| | `QCOS_API_KEY` | none | your key; over stdio every tool uses it |
| | `QCOS_API_URL` | `https://api.softquantus.com` | the QCOS API the tools call |
| | `QCOS_LOG_LEVEL` | `info` | server log level |
| | `QCOS_ENV` | `development` | `production` requires a bearer token on `/mcp`, see below |
| | `QCOS_MCP_BEARER_TOKEN` | none | the token `/mcp` demands when `QCOS_ENV=production` |

A flag wins over its environment variable. Binding anything other than
loopback (`--host 0.0.0.0`) exposes `/mcp` to the network; read "Running it for
others" first.

## Your API key

Create an API key at <https://platform.softquantus.com/#apikeys>. The MCP
client sends it on every request in the `x-api-key` header (`x-qcos-api-key`
is accepted too). The server forwards it to the API for that call and keeps
nothing.

Identity comes from the key alone. `x-tenant-id`, `x-project-id` and
`x-user-id` headers are ignored: anyone could write any value in them.

## Connect a client

Claude Code:

```bash
claude mcp add --transport http softqcos http://127.0.0.1:8100/mcp --header "x-api-key: $QCOS_API_KEY"
```

Cursor (`.cursor/mcp.json`) and other clients that take a URL and headers:

```json
{
  "mcpServers": {
    "softqcos": {
      "url": "http://127.0.0.1:8100/mcp",
      "headers": { "x-api-key": "<your key>" }
    }
  }
}
```

VS Code (`.vscode/mcp.json`):

```json
{
  "servers": {
    "softqcos": {
      "type": "http",
      "url": "http://127.0.0.1:8100/mcp",
      "headers": { "x-api-key": "${input:qcos-api-key}" }
    }
  },
  "inputs": [
    { "id": "qcos-api-key", "type": "promptString", "description": "QCOS API key", "password": true }
  ]
}
```

Claude Desktop (`claude_desktop_config.json`) starts the server itself, over
stdio:

```json
{
  "mcpServers": {
    "softqcos": {
      "command": "softqcos-mcp",
      "args": ["--transport", "stdio"],
      "env": { "QCOS_API_KEY": "<your key>" }
    }
  }
}
```

If Claude Desktop cannot find `softqcos-mcp`, put the full path that
`which softqcos-mcp` prints in `command`.

From Python, with the `mcp` package this server already depends on:

```python
import asyncio
import os

from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client


async def main() -> None:
    headers = {"x-api-key": os.environ["QCOS_API_KEY"]}
    async with streamablehttp_client("http://127.0.0.1:8100/mcp", headers=headers) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = (await session.list_tools()).tools
            print(len(tools), "tools")
            result = await session.call_tool("qcos_get_v1_runtime_capabilities", {})
            print(result.structuredContent)


asyncio.run(main())
```

## What each tool needs

Every tool belongs to one class, and the class decides what the caller must
present. A tool refused for a missing requirement returns an error result that
names the reason; it never runs halfway.

| Class | What it does | Caller key | Entitlement |
|---|---|---|---|
| public | catalogues, backend lists, estimates, health | not needed | not needed |
| tenant | reads, cancels and releases your own jobs, evidence, sessions | required | not needed |
| compute | uses QCOS compute without naming a device | required | required |
| device | sends work to a named backend (`backend_id`) | required | required, for that backend |

Public examples: `qcos_get_v1_runtime_capabilities`, `qcos_get_v1_backends`,
`qcos_post_v1_estimate`, `qcos_qec_codes_catalog`. Tenant examples:
`qcos_get_v1_jobs`, `qcos_get_v1_jobs_job_id_result`,
`qcos_get_v1_runtime_jobs_job_id_execution_evidence`. Device example:
`qcos_post_v1_jobs`.

Tenant tools stay available when an entitlement has run out, so a job can
always be read and cancelled.

"Not needed" is this server's own check: it forwards a public call without
asking for a key. The API can still ask for one, and `api.softquantus.com`
does for most public tools (`qcos_get_v1_runtime_capabilities` needs a key with
`qcos:read`), so send your key on every call. A public call that arrives
without `x-api-key` goes out under the server process's own `QCOS_API_KEY` when
one is set, and with no key otherwise.

**Over stdio, every class runs under your key.** `softqcos-mcp --transport
stdio` is your own client, acting for the `QCOS_API_KEY` in your environment,
so compute and device tools go straight to the API. The API checks your key,
scopes and plan on every call, as it does for the SDK and the CLI. A key is
still required, and a device tool still has to name its `backend_id`.

**Over HTTP, compute and device tools also ask the entitlement gate.** That
gate is part of the QCOS server, not of this package. The hosted gateway has
it; an HTTP server started from `pip install softqcos-mcp` cannot load it, so
it refuses those tools with `entitlement_gate_unavailable` instead of running
them unchecked. Public and tenant tools work over both transports. From 3.9.0
to 3.11.0, stdio refused compute and device tools the same way; 3.11.1 lifts
that refusal.

## Running it for others

`QCOS_ENV=production` makes `/mcp` demand
`Authorization: Bearer <QCOS_MCP_BEARER_TOKEN>` on every request, and the
server refuses to start without that token. The bearer token says which
gateway is calling; it is shared by every caller and identifies no customer,
so each request still needs the customer's own `x-api-key`. `/health` is
always open.

Do not set `QCOS_API_KEY` on a server other people call: their public calls
without a key of their own would run under it.

Put the server behind TLS before it leaves the machine. API keys travel in a
header, and plain HTTP shows them to anyone on the path.

## What each version added

Generated from the code at release time, so it cannot drift from what this
version runs. Tool names as a client sees them.

<!-- BEGIN release-surface: generated by scripts/gen_client_surface.py; edit the script, not this block -->

Package version on this source tree: **3.13.1**. `softqcos-sdk`, `softqcos` and
`softqcos-mcp` always carry the same number, so the row a name sits in is the
first version of THIS package that has it. A version is listed once it is tagged;
it reaches PyPI when that release is published, and
`pip index versions softqcos-mcp` shows which versions PyPI has.

| Version | Added |
|---|---|
| 3.13.0 | `qcos_hybrid_capabilities`, `qcos_hybrid_reconcile`, `qcos_hybrid_status`, `qcos_hybrid_submit` |
| 3.12.0 | `qcos_qec_dem_lint`, `qcos_qec_resource_tree` |
| 3.11.0 | `qcos_science_jobspec`, `qcos_science_physics`, `qcos_science_simulation_export`, `qcos_science_simulation_graph_plan`, `qcos_science_simulation_graph_run`, `qcos_science_simulation_plan`, `qcos_science_simulation_run`, `qcos_science_solver`, `qcos_science_solver_resolve`, `qcos_science_solvers`, `qcos_science_surrogate_train` |
| 3.10.0 | `qcos_science_algorithm`, `qcos_science_algorithms`, `qcos_science_capabilities`, `qcos_science_eigensolve`, `qcos_science_evolve`, `qcos_science_optimize`, `qcos_science_plan`, `qcos_science_resource_estimate`, `qcos_science_simulate_quantum_system`, `qcos_science_validate` |
| 3.9.0 | `qcos_delete_v1_hal_jobs_job_id`, `qcos_get_v1_hal_backends`, `qcos_get_v1_hal_backends_name`, `qcos_get_v1_hal_jobs_job_id`, `qcos_get_v1_hal_jobs_job_id_result`, `qcos_post_v1_hal_jobs`, `qcos_quantus_analyze_problem`, `qcos_quantus_build_problem`, `qcos_quantus_compile_problem`, `qcos_quantus_execute`, `qcos_quantus_execution_store`, `qcos_quantus_explain_coefficient`, `qcos_quantus_explain_execution`, `qcos_quantus_get_advantage`, `qcos_quantus_get_evidence`, `qcos_quantus_get_execution`, `qcos_quantus_get_lifecycle`, `qcos_quantus_list_backends`, `qcos_quantus_normalize_data`, `qcos_quantus_routing_rules`, `qcos_quantus_seal_execution`, `qcos_quantus_suitability`, `qcos_quantus_validate_problem`, `qcos_research_apps_get`, `qcos_research_apps_list`, `qcos_research_apps_molecular_vqe_run`, `qcos_research_apps_plan` |
| 3.8.3 | `qcos_get_v1_runtime_evidence`, `qcos_get_v1_runtime_evidence_list`, `qcos_get_v1_runtime_evidence_trust`, `qcos_post_v1_runtime_evidence_verify` |
| 3.8.2 | `qcos_post_v1_runtime_dynamics_analyze`, `qcos_post_v1_runtime_state_analyze` |
| <=3.8.0 | 86 tools, all present in 3.8.0, the first version of the shared numbering |

<!-- END release-surface -->

## License

Copyright © 2024-2026 SoftQuantus innovative OÜ. All Rights Reserved.

QCOS™ is a trademark of SoftQuantus innovative OÜ.
Registry Code: 17048927 | Tallinn, Estonia

- License: https://softquantus.com/legal/license
- EULA: https://softquantus.com/legal/eula
- Support: support@softquantus.com
