Turn any MCP server into a typed Python SDK.
Compile MCP tools into code.
mcp-skill introspects an MCP server and generates a Python class where each tool becomes a typed async method.
Before and After
Before
Agents call MCP tools through the model loop — one round-trip per tool call:
llm.call_tool("web_search_preview", {"query": "..."})
# → model decides next step → calls another tool → model decides again → ...
After
Agents call tools directly in code:
result = await app.web_search_preview( objective="find latest news", search_queries=["topic X 2026"] ) # Agent processes result in code — no round-trip back to model
How It Works
┌─────────────┐ ┌───────────────┐ ┌────────────────────┐
│ MCP Server │─────▶│ mcp-skill │─────▶│ Generated Skill │
│ (any URL) │ │ CLI │ │ │
│ │ │ │ │ app.py │
│ Tools: │ │ 1. Connect │ │ ├─ Typed class │
│ - search │ │ 2. Introspec │ │ ├─ Async methods │
│ - fetch │ │ 3. Map types │ │ ├─ Auth + storage │
│ - ... │ │ 4. Generate │ │ └─ JSON parsing │
│ │ │ 5. Validate │ │ │
└─────────────┘ └───────────────┘ │ SKILL.md │
│ └─ Agent docs │
└────────────────────┘
- Connects to the MCP server using fastmcp
- Introspects all available tools via
list_tools() - Converts each tool's JSON Schema into Python type annotations
- Generates a typed
Appclass where each MCP tool becomes anasyncmethod - Validates the output with
ast.parse→ruff→ty - Generates
SKILL.mdwith tool documentation and usage examples for agents
Motivation
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.
Programmatic Tool Calling fixes this: the agent writes code that calls tools directly, without model round-trips per invocation. Fetch, filter, aggregate — all in one code block.
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.
from parallel_search.app import ParallelApp app = ParallelApp(auth="sk-...") result = await app.web_search_preview( objective="find latest news on topic X", search_queries=["topic X 2026"] )
Setup
# Install with uv uv pip install -e . # Or use directly uv run mcp-skill create --url https://your-mcp-server.com/mcp --auth api-key
Requires uv and Python 3.10+.
Usage
# Interactive mode — prompts for URL, auth type, etc. mcp-skill create # Non-interactive mode mcp-skill create \ --url https://search-mcp.parallel.ai/mcp \ --auth api-key \ --api-key YOUR_KEY \ --name parallel-search \ --non-interactive
Generated Output
The skill lands in .agents/skills/<name>/ as a Python package:
.agents/skills/parallel_search/
├── __init__.py
├── app.py # Typed Python class wrapping the MCP server
└── SKILL.md # Agent-facing docs, dependencies, and usage
Here's what the generated app.py looks like:
class ParallelApp: def __init__(self, url: str = "https://...", auth=None) -> None: ... async def web_search_preview( self, objective: str, search_queries: list[str], ) -> dict[str, Any]: """Search the web with multiple queries in parallel.""" ... async def fetch_url( self, url: str, max_length: int = None, ) -> dict[str, Any]: """Fetch and extract content from a URL.""" ... def list_tools(self): return [self.web_search_preview, self.fetch_url]
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.
Who Is This For?
Developers building:
- MCP-based agents that need direct tool access without model round-trips
- Automation systems using MCP tools as programmatic building blocks
- Code-execution agents using Programmatic Tool Calling
Current Limitations
- Auth: Supports API key (Bearer or custom header), OAuth, and none — no mTLS or complex auth flows
- Runtime dependency: Generated code depends on fastmcp for MCP client connections
- Connection per call: Each method creates a new MCP client connection (no pooling)
- Tools only: MCP resources and prompts not yet supported
Task List
Tracked improvements based on real-world usage:
- Fix output directory path — Changed from
.agents/skill/<name>to.agents/skills/<name> - Add dependency info to SKILL.md — Dependencies listed with
uvandpipinstall commands - Generate
__init__.py— Skill directory is a proper Python package - Post-generation validation —
ast.parse→ruff check→ty checkwithuvxfallback - Package-style imports — Moved
app.pyto skill root; import viafrom <skill>.app import <Class> - Persistent token storage — Disk-backed credential storage at
~/.mcp-skill/<name>/ - Unified auth signature — All auth types use
auth=Nonein__init__ - Sanitize skill names — Hyphens/dots converted to underscores for valid Python identifiers
- Support MCP resources and prompts — Currently only tools are introspected and generated