The Model Context Protocol (MCP) is how AI tools like Claude, Cursor, and
Windsurf connect to external software. You write an MCP server that exposes
"tools", and the AI can call them.
The problem? Writing MCP servers by hand is tedious. You're essentially
translating your app's existing API surface — CLI commands, HTTP endpoints,
Python functions — into MCP tool definitions. For a FastAPI app with 20 routes,
that's a lot of boilerplate.
So I automated it.
What is MCP-Anything?
pip install -e ".[dev]"
mcp-anything generate /path/to/your/app
One command. It scans your project, figures out what it does, and generates a
complete MCP server package — pip-installable, with tests, docs, and a
ready-to-paste config for Claude Code.
GitHub: github.com/gabrielekarra/mcp-anything
How it works
The pipeline has 6 phases:
ANALYZE → DESIGN → IMPLEMENT → TEST → DOCUMENT → PACKAGE
Phase 1: Analyze
Eight detectors scan for IPC mechanisms — how does this app communicate with the
outside world?
- CLI — argparse, click, typer, fire
- HTTP frameworks — FastAPI, Flask (Python AST-based)
- Spring Boot — Java annotations via regex
- OpenAPI/Swagger — spec file parsing (no source code needed)
- Sockets, gRPC, file I/O — and more
For Python apps, the analyzer uses the ast module to extract functions,
parameters with types, default values, docstrings, and decorators. No regex hacks
for Python — full AST parsing.
For FastAPI specifically, it understands:
- @app.get("/users/{user_id}") route decorators
- Query(), Path(), Body() parameter injection
- Depends() filtering (skips injected dependencies)
- APIRouter(prefix="/items") prefix resolution
- Pydantic models as request body types
Phase 2: Design
Each detected capability gets mapped to an implementation strategy:
┌────────────────┬─────────────────────────────┬─────────────────────────────┐
│ Strategy │ When │ How it works │
├────────────────┼─────────────────────────────┼─────────────────────────────┤
│ http_call │ REST endpoints │ Async HTTP request via │
│ │ │ httpx │
├────────────────┼─────────────────────────────┼─────────────────────────────┤
│ cli_subcommand │ CLI apps │ Subprocess with args │
├────────────────┼─────────────────────────────┼─────────────────────────────┤
│ python_call │ Python libraries │ Direct import and call │
├────────────────┼─────────────────────────────┼─────────────────────────────┤
│ cli_function │ Python functions in CLI │ Wrapped subprocess call │
│ │ apps │ │
└────────────────┴─────────────────────────────┴─────────────────────────────┘
Parameters are categorized as path, query, or body based on their position in the
route and annotations.
Phase 3-6: Generate
Jinja2 templates produce a complete Python package:
mcp-my-app-server/
├── src/my_app/
│ ├── server.py # FastMCP entry point
│ ├── backend.py # Async HTTP/CLI backend
│ └── tools/ # Generated tool modules
├── tests/ # Pytest tests
├── docs/ # API documentation
└── pyproject.toml # Ready to pip install
Every generated Python file is validated with ast.parse() before writing. If the
templates produce invalid syntax, the pipeline fails fast.
Real example: FastAPI app
I tested against tiangolo's full-stack-fastapi-template — a production FastAPI
app with users, items, auth, and multiple routers.
mcp-anything generate ./backend/app --name fastapi-users --no-llm
Output:
Scanning codebase...
Found 27 source files
Running IPC detectors...
Flask/FastAPI Detector: protocol (confidence: 0.95, 28 signals)
Running AST analysis...
AST: 64 functions, 22 classes
Fastapi: 23 HTTP routes
✓ analyze complete
Designed 49 tools
✓ All phases complete
23 HTTP routes extracted across 5 routers (/items/, /users/, /login/, /utils/,
/private/), with correct parameter schemas. The generated tools include things
like:
@server.tool()
async def get_users(
skip: int | None = 0,
limit: int | None = 100,
) -> str:
"""GET /users/ - Retrieve users."""
query_params = {}
if skip is not None:
query_params["skip"] = str(skip)
if limit is not None:
query_params["limit"] = str(limit)
return await backend.request("GET", "/users/", params=query_params)
The SessionDep and CurrentUser dependency-injected params were correctly filtered
out. Only user-facing parameters made it into the tool schema.
Real example: OpenAPI spec (no source code)
You don't even need source code. Point it at a directory with an OpenAPI spec:
mcp-anything generate /path/to/realworld --name conduit-api --no-llm
It found specs/api/openapi.yml via recursive search and extracted 19 endpoints
from the RealWorld Conduit API:
login POST /users/login - Existing user login
createuser POST /users - Register a new user
getarticles GET /articles - Get recent articles globally
createarticle POST /articles - Create an article
getarticle GET /articles/{slug} - Get an article
createarticlecomment POST /articles/{slug}/comments - Create a comment
...
With $ref resolution for request body schemas, enum values, and nested path
parameters.
The interesting technical bits
AST-based parameter extraction for FastAPI
FastAPI uses Python type annotations and default values for dependency injection.
A function like:
async def list_users(
session: SessionDep,
current_user: CurrentUser,
skip: int = 0,
limit: int = Query(10, description="Max results"),
):
The analyzer needs to:
- Skip session and current_user (dependency-injected types)
- Recognize skip as a plain query parameter with default 0
- Parse Query(10, description="Max results") to extract the default value and description
- Handle Depends(), Path(), Body(), Header(), Cookie() — each one differently
This is all done via Python's ast module — no imports, no runtime inspection, no
dependencies on FastAPI itself.
Java annotation parsing without a Java parser
For Spring Boot, I couldn't use Python's ast module. Instead, it's regex-based
with some tricks:
- A balanced parenthesis extractor handles nested annotations like @RequestParam(required = false, defaultValue = "10")
- Generic return types like List are captured with [\w<>,\s]+?
- Controller-level @RequestMapping("/api") is prepended to method-level paths
OpenAPI $ref resolution
OpenAPI specs reference schemas via $ref: "#/components/schemas/Pet". The
analyzer resolves these by walking the JSON pointer path and inlining the
referenced schema's properties as individual tool parameters — so instead of a
single opaque body: object parameter, you get name: string (required), species:
string, age: integer.
What's missing
- Authentication — no API key, Bearer token, or OAuth support yet. Generated servers can't auth against protected APIs.
- More languages — Express.js, Go, Django REST Framework, Rust are on the roadmap.
- Schema depth — nested object properties aren't fully expanded yet.
See the full ROADMAP for planned features.
Try it
git clone https://github.com/gabrielekarra/mcp-anything.git
cd mcp-anything
pip install -e ".[dev]"
# Try it on your own project
mcp-anything generate /path/to/your/app --no-llm
# Or on an OpenAPI spec
mcp-anything generate /path/to/spec-directory --name my-api --no-llm
162 tests, ~7k lines of code, MIT licensed.
What framework would you want supported next? I'm prioritizing
Top comments (0)