mcp2mcpb 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. mcp2mcpb-0.1.0/.github/workflows/build-mcpb.yml +162 -0
  2. mcp2mcpb-0.1.0/.github/workflows/build-upstream.yml +137 -0
  3. mcp2mcpb-0.1.0/.github/workflows/check-upstream.yml +62 -0
  4. mcp2mcpb-0.1.0/.github/workflows/cicd.yml +295 -0
  5. mcp2mcpb-0.1.0/.gitignore +42 -0
  6. mcp2mcpb-0.1.0/.mcp.json +35 -0
  7. mcp2mcpb-0.1.0/.serena/.gitignore +2 -0
  8. mcp2mcpb-0.1.0/.serena/project.yml +135 -0
  9. mcp2mcpb-0.1.0/CHANGELOG.md +57 -0
  10. mcp2mcpb-0.1.0/CLAUDE.md +64 -0
  11. mcp2mcpb-0.1.0/LICENSE +21 -0
  12. mcp2mcpb-0.1.0/PKG-INFO +553 -0
  13. mcp2mcpb-0.1.0/README.md +520 -0
  14. mcp2mcpb-0.1.0/action.yml +182 -0
  15. mcp2mcpb-0.1.0/assets/claude-desktop-extensions.png +0 -0
  16. mcp2mcpb-0.1.0/assets/claude-desktop-install.png +0 -0
  17. mcp2mcpb-0.1.0/pyproject.toml +146 -0
  18. mcp2mcpb-0.1.0/scripts/check_version.py +82 -0
  19. mcp2mcpb-0.1.0/src/mcp2mcpb/__init__.py +6 -0
  20. mcp2mcpb-0.1.0/src/mcp2mcpb/__main__.py +486 -0
  21. mcp2mcpb-0.1.0/src/mcp2mcpb/bundler.py +177 -0
  22. mcp2mcpb-0.1.0/src/mcp2mcpb/exceptions.py +32 -0
  23. mcp2mcpb-0.1.0/src/mcp2mcpb/fetcher.py +252 -0
  24. mcp2mcpb-0.1.0/src/mcp2mcpb/generator.py +180 -0
  25. mcp2mcpb-0.1.0/src/mcp2mcpb/inspector.py +332 -0
  26. mcp2mcpb-0.1.0/src/mcp2mcpb/launch.py +145 -0
  27. mcp2mcpb-0.1.0/src/mcp2mcpb/licensing.py +67 -0
  28. mcp2mcpb-0.1.0/src/mcp2mcpb/models.py +241 -0
  29. mcp2mcpb-0.1.0/src/mcp2mcpb/packer.py +81 -0
  30. mcp2mcpb-0.1.0/src/mcp2mcpb/prober.py +103 -0
  31. mcp2mcpb-0.1.0/src/mcp2mcpb/py.typed +0 -0
  32. mcp2mcpb-0.1.0/src/mcp2mcpb/sandbox.py +351 -0
  33. mcp2mcpb-0.1.0/src/mcp2mcpb/ui.py +66 -0
  34. mcp2mcpb-0.1.0/tests/conftest.py +180 -0
  35. mcp2mcpb-0.1.0/tests/factories.py +151 -0
  36. mcp2mcpb-0.1.0/tests/test_bundler.py +350 -0
  37. mcp2mcpb-0.1.0/tests/test_cli.py +80 -0
  38. mcp2mcpb-0.1.0/tests/test_cli_more.py +374 -0
  39. mcp2mcpb-0.1.0/tests/test_conversion.py +257 -0
  40. mcp2mcpb-0.1.0/tests/test_coverage_small.py +250 -0
  41. mcp2mcpb-0.1.0/tests/test_fetcher.py +208 -0
  42. mcp2mcpb-0.1.0/tests/test_generator.py +406 -0
  43. mcp2mcpb-0.1.0/tests/test_inspector.py +147 -0
  44. mcp2mcpb-0.1.0/tests/test_launch.py +160 -0
  45. mcp2mcpb-0.1.0/tests/test_licensing.py +83 -0
  46. mcp2mcpb-0.1.0/tests/test_models.py +127 -0
  47. mcp2mcpb-0.1.0/tests/test_packer.py +103 -0
  48. mcp2mcpb-0.1.0/tests/test_prober.py +93 -0
  49. mcp2mcpb-0.1.0/tests/test_sandbox.py +208 -0
  50. mcp2mcpb-0.1.0/tests/test_sandbox_internals.py +402 -0
  51. mcp2mcpb-0.1.0/uv.lock +611 -0
  52. mcp2mcpb-0.1.0/versions.json +1 -0
@@ -0,0 +1,162 @@
1
+ name: Build .mcpb bundle (reusable)
2
+
3
+ # Reusable workflow for MCP server authors. Add one job to your release
4
+ # pipeline:
5
+ #
6
+ # jobs:
7
+ # bundle:
8
+ # uses: Anselmoo/mcp2mcpb/.github/workflows/build-mcpb.yml@v1
9
+ # with:
10
+ # package: my-mcp-server
11
+ # registry: pypi
12
+ # mode: complete
13
+ #
14
+ # Self-contained: it installs the published `mcp2mcpb` package from PyPI
15
+ # and runs the CLI directly (it does NOT depend on the composite action tag).
16
+ # Launch recipe (runner/entry-script/extras/subcommand/transport) is optional โ€”
17
+ # omit it and the converter auto-detects from the package (and any in-package
18
+ # [tool.mcpb]/mcpb.toml). On a `release` event the bundle is attached to the
19
+ # release automatically.
20
+
21
+ on:
22
+ workflow_call:
23
+ inputs:
24
+ package:
25
+ description: Package name on PyPI or npm.
26
+ required: true
27
+ type: string
28
+ registry:
29
+ description: "'pypi' or 'npm'."
30
+ required: false
31
+ type: string
32
+ default: pypi
33
+ version:
34
+ description: Exact version to bundle. Defaults to the release tag (v-stripped).
35
+ required: false
36
+ type: string
37
+ default: ""
38
+ mode:
39
+ description: "'complete' (vendor deps) or 'reference' (uvx/npx)."
40
+ required: false
41
+ type: string
42
+ default: complete
43
+ python-version:
44
+ description: Python version used when vendoring dependencies.
45
+ required: false
46
+ type: string
47
+ default: "3.12"
48
+ output-dir:
49
+ description: Directory where .mcpb files are written.
50
+ required: false
51
+ type: string
52
+ default: dist
53
+ mcp2mcpb-version:
54
+ description: Version spec of the mcp2mcpb package to install from PyPI.
55
+ required: false
56
+ type: string
57
+ default: ">=0.1"
58
+ runner:
59
+ description: "Launch runner: uvx | npx | uv-run | python | node (empty = infer)."
60
+ required: false
61
+ type: string
62
+ default: ""
63
+ entry-script:
64
+ description: Console script (PyPI) / bin name (npm) to launch (empty = auto-detect).
65
+ required: false
66
+ type: string
67
+ default: ""
68
+ extras:
69
+ description: Space-separated package extras (PyPI/uv only), e.g. "mcp".
70
+ required: false
71
+ type: string
72
+ default: ""
73
+ subcommand:
74
+ description: Arguments appended after the entry script, e.g. "start-mcp-server".
75
+ required: false
76
+ type: string
77
+ default: ""
78
+ transport:
79
+ description: "Transport flag: stdio | none | auto (empty = auto)."
80
+ required: false
81
+ type: string
82
+ default: ""
83
+ no-probe:
84
+ description: Disable the --help auto-detection probe.
85
+ required: false
86
+ type: boolean
87
+ default: false
88
+ attach-to-release:
89
+ description: Attach the .mcpb files to the GitHub Release on a 'release' event.
90
+ required: false
91
+ type: boolean
92
+ default: true
93
+
94
+ jobs:
95
+ build-mcpb:
96
+ name: Build .mcpb bundle
97
+ runs-on: ubuntu-latest
98
+ permissions:
99
+ contents: write # needed to attach assets to a release
100
+ steps:
101
+ - name: Set up uv
102
+ uses: astral-sh/setup-uv@v4
103
+ with:
104
+ version: latest
105
+
106
+ - name: Set up Python
107
+ uses: actions/setup-python@v5
108
+ with:
109
+ python-version: ${{ inputs.python-version }}
110
+
111
+ - name: Install mcp2mcpb
112
+ shell: bash
113
+ env:
114
+ SPEC: ${{ inputs.mcp2mcpb-version }}
115
+ run: uv pip install --system "mcp2mcpb${SPEC}"
116
+
117
+ - name: Resolve version
118
+ id: resolve-version
119
+ shell: bash
120
+ env:
121
+ VERSION_INPUT: ${{ inputs.version }}
122
+ run: |
123
+ VERSION="$VERSION_INPUT"
124
+ if [[ -z "$VERSION" && -n "$GITHUB_REF_NAME" ]]; then
125
+ VERSION="${GITHUB_REF_NAME#v}"
126
+ fi
127
+ if [[ -z "$VERSION" ]]; then
128
+ echo "Error: could not resolve version. Set the 'version' input." >&2
129
+ exit 1
130
+ fi
131
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
132
+
133
+ - name: Build .mcpb bundle
134
+ shell: bash
135
+ env:
136
+ PACKAGE: ${{ inputs.package }}
137
+ REGISTRY: ${{ inputs.registry }}
138
+ VERSION: ${{ steps.resolve-version.outputs.version }}
139
+ MODE: ${{ inputs.mode }}
140
+ OUTPUT_DIR: ${{ inputs.output-dir }}
141
+ RUNNER: ${{ inputs.runner }}
142
+ ENTRY_SCRIPT: ${{ inputs.entry-script }}
143
+ EXTRAS: ${{ inputs.extras }}
144
+ SUBCOMMAND: ${{ inputs.subcommand }}
145
+ TRANSPORT: ${{ inputs.transport }}
146
+ NO_PROBE: ${{ inputs.no-probe }}
147
+ run: |
148
+ ARGS=( "$PACKAGE" --registry "$REGISTRY" --pin "$VERSION"
149
+ --mode "$MODE" --output "$OUTPUT_DIR" )
150
+ [[ -n "$RUNNER" ]] && ARGS+=( --runner "$RUNNER" )
151
+ [[ -n "$ENTRY_SCRIPT" ]] && ARGS+=( --entry-script "$ENTRY_SCRIPT" )
152
+ for extra in $EXTRAS; do ARGS+=( --extra "$extra" ); done
153
+ [[ -n "$SUBCOMMAND" ]] && ARGS+=( --subcommand "$SUBCOMMAND" )
154
+ [[ -n "$TRANSPORT" ]] && ARGS+=( --transport "$TRANSPORT" )
155
+ [[ "$NO_PROBE" == "true" ]] && ARGS+=( --no-probe )
156
+ python -m mcp2mcpb "${ARGS[@]}"
157
+
158
+ - name: Attach to GitHub Release
159
+ if: inputs.attach-to-release && github.event_name == 'release'
160
+ uses: softprops/action-gh-release@v2
161
+ with:
162
+ files: ${{ inputs.output-dir }}/*.mcpb
@@ -0,0 +1,137 @@
1
+ name: Build .mcpb bundles (upstream)
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ package:
7
+ description: Package name
8
+ required: true
9
+ type: string
10
+ version:
11
+ description: Exact upstream version to bundle
12
+ required: true
13
+ type: string
14
+ registry:
15
+ description: pypi or npm
16
+ required: true
17
+ default: pypi
18
+ type: choice
19
+ options: [pypi, npm]
20
+
21
+ jobs:
22
+ build:
23
+ name: Build (${{ matrix.platform }})
24
+ strategy:
25
+ fail-fast: false
26
+ matrix:
27
+ include:
28
+ - os: ubuntu-latest
29
+ platform: linux-x86_64
30
+ - os: macos-13
31
+ platform: macos-x86_64
32
+ - os: macos-latest
33
+ platform: macos-arm64
34
+ - os: windows-latest
35
+ platform: windows-x86_64
36
+
37
+ runs-on: ${{ matrix.os }}
38
+
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+
42
+ - uses: astral-sh/setup-uv@v4
43
+
44
+ - name: Set up Python
45
+ uses: actions/setup-python@v5
46
+ with:
47
+ python-version: "3.12"
48
+
49
+ - name: Install mcp2mcpb
50
+ run: uv pip install --system -e ".[dev]"
51
+
52
+ - name: Build .mcpb bundle
53
+ run: |
54
+ python -m mcp2mcpb \
55
+ "${{ inputs.package }}" \
56
+ --registry "${{ inputs.registry }}" \
57
+ --pin "${{ inputs.version }}" \
58
+ --mode complete \
59
+ --output dist/
60
+
61
+ - name: Upload artifact
62
+ uses: actions/upload-artifact@v4
63
+ with:
64
+ name: mcpb-${{ matrix.platform }}
65
+ path: dist/*.mcpb
66
+ if-no-files-found: error
67
+
68
+ release:
69
+ name: Create GitHub Release
70
+ needs: build
71
+ runs-on: ubuntu-latest
72
+ permissions:
73
+ contents: write
74
+
75
+ steps:
76
+ - uses: actions/checkout@v4
77
+ with:
78
+ token: ${{ secrets.GITHUB_TOKEN }}
79
+
80
+ - name: Download all platform artifacts
81
+ uses: actions/download-artifact@v4
82
+ with:
83
+ path: dist/
84
+ merge-multiple: true
85
+
86
+ - name: List produced files
87
+ run: ls -lh dist/*.mcpb
88
+
89
+ - name: Create GitHub Release
90
+ uses: softprops/action-gh-release@v2
91
+ with:
92
+ tag_name: "${{ inputs.package }}-v${{ inputs.version }}"
93
+ name: "${{ inputs.package }} ${{ inputs.version }}"
94
+ files: dist/*.mcpb
95
+ generate_release_notes: false
96
+ body: |
97
+ ## ${{ inputs.package }} ${{ inputs.version }}
98
+
99
+ One-click `.mcpb` bundles for Claude Desktop and any MCPB-compatible client.
100
+
101
+ ### Installation
102
+
103
+ 1. Download the `.mcpb` file for your platform below.
104
+ 2. Open Claude Desktop โ†’ Settings โ†’ Extensions.
105
+ 3. Drag and drop the `.mcpb` file into the Extensions page.
106
+
107
+ ### Platform files
108
+
109
+ | File | Platform |
110
+ |------|----------|
111
+ | `*-linux-x86_64.mcpb` | Linux (x86_64) |
112
+ | `*-macos-x86_64.mcpb` | macOS (Intel) |
113
+ | `*-macos-arm64.mcpb` | macOS (Apple Silicon) |
114
+ | `*-windows-x86_64.mcpb` | Windows (x86_64) |
115
+
116
+ ---
117
+ *Built by [mcp2mcpb](https://github.com/Anselmoo/mcp2mcpb)*
118
+
119
+ - name: Update versions.json
120
+ run: |
121
+ python - <<'EOF'
122
+ import json
123
+ from pathlib import Path
124
+ f = Path("versions.json")
125
+ versions = json.loads(f.read_text()) if f.exists() else {}
126
+ versions["${{ inputs.registry }}/${{ inputs.package }}"] = "${{ inputs.version }}"
127
+ f.write_text(json.dumps(versions, indent=2, sort_keys=True) + "\n")
128
+ EOF
129
+
130
+ - name: Commit updated versions.json
131
+ run: |
132
+ git config user.name "github-actions[bot]"
133
+ git config user.email "github-actions[bot]@users.noreply.github.com"
134
+ git add versions.json
135
+ git diff --cached --quiet || git commit -m \
136
+ "chore(versions): ${{ inputs.package }} โ†’ ${{ inputs.version }}"
137
+ git push
@@ -0,0 +1,62 @@
1
+ name: Check upstream releases
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "0 6 * * *" # daily 06:00 UTC
6
+ workflow_dispatch:
7
+ inputs:
8
+ package:
9
+ description: Package name (e.g. mcp-server-fetch)
10
+ required: true
11
+ type: string
12
+ registry:
13
+ description: pypi or npm
14
+ required: true
15
+ default: pypi
16
+ type: choice
17
+ options: [pypi, npm]
18
+
19
+ jobs:
20
+ check:
21
+ runs-on: ubuntu-latest
22
+ permissions:
23
+ actions: write # needed to trigger workflow_dispatch
24
+
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+
28
+ - name: Set up Python
29
+ uses: actions/setup-python@v5
30
+ with:
31
+ python-version: "3.12"
32
+
33
+ - name: Install httpx
34
+ run: pip install httpx --quiet
35
+
36
+ - name: Check for new version
37
+ id: check
38
+ env:
39
+ PACKAGE: ${{ inputs.package || 'mcp-server-fetch' }}
40
+ REGISTRY: ${{ inputs.registry || 'pypi' }}
41
+ run: |
42
+ python scripts/check_version.py \
43
+ --package "$PACKAGE" \
44
+ --registry "$REGISTRY" \
45
+ --versions-file versions.json
46
+
47
+ - name: Trigger build if new version found
48
+ if: steps.check.outputs.new_version != ''
49
+ uses: actions/github-script@v7
50
+ with:
51
+ script: |
52
+ await github.rest.actions.createWorkflowDispatch({
53
+ owner: context.repo.owner,
54
+ repo: context.repo.repo,
55
+ workflow_id: 'build-upstream.yml',
56
+ ref: 'main',
57
+ inputs: {
58
+ package: '${{ steps.check.outputs.package }}',
59
+ version: '${{ steps.check.outputs.new_version }}',
60
+ registry: '${{ steps.check.outputs.registry }}',
61
+ },
62
+ })
@@ -0,0 +1,295 @@
1
+ name: ๐Ÿš€ CI/CD
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - feat/**
8
+ - fix/**
9
+ - chore/**
10
+ - docs/**
11
+ - refactor/**
12
+ - test/**
13
+ - ci/**
14
+ - perf/**
15
+ - style/**
16
+ - build/**
17
+ tags: ["v*.*.*"]
18
+ pull_request:
19
+ branches: [main]
20
+ workflow_dispatch:
21
+
22
+ permissions:
23
+ contents: read
24
+
25
+ concurrency:
26
+ group: ci-cd-${{ github.ref }}
27
+ cancel-in-progress: true
28
+
29
+ env:
30
+ PACKAGE_NAME: "mcp2mcpb"
31
+ PYTHON_VERSION: "3.12"
32
+
33
+ jobs:
34
+ lint:
35
+ name: ๐Ÿ” Lint & types
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - name: Checkout
39
+ uses: actions/checkout@v6
40
+ with:
41
+ fetch-depth: 0 # rrt needs history for branch/commit/changelog checks
42
+
43
+ - name: Set up Python
44
+ uses: actions/setup-python@v6
45
+ with:
46
+ python-version: ${{ env.PYTHON_VERSION }}
47
+
48
+ - name: Set up uv
49
+ uses: astral-sh/setup-uv@v7
50
+ with:
51
+ enable-cache: true
52
+
53
+ - name: Sync dependencies
54
+ run: uv sync --extra dev
55
+
56
+ - name: Ruff lint
57
+ run: uv run ruff check src tests
58
+
59
+ - name: Ruff format check
60
+ run: uv run ruff format --check src tests
61
+
62
+ - name: ty type check
63
+ run: uv run ty check
64
+
65
+ - name: Validate release policy (rrt)
66
+ if: github.event_name == 'pull_request' || startsWith(github.ref, 'refs/tags/')
67
+ uses: Anselmoo/repo-release-tools@v1.9.0
68
+ with:
69
+ check-branch-name: "true"
70
+ check-commit-subject: "true"
71
+ check-changelog: "true"
72
+ changelog-strategy: "release-only"
73
+ check-release-health: "true"
74
+
75
+ test:
76
+ name: ๐Ÿงช Tests (${{ matrix.python-version }})
77
+ needs: lint
78
+ runs-on: ubuntu-latest
79
+ strategy:
80
+ fail-fast: false
81
+ matrix:
82
+ python-version: ["3.12", "3.13", "3.14"]
83
+ steps:
84
+ - name: Checkout
85
+ uses: actions/checkout@v6
86
+
87
+ - name: Set up Python
88
+ uses: actions/setup-python@v6
89
+ with:
90
+ python-version: ${{ matrix.python-version }}
91
+
92
+ - name: Set up uv
93
+ uses: astral-sh/setup-uv@v7
94
+ with:
95
+ enable-cache: true
96
+
97
+ - name: Sync dependencies
98
+ run: uv sync --extra dev
99
+
100
+ - name: Run tests
101
+ run: uv run pytest --cov=src/mcp2mcpb --cov-report=xml
102
+
103
+ - name: Upload coverage to Codecov
104
+ uses: codecov/codecov-action@v5
105
+ with:
106
+ token: ${{ secrets.CODECOV_TOKEN }}
107
+ files: ./coverage.xml
108
+ flags: python-${{ matrix.python-version }}
109
+ name: mcp2mcpb-${{ matrix.python-version }}
110
+ fail_ci_if_error: false
111
+ verbose: false
112
+
113
+ build:
114
+ name: ๐Ÿ“ฆ Build package
115
+ needs: test
116
+ if: github.event_name != 'pull_request'
117
+ runs-on: ubuntu-latest
118
+ steps:
119
+ - name: Checkout
120
+ uses: actions/checkout@v6
121
+
122
+ - name: Set up Python
123
+ uses: actions/setup-python@v6
124
+ with:
125
+ python-version: ${{ env.PYTHON_VERSION }}
126
+
127
+ - name: Set up uv
128
+ uses: astral-sh/setup-uv@v7
129
+ with:
130
+ enable-cache: true
131
+
132
+ - name: Build distributions
133
+ run: uv build
134
+
135
+ - name: Check distributions
136
+ run: uvx twine check dist/*
137
+
138
+ - name: Upload distribution artifacts
139
+ uses: actions/upload-artifact@v7
140
+ with:
141
+ name: python-package-distributions
142
+ path: dist/
143
+
144
+ sbom:
145
+ name: ๐Ÿ“œ Generate SBOM
146
+ needs: build
147
+ if: github.event_name != 'pull_request'
148
+ runs-on: ubuntu-latest
149
+ permissions:
150
+ contents: read
151
+ steps:
152
+ - name: Checkout
153
+ uses: actions/checkout@v6
154
+
155
+ - name: Generate SBOM
156
+ uses: anchore/sbom-action@v0
157
+ with:
158
+ format: spdx-json
159
+ output-file: sbom.spdx.json
160
+ path: .
161
+
162
+ - name: Upload SBOM artifact
163
+ uses: actions/upload-artifact@v7
164
+ with:
165
+ name: sbom
166
+ path: sbom.spdx.json
167
+
168
+ attest:
169
+ name: ๐Ÿ›ก๏ธ Attest build provenance
170
+ needs: build
171
+ if: github.event_name != 'pull_request'
172
+ runs-on: ubuntu-latest
173
+ permissions:
174
+ contents: read
175
+ id-token: write
176
+ attestations: write
177
+ steps:
178
+ - name: Download distribution artifacts
179
+ uses: actions/download-artifact@v8
180
+ with:
181
+ name: python-package-distributions
182
+ path: dist/
183
+
184
+ - name: Generate provenance attestation
185
+ uses: actions/attest-build-provenance@v4
186
+ with:
187
+ subject-path: dist/*
188
+
189
+ publish-testpypi:
190
+ name: ๐Ÿงช Publish to TestPyPI
191
+ needs: [build, sbom, attest]
192
+ if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
193
+ runs-on: ubuntu-latest
194
+ permissions:
195
+ id-token: write
196
+ environment:
197
+ name: testpypi
198
+ url: https://test.pypi.org/p/mcp2mcpb
199
+ steps:
200
+ - name: Download distribution artifacts
201
+ uses: actions/download-artifact@v8
202
+ with:
203
+ name: python-package-distributions
204
+ path: dist/
205
+
206
+ - name: Publish to TestPyPI
207
+ uses: pypa/gh-action-pypi-publish@release/v1
208
+ with:
209
+ repository-url: https://test.pypi.org/legacy/
210
+ skip-existing: true
211
+
212
+ verify-testpypi:
213
+ name: ๐Ÿงช Verify TestPyPI package
214
+ needs: publish-testpypi
215
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
216
+ runs-on: ubuntu-latest
217
+ steps:
218
+ - name: Set up Python
219
+ uses: actions/setup-python@v6
220
+ with:
221
+ python-version: ${{ env.PYTHON_VERSION }}
222
+
223
+ - name: Upgrade pip
224
+ run: python -m pip install --upgrade pip
225
+
226
+ - name: Install package from TestPyPI
227
+ shell: bash
228
+ run: |
229
+ set -euo pipefail
230
+ VERSION="${GITHUB_REF_NAME#v}"
231
+ for attempt in {1..8}; do
232
+ if python -m pip install \
233
+ --index-url https://test.pypi.org/simple/ \
234
+ --extra-index-url https://pypi.org/simple/ \
235
+ "${PACKAGE_NAME}==${VERSION}"; then
236
+ break
237
+ fi
238
+ if [ "$attempt" -eq 8 ]; then
239
+ echo "Package was not installable from TestPyPI after retries."
240
+ exit 1
241
+ fi
242
+ echo "Retrying TestPyPI install in 20s (attempt ${attempt}/8)..."
243
+ sleep 20
244
+ done
245
+
246
+ - name: Smoke test installed CLI
247
+ run: mcp2mcpb --version
248
+
249
+ publish-pypi:
250
+ name: ๐Ÿš€ Publish to PyPI
251
+ needs: [build, sbom, attest, publish-testpypi, verify-testpypi]
252
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
253
+ runs-on: ubuntu-latest
254
+ permissions:
255
+ id-token: write
256
+ environment:
257
+ name: pypi
258
+ url: https://pypi.org/p/mcp2mcpb
259
+ steps:
260
+ - name: Download distribution artifacts
261
+ uses: actions/download-artifact@v8
262
+ with:
263
+ name: python-package-distributions
264
+ path: dist/
265
+
266
+ - name: Publish to PyPI
267
+ uses: pypa/gh-action-pypi-publish@release/v1
268
+
269
+ github-release:
270
+ name: ๐ŸŽ Create GitHub release
271
+ needs: [publish-pypi, sbom]
272
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
273
+ runs-on: ubuntu-latest
274
+ permissions:
275
+ contents: write
276
+ steps:
277
+ - name: Download distribution artifacts
278
+ uses: actions/download-artifact@v8
279
+ with:
280
+ name: python-package-distributions
281
+ path: dist/
282
+
283
+ - name: Download SBOM artifact
284
+ uses: actions/download-artifact@v8
285
+ with:
286
+ name: sbom
287
+ path: .
288
+
289
+ - name: Create GitHub release
290
+ uses: softprops/action-gh-release@v2
291
+ with:
292
+ generate_release_notes: true
293
+ files: |
294
+ dist/*
295
+ sbom.spdx.json
@@ -0,0 +1,42 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .eggs/
11
+ *.egg
12
+
13
+ # Virtual environments
14
+ .venv/
15
+ venv/
16
+ env/
17
+
18
+ # uv
19
+ .uv/
20
+
21
+ # Testing / coverage
22
+ .pytest_cache/
23
+ .mypy_cache/
24
+ .ruff_cache/
25
+ .coverage
26
+ .coverage.*
27
+ htmlcov/
28
+
29
+ # IDEs
30
+ .idea/
31
+ .vscode/
32
+
33
+ # OS
34
+ .DS_Store
35
+
36
+ # Local Claude Code settings
37
+ .claude/settings.local.json
38
+
39
+ # mcp2mcpb artifacts
40
+ *.mcpb
41
+ .mcpb-cache/
42
+ docs/