| name | qiskit-mcp-dev |
| description | Guide development of Qiskit MCP servers. Use when creating tools, resources, tests, or new servers. Helps with async patterns, pytest fixtures, GitHub CI/CD setup, and code quality. Trigger on "add tool", "add resource", "write test", "new server", "FastMCP", "MCP development", or "GitHub workflow". |
| allowed-tools | Read, Grep, Glob, Edit, Write, Bash |
Qiskit MCP Server Development Guide
Instructions
When helping with MCP server development in this repository:
- Read existing code first - Check
server.pyand core modules before adding new functionality - Follow established patterns - Match the style of existing tools and tests
- Include license headers - All new files need the Apache 2.0 header
- Keep tools async - MCP tools use
async def, delegate to core module functions - Mock external services - Never call real IBM Quantum APIs in tests
- Update GitHub CI/CD - New servers require workflow updates (see below)
License Header (Required for all new files)
# This code is part of Qiskit.
#
# (C) Copyright IBM 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
Adding a Tool
Tools go in server.py and delegate to async functions in the core module:
# Example: from qiskit_<name>_mcp_server.<core> import my_function
from qiskit_ibm_runtime_mcp_server.ibm_runtime import my_function
@mcp.tool()
async def my_tool_name(param: str, optional_param: int = 10) -> dict[str, Any]:
"""Clear description of what this tool does.
Args:
param: Description of the parameter
optional_param: Description with default behavior
Returns:
Description of the return structure
"""
return await my_function(param, optional_param)
Key patterns:
- Tool function name ends with
_toolsuffix - Docstring describes the tool for AI assistants
- Return type is
dict[str, Any] - Delegate to core module, don't implement logic here
Error Handling Pattern
All tools return consistent response format:
# Success
return {"status": "success", "data": result, ...}
# Error
return {"status": "error", "message": "Description of what went wrong"}
Adding a Resource
@mcp.resource("protocol://path", mime_type="application/json")
async def get_resource_name() -> dict[str, Any]:
"""Description of the resource."""
return await get_data_from_core_module()
Writing Tests
Tests use pytest with mocked services. See tests/conftest.py for fixtures.
import pytest
from unittest.mock import Mock, patch
@pytest.mark.asyncio
async def test_my_tool(mock_service):
"""Test description."""
with patch(
"qiskit_<name>_mcp_server.<core>.ExternalService",
return_value=mock_service,
):
result = await my_function()
assert result["status"] == "success"
Common fixture patterns in conftest.py (names vary by server):
mock_*_service- Mocked external service (e.g.,mock_runtime_service,mock_http_client)mock_env_vars- Sets environment variables likeQISKIT_IBM_TOKENreset_*- Resets global state between tests (oftenautouse=True)
Code Quality
Run from the server directory:
uv run ruff format src/ tests/ # Format
uv run ruff check src/ tests/ # Lint
uv run mypy src/ # Type check
uv run pytest # Test
Adding a New Server
When creating a new server qiskit-<name>-mcp-server:
- Create server directory with standard structure (see below)
- Add to workspace in root
pyproject.toml:[tool.uv.workspace] members = [..., "qiskit-<name>-mcp-server"] - Create examples/ with LangChain agent demos:
README.md- Setup instructions and usagelangchain_agent.ipynb- Interactive Jupyter tutoriallangchain_agent.py- CLI agent supporting multiple LLM providers
- Update GitHub CI -
.github/workflows/test.yml:- Add to
lintjob (install, ruff, mypy, bandit steps) - Add new
test-<name>job
- Add to
- Update GitHub CD -
.github/workflows/publish-pypi.yml:- Add to
workflow_dispatchoptions - Add
publish-<name>job - Add to
publish-meta-packageneeds array
- Add to
- Update documentation - README.md, AGENTS.md
Server Structure
qiskit-<name>-mcp-server/
├── src/qiskit_<name>_mcp_server/
│ ├── __init__.py # Package init, version
│ ├── server.py # FastMCP server, @mcp.tool() and @mcp.resource()
│ └── <core>.py # Async business logic
├── tests/
│ ├── conftest.py # Fixtures for mocking
│ └── test_*.py # Test files
├── examples/
│ ├── README.md # Example documentation
│ ├── langchain_agent.ipynb # Jupyter notebook tutorial
│ └── langchain_agent.py # CLI agent example
├── pyproject.toml
├── pytest.ini # (optional) pytest configuration
├── .env.example # (optional) Environment variable template
├── LICENSE # Apache 2.0 license (copy from root)
├── README.md
└── run_tests.sh
Quick Reference
| Task | File | Pattern |
|---|---|---|
| Add tool | server.py |
@mcp.tool() decorator |
| Add resource | server.py |
@mcp.resource("uri") decorator |
| Core logic | <core>.py |
Async functions, return dicts |
| Test fixtures | tests/conftest.py |
@pytest.fixture |
| Unit tests | tests/test_*.py |
@pytest.mark.asyncio |
| New server CI | .github/workflows/test.yml |
Add lint steps + test job |
| New server CD | .github/workflows/publish-pypi.yml |
Add publish job |
For comprehensive documentation, read AGENTS.md in the repository root.