mcp-eigenbau — neuen Sub-MCP bauen

Dieser Skill ist die Step-by-Step-Anleitung fuer einen neuen Sub-MCP der lokal in Marvins Claude-Setup laeuft. Pattern entspricht den existierenden eigenen MCPs (mcp-papierkram, mcp-ticketpay, mcp-m365, mcp-runway, mcp-replicate, mcp-sevdesk).

Source of Truth: ~/source/mcps/README.md — Konvention + Tabelle aller existierenden MCPs.

Wann triggert dieser Skill

  • “Baue einen MCP fuer ” / “wir brauchen einen MCP-Server fuer
  • “Fork ” — wenn ein bestehender Open-Source-MCP geforkt + erweitert werden soll
  • Bei Recherche zu neuer API: wenn klar wird dass die API regelmaessig im Agent-Workflow gebraucht wird
  • NICHT triggern wenn:
    • API nur einmalig genutzt wird → direkt im Code mit httpx
    • MCP fuer einen Kunden remote in claude.ai laufen soll → mcp-cloud-bereitstellung
    • Bestehender MCP nur erweitert wird → kein Skill, einfach Tool hinzufuegen + reload

Vor-Entscheidungen (10 Min)

Pflicht-Lektuere vor dem Bau: mcp-best-practices — speziell Regel 1.4 (Tool-Anzahl <30 pro Session) und Template E (Progressive Disclosure). Bei vielen Endpoints im Ziel-API NICHT 1:1 Tool-pro-Endpoint bauen — sonst sprengt das die Tool-Auswahl-Qualitaet des Modells (empirisch: Bedrock+Sonnet kippt um >50 Tools, Tool-Use-Aufrufe brechen ganz ab).

EntscheidungDefault
Eigenbau oder Fork?Wenn brauchbarer Open-Source-MCP existiert → Fork in ~/source/mcps/<name>/ mit Upstream-Remote. Sonst Eigenbau.
SprachePython + FastMCP (mcp.server.fastmcp.FastMCP aus offiziellem mcp SDK) — Konsistenz mit allen anderen eigenen MCPs
Transportstreamable-http als Default (Memory feedback_mcps_http_default.md), stdio als Fallback via MCP_TRANSPORT=stdio
HTTP-Portfreier Port aus 8765-8780-Range — Tabelle in ~/source/mcps/README.md, naechste freie Nummer nehmen
Auth-ModellAPI-Key in .env.local als <NAME>_API_KEY ENV — wird beim Start gelesen
Tool-AnzahlSchaetze grob bevor du anfaengst. <30 → 1:1-Pattern ok. 30-80 → Aggregations-Tools statt Endpoint-Tools (Template C). >80 oder Mono-Wrapper → Progressive Disclosure (Template E) von Anfang an einplanen.
Raw-Toolsraw_get/raw_post/etc. NUR fuer lokales Dev. Hard rule: <NAME>_EXPOSE_RAW-Env-Flag (default true lokal, false hosted) — siehe Template A. Spart kuenftige Halluzinations-Vorfaelle.

Step-by-Step (Default 1-2 Std)

1. Repo-Skeleton

mkdir -p ~/source/mcps/mcp-<name>/src/mcp_<name>
cd ~/source/mcps/mcp-<name>

pyproject.toml (Vorlage aus mcp-ticketpay/pyproject.toml kopieren, name/description/scripts anpassen).

src/mcp_<name>/__init__.py leer.

src/mcp_<name>/server.py mit Skeleton:

import json, logging, os, sys
from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
 
API_BASE = os.environ.get("<NAME>_BASE_URL", "https://api.example.com")
mcp = FastMCP("<name>")
 
def _client() -> httpx.Client:
    token = os.environ["<NAME>_API_KEY"]
    return httpx.Client(base_url=API_BASE, headers={"Authorization": f"Bearer {token}"}, timeout=30.0)
 
@mcp.tool()
def hello() -> dict:
    """Smoke-test tool."""
    return {"ok": True}
 
def main() -> None:
    transport = os.environ.get("MCP_TRANSPORT", "streamable-http").lower()
    if transport == "stdio":
        mcp.run()
    else:
        port = int(os.environ.get("PORT", "<freier port>"))
        host = os.environ.get("HOST", "127.0.0.1")
        mcp.run(transport="streamable-http", host=host, port=port)
 
if __name__ == "__main__":
    main()

2. .env-Setup

.env.local.example:

<NAME>_API_KEY=
<NAME>_BASE_URL=https://api.example.com
MCP_TRANSPORT=streamable-http
PORT=<freier port>

.env.local aus dem Beispiel kopieren, echten API-Key eintragen. Niemals .env.local ins Git. .gitignore mit .env.local ergaenzen wenn nicht schon im Mono-Repo abgedeckt.

3. Install + Smoke-Test

cd ~/source/mcps/mcp-<name>
uv tool install --force --editable .
uv run python -m mcp_<name>.server  # startet HTTP-Server lokal
# in anderem Terminal: curl http://127.0.0.1:<port>/mcp ohne weiteres geht nicht — MCP-Spec
# Smoke-Test mit Inspector:
npx @modelcontextprotocol/inspector
# URL: http://127.0.0.1:<port>/mcp, Transport: Streamable HTTP

4. Tools fuellen

Pro Endpoint einen @mcp.tool()-Funktion. Konventionen:

  • Funktionsname = verb_resource (z.B. list_invoices, get_user)
  • Docstring mit kurzer Beschreibung + erwartete Response-Felder (LLM braucht das fuer Tool-Selection)
  • Type-Hints auf alle Args + Return (FastMCP generiert Schema daraus)
  • Optionale Params mit Default = None
  • Bei groessen Responses: _maybe_compact()-Pattern aus mcp-ticketpay/server.py klauen — auto-truncate bei > 50 KB
  • Bei paginierten Endpoints: count_only=True + fields=[...] Params anbieten (Token-Spar-Pattern aus mcp-ticketpay)

5. In Claude Code/Desktop registrieren

# Claude Code
claude mcp add <name> --transport http http://127.0.0.1:<port>/mcp
 
# Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "<name>": {
      "type": "http",
      "url": "http://127.0.0.1:<port>/mcp"
    }
  }
}

Claude Code/Desktop neu starten → Tools tauchen mit mcp__<name>__*-Prefix auf.

6. Auto-Start-LaunchAgent (optional)

Wenn der MCP nach Reboot automatisch laufen soll: LaunchAgent unter ~/Library/LaunchAgents/com.agentic.mcp-<name>.plist mit RunAtLoad=true + KeepAlive. Vorlage in der Plugin-TODO-Liste unter “VibeFactory — Auto-Start”.

7. Dokumentation

  • ~/source/mcps/README.md — neuen Eintrag in der Tabelle (Tools-Anzahl, Port, Status, kurze Zweck-Beschreibung)
  • intern/capabilities/mcps/<name>.md — Setup-Doku nach Schema 5.18 (Tools-Liste, Auth, Quirks, Stolperer). Neuen Eintrag in _index ergaenzen.
  • intern/capabilities/repos/mcp-<name>.md — Repo-Pointer nach Schema 5.19. Eintrag in _index ergaenzen.
  • config — Port-Eintrag im Inventar.
  • Bei API-Quirks: in der MCP-Setup-Doku unter “Quirks & Stolperfallen” notieren — das spart in 6 Monaten Stunden

8. Cross-Refs

  • Memory: reference_<name>_mcp.md mit 1 Zeile + Pointer auf Setup-Doku
  • CLAUDE Routing-Zeile ergaenzen wenn vager User-Trigger auf das Tool zeigen koennte

Stolperer

  • Port-Kollision: vor Start checken ob Port frei ist (lsof -i :<port>)
  • Editable install reicht nicht alleine: Claude Code muss neu starten damit neue Tools sichtbar sind. Code-Aenderungen am Tool-Body wirken aber sofort ohne reinstall.
  • MCP_TRANSPORT=stdio braucht keinen Port — manche Tools (z.B. mcp-runway) sind explizit auf stdio. Default war frueher stdio, jetzt HTTP.
  • Tool-Output > 50 KB kostet Andre/User Tokens und kann Anthropic-Limits sprengen. Auto-Compact-Pattern aus mcp-ticketpay einbauen wenn API fett antwortet.
  • Date-Filter im Tool-Schema durchreichen — auch wenn nicht offensichtlich aus der API-Doku. Erspart raw_get-Workarounds.

Anti-Patterns

  • Eigene Auth-Layer im Sub-MCP — kein OAuth, kein Eigenbau-Crypto. Sub-MCPs nutzen API-Key aus ENV. Wenn Auth-Boundary gebraucht wird (Custom Connector fuer Kunden) → mcp-cloud-bereitstellung Skill, der baut Scalekit-OAuth davor.
  • Subprocess fuer jeden Tool-Call — der Sub-MCP laeuft als ein Prozess, halte HTTP-Client offen.
  • Aktuelle Datums-/Zeit-Logik im Tool-Code — nimm from_/to Parameter, lass den Caller (LLM) das Datum bestimmen.
  • mcp-best-practicesDesign-Regeln fuer alle MCPs (Tool-Design, Error-Handling, Response-Shape, Security). VOR dem Bau lesen, NACH dem Bau gegen die Audit-Checkliste pruefen.
  • ~/source/mcps/README.md — Mono-Repo Konvention
  • _index — Hub aller Setup-Docs
  • _index — Repo-Pointer-Hub
  • anthropic-skillsmcp-builder (offizieller Anthropic-Skill fuer komplexere Builds)
  • SKILL — wenn der MCP fuer einen Kunden in claude.ai laufen soll
  • config — Port-Inventar