Design wechseln

MCP-Server-Entwicklung: Ihren ersten MCP-Dienst von Grund auf aufsetzen

Easton editorial illustration: trace beacon network

Einleitung: Schreiben Sie Code in Cursor und wünschen Sie sich, dass die KI direkt die neueste Version Ihrer Projektabhängigkeiten nachschlägt? Oder analysieren Sie Daten in Claude und möchten, dass sie Informationen aus Ihrer Datenbank liest? Ohne MCP müssten Sie für jedes KI-Tool eine eigene Anpassungsschicht schreiben. Mit einem MCP Server reicht ein einziger Dienst – alle MCP-fähigen Clients können ihn nutzen. Dieser Artikel führt Sie von null an durch die Entwicklung eines vollständigen MCP Servers mit TypeScript.

30 Min.
Einstiegszeit
Von null bis lauffähig
3
Kernfähigkeiten
Tools/Resources/Prompts
1000+
MCP-Server
Open-Source-Community auf GitHub
Source: Offizielle MCP-Daten (2025)

Was ist MCP? Kernkonzepte in 3 Minuten

Die Geschichte eines USB-Anschlusses

Wer vor ein paar Jahren digitale Geräte genutzt hat, erinnert sich an die unbequeme Phase: Maus mit rundem Stecker, Tastatur mit eckigem, Drucker mit Parallelanschluss – jedes Gerät brauchte seinen eigenen Port. Dann kam USB und löste alles mit einem einzigen Anschluss.

MCP (Model Context Protocol) wird zum „USB-Standard“ der KI-Tool-Welt.

Ohne MCP brauchen Sie für jede Datenquelle eine eigene Anpassungsschicht: ein Plugin für Claude, eine Erweiterung für Cursor, noch eine für Windsurf … Die Komplexität ist N × M (N Datenquellen × M KI-Tools).

Mit MCP schreiben Sie einen MCP Server – alle MCP-fähigen Clients können ihn direkt aufrufen. Die Komplexität sinkt auf N + M.

Die Drei-Schichten-Architektur ist einfach:

+-------------+     +-------------+     +-------------+
|    Host     | ->  |   Client    | ->  |   Server    |
|  (Claude)   |     | (MCP-Client)|     | (Ihr Dienst)|
+-------------+     +-------------+     +-------------+
  • Host: Die KI-Anwendung selbst, z. B. Claude Desktop oder Cursor
  • Client: MCP-Client, kommuniziert mit dem Host
  • Server: Ihr eigener Dienst mit den konkreten Funktionen

Drei Fähigkeiten des MCP Servers

Ein MCP Server kann drei verschiedene Funktionstypen bereitstellen:

FähigkeitZweckBeispiel
ToolsAktionen ausführenWetter abfragen, Nachricht senden, Datenbank lesen
ResourcesDaten bereitstellenDateiinhalte, API-Antworten, Konfiguration
PromptsVordefinierte VorlagenCode-Review-Vorlage, Tagesbericht-Vorlage

Tools sind wie „Funktionen“ – die KI kann sie aufrufen, um eine Aktion auszuführen; Resources sind „Datenquellen“ – die KI kann deren Inhalt lesen; Prompts sind „Vorlagen“ – sie helfen der KI, die Aufgabe schneller zu verstehen.

Unterschied zu anderen Artikeln: In manchen MCP-Tutorials sehen Sie Python mit FastMCP. Dieser Artikel nutzt das native TypeScript-SDK – besser geeignet für Frontend- und Full-Stack-Entwickler. Beide Ansätze sind funktional gleichwertig; wählen Sie die Sprache, die Sie kennen.

"https://modelcontextprotocol.io"


Entwicklungsumgebung vorbereiten

Voraussetzungen

Dieser Artikel setzt voraus, dass Sie:

  • Node.js 18+ oder Bun 1.0+ installiert haben
  • TypeScript kennen und wissen, was interface und async/await sind
  • Claude Desktop oder einen MCP-fähigen Client (Cursor, Windsurf usw.) nutzen

Wenn Sie Bun noch nicht kennen: Probieren Sie es aus – es ist deutlich schneller als npm und bringt TypeScript-Support mit, ohne ts-node extra konfigurieren zu müssen.

Projekt initialisieren

# Projektverzeichnis anlegen
mkdir mcp-weather-server && cd mcp-weather-server

# Initialisieren (mit Bun oder npm)
bun init -y
# oder npm init -y

# MCP TypeScript SDK installieren
bun add @modelcontextprotocol/sdk zod
# oder npm install @modelcontextprotocol/sdk zod

Zwei Abhängigkeiten:

  • @modelcontextprotocol/sdk: Offizielles MCP TypeScript SDK
  • zod: Laufzeit-Typvalidierung in TypeScript, für Tool-Parameter-Schemas

TypeScript-Konfiguration

Mit bun init ist tsconfig.json bereits gesetzt. Bei manueller Konfiguration diese Optionen beachten:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "esModuleInterop": true,
    "strict": true
  }
}

moduleResolution: "bundler" ist wichtig für ESM-Module – sonst kann der Fehler „xxx is not defined“ auftreten.


Praxis: Wetterabfrage-MCP-Server von Hand

Dieses Tutorial führt Sie durch einen vollständigen MCP Server, der:

  1. KI-Aufrufe entgegennimmt
  2. die OpenWeatherMap-API für Echtzeitwetter abfragt
  3. formatierte Ergebnisse zurückgibt

Projektstruktur

mcp-weather-server/
+-- src/
|   +-- index.ts      # Einstieg
|   +-- weather.ts    # Wetter-Tool
|   +-- resources.ts  # Ressourcen
+-- package.json
+-- tsconfig.json

Der Code kann alles in index.ts stehen (wie in diesem Artikel) – Module erleichtern die Wartung.

Schritt 1: MCP-Server-Grundgerüst

Vom Einfachsten starten – ein lauffähiger MCP Server:

// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Server-Instanz erstellen
const server = new McpServer({
  name: "weather-service",
  version: "1.0.0",
});

// Tool registrieren (Tools)
server.tool(
  "get_weather",
  "Aktuelle Wetterinformationen für eine Stadt abrufen",
  {
    city: z.string().describe("Stadtname, z. B. Berlin, München"),
  },
  async ({ city }) => {
    // Tool-Implementierung folgt im nächsten Abschnitt
    return { content: [{ type: "text", text: `Wetter für ${city} wird abgefragt...` }] };
  }
);

// Server starten
const transport = new StdioServerTransport();
await server.connect(transport);

McpServer ist die Kernklasse des SDKs – name und version sind Pflicht. tool() registriert ein Tool: erster Parameter Name, zweiter Beschreibung, dritter Parameter-Schema, zuletzt die Ausführungsfunktion.

Schritt 2: Wetter-Tool implementieren (Kerncode)

Jetzt das Tool wirklich nutzbar machen – mit der kostenlosen OpenWeatherMap-API:

// src/weather.ts
import { z } from "zod";

// OpenWeatherMap API-Antworttyp
interface WeatherResponse {
  name: string;
  main: { temp: number; feels_like: number; humidity: number };
  weather: [{ description: string }];
  wind: { speed: number };
}

// Wetter-Tool-Implementierung
server.tool(
  "get_weather",
  "Aktuelle Wetterinformationen für eine Stadt abrufen",
  {
    city: z.string().describe("Stadtname, z. B. Berlin, München"),
  },
  async ({ city }) => {
    const API_KEY = process.env.OPENWEATHER_API_KEY;
    const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric&lang=de`;

    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`API-Anfrage fehlgeschlagen: ${response.status}`);
      }

      const data: WeatherResponse = await response.json();

      // Formatiertes Ergebnis zurückgeben
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify({
              city: data.name,
              temperature: `${data.main.temp}°C`,
              feels_like: `${data.main.feels_like}°C`,
              description: data.weather[0].description,
              humidity: `${data.main.humidity}%`,
              wind_speed: `${data.wind.speed} m/s`,
            }, null, 2),
          },
        ],
      };
    } catch (error) {
      return {
        content: [
          {
            type: "text",
            text: `Abfrage fehlgeschlagen: ${error instanceof Error ? error.message : 'Unbekannter Fehler'}`,
          },
        ],
        isError: true,
      };
    }
  }
);

Wichtig:

  1. API Key aus Umgebungsvariable: Schlüssel nie im Code hardcoden
  2. Fehlerbehandlung: isError: true signalisiert dem Client einen fehlgeschlagenen Aufruf
  3. Typdefinition: Das Interface WeatherResponse lässt TypeScript die Datenstruktur prüfen

Registrieren Sie sich bei OpenWeatherMap, holen Sie einen kostenlosen API Key und setzen Sie die Umgebungsvariable:

export OPENWEATHER_API_KEY=your_api_key_here

Schritt 3: Resources hinzufügen (optional, empfohlen)

Mit Resources liefert Ihr Server schreibgeschützte Daten – z. B. Serverstatus für die KI:

// src/resources.ts

// Serverstatus bereitstellen
server.resource(
  "server-status",
  "status://server",
  async (uri) => ({
    contents: [
      {
        uri: uri.href,
        text: JSON.stringify({
          name: "Weather Service",
          version: "1.0.0",
          status: "running",
          timestamp: new Date().toISOString(),
        }, null, 2),
      },
    ],
  })
);

// API-Dokumentation bereitstellen
server.resource(
  "api-docs",
  "docs://api",
  async (uri) => ({
    contents: [
      {
        uri: uri.href,
        text: `
# Weather MCP Server API

## Tools
- get_weather(city: string): Wetter für eine Stadt abrufen

## Resources
- status://server - Serverstatus
- docs://api - API-Dokumentation
        `.trim(),
      },
    ],
  })
);

Die ersten beiden Parameter von resource() sind Name und URI, der dritte die Lese-Funktion. Die URI kann ein beliebiges Schema haben, z. B. status:// oder docs://.

Schritt 4: Prompts hinzufügen (Fortgeschritten)

Prompts sind vordefinierte Dialogvorlagen – z. B. eine „Wetterbericht“-Vorlage, in die die KI automatisch den Stadtnamen einsetzt:

// Vordefinierte Wetterbericht-Vorlage
server.prompt(
  "weather_report",
  "Einen formatierten Wetterbericht erzeugen",
  {
    city: z.string().describe("Stadtname"),
    include_tips: z.boolean().optional().describe("Kleidungstipps einbeziehen"),
  },
  ({ city, include_tips }) => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `Bitte erstelle einen Wetterbericht für ${city}.${include_tips ? " Gib auch Kleidungsempfehlungen." : ""}`,
        },
      },
    ],
  })
);

Der Rückgabewert von prompt() ist ein Nachrichtenarray mit role und content – die KI erhält so vorgegebenen Kontext.

Schritt 5: Einstiegsdatei vervollständigen

Alles in src/index.ts zusammenführen und Fehlerbehandlung ergänzen:

// src/index.ts (vollständig)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({
  name: "weather-service",
  version: "1.0.0",
});

// Alle Tools, Resources und Prompts registrieren
// ... (Code von oben)

// Fehlerbehandlung
process.stdin.on("error", (err) => {
  console.error("Stdin-Fehler:", err);
  process.exit(1);
});

process.stdout.on("error", (err) => {
  console.error("Stdout-Fehler:", err);
  process.exit(1);
});

// Sauberes Beenden
process.on("SIGINT", async () => {
  await server.close();
  process.exit(0);
});

// Server starten
const transport = new StdioServerTransport();
await server.connect(transport);

console.error("MCP Weather Server gestartet, warte auf Verbindung...");

StdioServerTransport kommuniziert über Standard-Ein-/Ausgabe – Fehlerbehandlung für stdin/stdout ist wichtig. SIGINT ermöglicht sauberes Stoppen mit Strg+C.

Start mit bun run src/index.ts – die Meldung „gestartet“ bestätigt, dass alles läuft.


Client konfigurieren: Claude anbinden

Der Server steht – jetzt Claude Desktop oder Cursor daran anschließen.

Claude Desktop

Konfigurationsdatei:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Server-Eintrag hinzufügen:

{
  "mcpServers": {
    "weather": {
      "command": "bun",
      "args": ["run", "/absolute/path/to/mcp-weather-server/src/index.ts"],
      "env": {
        "OPENWEATHER_API_KEY": "Ihr API Key"
      }
    }
  }
}

Hinweis: Der Pfad in args muss absolut sein – relative Pfade führen oft zu Startfehlern.

Cursor / Windsurf

Ähnliche Konfiguration in den IDE-Einstellungen unter MCP (Format wie oben).

Cursor-Konfiguration typischerweise:

  • macOS: ~/Library/Application Support/Cursor/User/globalStorage/state.vscdb
  • oder in der IDE: Einstellungen → KI → MCP → Server hinzufügen

Server testen

  1. Claude Desktop / Cursor neu starten
  2. In den Chat eingeben: „Wie ist das Wetter in Berlin?“
  3. Claude sollte Ihren MCP Server automatisch aufrufen

Erfolgreich sieht die Ausgabe etwa so aus:

{
  "city": "Berlin",
  "temperature": "18°C",
  "feels_like": "16°C",
  "description": "bewölkt",
  "humidity": "65%",
  "wind_speed": "3.2 m/s"
}

Häufige Probleme

ProblemMögliche UrsacheLösung
Server nicht verbundenFalscher PfadAbsoluten Pfad in args prüfen
API Key ungültigUmgebungsvariable fehltenv-Konfiguration prüfen
Keine AntwortTypeScript-FehlerZuerst mit bun build oder tsc kompilieren
BerechtigungsfehlerConfig-RechteKonfigurationsdatei lesbar machen

"https://github.com/modelcontextprotocol/typescript-sdk"


Erweiterung und Deployment

Weitere Tools

Wetterabfrage ist nur der Anfang. Möglich sind z. B.:

  • Historisches Wetter: Historische API, Wetter eines vergangenen Tages
  • Mehrstädte-Vergleich: Mehrere Städte parallel, Vergleichstabelle
  • Wetterwarnungen: Prüfung auf Unwetterwarnungen

Registrierung wie bei get_weather, nur andere Logik.

Deployment-Optionen

Für Team-Nutzung reicht lokales stdio nicht. Vergleich:

MethodeEinsatzVorteileNachteile
Lokales stdioPersönlich, EntwicklungEinfach, sicherNicht teilbar
HTTP/SSETeam, mehrere NutzerRemote-ZugriffAuthentifizierung nötig
ServerlessProduktionAuto-SkalierungCold-Start-Latenz

Produktion

Authentifizierung: Bei HTTP-Deployment Pflicht. MCP unterstützt OAuth 2.1; einfacher API Key geht auch:

// API Key im Request-Header prüfen
const apiKey = request.headers.get("Authorization");
if (apiKey !== `Bearer ${process.env.API_KEY}`) {
  return new Response("Unauthorized", { status: 401 });
}

Rate Limiting: Schutz vor Missbrauch und API-Kontingent-Erschöpfung – z. B. express-rate-limit oder Cloudflare Workers.

Logging: Mit pino oder winston Tool-Aufrufe protokollieren:

import pino from "pino";
const logger = pino();

server.tool("get_weather", /* ... */, async ({ city }) => {
  logger.info({ city }, "Wetterabfrage");
  // ...
});

Monitoring: Erfolgsrate und Antwortzeit – oft Prometheus + Grafana.


Fazit

Dieser Artikel zeigt, wie Sie einen MCP Server mit TypeScript von Grund auf schreiben:

  • MCP-Kernkonzepte und Drei-Schichten-Architektur
  • Server mit MCP TypeScript SDK
  • Wetter-Tool (Tools)
  • Serverstatus-Ressource (Resources)
  • Wetterbericht-Vorlage (Prompts)
  • Claude Desktop / Cursor anbinden

Als Nächstes können Sie:

  1. MCP-Wrapper für APIs bauen (GitHub, Slack, Notion …)
  2. MCP-Schnittstellen für interne Systeme (CRM, Datenbank)
  3. Die MCP-Community erkunden

Weiterführende Ressourcen:

Für MCP-Protokoll im Detail: MCP-Protokoll vertieft verstehen.

FAQ

Welche Vorkenntnisse brauche ich für MCP-Server-Entwicklung?
JavaScript/TypeScript-Grundlagen. Dieser Artikel nutzt das MCP TypeScript SDK – async/await und Typdefinitionen reichen zum Einstieg.
Was ist der Unterschied zwischen MCP Server und FastMCP?
FastMCP ist ein Python-Framework für Python-Entwickler. Hier das native TypeScript-SDK für Frontend/Full-Stack. Funktional gleichwertig – Wahl nach Tech-Stack.
Wie teste ich, ob der MCP Server funktioniert?
Nach Claude-Desktop-Konfiguration eine natürliche Anfrage stellen (z. B. „Wetter in Berlin“). Ruft Claude das Tool auf und liefert Ergebnisse, läuft alles.
Kann ich den MCP Server remote deployen?
Ja. stdio eignet sich für lokale Entwicklung. Produktion oft HTTP/SSE mit OAuth und Rate Limiting.

8 Min. Lesezeit · Veröffentlicht am: 19. März 2026 · Aktualisiert am: 14. Juli 2026

Ähnliche Beiträge

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog