Isometrisches Blueprint-Diagramm: MCP-Server als zentraler Hub, der ein bestehendes Backend mit Auth-Layer, Tools und Resources verbindet

    MCP-Server bauen: Schritt-für-Schritt-Anleitung für REST- und GraphQL-Backends

    13. Mai 20267 min Lesezeit
    Till Freitag

    TL;DR: „In 7 Schritten zum produktionsreifen MCP-Server: Backend kapseln, Tools modellieren, Resources exponieren, OAuth absichern, deployen. Code-Beispiele in TypeScript inklusive."

    — Till Freitag

    Warum diese Anleitung?

    Wenn du die strategische Begründung für MCP gelesen hast, weißt du: Ohne MCP-Server bist du in 12 Monaten nicht mehr im Workflow deiner Nutzer. Die Frage ist nur noch: Wie baust du das konkret?

    Diese Anleitung zeigt dir den schnellsten Weg von „bestehendes REST/GraphQL-Backend" zu „funktionsfähiger MCP-Server, an dem Claude, Codex und ChatGPT andocken können". Du brauchst dafür keinen Greenfield – wir kapseln, was schon da ist.

    Wenn du die Grundlagen noch nicht kennst: Lies vorher kurz MCP für Einsteiger.

    Die Architektur in 30 Sekunden

    ┌──────────────┐    MCP     ┌──────────────┐    HTTP    ┌──────────────┐
    │  AI-Client   │  ────────▶ │  MCP-Server  │  ────────▶ │ Dein Backend │
    │ (Claude etc.)│            │  (Adapter)   │            │ (REST/GraphQL│
    └──────────────┘            └──────────────┘            └──────────────┘
                                       │
                                       └─ exponiert: Tools, Resources, Prompts

    Der MCP-Server ist kein Replacement deines Backends. Er ist ein dünner Adapter davor, der deine APIs in der Sprache spricht, die Agenten verstehen.

    Voraussetzungen

    • Bestehendes REST- oder GraphQL-Backend mit dokumentierten Endpoints
    • Node.js 20+ oder Python 3.11+
    • Ein Auth-Mechanismus deines Backends (API-Key, OAuth, JWT)
    • Eine Hosting-Option für Long-running HTTP (Cloudflare Workers, Railway, Fly, Render, Supabase Edge Functions)

    Schritt 1: Tools, Resources und Prompts identifizieren

    Bevor du Code schreibst, mach diese eine Liste. Sie ist 80 % der Arbeit:

    MCP-Konzept Wofür? Beispiel aus deinem Backend
    Tool Aktion, die der Agent ausführen darf POST /dealscreate_deal
    Resource Daten, die der Agent lesen darf GET /deals/:iddeal://{id}
    Prompt Vordefinierte Vorlage für wiederkehrende Tasks „Erstelle Wochenreport aus Deals"

    Faustregel: Starte mit den 3 wichtigsten Workflows, nicht mit dem ganzen API-Surface. Lemlist hat mit 3 Tools 700 Kunden geholt. Dein API hat wahrscheinlich 200 Endpoints – ignoriere 197 davon im ersten Wurf.

    Schritt 2: Setup mit dem offiziellen TypeScript-SDK

    mkdir my-mcp-server && cd my-mcp-server
    npm init -y
    npm install @modelcontextprotocol/sdk zod
    npm install -D typescript tsx @types/node
    npx tsc --init

    Grundgerüst in src/index.ts:

    import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
    import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
    import { z } from "zod";
    
    const server = new McpServer({
      name: "acme-crm",
      version: "0.1.0",
    });
    
    // ... Tools & Resources hier registrieren ...
    
    const transport = new StreamableHTTPServerTransport();
    await server.connect(transport);

    Schritt 3: REST-Endpoints als Tools kapseln

    Pro Tool brauchst du Name, Beschreibung, Input-Schema und Handler. Die Beschreibung ist entscheidend – sie ist der Prompt, an dem der Agent entscheidet, ob er dein Tool nutzt.

    server.tool(
      "create_deal",
      "Erstellt einen neuen Deal im CRM. Nutze dies, wenn der User von einem neuen Kunden, einer Opportunity oder einem Verkaufsabschluss spricht.",
      {
        title: z.string().describe("Klarer Deal-Titel, z. B. 'Acme Corp – Enterprise Q3'"),
        value: z.number().describe("Deal-Wert in Euro, ohne Komma"),
        contactId: z.string().describe("ID des Kontakts aus dem CRM"),
      },
      async ({ title, value, contactId }, { authInfo }) => {
        const res = await fetch(`${process.env.BACKEND_URL}/api/deals`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${authInfo?.token}`,
          },
          body: JSON.stringify({ title, value, contactId }),
        });
    
        if (!res.ok) {
          return {
            content: [{ type: "text", text: `Fehler: ${res.status} ${await res.text()}` }],
            isError: true,
          };
        }
    
        const deal = await res.json();
        return {
          content: [{ type: "text", text: `Deal angelegt: ${deal.id} (${title})` }],
        };
      },
    );

    Drei Dinge, die hier wichtig sind:

    1. Beschreibungen sind Prompts – schreib sie für den Agenten, nicht für den Menschen
    2. Fehler immer als isError: true zurückgeben, sonst halluziniert der Agent Erfolg
    3. authInfo.token kommt aus der OAuth-Session (siehe Schritt 5)

    Schritt 4: GraphQL-Backends kapseln

    Bei GraphQL hast du den Vorteil, dass du die Query genau auf das zuschneiden kannst, was der Agent braucht. Mach das. Gib niemals das ganze Schema frei.

    server.tool(
      "search_customers",
      "Sucht Kunden anhand eines Suchbegriffs. Liefert Name, Email und letzten Kontakt.",
      { query: z.string().min(2) },
      async ({ query }, { authInfo }) => {
        const gql = `
          query Search($q: String!) {
            customers(search: $q, limit: 10) {
              id name email lastContactAt
            }
          }
        `;
        const res = await fetch(`${process.env.BACKEND_URL}/graphql`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${authInfo?.token}`,
          },
          body: JSON.stringify({ query: gql, variables: { q: query } }),
        });
        const { data } = await res.json();
        return { content: [{ type: "text", text: JSON.stringify(data.customers, null, 2) }] };
      },
    );

    Anti-Pattern: Ein generisches execute_graphql-Tool, das beliebige Queries entgegennimmt. Damit verlierst du Auth-Granularität, gibst dem Agenten zu viele Optionen und fängst dir Schema-Lecks ein.

    Schritt 5: Resources – Daten zum Lesen exponieren

    Resources sind URIs, die der Client laden kann, ohne dass der Agent eine Aktion ausführt. Perfekt für kontextuelle Daten, die der Agent „immer dabei" haben soll.

    server.resource(
      "deal",
      "deal://{id}",
      async (uri, { id }) => {
        const res = await fetch(`${process.env.BACKEND_URL}/api/deals/${id}`);
        const deal = await res.json();
        return {
          contents: [{
            uri: uri.href,
            mimeType: "application/json",
            text: JSON.stringify(deal),
          }],
        };
      },
    );

    Beispiele für sinnvolle Resources:

    • customer://{id} – Kunden-Stammdaten
    • report://weekly/{date} – wöchentlicher Report
    • policy://refund – statische Policy-Dokumente, die der Agent zitieren soll

    Schritt 6: OAuth – der unsexy, aber kritische Teil

    Hier scheitern 80 % der ersten MCP-Server. Drei Optionen, sortiert nach Aufwand:

    Option A: API-Key per Header (nur für interne MCPs)

    Schnell, aber nicht multi-tenant. Für interne Server hinter SSO okay.

    const transport = new StreamableHTTPServerTransport({
      authenticate: async (req) => {
        const key = req.headers.get("x-api-key");
        if (key !== process.env.INTERNAL_KEY) throw new Error("Unauthorized");
        return { token: key };
      },
    });

    Option B: Bearer-Token-Forwarding (B2B-Setups)

    Der Client schickt sein eigenes Backend-Token mit. Du leitest es 1:1 weiter. Funktioniert, wenn deine Kunden technisch sind und API-Tokens generieren können.

    Option C: OAuth 2.1 mit Dynamic Client Registration (echter SaaS-Standard)

    Das ist, was du willst, wenn dein MCP-Server in Claude Desktop, ChatGPT und Cursor automatisch funktionieren soll. Das SDK bringt das mit:

    import { ProxyOAuthServerProvider } from "@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js";
    
    const oauth = new ProxyOAuthServerProvider({
      endpoints: {
        authorizationUrl: "https://auth.deinbackend.de/oauth/authorize",
        tokenUrl: "https://auth.deinbackend.de/oauth/token",
        revocationUrl: "https://auth.deinbackend.de/oauth/revoke",
      },
      verifyAccessToken: async (token) => {
        // gegen dein Auth-Backend prüfen
        const res = await fetch("https://auth.deinbackend.de/introspect", {
          method: "POST",
          body: new URLSearchParams({ token }),
        });
        const data = await res.json();
        if (!data.active) throw new Error("Invalid token");
        return { token, clientId: data.client_id, scopes: data.scope?.split(" ") ?? [] };
      },
      getClient: async (clientId) => {
        // aus deiner Client-DB laden
        return { client_id: clientId, redirect_uris: ["..."] };
      },
    });

    Dazu noch die Discovery-Endpoints ausliefern (/.well-known/oauth-authorization-server und /.well-known/oauth-protected-resource) – das SDK übernimmt das, wenn du den Provider registrierst.

    Wenn dein Backend noch keinen OAuth-Server hat: Das ist okay. Nutz Auth0, Clerk, WorkOS oder Supabase Auth davor. Bau das nicht selbst.

    Schritt 7: Lokal testen mit dem MCP Inspector

    Das offizielle Test-Tool – wahrscheinlich das wertvollste Stück der Toolchain:

    npx @modelcontextprotocol/inspector npx tsx src/index.ts

    Es startet eine Web-UI auf localhost:6274, in der du jedes Tool, jede Resource und den OAuth-Flow durchklicken kannst, bevor du Claude überhaupt ranlässt.

    Erst wenn das hier grün ist, schaltest du Claude Desktop oder Codex an.

    Schritt 8: Deployment

    Die Streamable-HTTP-Transport-Variante läuft überall, wo Long-running HTTP geht. Empfehlungen aus unseren Projekten:

    Plattform Wann nehmen? Trade-off
    Cloudflare Workers SaaS-Server für viele Tenants Globale Edge, aber 30s CPU-Limit pro Request
    Supabase Edge Functions Wenn dein Backend eh Supabase nutzt Auth & DB direkt verfügbar, Deno-Runtime
    Railway / Fly / Render Klassischer Long-running Node-Server Einfach, kein Vendor-Lock-in
    AWS Lambda + API Gateway Enterprise mit AWS-Vorgabe Cold Starts, mehr Boilerplate

    Setze hinter den Server CDN + Rate Limit und log jede Tool-Invocation mit Tenant-ID. Du wirst diese Logs lieben, sobald jemand fragt „Warum hat der Agent das gemacht?".

    Schritt 9: Listing & Distribution

    Sobald der Server live ist:

    1. Eintrag in mcp.so und glama.ai/mcp/servers
    2. Eigene Docs-Seite mit „Connect to Claude"-Button (Deep-Link claude://mcp/install?...)
    3. Submit für native Marketplaces in Claude und ChatGPT
    4. Beispiele in deiner Doku, die zeigen, welcher Prompt welches Tool triggert – das ist Onboarding für Agenten und Menschen

    Häufige Fehler – und wie du sie vermeidest

    Fehler Konsequenz Fix
    Zu viele Tools (>30) Agent wählt schlecht aus Auf 5–10 Kern-Tools reduzieren
    Vage Beschreibungen Tool wird nie aufgerufen Beschreibung mit „Nutze dies, wenn..." starten
    Keine Idempotenz Doppelte Deals, doppelte Mails idempotency_key Param ergänzen
    Auth-Token loggen DSGVO-GAU Strukturiertes Logging mit Token-Redaction
    Schema-Drift Agent ruft veraltete Felder auf Zod-Schemas aus Backend-Types generieren

    Was als Nächstes?

    Wenn dein erster MCP-Server läuft, hast du den schwersten Teil hinter dir. Die nächsten Iterationen:

    • Skills schreiben (SKILL.md), damit Agenten deinen Server systematisch nutzen
    • Multi-Tenant-Logging mit OpenTelemetry
    • Eval-Suite mit echten Prompts, damit Schema-Änderungen Agenten-Verhalten nicht brechen

    Fazit

    Ein erster, produktionsreifer MCP-Server ist kein Monatsprojekt. Mit einem bestehenden REST- oder GraphQL-Backend, dem offiziellen SDK und einer der genannten Hosting-Optionen schaffst du das in 1–3 Tagen – mit OAuth in 1–2 Wochen.

    Der Punkt ist nicht, perfekt zu sein. Der Punkt ist, da zu sein, bevor deine Konkurrenz es ist.

    Die beste Zeit, deinen MCP-Server zu launchen, war gestern. Die zweitbeste ist heute.


    Du willst diesen Schritt nicht alleine gehen? Wir bauen MCP-Server für SaaS und interne Plattformen – mit OAuth, Multi-Tenant und sauberem Tool-Design. Lass uns reden →

    TeilenLinkedInWhatsAppE-Mail

    Verwandte Artikel

    Schwebende Fragezeichen über einem MCP-Server mit Auth-Shield und vernetzten Knoten – visuelle FAQ-Metapher
    13. Mai 20267 min

    MCP-FAQ: Die 12 häufigsten Einwände – ehrlich beantwortet

    Brauche ich überhaupt MCP, wenn ich schon eine API habe? Was kostet das? Wie sicher ist das wirklich? Wir beantworten di…

    Weiterlesen
    MCP als zentraler Hub, der KI-Agenten mit CRM, ERP, Datenbanken und SaaS-Tools verbindet
    13. Mai 20265 min

    No MCP, no Party: Warum kein Unternehmen mehr an MCP vorbeikommt

    MCP ist nicht mehr nur ein Protokoll – es ist ein neuer Distributionskanal. Wer als SaaS oder Unternehmen jetzt keinen M…

    Weiterlesen
    Schematische Darstellung des Model Context Protocol: KI-Gehirn verbunden mit Datenbanken, Kalender, CRM und Dokumenten
    12. März 20266 min

    MCP für Einsteiger: Alles, was du über das Model Context Protocol wissen musst

    MCP ist der offene Standard, der KI-Modelle mit deinen Tools verbindet. Was es ist, wie es funktioniert und warum kein W…

    Weiterlesen
    monday.com MCP-Integrationen – AI-Agents verbinden sich mit der Work-Management-Plattform
    15. April 20265 min

    monday.com MCP: Alle verfügbaren Tools und Integrationen im Überblick

    monday.com bietet mit dem Platform MCP und dem Apps MCP zwei leistungsstarke MCP-Server – plus native Integrationen für …

    Weiterlesen
    Claude Managed Agents Architektur – Gehirn verbunden mit mehreren Händen für Tools und Sandboxes
    8. April 20265 min

    Claude Managed Agents: Anthropics Griff nach der Agent-Runtime

    Anthropic launcht Managed Agents in der Public Beta – eine gehostete Runtime, die das 'Gehirn' von den 'Händen' entkoppe…

    Weiterlesen
    Warum wir von ChatGPT auf Claude umgestiegen sind – und was wir dabei über LLMs gelernt haben
    20. Februar 20265 min

    Warum wir von ChatGPT auf Claude umgestiegen sind – und was wir dabei über LLMs gelernt haben

    Wir haben 18 Monate mit ChatGPT gearbeitet – und sind dann auf Claude umgestiegen. Hier ist der ehrliche Vergleich aller…

    Weiterlesen
    Die nächste Generation CRM: Warum monday CRM dazugehört – und das Interface der Zukunft ein Chat istDeep Dive
    15. Juli 20258 min

    Die nächste Generation CRM: Warum monday CRM dazugehört – und das Interface der Zukunft ein Chat ist

    Das CRM der Zukunft ist kein Dashboard – es ist ein Chat. Warum monday CRM zur nächsten Generation gehört und wie MCP da…

    Weiterlesen
    P9 AI Fluency Index: Wie Christoph Janz mit Claude ein Benchmark-Tool für AI-pilled Companies gebaut hat
    9. Mai 20264 min

    P9 AI Fluency Index: Wie Christoph Janz mit Claude ein Benchmark-Tool für AI-pilled Companies gebaut hat

    Point Nine Partner Christoph Janz hat mit Claude den P9 AI Fluency Index gebaut – ein Benchmark-Tool für Founders, das m…

    Weiterlesen
    Railway-Plattform verbunden mit Claude Code – Deployment per Agent Skill
    1. Mai 20263 min

    Railway + Claude Code: Deployment per Prompt – wie die Integration funktioniert

    Was ist Railway – und warum ist die Plattform plötzlich der heimliche Favorit für AI-First-Teams? Ein Blick auf das Clau…

    Weiterlesen