mcp-skill 0.2.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.
- mcp_skill-0.2.0/.github/workflows/release.yml +71 -0
- mcp_skill-0.2.0/.release-please-manifest.json +3 -0
- mcp_skill-0.2.0/CHANGELOG.md +30 -0
- mcp_skill-0.2.0/LICENSE +21 -0
- mcp_skill-0.2.0/PKG-INFO +198 -0
- mcp_skill-0.2.0/README.md +169 -0
- mcp_skill-0.2.0/pyproject.toml +43 -0
- mcp_skill-0.2.0/release-please-config.json +12 -0
- mcp_skill-0.2.0/skills/airtable/SKILL.md +308 -0
- mcp_skill-0.2.0/skills/airtable/__init__.py +0 -0
- mcp_skill-0.2.0/skills/airtable/app.py +371 -0
- mcp_skill-0.2.0/skills/canva/SKILL.md +500 -0
- mcp_skill-0.2.0/skills/canva/__init__.py +0 -0
- mcp_skill-0.2.0/skills/canva/app.py +1004 -0
- mcp_skill-0.2.0/skills/clickup/SKILL.md +500 -0
- mcp_skill-0.2.0/skills/clickup/__init__.py +0 -0
- mcp_skill-0.2.0/skills/clickup/app.py +1304 -0
- mcp_skill-0.2.0/skills/context7/SKILL.md +152 -0
- mcp_skill-0.2.0/skills/context7/__init__.py +0 -0
- mcp_skill-0.2.0/skills/context7/app.py +120 -0
- mcp_skill-0.2.0/skills/linear/SKILL.md +500 -0
- mcp_skill-0.2.0/skills/linear/__init__.py +0 -0
- mcp_skill-0.2.0/skills/linear/app.py +1243 -0
- mcp_skill-0.2.0/skills/notion/SKILL.md +500 -0
- mcp_skill-0.2.0/skills/notion/__init__.py +0 -0
- mcp_skill-0.2.0/skills/notion/app.py +794 -0
- mcp_skill-0.2.0/skills/parallel_search/SKILL.md +135 -0
- mcp_skill-0.2.0/skills/parallel_search/__init__.py +0 -0
- mcp_skill-0.2.0/skills/parallel_search/app.py +93 -0
- mcp_skill-0.2.0/skills/pubmed/SKILL.md +409 -0
- mcp_skill-0.2.0/skills/pubmed/__init__.py +0 -0
- mcp_skill-0.2.0/skills/pubmed/app.py +463 -0
- mcp_skill-0.2.0/skills/sentry/SKILL.md +500 -0
- mcp_skill-0.2.0/skills/sentry/__init__.py +0 -0
- mcp_skill-0.2.0/skills/sentry/app.py +785 -0
- mcp_skill-0.2.0/src/mcp_skill/__init__.py +3 -0
- mcp_skill-0.2.0/src/mcp_skill/auth/__init__.py +16 -0
- mcp_skill-0.2.0/src/mcp_skill/auth/api_key.py +45 -0
- mcp_skill-0.2.0/src/mcp_skill/auth/bearer.py +39 -0
- mcp_skill-0.2.0/src/mcp_skill/auth/client_credentials.py +16 -0
- mcp_skill-0.2.0/src/mcp_skill/cli.py +160 -0
- mcp_skill-0.2.0/src/mcp_skill/generator.py +184 -0
- mcp_skill-0.2.0/src/mcp_skill/introspector.py +69 -0
- mcp_skill-0.2.0/src/mcp_skill/templates/app.py.j2 +98 -0
- mcp_skill-0.2.0/src/mcp_skill/templates/skill.md.j2 +166 -0
- mcp_skill-0.2.0/src/mcp_skill/type_mapper.py +210 -0
- mcp_skill-0.2.0/src/mcp_skill/validator.py +91 -0
- mcp_skill-0.2.0/uv.lock +1676 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# .github/workflows/release.yml
|
|
2
|
+
#
|
|
3
|
+
# Release workflow: automated changelog + PyPI publishing
|
|
4
|
+
#
|
|
5
|
+
# How it works:
|
|
6
|
+
# 1. On every push to main, release-please checks for conventional commits
|
|
7
|
+
# and maintains a "Release PR" with updated CHANGELOG.md and version bump.
|
|
8
|
+
# 2. When the Release PR is merged, release-please creates a GitHub release.
|
|
9
|
+
# 3. The publish job then builds and publishes to PyPI using trusted publishing.
|
|
10
|
+
#
|
|
11
|
+
# Setup required (one-time, manual):
|
|
12
|
+
# - On PyPI: Settings → Publishing → Add trusted publisher
|
|
13
|
+
# Publisher: GitHub Actions
|
|
14
|
+
# Repository: manojbajaj95/mcp-skill
|
|
15
|
+
# Workflow: release.yml
|
|
16
|
+
# Environment: pypi
|
|
17
|
+
# - On GitHub: Settings → Environments → Create "pypi" environment
|
|
18
|
+
#
|
|
19
|
+
# Conventional commits required for release-please:
|
|
20
|
+
# feat: → minor version bump (0.1.0 → 0.2.0)
|
|
21
|
+
# fix: → patch version bump (0.1.0 → 0.1.1)
|
|
22
|
+
# feat!: → major version bump (0.1.0 → 1.0.0)
|
|
23
|
+
|
|
24
|
+
name: release
|
|
25
|
+
|
|
26
|
+
on:
|
|
27
|
+
push:
|
|
28
|
+
branches:
|
|
29
|
+
- main
|
|
30
|
+
workflow_dispatch:
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
release-please:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
permissions:
|
|
36
|
+
contents: write
|
|
37
|
+
pull-requests: write
|
|
38
|
+
outputs:
|
|
39
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
40
|
+
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
41
|
+
steps:
|
|
42
|
+
- uses: googleapis/release-please-action@v4
|
|
43
|
+
id: release
|
|
44
|
+
with:
|
|
45
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
46
|
+
config-file: release-please-config.json
|
|
47
|
+
manifest-file: .release-please-manifest.json
|
|
48
|
+
|
|
49
|
+
publish:
|
|
50
|
+
name: Publish to PyPI
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
needs: release-please
|
|
53
|
+
if: ${{ needs.release-please.outputs.release_created }}
|
|
54
|
+
environment:
|
|
55
|
+
name: pypi
|
|
56
|
+
url: https://pypi.org/project/mcp-skill/
|
|
57
|
+
permissions:
|
|
58
|
+
id-token: write # Required for PyPI trusted publishing (OIDC)
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
|
|
62
|
+
- name: Install uv
|
|
63
|
+
uses: astral-sh/setup-uv@v7
|
|
64
|
+
with:
|
|
65
|
+
enable-cache: true
|
|
66
|
+
|
|
67
|
+
- name: Build package
|
|
68
|
+
run: uv build
|
|
69
|
+
|
|
70
|
+
- name: Publish to PyPI
|
|
71
|
+
run: uv publish --trusted-publishing always
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.0](https://github.com/manojbajaj95/mcp-skill/compare/v0.1.0...v0.2.0) (2026-03-11)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* Add generated skills ([c9c81b9](https://github.com/manojbajaj95/mcp-skill/commit/c9c81b93344c1e45ce152e8a58ef264835f89f8d))
|
|
9
|
+
* add persistent disk-backed token storage for all auth types ([531e402](https://github.com/manojbajaj95/mcp-skill/commit/531e40278268fb2997be012ef39e4b3ee0774078))
|
|
10
|
+
* **auth:** add ClientCredentialsAuth stub (NotImplementedError placeholder) ([133ebcc](https://github.com/manojbajaj95/mcp-skill/commit/133ebcc50aad4b04d3c60ff5dfe064daca4abc87))
|
|
11
|
+
* **auth:** extract auth classes into importable mcp_skill.auth module ([e0d0873](https://github.com/manojbajaj95/mcp-skill/commit/e0d0873d208100b05d40705108e78ecb47b48f0d))
|
|
12
|
+
* **cli:** add interactive wizard and non-interactive CLI mode ([2e7e30c](https://github.com/manojbajaj95/mcp-skill/commit/2e7e30c8d38d654368ab23b36dd06249b1c4770e))
|
|
13
|
+
* **core:** add introspector, app.py generator, and SKILL.md generator ([5256855](https://github.com/manojbajaj95/mcp-skill/commit/52568551306122451ef5cafcbe63103d22138b1a))
|
|
14
|
+
* fix skill output, add deps/usage to SKILL.md, add post-gen validation ([516218b](https://github.com/manojbajaj95/mcp-skill/commit/516218bc74c4153b7b569bf9f7668cd04a9ec86a))
|
|
15
|
+
* **scaffold:** initialize mcp-skill project with UV and pyproject.toml ([afcecd9](https://github.com/manojbajaj95/mcp-skill/commit/afcecd9f045a08c17a48bfd3973ac5b2d18a9ff3))
|
|
16
|
+
* **templates:** unify __init__ signature to use auth=None across all auth types ([99bd4ef](https://github.com/manojbajaj95/mcp-skill/commit/99bd4ef5ff54cd1c681d3b44a9887ff4eef22058))
|
|
17
|
+
* **types:** add JSON Schema to Python type mapper and name sanitizer ([6234fa1](https://github.com/manojbajaj95/mcp-skill/commit/6234fa1781963897c7b4407c923a1eb1b6d5d15f))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* Fix parallel search name ([c5afbf6](https://github.com/manojbajaj95/mcp-skill/commit/c5afbf62d9c51346709c8fd7f87e0945138c35b5))
|
|
23
|
+
* **generate:** use per-method client pattern and user-provided app name ([c65bb3e](https://github.com/manojbajaj95/mcp-skill/commit/c65bb3e2412f740706f155b4a243389003ea2f6f))
|
|
24
|
+
* **generate:** use result.content for CallToolResult and filter None optional args ([48e4b56](https://github.com/manojbajaj95/mcp-skill/commit/48e4b566e3d2a88e0daaaaa6955c269ce944e764))
|
|
25
|
+
* sanitize skill name to valid Python identifier for folder and imports ([afba3b6](https://github.com/manojbajaj95/mcp-skill/commit/afba3b683860c294ec28d7716eab5a553e1ff099))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Documentation
|
|
29
|
+
|
|
30
|
+
* rewrite README with before/after, diagram, generated output, and audience section ([e341453](https://github.com/manojbajaj95/mcp-skill/commit/e341453e690ff8fa46422b270a70f8bf271426d8))
|
mcp_skill-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mcp-skill contributors
|
|
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.
|
mcp_skill-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-skill
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Convert any MCP server into an Agent Skill
|
|
5
|
+
Project-URL: Homepage, https://github.com/manojbajaj95/mcp-skill
|
|
6
|
+
Project-URL: Repository, https://github.com/manojbajaj95/mcp-skill
|
|
7
|
+
Project-URL: Issues, https://github.com/manojbajaj95/mcp-skill/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agent,code-generation,fastmcp,llm,mcp,skill
|
|
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.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: click>=8.0
|
|
23
|
+
Requires-Dist: fastmcp>=2.0
|
|
24
|
+
Requires-Dist: jinja2>=3.1
|
|
25
|
+
Requires-Dist: py-key-value-aio[disk]
|
|
26
|
+
Requires-Dist: ruff>=0.15.4
|
|
27
|
+
Requires-Dist: ty>=0.0.20
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# mcp-skill
|
|
31
|
+
|
|
32
|
+
Turn any MCP server into a typed Python SDK.
|
|
33
|
+
|
|
34
|
+
> **Compile MCP tools into code.**
|
|
35
|
+
|
|
36
|
+
`mcp-skill` introspects an MCP server and generates a Python class where each tool becomes a typed async method.
|
|
37
|
+
|
|
38
|
+
## Before and After
|
|
39
|
+
|
|
40
|
+
### Before
|
|
41
|
+
|
|
42
|
+
Agents call MCP tools through the model loop — one round-trip per tool call:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
llm.call_tool("web_search_preview", {"query": "..."})
|
|
46
|
+
# → model decides next step → calls another tool → model decides again → ...
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### After
|
|
50
|
+
|
|
51
|
+
Agents call tools directly in code:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
result = await app.web_search_preview(
|
|
55
|
+
objective="find latest news",
|
|
56
|
+
search_queries=["topic X 2026"]
|
|
57
|
+
)
|
|
58
|
+
# Agent processes result in code — no round-trip back to model
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## How It Works
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
┌─────────────┐ ┌───────────────┐ ┌────────────────────┐
|
|
65
|
+
│ MCP Server │─────▶│ mcp-skill │─────▶│ Generated Skill │
|
|
66
|
+
│ (any URL) │ │ CLI │ │ │
|
|
67
|
+
│ │ │ │ │ app.py │
|
|
68
|
+
│ Tools: │ │ 1. Connect │ │ ├─ Typed class │
|
|
69
|
+
│ - search │ │ 2. Introspec │ │ ├─ Async methods │
|
|
70
|
+
│ - fetch │ │ 3. Map types │ │ ├─ Auth + storage │
|
|
71
|
+
│ - ... │ │ 4. Generate │ │ └─ JSON parsing │
|
|
72
|
+
│ │ │ 5. Validate │ │ │
|
|
73
|
+
└─────────────┘ └───────────────┘ │ SKILL.md │
|
|
74
|
+
│ └─ Agent docs │
|
|
75
|
+
└────────────────────┘
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
1. Connects to the MCP server using [fastmcp](https://github.com/jlowin/fastmcp)
|
|
79
|
+
2. Introspects all available tools via `list_tools()`
|
|
80
|
+
3. Converts each tool's JSON Schema into Python type annotations
|
|
81
|
+
4. Generates a typed `App` class where each MCP tool becomes an `async` method
|
|
82
|
+
5. Validates the output with `ast.parse` → `ruff` → `ty`
|
|
83
|
+
6. Generates `SKILL.md` with tool documentation and usage examples for agents
|
|
84
|
+
|
|
85
|
+
## Motivation
|
|
86
|
+
|
|
87
|
+
MCP servers give agents access to tools, but every tool call round-trips through the model — request tool, execute, full result back into context, decide next step. For large payloads or sequential calls, this burns tokens and adds latency.
|
|
88
|
+
|
|
89
|
+
[Programmatic Tool Calling](https://platform.claude.com/cookbook/tool-use-programmatic-tool-calling-ptc) fixes this: the agent writes code that calls tools directly, without model round-trips per invocation. Fetch, filter, aggregate — all in one code block.
|
|
90
|
+
|
|
91
|
+
**mcp-skill** makes this possible by compiling any MCP server into a plain Python class. Each tool becomes a typed async method. The agent just writes Python.
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from parallel_search.app import ParallelApp
|
|
95
|
+
|
|
96
|
+
app = ParallelApp(auth="sk-...")
|
|
97
|
+
result = await app.web_search_preview(
|
|
98
|
+
objective="find latest news on topic X",
|
|
99
|
+
search_queries=["topic X 2026"]
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Setup
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Install with uv
|
|
107
|
+
uv pip install -e .
|
|
108
|
+
|
|
109
|
+
# Or use directly
|
|
110
|
+
uv run mcp-skill create --url https://your-mcp-server.com/mcp --auth api-key
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Requires [uv](https://github.com/astral-sh/uv) and Python 3.10+.
|
|
114
|
+
|
|
115
|
+
## Usage
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Interactive mode — prompts for URL, auth type, etc.
|
|
119
|
+
mcp-skill create
|
|
120
|
+
|
|
121
|
+
# Non-interactive mode
|
|
122
|
+
mcp-skill create \
|
|
123
|
+
--url https://search-mcp.parallel.ai/mcp \
|
|
124
|
+
--auth api-key \
|
|
125
|
+
--api-key YOUR_KEY \
|
|
126
|
+
--name parallel-search \
|
|
127
|
+
--non-interactive
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Generated Output
|
|
131
|
+
|
|
132
|
+
The skill lands in `.agents/skills/<name>/` as a Python package:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
.agents/skills/parallel_search/
|
|
136
|
+
├── __init__.py
|
|
137
|
+
├── app.py # Typed Python class wrapping the MCP server
|
|
138
|
+
└── SKILL.md # Agent-facing docs, dependencies, and usage
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Here's what the generated `app.py` looks like:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
class ParallelApp:
|
|
145
|
+
|
|
146
|
+
def __init__(self, url: str = "https://...", auth=None) -> None:
|
|
147
|
+
...
|
|
148
|
+
|
|
149
|
+
async def web_search_preview(
|
|
150
|
+
self,
|
|
151
|
+
objective: str,
|
|
152
|
+
search_queries: list[str],
|
|
153
|
+
) -> dict[str, Any]:
|
|
154
|
+
"""Search the web with multiple queries in parallel."""
|
|
155
|
+
...
|
|
156
|
+
|
|
157
|
+
async def fetch_url(
|
|
158
|
+
self,
|
|
159
|
+
url: str,
|
|
160
|
+
max_length: int = None,
|
|
161
|
+
) -> dict[str, Any]:
|
|
162
|
+
"""Fetch and extract content from a URL."""
|
|
163
|
+
...
|
|
164
|
+
|
|
165
|
+
def list_tools(self):
|
|
166
|
+
return [self.web_search_preview, self.fetch_url]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Each method connects to the MCP server, calls the underlying tool, and returns parsed JSON. Auth credentials are persisted to disk (`~/.mcp-skill/<name>/`) after first use — provide once, reuse automatically.
|
|
170
|
+
|
|
171
|
+
## Who Is This For?
|
|
172
|
+
|
|
173
|
+
Developers building:
|
|
174
|
+
|
|
175
|
+
- **MCP-based agents** that need direct tool access without model round-trips
|
|
176
|
+
- **Automation systems** using MCP tools as programmatic building blocks
|
|
177
|
+
- **Code-execution agents** using [Programmatic Tool Calling](https://platform.claude.com/cookbook/tool-use-programmatic-tool-calling-ptc)
|
|
178
|
+
|
|
179
|
+
## Current Limitations
|
|
180
|
+
|
|
181
|
+
- **Auth**: Supports API key (Bearer or custom header), OAuth, and none — no mTLS or complex auth flows
|
|
182
|
+
- **Runtime dependency**: Generated code depends on [fastmcp](https://github.com/jlowin/fastmcp) for MCP client connections
|
|
183
|
+
- **Connection per call**: Each method creates a new MCP client connection (no pooling)
|
|
184
|
+
- **Tools only**: MCP resources and prompts not yet supported
|
|
185
|
+
|
|
186
|
+
## Task List
|
|
187
|
+
|
|
188
|
+
Tracked improvements based on real-world usage:
|
|
189
|
+
|
|
190
|
+
- [x] **Fix output directory path** — Changed from `.agents/skill/<name>` to `.agents/skills/<name>`
|
|
191
|
+
- [x] **Add dependency info to SKILL.md** — Dependencies listed with `uv` and `pip` install commands
|
|
192
|
+
- [x] **Generate `__init__.py`** — Skill directory is a proper Python package
|
|
193
|
+
- [x] **Post-generation validation** — `ast.parse` → `ruff check` → `ty check` with `uvx` fallback
|
|
194
|
+
- [x] **Package-style imports** — Moved `app.py` to skill root; import via `from <skill>.app import <Class>`
|
|
195
|
+
- [x] **Persistent token storage** — Disk-backed credential storage at `~/.mcp-skill/<name>/`
|
|
196
|
+
- [x] **Unified auth signature** — All auth types use `auth=None` in `__init__`
|
|
197
|
+
- [x] **Sanitize skill names** — Hyphens/dots converted to underscores for valid Python identifiers
|
|
198
|
+
- [ ] **Support MCP resources and prompts** — Currently only tools are introspected and generated
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# mcp-skill
|
|
2
|
+
|
|
3
|
+
Turn any MCP server into a typed Python SDK.
|
|
4
|
+
|
|
5
|
+
> **Compile MCP tools into code.**
|
|
6
|
+
|
|
7
|
+
`mcp-skill` introspects an MCP server and generates a Python class where each tool becomes a typed async method.
|
|
8
|
+
|
|
9
|
+
## Before and After
|
|
10
|
+
|
|
11
|
+
### Before
|
|
12
|
+
|
|
13
|
+
Agents call MCP tools through the model loop — one round-trip per tool call:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
llm.call_tool("web_search_preview", {"query": "..."})
|
|
17
|
+
# → model decides next step → calls another tool → model decides again → ...
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### After
|
|
21
|
+
|
|
22
|
+
Agents call tools directly in code:
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
result = await app.web_search_preview(
|
|
26
|
+
objective="find latest news",
|
|
27
|
+
search_queries=["topic X 2026"]
|
|
28
|
+
)
|
|
29
|
+
# Agent processes result in code — no round-trip back to model
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## How It Works
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
┌─────────────┐ ┌───────────────┐ ┌────────────────────┐
|
|
36
|
+
│ MCP Server │─────▶│ mcp-skill │─────▶│ Generated Skill │
|
|
37
|
+
│ (any URL) │ │ CLI │ │ │
|
|
38
|
+
│ │ │ │ │ app.py │
|
|
39
|
+
│ Tools: │ │ 1. Connect │ │ ├─ Typed class │
|
|
40
|
+
│ - search │ │ 2. Introspec │ │ ├─ Async methods │
|
|
41
|
+
│ - fetch │ │ 3. Map types │ │ ├─ Auth + storage │
|
|
42
|
+
│ - ... │ │ 4. Generate │ │ └─ JSON parsing │
|
|
43
|
+
│ │ │ 5. Validate │ │ │
|
|
44
|
+
└─────────────┘ └───────────────┘ │ SKILL.md │
|
|
45
|
+
│ └─ Agent docs │
|
|
46
|
+
└────────────────────┘
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
1. Connects to the MCP server using [fastmcp](https://github.com/jlowin/fastmcp)
|
|
50
|
+
2. Introspects all available tools via `list_tools()`
|
|
51
|
+
3. Converts each tool's JSON Schema into Python type annotations
|
|
52
|
+
4. Generates a typed `App` class where each MCP tool becomes an `async` method
|
|
53
|
+
5. Validates the output with `ast.parse` → `ruff` → `ty`
|
|
54
|
+
6. Generates `SKILL.md` with tool documentation and usage examples for agents
|
|
55
|
+
|
|
56
|
+
## Motivation
|
|
57
|
+
|
|
58
|
+
MCP servers give agents access to tools, but every tool call round-trips through the model — request tool, execute, full result back into context, decide next step. For large payloads or sequential calls, this burns tokens and adds latency.
|
|
59
|
+
|
|
60
|
+
[Programmatic Tool Calling](https://platform.claude.com/cookbook/tool-use-programmatic-tool-calling-ptc) fixes this: the agent writes code that calls tools directly, without model round-trips per invocation. Fetch, filter, aggregate — all in one code block.
|
|
61
|
+
|
|
62
|
+
**mcp-skill** makes this possible by compiling any MCP server into a plain Python class. Each tool becomes a typed async method. The agent just writes Python.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from parallel_search.app import ParallelApp
|
|
66
|
+
|
|
67
|
+
app = ParallelApp(auth="sk-...")
|
|
68
|
+
result = await app.web_search_preview(
|
|
69
|
+
objective="find latest news on topic X",
|
|
70
|
+
search_queries=["topic X 2026"]
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Setup
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Install with uv
|
|
78
|
+
uv pip install -e .
|
|
79
|
+
|
|
80
|
+
# Or use directly
|
|
81
|
+
uv run mcp-skill create --url https://your-mcp-server.com/mcp --auth api-key
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Requires [uv](https://github.com/astral-sh/uv) and Python 3.10+.
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Interactive mode — prompts for URL, auth type, etc.
|
|
90
|
+
mcp-skill create
|
|
91
|
+
|
|
92
|
+
# Non-interactive mode
|
|
93
|
+
mcp-skill create \
|
|
94
|
+
--url https://search-mcp.parallel.ai/mcp \
|
|
95
|
+
--auth api-key \
|
|
96
|
+
--api-key YOUR_KEY \
|
|
97
|
+
--name parallel-search \
|
|
98
|
+
--non-interactive
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Generated Output
|
|
102
|
+
|
|
103
|
+
The skill lands in `.agents/skills/<name>/` as a Python package:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
.agents/skills/parallel_search/
|
|
107
|
+
├── __init__.py
|
|
108
|
+
├── app.py # Typed Python class wrapping the MCP server
|
|
109
|
+
└── SKILL.md # Agent-facing docs, dependencies, and usage
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Here's what the generated `app.py` looks like:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
class ParallelApp:
|
|
116
|
+
|
|
117
|
+
def __init__(self, url: str = "https://...", auth=None) -> None:
|
|
118
|
+
...
|
|
119
|
+
|
|
120
|
+
async def web_search_preview(
|
|
121
|
+
self,
|
|
122
|
+
objective: str,
|
|
123
|
+
search_queries: list[str],
|
|
124
|
+
) -> dict[str, Any]:
|
|
125
|
+
"""Search the web with multiple queries in parallel."""
|
|
126
|
+
...
|
|
127
|
+
|
|
128
|
+
async def fetch_url(
|
|
129
|
+
self,
|
|
130
|
+
url: str,
|
|
131
|
+
max_length: int = None,
|
|
132
|
+
) -> dict[str, Any]:
|
|
133
|
+
"""Fetch and extract content from a URL."""
|
|
134
|
+
...
|
|
135
|
+
|
|
136
|
+
def list_tools(self):
|
|
137
|
+
return [self.web_search_preview, self.fetch_url]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Each method connects to the MCP server, calls the underlying tool, and returns parsed JSON. Auth credentials are persisted to disk (`~/.mcp-skill/<name>/`) after first use — provide once, reuse automatically.
|
|
141
|
+
|
|
142
|
+
## Who Is This For?
|
|
143
|
+
|
|
144
|
+
Developers building:
|
|
145
|
+
|
|
146
|
+
- **MCP-based agents** that need direct tool access without model round-trips
|
|
147
|
+
- **Automation systems** using MCP tools as programmatic building blocks
|
|
148
|
+
- **Code-execution agents** using [Programmatic Tool Calling](https://platform.claude.com/cookbook/tool-use-programmatic-tool-calling-ptc)
|
|
149
|
+
|
|
150
|
+
## Current Limitations
|
|
151
|
+
|
|
152
|
+
- **Auth**: Supports API key (Bearer or custom header), OAuth, and none — no mTLS or complex auth flows
|
|
153
|
+
- **Runtime dependency**: Generated code depends on [fastmcp](https://github.com/jlowin/fastmcp) for MCP client connections
|
|
154
|
+
- **Connection per call**: Each method creates a new MCP client connection (no pooling)
|
|
155
|
+
- **Tools only**: MCP resources and prompts not yet supported
|
|
156
|
+
|
|
157
|
+
## Task List
|
|
158
|
+
|
|
159
|
+
Tracked improvements based on real-world usage:
|
|
160
|
+
|
|
161
|
+
- [x] **Fix output directory path** — Changed from `.agents/skill/<name>` to `.agents/skills/<name>`
|
|
162
|
+
- [x] **Add dependency info to SKILL.md** — Dependencies listed with `uv` and `pip` install commands
|
|
163
|
+
- [x] **Generate `__init__.py`** — Skill directory is a proper Python package
|
|
164
|
+
- [x] **Post-generation validation** — `ast.parse` → `ruff check` → `ty check` with `uvx` fallback
|
|
165
|
+
- [x] **Package-style imports** — Moved `app.py` to skill root; import via `from <skill>.app import <Class>`
|
|
166
|
+
- [x] **Persistent token storage** — Disk-backed credential storage at `~/.mcp-skill/<name>/`
|
|
167
|
+
- [x] **Unified auth signature** — All auth types use `auth=None` in `__init__`
|
|
168
|
+
- [x] **Sanitize skill names** — Hyphens/dots converted to underscores for valid Python identifiers
|
|
169
|
+
- [ ] **Support MCP resources and prompts** — Currently only tools are introspected and generated
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mcp-skill"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Convert any MCP server into an Agent Skill"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
keywords = ["mcp", "agent", "skill", "code-generation", "fastmcp", "llm"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Topic :: Software Development :: Code Generators",
|
|
23
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"fastmcp>=2.0",
|
|
27
|
+
"click>=8.0",
|
|
28
|
+
"jinja2>=3.1",
|
|
29
|
+
"ruff>=0.15.4",
|
|
30
|
+
"ty>=0.0.20",
|
|
31
|
+
"py-key-value-aio[disk]",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/manojbajaj95/mcp-skill"
|
|
36
|
+
Repository = "https://github.com/manojbajaj95/mcp-skill"
|
|
37
|
+
Issues = "https://github.com/manojbajaj95/mcp-skill/issues"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
mcp-skill = "mcp_skill.cli:main"
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.wheel]
|
|
43
|
+
packages = ["src/mcp_skill"]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
|
|
3
|
+
"packages": {
|
|
4
|
+
".": {
|
|
5
|
+
"release-type": "python",
|
|
6
|
+
"package-name": "mcp-skill",
|
|
7
|
+
"bump-minor-pre-major": true,
|
|
8
|
+
"changelog-path": "CHANGELOG.md",
|
|
9
|
+
"include-component-in-tag": false
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|