mcp-client-kit 0.0.3__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.
- mcp_client_kit-0.0.3/.claude-plugin/marketplace.json +25 -0
- mcp_client_kit-0.0.3/.claude-plugin/plugin.json +5 -0
- mcp_client_kit-0.0.3/.github/workflows/ci.yml +56 -0
- mcp_client_kit-0.0.3/.github/workflows/publish.yml +36 -0
- mcp_client_kit-0.0.3/.gitignore +11 -0
- mcp_client_kit-0.0.3/LICENSE +21 -0
- mcp_client_kit-0.0.3/PKG-INFO +196 -0
- mcp_client_kit-0.0.3/README.md +172 -0
- mcp_client_kit-0.0.3/doc/CODEGEN_SKILL_IDEA.md +73 -0
- mcp_client_kit-0.0.3/doc/DISTRIBUTION.md +276 -0
- mcp_client_kit-0.0.3/doc/EXTRACTION_ANALYSIS.md +97 -0
- mcp_client_kit-0.0.3/doc/LANDSCAPE.md +99 -0
- mcp_client_kit-0.0.3/doc/MARKET_RESEARCH.md +209 -0
- mcp_client_kit-0.0.3/doc/RUNNING_LOCALLY.md +76 -0
- mcp_client_kit-0.0.3/doc/USAGE.md +432 -0
- mcp_client_kit-0.0.3/doc/VERDICT.md +129 -0
- mcp_client_kit-0.0.3/mcpgen/__init__.py +23 -0
- mcp_client_kit-0.0.3/mcpgen/_bridge.py +1159 -0
- mcp_client_kit-0.0.3/mcpgen/cli.py +896 -0
- mcp_client_kit-0.0.3/mcpgen/codegen.py +674 -0
- mcp_client_kit-0.0.3/mcpgen/discovery.py +442 -0
- mcp_client_kit-0.0.3/mcpgen/seam.py +18 -0
- mcp_client_kit-0.0.3/pyproject.toml +60 -0
- mcp_client_kit-0.0.3/servers.example.json +9 -0
- mcp_client_kit-0.0.3/skills/generate-mcp-runner/SKILL.md +270 -0
- mcp_client_kit-0.0.3/skills/generate-mcp-runner/runner_templates/http_bearer.py +31 -0
- mcp_client_kit-0.0.3/skills/generate-mcp-runner/runner_templates/http_oauth.py +34 -0
- mcp_client_kit-0.0.3/skills/generate-mcp-runner/runner_templates/http_public.py +27 -0
- mcp_client_kit-0.0.3/skills/generate-mcp-runner/runner_templates/stdio.py +25 -0
- mcp_client_kit-0.0.3/skills/generate-mcp-wrappers/SKILL.md +489 -0
- mcp_client_kit-0.0.3/tests/test_bridge.py +984 -0
- mcp_client_kit-0.0.3/tests/test_cli_call.py +146 -0
- mcp_client_kit-0.0.3/tests/test_cli_env.py +149 -0
- mcp_client_kit-0.0.3/tests/test_cli_merge.py +525 -0
- mcp_client_kit-0.0.3/tests/test_cli_probe.py +163 -0
- mcp_client_kit-0.0.3/tests/test_codegen.py +963 -0
- mcp_client_kit-0.0.3/tests/test_config.py +187 -0
- mcp_client_kit-0.0.3/tests/test_discovery.py +379 -0
- mcp_client_kit-0.0.3/uv.lock +1193 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-marketplace.json",
|
|
3
|
+
"name": "mcp-client-kit",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Marketplace for the mcpgen plugin — typed Python wrappers for any MCP server.",
|
|
6
|
+
"owner": {
|
|
7
|
+
"name": "Sviatoslav Sviridov",
|
|
8
|
+
"email": "sviridov@gmail.com"
|
|
9
|
+
},
|
|
10
|
+
"plugins": [
|
|
11
|
+
{
|
|
12
|
+
"name": "mcp-client-kit",
|
|
13
|
+
"displayName": "mcp-client-kit",
|
|
14
|
+
"source": "./",
|
|
15
|
+
"version": "0.1.0",
|
|
16
|
+
"description": "Generate typed Python wrappers for any MCP server (skill: generate-mcp-wrappers).",
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "Sviatoslav Sviridov",
|
|
19
|
+
"email": "sviridov@gmail.com"
|
|
20
|
+
},
|
|
21
|
+
"keywords": ["mcp", "codegen", "typed-wrappers", "python", "oauth"],
|
|
22
|
+
"license": "MIT"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
matrix:
|
|
12
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: uv sync --dev
|
|
23
|
+
|
|
24
|
+
- name: Run tests
|
|
25
|
+
run: uv run pytest
|
|
26
|
+
|
|
27
|
+
lint:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
|
|
32
|
+
- name: Install uv
|
|
33
|
+
uses: astral-sh/setup-uv@v5
|
|
34
|
+
|
|
35
|
+
- name: Install dependencies
|
|
36
|
+
run: uv sync --dev
|
|
37
|
+
|
|
38
|
+
- name: Lint (ruff check)
|
|
39
|
+
run: uv run ruff check .
|
|
40
|
+
|
|
41
|
+
- name: Format (ruff format)
|
|
42
|
+
run: uv run ruff format --check .
|
|
43
|
+
|
|
44
|
+
typecheck:
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
|
|
49
|
+
- name: Install uv
|
|
50
|
+
uses: astral-sh/setup-uv@v5
|
|
51
|
+
|
|
52
|
+
- name: Install dependencies
|
|
53
|
+
run: uv sync --dev
|
|
54
|
+
|
|
55
|
+
- name: Type check (mypy)
|
|
56
|
+
run: uv run mypy mcpgen
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Publish engine to PyPI
|
|
2
|
+
|
|
3
|
+
# Fires only on bare engine tags (vX.Y.Z). 'plugin-v*' tags never publish.
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
tags:
|
|
7
|
+
- "v[0-9]+.[0-9]+.[0-9]+"
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
publish:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
environment: pypi # must match the PyPI Trusted Publisher config
|
|
13
|
+
permissions:
|
|
14
|
+
id-token: write # OIDC token for Trusted Publishing (no API token needed)
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v5
|
|
20
|
+
|
|
21
|
+
- name: Verify tag matches package version
|
|
22
|
+
run: |
|
|
23
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
24
|
+
pkg=$(grep -m1 -E '^version *= *"' pyproject.toml | sed -E 's/.*"(.*)".*/\1/')
|
|
25
|
+
if [ "$tag" != "$pkg" ]; then
|
|
26
|
+
echo "Tag v$tag does not match pyproject version $pkg"; exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
- name: Build sdist + wheel
|
|
30
|
+
run: uv build
|
|
31
|
+
|
|
32
|
+
- name: Check distribution metadata
|
|
33
|
+
run: uvx twine check dist/*
|
|
34
|
+
|
|
35
|
+
- name: Publish to PyPI (Trusted Publishing)
|
|
36
|
+
run: uv publish --trusted-publishing always
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sviatoslav Sviridov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-client-kit
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: mcpgen — generate typed Python wrappers for any MCP server (codegen skill + CLI).
|
|
5
|
+
Project-URL: Homepage, https://github.com/svd/mcp-client-kit
|
|
6
|
+
Project-URL: Source, https://github.com/svd/mcp-client-kit
|
|
7
|
+
Project-URL: Issues, https://github.com/svd/mcp-client-kit/issues
|
|
8
|
+
Author-email: Sviatoslav Sviridov <sviridov@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: httpx>=0.28
|
|
21
|
+
Requires-Dist: keyring>=24
|
|
22
|
+
Requires-Dist: mcp[cli]<2,>=1.27
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# mcpgen
|
|
26
|
+
|
|
27
|
+
**Write your MCP server wrappers once — from the live server. Keep them as real Python source you can diff, review, and pin.**
|
|
28
|
+
|
|
29
|
+
`mcpgen` turns any MCP server into a typed Python module: one `async def` per tool, real return types, no live server needed to read it. Call your tools from code instead of pumping their schemas through the model's context — the pattern Anthropic measured at up to **98% token reduction**.
|
|
30
|
+
|
|
31
|
+
> Two artifacts, one repo: a **CLI** (`mcpgen`) you run anywhere, and a **Claude Code plugin** (`generate-mcp-wrappers` skill) that drives it for the parts that need judgment.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## The problem
|
|
36
|
+
|
|
37
|
+
MCP tool schemas eat your context window before the agent does any work.
|
|
38
|
+
|
|
39
|
+
Every tool definition costs **300–600 tokens** for its name, description, and JSON schema. That adds up fast:
|
|
40
|
+
|
|
41
|
+
- The **GitHub MCP server alone** burns ~55,000 tokens across its 93 tools.
|
|
42
|
+
- One developer measured **66,000 tokens consumed at conversation start** — a third of a 200k window, gone before the first query.
|
|
43
|
+
- A SaaS server with 50+ endpoints can spend **30,000+ tokens just describing what it *could* do.**
|
|
44
|
+
|
|
45
|
+
Anthropic's validated fix ("Code execution with MCP," Nov 2025): stop routing schemas through the model. Generate wrapper code and call the tools from code instead — an approach Anthropic measured shrinking one workflow from ~150,000 tokens to ~2,000 (**98.7%**), with independent benchmarks landing around **78–85%** on less extreme workloads.
|
|
46
|
+
|
|
47
|
+
The catch for Python teams: no good tool generated **standalone, importable, reviewable `.py` wrappers** from a live MCP server. So everyone hand-writes `jira.py`, `github.py`, `slack.py` — slowly, inconsistently, and they silently rot when the server changes.
|
|
48
|
+
|
|
49
|
+
That's the gap mcpgen fills.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## What you get
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv tool install mcp-client-kit # puts `mcpgen` on your PATH
|
|
57
|
+
mcpgen login github # browser OAuth, tokens persisted
|
|
58
|
+
mcpgen codegen github --out github.py # typed wrappers for every tool
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import asyncio
|
|
63
|
+
from mcpgen import McpBridgeCaller
|
|
64
|
+
import github # the file you just generated
|
|
65
|
+
|
|
66
|
+
async def main():
|
|
67
|
+
caller = McpBridgeCaller(url="https://api.githubcopilot.com/mcp/")
|
|
68
|
+
me = await github.get_me(caller) # -> GitHubUser
|
|
69
|
+
issues = await github.list_issues(caller, owner="octocat", repo="hello-world")
|
|
70
|
+
print(me, issues)
|
|
71
|
+
|
|
72
|
+
asyncio.run(main())
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`github.py` is just Python. Open it in your IDE, review it in a PR, pin it to a commit, ship it. No runtime proxy, no framework lock-in, no live server required to read what your tools return.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Why developers pick it
|
|
80
|
+
|
|
81
|
+
**Real source you own.** Importable `.py` modules — not `.pyi` stubs (mcp2py), not a runtime proxy, not tied to one execution framework (ipybox). You can diff it, review it, pin it, and read it in your IDE without a server running.
|
|
82
|
+
|
|
83
|
+
**Types that match reality.** A tool's `inputSchema` describes its *inputs* — it tells you nothing about the *output* shape. mcpgen's `--probe` makes one live call and records the actual response, so your return types reflect what the server really sends. No other generator does this.
|
|
84
|
+
|
|
85
|
+
**OAuth that survives restarts.** Pre-flight token refresh means a fresh process renews a near-expired token silently from the refresh token — no surprise browser pop-up at cold start. (The official SDK's canonical example is in-memory only; every restart re-authenticates.)
|
|
86
|
+
|
|
87
|
+
**Swap auth without regenerating.** Every wrapper takes an `McpCaller` as its first argument. Change transports or auth backends — bearer, OAuth, stdio, a fake for tests — without touching the generated code.
|
|
88
|
+
|
|
89
|
+
**Built for production teams.** Works with any MCP server (HTTP URL, stdio, or bearer/PAT). Generated code lives in git like any other module, so it survives code review, audits, and pinning.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## How it works
|
|
94
|
+
|
|
95
|
+
| Step | Command | What happens |
|
|
96
|
+
|------|---------|--------------|
|
|
97
|
+
| 1. Generate | `mcpgen codegen <server> --out <server>.py` | One typed `async def` per tool. |
|
|
98
|
+
| 2. Probe (optional) | `mcpgen probe <server> <tool> --args '{}' --emit-shape <server>.shapes.json` | Records the *real* response shape from a live call. |
|
|
99
|
+
| 3. Regenerate | `mcpgen codegen <server> --out <server>.py --shapes <server>.shapes.json` | Wrappers now return precise types (`TypedDict`s, unions, lists). |
|
|
100
|
+
|
|
101
|
+
Polymorphic tools — ones that return different shapes depending on an input (`entityType=1` → `Person`, `=2` → `Position`) — get typed `@overload`s, so your type checker narrows the return at every call site.
|
|
102
|
+
|
|
103
|
+
The full reference, including the shape-spec format and credential backends, is in [`doc/USAGE.md`](doc/USAGE.md).
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Install
|
|
108
|
+
|
|
109
|
+
> The PyPI package is **`mcp-client-kit`**; the command it installs is **`mcpgen`**.
|
|
110
|
+
|
|
111
|
+
**CLI on your PATH:**
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv tool install mcp-client-kit
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**One-off, no install:**
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
uvx --from mcp-client-kit mcpgen codegen <server> --out <server>.py
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**As a project dependency:**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
uv add mcp-client-kit # or: pip install mcp-client-kit
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Requires Python 3.11+.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Claude Code plugin
|
|
134
|
+
|
|
135
|
+
The plugin bundles the `generate-mcp-wrappers` skill, which drives the CLI through the 20% that needs judgment — curating which tools matter, probing live responses, and editing the shape-spec — then regenerates and verifies the module.
|
|
136
|
+
|
|
137
|
+
The CLI is not bundled with the plugin — install it separately (`uv add mcp-client-kit`, see [Install](#install) above). The skill requires **mcpgen >= 0.1.0** and checks this before running; a local editable install (`uv pip install -e .`) satisfies it for development. This is a version floor, not an exact pin, so the skill and CLI can be upgraded independently as long as the CLI stays at or above the floor.
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
/plugin marketplace add svd/mcpgen
|
|
141
|
+
/mcp-client-kit:generate-mcp-wrappers
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
A companion skill, `generate-mcp-runner`, writes a standalone smoke-test `run.py` that exercises the generated wrappers end-to-end.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Command reference
|
|
149
|
+
|
|
150
|
+
| Command | What it does |
|
|
151
|
+
|---------|--------------|
|
|
152
|
+
| `codegen <server>` | Emit typed wrappers; `--shapes` applies the shape-spec, `--probe` records a response shape inline. |
|
|
153
|
+
| `list <server>` | Print a server's tools as JSON. |
|
|
154
|
+
| `probe <server> <tool>` | Live call(s) → response-shape skeleton. |
|
|
155
|
+
| `call <server> <tool> --out <p>` | One live call, raw payload to disk — bootstrap ids or inspect output. |
|
|
156
|
+
| `merge <server>` | Consolidate probe parts into `<server>.shapes.json`. |
|
|
157
|
+
| `login <server>` | Browser OAuth login; tokens stored at `~/.mcpgen/credentials.json`. |
|
|
158
|
+
| `migrate-creds` | Move stored OAuth tokens between `file` / `keyring` backends. |
|
|
159
|
+
| `discover` | List MCP servers configured in your installed agent hosts. |
|
|
160
|
+
|
|
161
|
+
Full workflow and flags: [`doc/USAGE.md`](doc/USAGE.md).
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Authentication
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
mcpgen login <server> # OAuth (most servers)
|
|
169
|
+
mcpgen codegen <server> --bearer "$TOKEN" --out s.py # PAT / bearer
|
|
170
|
+
mcpgen codegen <server> --stdio "python server.py" --out s.py # local stdio, no auth
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Tokens persist in `~/.mcpgen/credentials.json` (chmod 0600) or your OS keystore via `--cred-backend keyring`. In code, `ensure_login(server, url=...)` refreshes silently and only opens a browser when a real login is required.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Who it's for
|
|
178
|
+
|
|
179
|
+
Python developers building AI agent pipelines on MCP servers — especially Claude Code users who've already hand-written at least one `<server>.py` wrapper and felt the pain. And platform teams running multi-server MCP environments where token cost and auth reliability are production concerns.
|
|
180
|
+
|
|
181
|
+
If you write your agent logic in Python and want generated tool wrappers you can actually own — review, pin, and keep in git — this is built for you.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Docs
|
|
186
|
+
|
|
187
|
+
- [`doc/USAGE.md`](doc/USAGE.md) — full end-user guide: install paths, server config, auth, the shape-spec, and calling generated wrappers.
|
|
188
|
+
- [`doc/RUNNING_LOCALLY.md`](doc/RUNNING_LOCALLY.md) — run from a local clone without installing.
|
|
189
|
+
|
|
190
|
+
## Status
|
|
191
|
+
|
|
192
|
+
Early access (`v0.x`). The codegen engine, OAuth persistence, live-probe shaping, and both Claude Code skills are working today. On the roadmap: `--check` drift mode, so CI can flag when a server's tools change out from under your wrappers.
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
MIT.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# mcpgen
|
|
2
|
+
|
|
3
|
+
**Write your MCP server wrappers once — from the live server. Keep them as real Python source you can diff, review, and pin.**
|
|
4
|
+
|
|
5
|
+
`mcpgen` turns any MCP server into a typed Python module: one `async def` per tool, real return types, no live server needed to read it. Call your tools from code instead of pumping their schemas through the model's context — the pattern Anthropic measured at up to **98% token reduction**.
|
|
6
|
+
|
|
7
|
+
> Two artifacts, one repo: a **CLI** (`mcpgen`) you run anywhere, and a **Claude Code plugin** (`generate-mcp-wrappers` skill) that drives it for the parts that need judgment.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The problem
|
|
12
|
+
|
|
13
|
+
MCP tool schemas eat your context window before the agent does any work.
|
|
14
|
+
|
|
15
|
+
Every tool definition costs **300–600 tokens** for its name, description, and JSON schema. That adds up fast:
|
|
16
|
+
|
|
17
|
+
- The **GitHub MCP server alone** burns ~55,000 tokens across its 93 tools.
|
|
18
|
+
- One developer measured **66,000 tokens consumed at conversation start** — a third of a 200k window, gone before the first query.
|
|
19
|
+
- A SaaS server with 50+ endpoints can spend **30,000+ tokens just describing what it *could* do.**
|
|
20
|
+
|
|
21
|
+
Anthropic's validated fix ("Code execution with MCP," Nov 2025): stop routing schemas through the model. Generate wrapper code and call the tools from code instead — an approach Anthropic measured shrinking one workflow from ~150,000 tokens to ~2,000 (**98.7%**), with independent benchmarks landing around **78–85%** on less extreme workloads.
|
|
22
|
+
|
|
23
|
+
The catch for Python teams: no good tool generated **standalone, importable, reviewable `.py` wrappers** from a live MCP server. So everyone hand-writes `jira.py`, `github.py`, `slack.py` — slowly, inconsistently, and they silently rot when the server changes.
|
|
24
|
+
|
|
25
|
+
That's the gap mcpgen fills.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## What you get
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv tool install mcp-client-kit # puts `mcpgen` on your PATH
|
|
33
|
+
mcpgen login github # browser OAuth, tokens persisted
|
|
34
|
+
mcpgen codegen github --out github.py # typed wrappers for every tool
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import asyncio
|
|
39
|
+
from mcpgen import McpBridgeCaller
|
|
40
|
+
import github # the file you just generated
|
|
41
|
+
|
|
42
|
+
async def main():
|
|
43
|
+
caller = McpBridgeCaller(url="https://api.githubcopilot.com/mcp/")
|
|
44
|
+
me = await github.get_me(caller) # -> GitHubUser
|
|
45
|
+
issues = await github.list_issues(caller, owner="octocat", repo="hello-world")
|
|
46
|
+
print(me, issues)
|
|
47
|
+
|
|
48
|
+
asyncio.run(main())
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`github.py` is just Python. Open it in your IDE, review it in a PR, pin it to a commit, ship it. No runtime proxy, no framework lock-in, no live server required to read what your tools return.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Why developers pick it
|
|
56
|
+
|
|
57
|
+
**Real source you own.** Importable `.py` modules — not `.pyi` stubs (mcp2py), not a runtime proxy, not tied to one execution framework (ipybox). You can diff it, review it, pin it, and read it in your IDE without a server running.
|
|
58
|
+
|
|
59
|
+
**Types that match reality.** A tool's `inputSchema` describes its *inputs* — it tells you nothing about the *output* shape. mcpgen's `--probe` makes one live call and records the actual response, so your return types reflect what the server really sends. No other generator does this.
|
|
60
|
+
|
|
61
|
+
**OAuth that survives restarts.** Pre-flight token refresh means a fresh process renews a near-expired token silently from the refresh token — no surprise browser pop-up at cold start. (The official SDK's canonical example is in-memory only; every restart re-authenticates.)
|
|
62
|
+
|
|
63
|
+
**Swap auth without regenerating.** Every wrapper takes an `McpCaller` as its first argument. Change transports or auth backends — bearer, OAuth, stdio, a fake for tests — without touching the generated code.
|
|
64
|
+
|
|
65
|
+
**Built for production teams.** Works with any MCP server (HTTP URL, stdio, or bearer/PAT). Generated code lives in git like any other module, so it survives code review, audits, and pinning.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## How it works
|
|
70
|
+
|
|
71
|
+
| Step | Command | What happens |
|
|
72
|
+
|------|---------|--------------|
|
|
73
|
+
| 1. Generate | `mcpgen codegen <server> --out <server>.py` | One typed `async def` per tool. |
|
|
74
|
+
| 2. Probe (optional) | `mcpgen probe <server> <tool> --args '{}' --emit-shape <server>.shapes.json` | Records the *real* response shape from a live call. |
|
|
75
|
+
| 3. Regenerate | `mcpgen codegen <server> --out <server>.py --shapes <server>.shapes.json` | Wrappers now return precise types (`TypedDict`s, unions, lists). |
|
|
76
|
+
|
|
77
|
+
Polymorphic tools — ones that return different shapes depending on an input (`entityType=1` → `Person`, `=2` → `Position`) — get typed `@overload`s, so your type checker narrows the return at every call site.
|
|
78
|
+
|
|
79
|
+
The full reference, including the shape-spec format and credential backends, is in [`doc/USAGE.md`](doc/USAGE.md).
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Install
|
|
84
|
+
|
|
85
|
+
> The PyPI package is **`mcp-client-kit`**; the command it installs is **`mcpgen`**.
|
|
86
|
+
|
|
87
|
+
**CLI on your PATH:**
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
uv tool install mcp-client-kit
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**One-off, no install:**
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
uvx --from mcp-client-kit mcpgen codegen <server> --out <server>.py
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**As a project dependency:**
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
uv add mcp-client-kit # or: pip install mcp-client-kit
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Requires Python 3.11+.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Claude Code plugin
|
|
110
|
+
|
|
111
|
+
The plugin bundles the `generate-mcp-wrappers` skill, which drives the CLI through the 20% that needs judgment — curating which tools matter, probing live responses, and editing the shape-spec — then regenerates and verifies the module.
|
|
112
|
+
|
|
113
|
+
The CLI is not bundled with the plugin — install it separately (`uv add mcp-client-kit`, see [Install](#install) above). The skill requires **mcpgen >= 0.1.0** and checks this before running; a local editable install (`uv pip install -e .`) satisfies it for development. This is a version floor, not an exact pin, so the skill and CLI can be upgraded independently as long as the CLI stays at or above the floor.
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
/plugin marketplace add svd/mcpgen
|
|
117
|
+
/mcp-client-kit:generate-mcp-wrappers
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
A companion skill, `generate-mcp-runner`, writes a standalone smoke-test `run.py` that exercises the generated wrappers end-to-end.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Command reference
|
|
125
|
+
|
|
126
|
+
| Command | What it does |
|
|
127
|
+
|---------|--------------|
|
|
128
|
+
| `codegen <server>` | Emit typed wrappers; `--shapes` applies the shape-spec, `--probe` records a response shape inline. |
|
|
129
|
+
| `list <server>` | Print a server's tools as JSON. |
|
|
130
|
+
| `probe <server> <tool>` | Live call(s) → response-shape skeleton. |
|
|
131
|
+
| `call <server> <tool> --out <p>` | One live call, raw payload to disk — bootstrap ids or inspect output. |
|
|
132
|
+
| `merge <server>` | Consolidate probe parts into `<server>.shapes.json`. |
|
|
133
|
+
| `login <server>` | Browser OAuth login; tokens stored at `~/.mcpgen/credentials.json`. |
|
|
134
|
+
| `migrate-creds` | Move stored OAuth tokens between `file` / `keyring` backends. |
|
|
135
|
+
| `discover` | List MCP servers configured in your installed agent hosts. |
|
|
136
|
+
|
|
137
|
+
Full workflow and flags: [`doc/USAGE.md`](doc/USAGE.md).
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Authentication
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
mcpgen login <server> # OAuth (most servers)
|
|
145
|
+
mcpgen codegen <server> --bearer "$TOKEN" --out s.py # PAT / bearer
|
|
146
|
+
mcpgen codegen <server> --stdio "python server.py" --out s.py # local stdio, no auth
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Tokens persist in `~/.mcpgen/credentials.json` (chmod 0600) or your OS keystore via `--cred-backend keyring`. In code, `ensure_login(server, url=...)` refreshes silently and only opens a browser when a real login is required.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Who it's for
|
|
154
|
+
|
|
155
|
+
Python developers building AI agent pipelines on MCP servers — especially Claude Code users who've already hand-written at least one `<server>.py` wrapper and felt the pain. And platform teams running multi-server MCP environments where token cost and auth reliability are production concerns.
|
|
156
|
+
|
|
157
|
+
If you write your agent logic in Python and want generated tool wrappers you can actually own — review, pin, and keep in git — this is built for you.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Docs
|
|
162
|
+
|
|
163
|
+
- [`doc/USAGE.md`](doc/USAGE.md) — full end-user guide: install paths, server config, auth, the shape-spec, and calling generated wrappers.
|
|
164
|
+
- [`doc/RUNNING_LOCALLY.md`](doc/RUNNING_LOCALLY.md) — run from a local clone without installing.
|
|
165
|
+
|
|
166
|
+
## Status
|
|
167
|
+
|
|
168
|
+
Early access (`v0.x`). The codegen engine, OAuth persistence, live-probe shaping, and both Claude Code skills are working today. On the roadmap: `--check` drift mode, so CI can flag when a server's tools change out from under your wrappers.
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
MIT.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Idea 2: Claude Code skill that generates typed Python wrappers for any MCP server
|
|
2
|
+
|
|
3
|
+
## What it would do
|
|
4
|
+
|
|
5
|
+
Given an MCP server (URL or local command), the skill:
|
|
6
|
+
|
|
7
|
+
1. Connects via the extracted client library, calls `tools/list`.
|
|
8
|
+
2. For each tool, reads `name`, `description`, `inputSchema` (JSON Schema).
|
|
9
|
+
3. Generates a module like `acme.py`: one `async def` per tool, typed
|
|
10
|
+
arguments derived from the schema, docstring from the description,
|
|
11
|
+
`parse_tool_result` unwrapping.
|
|
12
|
+
4. Optionally probes one real call per tool (with user-supplied sample args)
|
|
13
|
+
to record the actual response shape — the key lesson: schemas describe
|
|
14
|
+
*inputs* well, but response shapes need empirical validation.
|
|
15
|
+
5. Writes a `servers.toml` entry (URL, verify tool, auth mode) so the
|
|
16
|
+
generated module is runnable immediately.
|
|
17
|
+
|
|
18
|
+
## Why a skill and not a pure codegen CLI
|
|
19
|
+
|
|
20
|
+
Two-phase reality:
|
|
21
|
+
|
|
22
|
+
- **Deterministic part** (tools/list → function stubs) could be a plain
|
|
23
|
+
`mcpgen codegen` CLI command — no LLM needed. This should live in the
|
|
24
|
+
library, not the skill.
|
|
25
|
+
- **Judgment part** (which fields to project, how to unwrap vendor envelopes,
|
|
26
|
+
which tools matter for the user's pipeline, sample-call validation) needs an
|
|
27
|
+
LLM in the loop. That's the skill: it drives the CLI, probes live responses,
|
|
28
|
+
and edits the generated code to match observed shapes.
|
|
29
|
+
|
|
30
|
+
Recommended split: CLI generates 80% mechanically; skill does the empirical
|
|
31
|
+
validation pass and trims/curates. This mirrors how the origin project evolved
|
|
32
|
+
(projections were validated one by one against live responses).
|
|
33
|
+
|
|
34
|
+
## Token-economics argument (why colleagues should care)
|
|
35
|
+
|
|
36
|
+
The pattern this enables — confirmed by the landscape research (see
|
|
37
|
+
LANDSCAPE.md): "code execution with MCP" — LLM writes/uses code that calls
|
|
38
|
+
tools, instead of tool schemas + tool results flowing through the model
|
|
39
|
+
context every time.
|
|
40
|
+
|
|
41
|
+
The origin project as case study: stages 1–4 moved from LLM tool-calls to
|
|
42
|
+
Python, eliminating per-record JSON payloads (100–500 KB each) from model
|
|
43
|
+
context entirely.
|
|
44
|
+
|
|
45
|
+
## Locked architecture: the client seam (2026-06-14)
|
|
46
|
+
|
|
47
|
+
Generated wrappers depend on an **injected client Protocol**, never a concrete
|
|
48
|
+
import. This keeps generated modules reusable for colleagues and lets the auth
|
|
49
|
+
backend swap without regenerating:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from typing import Any, Protocol
|
|
53
|
+
|
|
54
|
+
class McpCaller(Protocol):
|
|
55
|
+
async def call(self, server: str, tool: str, arguments: dict) -> Any: ...
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Each generated `async def` takes the caller as its first argument and forwards to
|
|
59
|
+
`caller.call(SERVER, "<tool>", {...})`. Behind the seam today sits `mcpgen/_bridge.py` over the official `mcp`
|
|
60
|
+
SDK (`ClientSession` + `streamablehttp_client`). FastMCP was evaluated and
|
|
61
|
+
rejected as the backend. Wrappers don't change. See VERDICT.md §Correction.
|
|
62
|
+
|
|
63
|
+
## Risks specific to the skill
|
|
64
|
+
|
|
65
|
+
- **Schema drift**: generated wrappers go stale when the server changes.
|
|
66
|
+
Mitigation: `mcpgen codegen --check` mode that re-lists tools and diffs
|
|
67
|
+
against generated code; CI-able.
|
|
68
|
+
- **Response-shape assumptions**: generation from inputSchema alone produces
|
|
69
|
+
wrappers that lie about outputs. Mitigation: validation pass is mandatory in
|
|
70
|
+
the skill procedure, optional in CLI.
|
|
71
|
+
- **Auth variance**: corporate servers (OAuth PKCE) vs local stdio servers vs
|
|
72
|
+
API-key headers. Library must support all three before the skill can claim
|
|
73
|
+
"any MCP server".
|