<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gabriele Karra</title>
    <description>The latest articles on DEV Community by Gabriele Karra (@gabriele_karra_f0c2b7bedd).</description>
    <link>https://dev.to/gabriele_karra_f0c2b7bedd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3824631%2F1be09ad4-7006-4b4c-a6f5-ccb489759b5d.png</url>
      <title>DEV Community: Gabriele Karra</title>
      <link>https://dev.to/gabriele_karra_f0c2b7bedd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gabriele_karra_f0c2b7bedd"/>
    <language>en</language>
    <item>
      <title>I built a tool that auto-generates MCP servers from any codebase</title>
      <dc:creator>Gabriele Karra</dc:creator>
      <pubDate>Sat, 14 Mar 2026 23:07:39 +0000</pubDate>
      <link>https://dev.to/gabriele_karra_f0c2b7bedd/i-built-a-tool-that-auto-generates-mcp-servers-from-any-codebase-ebg</link>
      <guid>https://dev.to/gabriele_karra_f0c2b7bedd/i-built-a-tool-that-auto-generates-mcp-servers-from-any-codebase-ebg</guid>
      <description>&lt;p&gt;The Model Context Protocol (MCP) is how AI tools like Claude, Cursor, and&lt;br&gt;
  Windsurf connect to external software. You write an MCP server that exposes&lt;br&gt;
  "tools", and the AI can call them.&lt;/p&gt;

&lt;p&gt;The problem? Writing MCP servers by hand is tedious. You're essentially&lt;br&gt;
  translating your app's existing API surface — CLI commands, HTTP endpoints,&lt;br&gt;
  Python functions — into MCP tool definitions. For a FastAPI app with 20 routes,&lt;br&gt;
  that's a lot of boilerplate.&lt;/p&gt;

&lt;p&gt;So I automated it.&lt;/p&gt;

&lt;p&gt;What is MCP-Anything?&lt;/p&gt;

&lt;p&gt;pip install -e ".[dev]"&lt;br&gt;
  mcp-anything generate /path/to/your/app&lt;/p&gt;

&lt;p&gt;One command. It scans your project, figures out what it does, and generates a&lt;br&gt;
  complete MCP server package — pip-installable, with tests, docs, and a&lt;br&gt;
  ready-to-paste config for Claude Code.&lt;/p&gt;

&lt;p&gt;GitHub: github.com/gabrielekarra/mcp-anything&lt;/p&gt;

&lt;p&gt;How it works&lt;/p&gt;

&lt;p&gt;The pipeline has 6 phases:&lt;/p&gt;

&lt;p&gt;ANALYZE → DESIGN → IMPLEMENT → TEST → DOCUMENT → PACKAGE&lt;/p&gt;

&lt;p&gt;Phase 1: Analyze&lt;/p&gt;

&lt;p&gt;Eight detectors scan for IPC mechanisms — how does this app communicate with the&lt;br&gt;
  outside world?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLI — argparse, click, typer, fire&lt;/li&gt;
&lt;li&gt;HTTP frameworks — FastAPI, Flask (Python AST-based)&lt;/li&gt;
&lt;li&gt;Spring Boot — Java annotations via regex&lt;/li&gt;
&lt;li&gt;OpenAPI/Swagger — spec file parsing (no source code needed)&lt;/li&gt;
&lt;li&gt;Sockets, gRPC, file I/O — and more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Python apps, the analyzer uses the ast module to extract functions,&lt;br&gt;
  parameters with types, default values, docstrings, and decorators. No regex hacks&lt;br&gt;
   for Python — full AST parsing.&lt;/p&gt;

&lt;p&gt;For FastAPI specifically, it understands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;@app.get("/users/{user_id}") route decorators&lt;/li&gt;
&lt;li&gt;Query(), Path(), Body() parameter injection&lt;/li&gt;
&lt;li&gt;Depends() filtering (skips injected dependencies)&lt;/li&gt;
&lt;li&gt;APIRouter(prefix="/items") prefix resolution&lt;/li&gt;
&lt;li&gt;Pydantic models as request body types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Phase 2: Design&lt;/p&gt;

&lt;p&gt;Each detected capability gets mapped to an implementation strategy:&lt;/p&gt;

&lt;p&gt;┌────────────────┬─────────────────────────────┬─────────────────────────────┐&lt;br&gt;
  │    Strategy    │            When             │        How it works         │&lt;br&gt;
  ├────────────────┼─────────────────────────────┼─────────────────────────────┤&lt;br&gt;
  │ http_call      │ REST endpoints              │ Async HTTP request via      │&lt;br&gt;
  │                │                             │ httpx                       │&lt;br&gt;
  ├────────────────┼─────────────────────────────┼─────────────────────────────┤&lt;br&gt;
  │ cli_subcommand │ CLI apps                    │ Subprocess with args        │&lt;br&gt;
  ├────────────────┼─────────────────────────────┼─────────────────────────────┤&lt;br&gt;
  │ python_call    │ Python libraries            │ Direct import and call      │&lt;br&gt;
  ├────────────────┼─────────────────────────────┼─────────────────────────────┤&lt;br&gt;
  │ cli_function   │ Python functions in CLI     │ Wrapped subprocess call     │&lt;br&gt;
  │                │ apps                        │                             │&lt;br&gt;
  └────────────────┴─────────────────────────────┴─────────────────────────────┘&lt;/p&gt;

&lt;p&gt;Parameters are categorized as path, query, or body based on their position in the&lt;br&gt;
   route and annotations.&lt;/p&gt;

&lt;p&gt;Phase 3-6: Generate&lt;/p&gt;

&lt;p&gt;Jinja2 templates produce a complete Python package:&lt;/p&gt;

&lt;p&gt;mcp-my-app-server/&lt;br&gt;
  ├── src/my_app/&lt;br&gt;
  │   ├── server.py        # FastMCP entry point&lt;br&gt;
  │   ├── backend.py       # Async HTTP/CLI backend&lt;br&gt;
  │   └── tools/           # Generated tool modules&lt;br&gt;
  ├── tests/               # Pytest tests&lt;br&gt;
  ├── docs/                # API documentation&lt;br&gt;
  └── pyproject.toml       # Ready to pip install&lt;/p&gt;

&lt;p&gt;Every generated Python file is validated with ast.parse() before writing. If the&lt;br&gt;
  templates produce invalid syntax, the pipeline fails fast.&lt;/p&gt;

&lt;p&gt;Real example: FastAPI app&lt;/p&gt;

&lt;p&gt;I tested against tiangolo's full-stack-fastapi-template — a production FastAPI&lt;br&gt;
  app with users, items, auth, and multiple routers.&lt;/p&gt;

&lt;p&gt;mcp-anything generate ./backend/app --name fastapi-users --no-llm&lt;/p&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;p&gt;Scanning codebase...&lt;br&gt;
  Found 27 source files&lt;br&gt;
  Running IPC detectors...&lt;br&gt;
    Flask/FastAPI Detector: protocol (confidence: 0.95, 28 signals)&lt;br&gt;
  Running AST analysis...&lt;br&gt;
    AST: 64 functions, 22 classes&lt;br&gt;
    Fastapi: 23 HTTP routes&lt;br&gt;
  ✓ analyze complete&lt;br&gt;
    Designed 49 tools&lt;br&gt;
  ✓ All phases complete&lt;/p&gt;

&lt;p&gt;23 HTTP routes extracted across 5 routers (/items/, /users/, /login/, /utils/,&lt;br&gt;
  /private/), with correct parameter schemas. The generated tools include things&lt;br&gt;
  like:&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/server"&gt;@server&lt;/a&gt;.tool()&lt;br&gt;
  async def get_users(&lt;br&gt;
      skip: int | None = 0,&lt;br&gt;
      limit: int | None = 100,&lt;br&gt;
  ) -&amp;gt; str:&lt;br&gt;
      """GET /users/ - Retrieve users."""&lt;br&gt;
      query_params = {}&lt;br&gt;
      if skip is not None:&lt;br&gt;
          query_params["skip"] = str(skip)&lt;br&gt;
      if limit is not None:&lt;br&gt;
          query_params["limit"] = str(limit)&lt;br&gt;
      return await backend.request("GET", "/users/", params=query_params)&lt;/p&gt;

&lt;p&gt;The SessionDep and CurrentUser dependency-injected params were correctly filtered&lt;br&gt;
   out. Only user-facing parameters made it into the tool schema.&lt;/p&gt;

&lt;p&gt;Real example: OpenAPI spec (no source code)&lt;/p&gt;

&lt;p&gt;You don't even need source code. Point it at a directory with an OpenAPI spec:&lt;/p&gt;

&lt;p&gt;mcp-anything generate /path/to/realworld --name conduit-api --no-llm&lt;/p&gt;

&lt;p&gt;It found specs/api/openapi.yml via recursive search and extracted 19 endpoints&lt;br&gt;
  from the RealWorld Conduit API:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With $ref resolution for request body schemas, enum values, and nested path&lt;br&gt;
  parameters.&lt;/p&gt;

&lt;p&gt;The interesting technical bits&lt;/p&gt;

&lt;p&gt;AST-based parameter extraction for FastAPI&lt;/p&gt;

&lt;p&gt;FastAPI uses Python type annotations and default values for dependency injection.&lt;br&gt;
   A function like:&lt;/p&gt;

&lt;p&gt;async def list_users(&lt;br&gt;
      session: SessionDep,&lt;br&gt;
      current_user: CurrentUser,&lt;br&gt;
      skip: int = 0,&lt;br&gt;
      limit: int = Query(10, description="Max results"),&lt;br&gt;
  ):&lt;/p&gt;

&lt;p&gt;The analyzer needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Skip session and current_user (dependency-injected types)&lt;/li&gt;
&lt;li&gt;Recognize skip as a plain query parameter with default 0&lt;/li&gt;
&lt;li&gt;Parse Query(10, description="Max results") to extract the default value and
description&lt;/li&gt;
&lt;li&gt;Handle Depends(), Path(), Body(), Header(), Cookie() — each one differently&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is all done via Python's ast module — no imports, no runtime inspection, no&lt;br&gt;
  dependencies on FastAPI itself.&lt;/p&gt;

&lt;p&gt;Java annotation parsing without a Java parser&lt;/p&gt;

&lt;p&gt;For Spring Boot, I couldn't use Python's ast module. Instead, it's regex-based&lt;br&gt;
  with some tricks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A balanced parenthesis extractor handles nested annotations like
@RequestParam(required = false, defaultValue = "10")&lt;/li&gt;
&lt;li&gt;Generic return types like List are captured with [\w&amp;lt;&amp;gt;,\s]+?&lt;/li&gt;
&lt;li&gt;Controller-level @RequestMapping("/api") is prepended to method-level paths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OpenAPI $ref resolution&lt;/p&gt;

&lt;p&gt;OpenAPI specs reference schemas via $ref: "#/components/schemas/Pet". The&lt;br&gt;
  analyzer resolves these by walking the JSON pointer path and inlining the&lt;br&gt;
  referenced schema's properties as individual tool parameters — so instead of a&lt;br&gt;
  single opaque body: object parameter, you get name: string (required), species:&lt;br&gt;
  string, age: integer.&lt;/p&gt;

&lt;p&gt;What's missing&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication — no API key, Bearer token, or OAuth support yet. Generated
servers can't auth against protected APIs.&lt;/li&gt;
&lt;li&gt;More languages — Express.js, Go, Django REST Framework, Rust are on the
roadmap.&lt;/li&gt;
&lt;li&gt;Schema depth — nested object properties aren't fully expanded yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the full ROADMAP for planned features.&lt;/p&gt;

&lt;p&gt;Try it&lt;/p&gt;

&lt;p&gt;git clone &lt;a href="https://github.com/gabrielekarra/mcp-anything.git" rel="noopener noreferrer"&gt;https://github.com/gabrielekarra/mcp-anything.git&lt;/a&gt;&lt;br&gt;
  cd mcp-anything&lt;br&gt;
  pip install -e ".[dev]"&lt;/p&gt;

&lt;p&gt;# Try it on your own project&lt;br&gt;
  mcp-anything generate /path/to/your/app --no-llm&lt;/p&gt;

&lt;p&gt;# Or on an OpenAPI spec&lt;br&gt;
  mcp-anything generate /path/to/spec-directory --name my-api --no-llm&lt;/p&gt;

&lt;p&gt;162 tests, ~7k lines of code, MIT licensed.&lt;/p&gt;

&lt;p&gt;What framework would you want supported next? I'm prioritizing &lt;/p&gt;

</description>
      <category>mcp</category>
      <category>opensource</category>
      <category>tutorial</category>
      <category>fastapi</category>
    </item>
  </channel>
</rss>
