Skip to main content

mcp-skill

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     │
                                             └────────────────────┘
  1. Connects to the MCP server using fastmcp
  2. Introspects all available tools via list_tools()
  3. Converts each tool's JSON Schema into Python type annotations
  4. Generates a typed App class where each MCP tool becomes an async method
  5. Validates the output with ast.parse → ruff → ty
  6. Generates SKILL.md with 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.

For a deeper explanation of why MCP vs. CLI is the wrong framing, see MCP vs CLI is the Wrong Question.

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 without installing globally
uvx --from . mcp-skill create --url https://your-mcp-server.com/mcp --auth api-key

Requires uv and Python 3.10+.

Release Workflow

Releases are managed by release-please, which only considers commits on main that follow the Conventional Commits format.

Use commit subjects like:

feat: add CLI command to inspect generated apps
fix: handle auth recovery when cached credentials are stale
chore: update release workflow docs

Version bump behavior:

  • feat: creates a minor release
  • fix: creates a patch release
  • feat!: or a BREAKING CHANGE: footer creates a major release

Best practices:

  • Prefer squash merges so the PR title becomes the commit on main
  • Write PR titles in Conventional Commits format
  • Keep the subject line short and imperative
  • Add a short body when the change needs extra context

Optional local enforcement with pre-commit:

uv tool install pre-commit
pre-commit install
pre-commit install --hook-type commit-msg

This repo includes a commit-msg hook in .pre-commit-config.yaml that rejects non-conventional commit subjects before they are created.

If release-please says a commit "could not be parsed", push a new conventional commit to main and rerun the workflow.

Usage

# Interactive mode — prompts for URL, auth type, etc.
uvx --from . mcp-skill create

# Non-interactive mode
uvx --from . mcp-skill create \
  --url https://search-mcp.parallel.ai/mcp \
  --auth api-key \
  --api-key YOUR_KEY \
  --name parallel-search \
  --non-interactive

Discover Existing Apps

Use the CLI as a local catalog for both version-controlled skills/ apps and generated .agents/skills/ apps.

# List every discovered app
uvx --from . mcp-skill list-apps

# List callable functions for a specific app
uvx --from . mcp-skill list-functions notion

# Inspect a specific function's signature and docstring
uvx --from . mcp-skill inspect notion notion_search

Use an App in Python

Once you find the app and method you need, call it from async Python:

import asyncio
from sentry.app import SentryApp


async def main():
    sentry = SentryApp()
    user = await sentry.whoami()
    print(user)


asyncio.run(main())

All generated tool wrappers are async. Use them carefully with await inside an async function. If you skip await, you will get a coroutine object instead of the actual result.

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 ~/.mcp-skill/auth/ after first use — keyed by server URL, so credentials persist across restarts 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 (including PKCE with either dynamic client registration or pre-registered client_id), and none — no mTLS
  • 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 uv and pip install commands
  • Generate __init__.py — Skill directory is a proper Python package
  • Post-generation validation — ast.parse → ruff check → ty check with uvx fallback
  • Package-style imports — Moved app.py to skill root; import via from <skill>.app import <Class>
  • Persistent token storage — FileTree-backed credential storage at ~/.mcp-skill/auth/, keyed by server URL
  • Unified auth signature — All auth types use auth=None in __init__
  • Sanitize skill names — Hyphens/dots converted to underscores for valid Python identifiers
  • Async CLI — CLI commands are fully async via asyncclick
  • Local app discovery — mcp-skill list-apps, mcp-skill list-functions <app>, and mcp-skill inspect <app> <function>
  • Support MCP resources and prompts — Currently only tools are introspected and generated

Metadata

Release files for mcp-skill 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-skill 0.4.0
File Size Uploaded Provenance
mcp_skill-0.4.0.tar.gz 179.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-skill 0.4.0
File Interpreter ABI Platform
mcp_skill-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 205.0 kB

Release files / mcp_skill-0.4.0.tar.gz

Download URL mcp_skill-0.4.0.tar.gz
Size 179.3 kB
Tags Source
SHA-256 checksum
How to use checksums
f9745f497256452627bdb61fc54c28f2ad36ef16c8add140f8f8fc7c156c8c6f
BLAKE2b-256 checksum
How to use checksums
d6a369aa70c66a62fa12bdee492794b0d2e6516e06639a68aa14b15fafc4cced
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / mcp_skill-0.4.0-py3-none-any.whl

Download URL mcp_skill-0.4.0-py3-none-any.whl
Size 25.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a01225645cea6b0debb29f952b24306c3240e089c1fde70b97a8c551a8537307
BLAKE2b-256 checksum
How to use checksums
19f9ce0581ea1a69ecdeea0ed9282ef0805fc3c532f724bbcdb9c087b06f425b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for mcp-skill 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page