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).
| Entscheidung | Default |
|---|---|
| Eigenbau oder Fork? | Wenn brauchbarer Open-Source-MCP existiert → Fork in ~/source/mcps/<name>/ mit Upstream-Remote. Sonst Eigenbau. |
| Sprache | Python + FastMCP (mcp.server.fastmcp.FastMCP aus offiziellem mcp SDK) — Konsistenz mit allen anderen eigenen MCPs |
| Transport | streamable-http als Default (Memory feedback_mcps_http_default.md), stdio als Fallback via MCP_TRANSPORT=stdio |
| HTTP-Port | freier Port aus 8765-8780-Range — Tabelle in ~/source/mcps/README.md, naechste freie Nummer nehmen |
| Auth-Modell | API-Key in .env.local als <NAME>_API_KEY ENV — wird beim Start gelesen |
| Tool-Anzahl | Schaetze 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-Tools | raw_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 HTTP4. 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 ausmcp-ticketpay/server.pyklauen — 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.mdmit 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=stdiobraucht 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-bereitstellungSkill, 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_/toParameter, lass den Caller (LLM) das Datum bestimmen.
Related
- mcp-best-practices — Design-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-skills —
mcp-builder(offizieller Anthropic-Skill fuer komplexere Builds) - SKILL — wenn der MCP fuer einen Kunden in claude.ai laufen soll
- config — Port-Inventar