global-mcp-framework
Architektur-Wissen und Arbeitsabläufe zum Erstellen von Custom-MCP-Servern auf diesem
Stack. Jeder Server ist ein eigener Cloudflare Worker, der eine gemeinsame Foundation
(<foundation-repo>) als versionierte Git-Dependency (Tag) einbindet und nur
noch service-spezifisch verdrahtet: Tools + Login-Titel + Outbound-Secret.
Betrieb ist web-only — kein lokaler Rechner, kein VPS, kein Terminal. Alles läuft
über Claude Code (Git/PR), GitHub-Web (Merge/Tag) und das Cloudflare-Dashboard
(KV/Secrets/Logs). Bei jeder Anweisung gilt deshalb: keine wrangler tail-/SSH-
Schritte vorschlagen, sondern die Dashboard-Entsprechung.
Abgrenzung zu global-git-conventions
Dieser Skill regelt nur die MCP-Mechanik (OAuth, Transport, KV, Secrets,
Naming, Folder-Struktur, Cloudflare-Build). Alles typ-übergreifende —
README-Pflichtsektionen, SemVer/Tags, CHANGELOG, Release-Automation
(release-please) und der GitHub-About-Block — lebt in global-git-conventions
und wird von hier nur referenziert, nie dupliziert (Single Source of Truth).
Konkret heißt das: das *-mcp-README-Template steht dort (assets/readme/mcp.md),
Tags entstehen ausschließlich über release-please, und der hier beschriebene
Cloudflare-Build ist davon unabhängig (siehe references/deploy.md).
So findest du das richtige Detail
Diese SKILL.md ist der Einstieg. Die Tiefe liegt in references/ — pro Sorge eine
Datei, damit ein Debug-Fall genau eine Datei lädt statt aller. Lies gezielt:
| Aufgabe / Symptom |
Datei |
OAuth-Provider verdrahten, /authorize-Login bauen, was der Provider selbst macht |
references/auth.md |
| Transport-Setup, "Session terminated" beim Tool-Call |
references/transport.md |
| KV-Binding/Namespace, KV-Hygiene (kein Cron auf Free Plan); R2 presigned Download (großer Payload statt base64) |
references/storage.md |
| Inbound-Hash vs. Outbound-Key setzen, "Authorization failed" nach Consent |
references/secrets.md |
| Repo-Layout, Workers-Builds Root directory, Deploy command, PR/Merge/Build, Foundation-Tag bumpen, Build-Cache-Falle, Non-Production-Branch-Builds abschalten, Renovate-Dependency-PR failt (Lockfile-Drift) |
references/deploy.md |
Repo-/Naming-Standard (Worker/Tool/Secret), Folder-Struktur, Connector-URL, login-Config |
references/conventions.md |
| Irgendein Fehlersymptom, Discovery-Check, Live-Logs, verifizierte CF-Fakten |
references/diagnostics.md |
Vorlagen zum Kopieren liegen in assets/: provider-wiring.ts, server.ts,
wrangler.jsonc, package.json.template, tsconfig.json.template, empty-ai.js,
hooks/. Das README-Template liegt nicht hier, sondern in global-git-conventions
(assets/readme/mcp.md).
Workflow: Neuen Custom-MCP-Server anlegen
Reihenfolge einhalten — Name verifizieren vor Anlegen, Secret vor Connector, Build
verifizieren vor Push, Connector-Test immer im frischen Chat. Primärquelle für den
Code ist immer das server-template/ der Foundation; die Dateien in assets/ sind
nur kommentierte Spiegel davon. Bei Abweichung gilt das server-template/.
- Name verifizieren — Über den Cloudflare-MCP
workers_list aufrufen. Existiert
der Worker schon, exakt diesen Namen übernehmen; sonst den geplanten Namen
festlegen. name in wrangler.jsonc = name in package.json = Repo = Cloudflare-
Service. Nie raten. Details: references/conventions.md.
- KV — KV-Namespace anlegen (per Cloudflare-MCP
kv_namespace_create oder im
Dashboard, Konvention MCP_OAUTH_<SERVICE>) und in wrangler.jsonc mit Binding
OAUTH_KV verdrahten. Details: references/storage.md.
- Repo + Wiring —
server-template/ der Foundation kopieren. src/index.ts
(Provider-Wiring via createOAuthWorker, Spiegel: assets/provider-wiring.ts),
src/server.ts (Tools + TOOL_ALLOWLIST, Spiegel: assets/server.ts),
wrangler.jsonc (Spiegel: assets/wrangler.jsonc), src/empty-ai.js (Spiegel:
assets/empty-ai.js). Foundation als Git-Tag in package.json (Spiegel:
assets/package.json.template), tsconfig.json (Spiegel:
assets/tsconfig.json.template). Konzepte: references/auth.md, references/transport.md.
Repo-Layout (wrangler.jsonc im Build-Root): references/deploy.md.
- Secrets —
MCP_AUTH_PASSWORD_HASH (SHA-256-Hex) und das Outbound-Secret am
Worker setzen. Outbound-Secret nicht zu setzen ist die häufigste Ursache für
"Authorization failed" nach dem Consent. Details: references/secrets.md.
- Build verifizieren — Vor dem Push:
npm run typecheck und
npx wrangler deploy --dry-run müssen grün sein. Das reproduziert die
Cloudflare-Build-Fehler ("entry not found" / "static files") lokal. Die Gate-Hooks
im server-template (assets/hooks/) erzwingen das. Details: references/deploy.md.
- Connector — In claude.ai den Connector auf
https://<service>-mcp.<account>.workers.dev/mcp zeigen lassen,
Transport streamable-http, kein Token-Feld (OAuth). Vor dem Connect den
Discovery-Check fahren (references/diagnostics.md).
- Test im frischen Chat — Funktionstest NIE im Debug-Thread, immer in einem
neuen Chat (klebende MCP-Sessions verfälschen sonst das Ergebnis). Erst wenn ein
echter Tool-Call durchläuft, gilt der Server als verifiziert.
Goldene Regeln (zeitlos)
- Funktionstest immer im frischen Chat. Das ist der einzige verlässliche
Funktionstest — ein langer Debug-Thread kann eine alte MCP-Session festhalten.
- Discovery-Check vor dem Connect. Die beiden
.well-known-Endpunkte im Browser
öffnen; sauberes JSON heißt: Wiring ist live und öffentlich erreichbar.
- Foundation-Bumps sind bewusst, nicht automatisch. Eine neue Foundation-Version
schlägt erst durch, wenn der Konsument seine
package.json aktiv bumpt und neu baut.
- Provider nicht nachbauen.
/token, /register und beide .well-known-Routen
liefert @cloudflare/workers-oauth-provider selbst. Selbst gebaut wird nur die
/authorize-Login-Seite.
- Namen nie erfinden. Vor dem Setzen von
name workers_list (Cloudflare-MCP)
fahren und den echten Service-Namen übernehmen. name muss in wrangler.jsonc,
package.json, Repo und Cloudflare-Service identisch sein. Ausnahme (mehrere Worker
aus einem Repo via Environments): name und KV divergieren pro env-Block, der
Repo-name bleibt die gemeinsame Basis — siehe references/deploy.md.
- Tool-
name ohne Punkt, ohne Prefix. Jeder Tool-name und jeder
TOOL_ALLOWLIST-Eintrag muss ^[a-zA-Z0-9_-]{1,64}$ erfüllen — kein Punkt, kein
Leerzeichen, kein camelCase. Konvention <verb>_<objekt>, snake_case, kein
Service-Prefix (z.B. create_invoice, list_files). Objekt nie weglassen
(list_files, nicht list). Ein Punkt baut serverseitig durch, wird aber an der
Frontend-Grenze abgewiesen (FrontendRemoteMcpToolDefinition.name) — fällt also erst
beim Verbinden auf. Der Pre-Push-Gate-Hook erzwingt die Regel.
name ≠ title. Der name ist die maschinenlesbare Aufruf-ID (snake_case,
regex-streng). Der title ist der menschenlesbare Anzeigename im Connector
(Title Case, Leerzeichen erlaubt: „Create Invoice", „Inspect URL"). Lesbarkeit lebt
im title, deshalb braucht der name kein Prefix. Der title unterliegt der Regex
NICHT und steht nie in der TOOL_ALLOWLIST.
- Infra-Namen tragen den Anbieter, Tool-Namen nicht. Worker, KV-Namespace und
Secrets sind infra-lesbar → Anbieter/Aussteller im Namen (
google-sheets-mcp,
MCP_OAUTH_GOOGLE_SHEETS, GOOGLE_REFRESH_TOKEN). Tool-names sind
modell-lesbar → kurzes <verb>_<objekt> ohne Anbieter und ohne Service-Prefix
(append_row, nicht sheets_append_row); die Server-Zuordnung macht der
Connector-Namespace. Secret = <AUSSTELLER>_<TYP>, nicht nach Worker benannt
(GOOGLE_…, nicht GSC_…). Details: references/conventions.md, references/secrets.md.
- Fremdimporte laufen über die Fassade, nie direkt. Consumer importieren
z aus
mcp-foundation/schema und McpServer aus mcp-foundation/sdk — nie direkt aus
zod oder @modelcontextprotocol/sdk. Die einzige Laufzeit-Dependency im Consumer ist
mcp-foundation (plus repo-eigene Libs); SDK und zod stehen nicht mehr in den
Consumer-dependencies. Die Foundation führt sie als eigene Deps und re-exportiert
über die beiden Subpaths. Der overrides-Pin ("@modelcontextprotocol/sdk": "1.29.0",
explizite Version) bleibt Pflicht — er deduppt gegen den agents-SDK-Pin und wirkt nur
vom Consumer-Root. Details: references/conventions.md, references/deploy.md.
- Build nie ungeprüft pushen.
npm run typecheck + npx wrangler deploy --dry-run
müssen grün sein, bevor gepusht wird — der Cloudflare-Build wirft sonst dieselben
Fehler erst nach dem Merge. Die Gate-Hooks (assets/hooks/) machen das verbindlich.
- server-template ist die Wahrheit. Die
assets/-Dateien sind Spiegel; bei
Abweichung gilt das server-template/ der Foundation. Keine Asset-Drift dulden.
- Non-Production-Branch-Builds aus, Lockfiles konsistent halten. Reine MCP-Worker
brauchen keine Preview-URLs — Cloudflares Branch-Builds für Renovate-Dependency-PRs
erzeugen nur fehlschlagende Builds (Lockfile hinkt dem Range-Bump hinterher). Pro
Worker in Settings → Build → Branch control die Checkbox „Builds for non-production
branches" deaktivieren (kein globales Toggle, nicht per MCP erreichbar). Das ist aber
nur Symptom-Kosmetik: den Drift selbst verhindert saubere Renovate-Lockfile-Pflege plus
Konsistenz-Check vor jedem Dependency-Merge — sonst failt der Bump im
main-Build. Details: references/deploy.md.
1---2name: global-mcp-framework3description: Generisches Framework zum Erstellen von Custom-MCP-Servern als Cloudflare Worker mit @cloudflare/workers-oauth-provider (OAuth 2.1), stateless Streamable-HTTP und einer gemeinsamen Foundation als versionierte Git-Tag-Dependency. IMMER laden, sobald ein MCP-Server in diesem Stack erstellt, verdrahtet, auf eine neue Foundation gebumpt oder debuggt wird — auch wenn das Wort Skill nicht fällt. Trigger u.a.: Custom MCP-Server bauen, Cloudflare Worker MCP, workers-oauth-provider, OAUTH_KV, OAuth 2.1 inbound, /authorize Login, Provider-Wiring, stateless Transport, sessionIdGenerator, "Session terminated", "Server misconfigured", "Authorization failed" nach Consent, neuen Connector anlegen, Foundation-Tag bumpen, wrangler.jsonc für MCP, KV-Namespace MCP_OAUTH, Discovery-Check well-known. Gilt für jeden neuen Custom-MCP-Server in diesem Stack.4---56# global-mcp-framework78Architektur-Wissen und Arbeitsabläufe zum Erstellen von Custom-MCP-Servern auf diesem9Stack. Jeder Server ist ein eigener Cloudflare Worker, der eine gemeinsame Foundation10(`<foundation-repo>`) als **versionierte Git-Dependency (Tag)** einbindet und nur11noch service-spezifisch verdrahtet: Tools + Login-Titel + Outbound-Secret.1213**Betrieb ist web-only** — kein lokaler Rechner, kein VPS, kein Terminal. Alles läuft14über Claude Code (Git/PR), GitHub-Web (Merge/Tag) und das Cloudflare-Dashboard15(KV/Secrets/Logs). Bei jeder Anweisung gilt deshalb: keine `wrangler tail`-/SSH-16Schritte vorschlagen, sondern die Dashboard-Entsprechung.1718## Abgrenzung zu `global-git-conventions`1920Dieser Skill regelt nur die **MCP-Mechanik** (OAuth, Transport, KV, Secrets,21Naming, Folder-Struktur, Cloudflare-Build). Alles **typ-übergreifende** —22README-Pflichtsektionen, SemVer/Tags, CHANGELOG, Release-Automation23(release-please) und der GitHub-About-Block — lebt in `global-git-conventions`24und wird von hier nur referenziert, nie dupliziert (Single Source of Truth).25Konkret heißt das: das *-mcp-README-Template steht dort (`assets/readme/mcp.md`),26Tags entstehen ausschließlich über release-please, und der hier beschriebene27Cloudflare-Build ist davon unabhängig (siehe `references/deploy.md`).2829## So findest du das richtige Detail3031Diese SKILL.md ist der Einstieg. Die Tiefe liegt in `references/` — pro Sorge eine32Datei, damit ein Debug-Fall genau eine Datei lädt statt aller. Lies gezielt:3334| Aufgabe / Symptom | Datei |35|---|---|36| OAuth-Provider verdrahten, `/authorize`-Login bauen, was der Provider selbst macht | `references/auth.md` |37| Transport-Setup, "Session terminated" beim Tool-Call | `references/transport.md` |38| KV-Binding/Namespace, KV-Hygiene (kein Cron auf Free Plan); R2 presigned Download (großer Payload statt base64) | `references/storage.md` |39| Inbound-Hash vs. Outbound-Key setzen, "Authorization failed" nach Consent | `references/secrets.md` |40| Repo-Layout, Workers-Builds Root directory, Deploy command, PR/Merge/Build, Foundation-Tag bumpen, Build-Cache-Falle, Non-Production-Branch-Builds abschalten, Renovate-Dependency-PR failt (Lockfile-Drift) | `references/deploy.md` |41| Repo-/Naming-Standard (Worker/Tool/Secret), Folder-Struktur, Connector-URL, `login`-Config | `references/conventions.md` |42| Irgendein Fehlersymptom, Discovery-Check, Live-Logs, verifizierte CF-Fakten | `references/diagnostics.md` |4344Vorlagen zum Kopieren liegen in `assets/`: `provider-wiring.ts`, `server.ts`,45`wrangler.jsonc`, `package.json.template`, `tsconfig.json.template`, `empty-ai.js`,46`hooks/`. Das README-Template liegt nicht hier, sondern in `global-git-conventions`47(`assets/readme/mcp.md`).4849## Workflow: Neuen Custom-MCP-Server anlegen5051Reihenfolge einhalten — Name verifizieren vor Anlegen, Secret vor Connector, Build52verifizieren vor Push, Connector-Test immer im frischen Chat. Primärquelle für den53Code ist immer das `server-template/` der Foundation; die Dateien in `assets/` sind54nur kommentierte Spiegel davon. Bei Abweichung gilt das `server-template/`.55560. **Name verifizieren** — Über den Cloudflare-MCP `workers_list` aufrufen. Existiert57 der Worker schon, exakt diesen Namen übernehmen; sonst den geplanten Namen58 festlegen. `name` in `wrangler.jsonc` = `name` in `package.json` = Repo = Cloudflare-59 Service. **Nie raten.** Details: `references/conventions.md`.601. **KV** — KV-Namespace anlegen (per Cloudflare-MCP `kv_namespace_create` oder im61 Dashboard, Konvention `MCP_OAUTH_<SERVICE>`) und in `wrangler.jsonc` mit **Binding62 `OAUTH_KV`** verdrahten. Details: `references/storage.md`.632. **Repo + Wiring** — `server-template/` der Foundation kopieren. `src/index.ts`64 (Provider-Wiring via `createOAuthWorker`, Spiegel: `assets/provider-wiring.ts`),65 `src/server.ts` (Tools + `TOOL_ALLOWLIST`, Spiegel: `assets/server.ts`),66 `wrangler.jsonc` (Spiegel: `assets/wrangler.jsonc`), `src/empty-ai.js` (Spiegel:67 `assets/empty-ai.js`). Foundation als Git-Tag in `package.json` (Spiegel:68 `assets/package.json.template`), `tsconfig.json` (Spiegel:69 `assets/tsconfig.json.template`). Konzepte: `references/auth.md`, `references/transport.md`.70 Repo-Layout (wrangler.jsonc im Build-Root): `references/deploy.md`.713. **Secrets** — `MCP_AUTH_PASSWORD_HASH` (SHA-256-Hex) und das Outbound-Secret **am72 Worker** setzen. Outbound-Secret nicht zu setzen ist die häufigste Ursache für73 "Authorization failed" nach dem Consent. Details: `references/secrets.md`.744. **Build verifizieren** — Vor dem Push: `npm run typecheck` und75 `npx wrangler deploy --dry-run` müssen grün sein. Das reproduziert die76 Cloudflare-Build-Fehler ("entry not found" / "static files") lokal. Die Gate-Hooks77 im `server-template` (`assets/hooks/`) erzwingen das. Details: `references/deploy.md`.785. **Connector** — In claude.ai den Connector auf79 `https://<service>-mcp.<account>.workers.dev/mcp` zeigen lassen,80 Transport streamable-http, **kein Token-Feld** (OAuth). Vor dem Connect den81 Discovery-Check fahren (`references/diagnostics.md`).826. **Test im frischen Chat** — Funktionstest NIE im Debug-Thread, immer in einem83 neuen Chat (klebende MCP-Sessions verfälschen sonst das Ergebnis). Erst wenn ein84 echter Tool-Call durchläuft, gilt der Server als verifiziert.8586## Goldene Regeln (zeitlos)8788- **Funktionstest immer im frischen Chat.** Das ist der einzige verlässliche89 Funktionstest — ein langer Debug-Thread kann eine alte MCP-Session festhalten.90- **Discovery-Check vor dem Connect.** Die beiden `.well-known`-Endpunkte im Browser91 öffnen; sauberes JSON heißt: Wiring ist live und öffentlich erreichbar.92- **Foundation-Bumps sind bewusst, nicht automatisch.** Eine neue Foundation-Version93 schlägt erst durch, wenn der Konsument seine `package.json` aktiv bumpt und neu baut.94- **Provider nicht nachbauen.** `/token`, `/register` und beide `.well-known`-Routen95 liefert `@cloudflare/workers-oauth-provider` selbst. Selbst gebaut wird nur die96 `/authorize`-Login-Seite.97- **Namen nie erfinden.** Vor dem Setzen von `name` `workers_list` (Cloudflare-MCP)98 fahren und den echten Service-Namen übernehmen. `name` muss in `wrangler.jsonc`,99 `package.json`, Repo und Cloudflare-Service identisch sein. Ausnahme (mehrere Worker100 aus einem Repo via Environments): `name` und KV divergieren pro `env`-Block, der101 Repo-`name` bleibt die gemeinsame Basis — siehe `references/deploy.md`.102- **Tool-`name` ohne Punkt, ohne Prefix.** Jeder Tool-`name` und jeder103 `TOOL_ALLOWLIST`-Eintrag muss `^[a-zA-Z0-9_-]{1,64}$` erfüllen — kein Punkt, kein104 Leerzeichen, kein camelCase. Konvention `<verb>_<objekt>`, snake_case, **kein105 Service-Prefix** (z.B. `create_invoice`, `list_files`). Objekt nie weglassen106 (`list_files`, nicht `list`). Ein Punkt baut serverseitig durch, wird aber an der107 Frontend-Grenze abgewiesen (`FrontendRemoteMcpToolDefinition.name`) — fällt also erst108 beim Verbinden auf. Der Pre-Push-Gate-Hook erzwingt die Regel.109- **`name` ≠ `title`.** Der `name` ist die maschinenlesbare Aufruf-ID (snake_case,110 regex-streng). Der `title` ist der menschenlesbare Anzeigename im Connector111 (Title Case, Leerzeichen erlaubt: „Create Invoice", „Inspect URL"). Lesbarkeit lebt112 im `title`, deshalb braucht der `name` kein Prefix. Der `title` unterliegt der Regex113 NICHT und steht nie in der `TOOL_ALLOWLIST`.114- **Infra-Namen tragen den Anbieter, Tool-Namen nicht.** Worker, KV-Namespace und115 Secrets sind infra-lesbar → Anbieter/Aussteller im Namen (`google-sheets-mcp`,116 `MCP_OAUTH_GOOGLE_SHEETS`, `GOOGLE_REFRESH_TOKEN`). Tool-`name`s sind117 modell-lesbar → kurzes `<verb>_<objekt>` ohne Anbieter und ohne Service-Prefix118 (`append_row`, nicht `sheets_append_row`); die Server-Zuordnung macht der119 Connector-Namespace. Secret = `<AUSSTELLER>_<TYP>`, nicht nach Worker benannt120 (`GOOGLE_…`, nicht `GSC_…`). Details: `references/conventions.md`, `references/secrets.md`.121- **Fremdimporte laufen über die Fassade, nie direkt.** Consumer importieren `z` aus122 `mcp-foundation/schema` und `McpServer` aus `mcp-foundation/sdk` — **nie** direkt aus123 `zod` oder `@modelcontextprotocol/sdk`. Die einzige Laufzeit-Dependency im Consumer ist124 `mcp-foundation` (plus repo-eigene Libs); SDK und zod stehen nicht mehr in den125 Consumer-`dependencies`. Die Foundation führt sie als eigene Deps und re-exportiert126 über die beiden Subpaths. Der `overrides`-Pin (`"@modelcontextprotocol/sdk": "1.29.0"`,127 explizite Version) bleibt Pflicht — er deduppt gegen den `agents`-SDK-Pin und wirkt nur128 vom Consumer-Root. Details: `references/conventions.md`, `references/deploy.md`.129- **Build nie ungeprüft pushen.** `npm run typecheck` + `npx wrangler deploy --dry-run`130 müssen grün sein, bevor gepusht wird — der Cloudflare-Build wirft sonst dieselben131 Fehler erst nach dem Merge. Die Gate-Hooks (`assets/hooks/`) machen das verbindlich.132- **server-template ist die Wahrheit.** Die `assets/`-Dateien sind Spiegel; bei133 Abweichung gilt das `server-template/` der Foundation. Keine Asset-Drift dulden.134- **Non-Production-Branch-Builds aus, Lockfiles konsistent halten.** Reine MCP-Worker135 brauchen keine Preview-URLs — Cloudflares Branch-Builds für Renovate-Dependency-PRs136 erzeugen nur fehlschlagende Builds (Lockfile hinkt dem Range-Bump hinterher). Pro137 Worker in **Settings → Build → Branch control** die Checkbox „Builds for non-production138 branches" deaktivieren (kein globales Toggle, nicht per MCP erreichbar). Das ist aber139 nur Symptom-Kosmetik: den Drift selbst verhindert saubere Renovate-Lockfile-Pflege plus140 Konsistenz-Check **vor** jedem Dependency-Merge — sonst failt der Bump im141 `main`-Build. Details: `references/deploy.md`.