diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 73818f8..eb01237 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -6,12 +6,13 @@ on: workflow_dispatch: inputs: package: - description: 'Package to publish (code-assistant, runtime, or both)' + description: 'Package to publish' required: true - default: 'both' + default: 'all' type: choice options: - - both + - all + - meta-package - code-assistant - runtime @@ -19,7 +20,7 @@ jobs: publish-code-assistant: if: | (github.event_name == 'release' && contains(github.ref, 'code-assistant')) || - (github.event_name == 'workflow_dispatch' && (github.event.inputs.package == 'both' || github.event.inputs.package == 'code-assistant')) + (github.event_name == 'workflow_dispatch' && (github.event.inputs.package == 'all' || github.event.inputs.package == 'code-assistant')) runs-on: ubuntu-latest permissions: id-token: write # Required for trusted publishing @@ -50,7 +51,7 @@ jobs: publish-runtime: if: | (github.event_name == 'release' && contains(github.ref, 'runtime')) || - (github.event_name == 'workflow_dispatch' && (github.event.inputs.package == 'both' || github.event.inputs.package == 'runtime')) + (github.event_name == 'workflow_dispatch' && (github.event.inputs.package == 'all' || github.event.inputs.package == 'runtime')) runs-on: ubuntu-latest permissions: id-token: write # Required for trusted publishing @@ -77,3 +78,39 @@ jobs: with: packages-dir: qiskit-ibm-runtime-mcp-server/dist/ verbose: true + + publish-meta-package: + runs-on: ubuntu-latest + # Meta-package should be published after individual servers + needs: [publish-code-assistant, publish-runtime] + # Allow meta-package to run even if individual packages were skipped (already published) + if: | + always() && + (needs.publish-code-assistant.result == 'success' || needs.publish-code-assistant.result == 'skipped') && + (needs.publish-runtime.result == 'success' || needs.publish-runtime.result == 'skipped') && + ((github.event_name == 'release' && contains(github.ref, 'meta')) || + (github.event_name == 'workflow_dispatch' && (github.event.inputs.package == 'all' || github.event.inputs.package == 'meta-package'))) + permissions: + id-token: write # Required for trusted publishing + contents: read + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + version: "latest" + + - name: Set up Python + run: uv python install 3.12 + + - name: Build meta-package + run: | + uv build + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: dist/ + verbose: true diff --git a/PUBLISHING.md b/PUBLISHING.md index 472596e..7a496b9 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -4,10 +4,24 @@ This guide covers how to publish the Qiskit MCP servers to PyPI, both manually a ## Packages -This repository contains two separate PyPI packages: +This repository contains multiple PyPI packages: 1. **qiskit-code-assistant-mcp-server** - MCP server for Qiskit Code Assistant 2. **qiskit-ibm-runtime-mcp-server** - MCP server for IBM Quantum Runtime +3. **qiskit-mcp-servers** - Meta-package that installs all MCP servers + +### Meta-Package + +The `qiskit-mcp-servers` meta-package provides a convenient way to install all servers at once: + +```bash +# Install all MCP servers +pip install qiskit-mcp-servers + +# Or install individual servers via extras +pip install qiskit-mcp-servers[code-assistant] # Only Code Assistant +pip install qiskit-mcp-servers[runtime] # Only Runtime +``` ## Automated Publishing (Recommended) @@ -40,38 +54,94 @@ Alternatively, you can create releases manually through the GitHub web interface 1. Go to PyPI and create the project (if it doesn't exist): - For `qiskit-code-assistant-mcp-server`: https://pypi.org/manage/project/qiskit-code-assistant-mcp-server/settings/publishing/ - For `qiskit-ibm-runtime-mcp-server`: https://pypi.org/manage/project/qiskit-ibm-runtime-mcp-server/settings/publishing/ + - For `qiskit-mcp-servers`: https://pypi.org/manage/project/qiskit-mcp-servers/settings/publishing/ 2. Add a "trusted publisher" with these settings: - - **PyPI Project Name**: `qiskit-code-assistant-mcp-server` (or `qiskit-ibm-runtime-mcp-server`) + - **PyPI Project Name**: `qiskit-code-assistant-mcp-server` (or `qiskit-ibm-runtime-mcp-server` or `qiskit-mcp-servers`) - **Owner**: `AI4quantum` - - **Repository**: `qiskit-mcp-servers` + - **Repository**: `mcp-servers` - **Workflow name**: `publish-pypi.yml` - **Environment name**: (leave blank) ### Publishing via GitHub Releases -The workflow automatically publishes when you create a GitHub release: +The workflow automatically publishes when you create a GitHub release. The tag name determines which package is published. + +#### Tag Naming Convention + +| Tag Pattern | Package Published | +|-------------|-------------------| +| `code-assistant-v*` | qiskit-code-assistant-mcp-server | +| `runtime-v*` | qiskit-ibm-runtime-mcp-server | +| `meta-v*` | qiskit-mcp-servers (meta-package) | + +#### Complete Release Workflow + +Follow these steps to release a package: + +##### Step 1: Update Version + +Edit the version in the appropriate `pyproject.toml`: +- **Code Assistant**: `qiskit-code-assistant-mcp-server/pyproject.toml` +- **Runtime**: `qiskit-ibm-runtime-mcp-server/pyproject.toml` +- **Meta-package**: `pyproject.toml` (root) + +##### Step 2: Commit and Push Changes + +```bash +# Stage and commit the version change +git add -A +git commit -m "Bump qiskit-code-assistant-mcp-server to v0.1.1" + +# Push to main branch +git push origin main +``` + +##### Step 3: Create and Push Tag -#### For Code Assistant Server: ```bash -# Update version in qiskit-code-assistant-mcp-server/pyproject.toml -# Then create and push a tag +# Create an annotated tag git tag -a code-assistant-v0.1.1 -m "Release qiskit-code-assistant-mcp-server v0.1.1" + +# Push the tag to GitHub git push origin code-assistant-v0.1.1 +``` + +##### Step 4: Create GitHub Release + +```bash +# Create the release (this triggers the publish workflow) +gh release create code-assistant-v0.1.1 \ + --title "qiskit-code-assistant-mcp-server v0.1.1" \ + --generate-notes +``` + +Or use `--notes "Your release notes here"` instead of `--generate-notes` for custom notes. + +#### Quick Reference Examples -# Create a GitHub release from this tag -gh release create code-assistant-v0.1.1 --title "qiskit-code-assistant-mcp-server v0.1.1" --notes "Release notes here" +**Code Assistant Server:** +```bash +# After updating version in qiskit-code-assistant-mcp-server/pyproject.toml +git add -A && git commit -m "Bump code-assistant to v0.1.1" && git push origin main +git tag -a code-assistant-v0.1.1 -m "Release v0.1.1" && git push origin code-assistant-v0.1.1 +gh release create code-assistant-v0.1.1 --title "qiskit-code-assistant-mcp-server v0.1.1" --generate-notes ``` -#### For Runtime Server: +**Runtime Server:** ```bash -# Update version in qiskit-ibm-runtime-mcp-server/pyproject.toml -# Then create and push a tag -git tag -a runtime-v0.1.1 -m "Release qiskit-ibm-runtime-mcp-server v0.1.1" -git push origin runtime-v0.1.1 +# After updating version in qiskit-ibm-runtime-mcp-server/pyproject.toml +git add -A && git commit -m "Bump runtime to v0.1.1" && git push origin main +git tag -a runtime-v0.1.1 -m "Release v0.1.1" && git push origin runtime-v0.1.1 +gh release create runtime-v0.1.1 --title "qiskit-ibm-runtime-mcp-server v0.1.1" --generate-notes +``` -# Create a GitHub release from this tag -gh release create runtime-v0.1.1 --title "qiskit-ibm-runtime-mcp-server v0.1.1" --notes "Release notes here" +**Meta-Package:** +```bash +# After updating version in pyproject.toml (root) +git add -A && git commit -m "Bump meta-package to v0.1.1" && git push origin main +git tag -a meta-v0.1.1 -m "Release v0.1.1" && git push origin meta-v0.1.1 +gh release create meta-v0.1.1 --title "qiskit-mcp-servers v0.1.1" --generate-notes ``` ### Manual Workflow Trigger @@ -79,21 +149,24 @@ gh release create runtime-v0.1.1 --title "qiskit-ibm-runtime-mcp-server v0.1.1" You can also trigger publishing manually via GitHub Actions using the CLI: ```bash -# Publish both packages -gh workflow run "Publish to PyPI" -f package=both +# Publish all packages (individual servers + meta-package) +gh workflow run "Publish to PyPI" -f package=all # Publish only code-assistant gh workflow run "Publish to PyPI" -f package=code-assistant # Publish only runtime gh workflow run "Publish to PyPI" -f package=runtime + +# Publish only meta-package +gh workflow run "Publish to PyPI" -f package=meta-package ``` Alternatively, you can trigger via the GitHub web interface: 1. Go to **Actions** → **Publish to PyPI** 2. Click **Run workflow** -3. Select which package to publish: `both`, `code-assistant`, or `runtime` +3. Select which package to publish: `all`, `meta-package`, `code-assistant`, or `runtime` ## Manual Publishing @@ -118,6 +191,7 @@ pip install uv Edit the version in `pyproject.toml`: - **Code Assistant**: `qiskit-code-assistant-mcp-server/pyproject.toml` - **Runtime**: `qiskit-ibm-runtime-mcp-server/pyproject.toml` +- **Meta-package**: `pyproject.toml` (root) #### 2. Build the Package @@ -143,6 +217,15 @@ uv build python -m build ``` +**For Meta-Package:** +```bash +# From repository root +uv build + +# Or with build +python -m build +``` + This creates `.whl` and `.tar.gz` files in the `dist/` directory. #### 3. Verify the Build @@ -188,6 +271,9 @@ pip install qiskit-code-assistant-mcp-server # For Runtime pip install qiskit-ibm-runtime-mcp-server + +# For Meta-Package (installs all servers) +pip install qiskit-mcp-servers ``` ## Version Management @@ -206,6 +292,7 @@ The current version for each package is defined in their respective `pyproject.t - **qiskit-code-assistant-mcp-server**: See [qiskit-code-assistant-mcp-server/pyproject.toml](qiskit-code-assistant-mcp-server/pyproject.toml) (search for `version =`) - **qiskit-ibm-runtime-mcp-server**: See [qiskit-ibm-runtime-mcp-server/pyproject.toml](qiskit-ibm-runtime-mcp-server/pyproject.toml) (search for `version =`) +- **qiskit-mcp-servers**: See [pyproject.toml](pyproject.toml) (search for `version =`) ## Pre-Publication Checklist diff --git a/README.md b/README.md index 2c54400..8eafc36 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,20 @@ Comprehensive interface to IBM Quantum hardware via Qiskit IBM Runtime - **IBM Quantum account** and API token - **Qiskit Code Assistant access** (for code assistant server) -### Installation & Usage +### Installation + +#### Install from PyPI + +```bash +# Install all MCP servers at once +pip install qiskit-mcp-servers + +# Or install individual servers +pip install qiskit-code-assistant-mcp-server +pip install qiskit-ibm-runtime-mcp-server +``` + +#### Install from Source Each server is designed to run independently. Choose the server you need: @@ -55,7 +68,7 @@ cd qiskit-code-assistant-mcp-server uv run qiskit-code-assistant-mcp-server ``` -#### ⚙️ IBM Runtime Server +#### ⚙️ IBM Runtime Server ```bash cd qiskit-ibm-runtime-mcp-server uv run qiskit-ibm-runtime-mcp-server diff --git a/pyproject.toml b/pyproject.toml index 6ed1e8c..b6f4250 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "qiskit-mcp-servers" version = "0.1.0" description = "Model Context Protocol servers for IBM Quantum services and Qiskit" readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.10,<3.15" license = {text = "Apache-2.0"} authors = [ {name = "Quantum+AI Team. IBM Quantum", email = "Quantum.Plus.AI@ibm.com"} @@ -23,6 +23,22 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", ] +# Meta-package dependencies: installs all MCP servers +dependencies = [ + "qiskit-code-assistant-mcp-server>=0.1.0", + "qiskit-ibm-runtime-mcp-server>=0.1.0", +] + +[project.optional-dependencies] +# Install individual servers +code-assistant = ["qiskit-code-assistant-mcp-server>=0.1.0"] +runtime = ["qiskit-ibm-runtime-mcp-server>=0.1.0"] +# Install all servers (same as default dependencies) +all = [ + "qiskit-code-assistant-mcp-server>=0.1.0", + "qiskit-ibm-runtime-mcp-server>=0.1.0", +] + [project.urls] Homepage = "https://github.com/Qiskit/mcp-servers" Repository = "https://github.com/Qiskit/mcp-servers" @@ -33,17 +49,21 @@ Documentation = "https://github.com/Qiskit/mcp-servers#readme" requires = ["hatchling"] build-backend = "hatchling.build" -# This is a workspace root, not an installable package [tool.hatch.build.targets.wheel] -only-include = ["nonexistent_dummy_path"] +packages = ["src/qiskit_mcp_servers"] # Workspace configuration for monorepo [tool.uv.workspace] members = [ "qiskit-code-assistant-mcp-server", - "qiskit-ibm-runtime-mcp-server" + "qiskit-ibm-runtime-mcp-server", ] +# Source configuration for workspace dependencies +[tool.uv.sources] +qiskit-code-assistant-mcp-server = { workspace = true } +qiskit-ibm-runtime-mcp-server = { workspace = true } + # Shared tool configurations [tool.ruff] # Extend the shared ruff.toml configuration diff --git a/src/qiskit_mcp_servers/__init__.py b/src/qiskit_mcp_servers/__init__.py new file mode 100644 index 0000000..3a6cc75 --- /dev/null +++ b/src/qiskit_mcp_servers/__init__.py @@ -0,0 +1,16 @@ +""" +Qiskit MCP Servers - Meta-package for all Qiskit MCP servers. + +This is a meta-package that installs all available Qiskit MCP servers: +- qiskit-code-assistant-mcp-server: MCP server for Qiskit Code Assistant +- qiskit-ibm-runtime-mcp-server: MCP server for IBM Quantum Runtime + +Usage: + pip install qiskit-mcp-servers + +To install individual servers: + pip install qiskit-mcp-servers[code-assistant] + pip install qiskit-mcp-servers[runtime] +""" + +__version__ = "0.1.0" diff --git a/uv.lock b/uv.lock index 1a50b34..6fa98e1 100644 --- a/uv.lock +++ b/uv.lock @@ -1719,7 +1719,7 @@ requires-dist = [ { name = "pytest-cov", marker = "extra == 'test'", specifier = ">=4.1.0" }, { name = "pytest-mock", marker = "extra == 'test'", specifier = ">=3.11.0" }, { name = "python-dotenv", specifier = ">=1.0.0" }, - { name = "qiskit-code-assistant-mcp-server", extras = ["dev", "test"], marker = "extra == 'all'" }, + { name = "qiskit-code-assistant-mcp-server", extras = ["dev", "test"], marker = "extra == 'all'", editable = "qiskit-code-assistant-mcp-server" }, { name = "respx", marker = "extra == 'test'", specifier = ">=0.20.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.11.13" }, ] @@ -1824,7 +1824,7 @@ requires-dist = [ { name = "pytest-mock", marker = "extra == 'test'", specifier = ">=3.11.0" }, { name = "python-dotenv", specifier = ">=1.0.0" }, { name = "qiskit-ibm-runtime", specifier = ">=0.40.0" }, - { name = "qiskit-ibm-runtime-mcp-server", extras = ["dev", "test"], marker = "extra == 'all'" }, + { name = "qiskit-ibm-runtime-mcp-server", extras = ["dev", "test"], marker = "extra == 'all'", editable = "qiskit-ibm-runtime-mcp-server" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.11.13" }, ] provides-extras = ["dev", "test", "all"] @@ -1846,6 +1846,33 @@ test = [ name = "qiskit-mcp-servers" version = "0.1.0" source = { editable = "." } +dependencies = [ + { name = "qiskit-code-assistant-mcp-server" }, + { name = "qiskit-ibm-runtime-mcp-server" }, +] + +[package.optional-dependencies] +all = [ + { name = "qiskit-code-assistant-mcp-server" }, + { name = "qiskit-ibm-runtime-mcp-server" }, +] +code-assistant = [ + { name = "qiskit-code-assistant-mcp-server" }, +] +runtime = [ + { name = "qiskit-ibm-runtime-mcp-server" }, +] + +[package.metadata] +requires-dist = [ + { name = "qiskit-code-assistant-mcp-server", editable = "qiskit-code-assistant-mcp-server" }, + { name = "qiskit-code-assistant-mcp-server", marker = "extra == 'all'", editable = "qiskit-code-assistant-mcp-server" }, + { name = "qiskit-code-assistant-mcp-server", marker = "extra == 'code-assistant'", editable = "qiskit-code-assistant-mcp-server" }, + { name = "qiskit-ibm-runtime-mcp-server", editable = "qiskit-ibm-runtime-mcp-server" }, + { name = "qiskit-ibm-runtime-mcp-server", marker = "extra == 'all'", editable = "qiskit-ibm-runtime-mcp-server" }, + { name = "qiskit-ibm-runtime-mcp-server", marker = "extra == 'runtime'", editable = "qiskit-ibm-runtime-mcp-server" }, +] +provides-extras = ["code-assistant", "runtime", "all"] [package.dev-dependencies] dev = [