MCP – Model Context Protocol erklärt: Architektur, Code-Beispiele und Anwendungsfälle
MCP – Model Context Protocol erklärt: Architektur, Code-Beispiele und Anwendungsfälle
Alles über das Model Context Protocol (MCP): Was es ist, wie es funktioniert, wie du eigene MCP-Server baust und warum es die KI-Entwicklung grundlegend verändert.
Was ist das Model Context Protocol?
Das Model Context Protocol (MCP) ist ein offenes Protokoll, das Anthropic Ende 2024 veröffentlicht hat und das seither die Art und Weise, wie KI-Modelle mit externen Systemen interagieren, grundlegend verändert. MCP standardisiert die Kommunikation zwischen KI-Assistenten und den Werkzeugen, Datenquellen und Diensten, die sie nutzen sollen.
Stell dir MCP als eine Art "USB-Standard für KI" vor. So wie USB dafür gesorgt hat, dass verschiedene Geräte miteinander kommunizieren können, ohne dass für jede Kombination eine eigene Schnittstelle entwickelt werden muss, standardisiert MCP die Kommunikation zwischen KI-Modellen und externen Ressourcen.
Vor MCP war das Problem folgendes: Wenn du Claude, GPT oder ein anderes Modell mit einer Datenbank, einem API oder einem lokalen Dateisystem verbinden wolltest, musstest du jede Integration individuell entwickeln. Das war teuer, zeitaufwendig und führte zu einem fragmentierten Ökosystem. MCP löst dieses Problem durch einen einheitlichen Standard.
Die Kernkonzepte von MCP
Hosts, Clients und Server
MCP definiert drei grundlegende Rollen in der Kommunikationsstruktur:
Host: Der Host ist die Anwendung, die das KI-Modell nutzt. Das kann Claude Desktop sein, eine eigene Anwendung oder ein anderes KI-Interface. Der Host verwaltet die Client-Verbindungen und koordiniert den Informationsfluss.
Client: Der Client lebt innerhalb des Hosts und pflegt eine 1:1-Verbindung zu einem MCP-Server. Er überträgt Anfragen des Modells an den Server und gibt die Antworten zurück.
Server: Der MCP-Server ist das Herzstück der Implementierung. Er stellt Ressourcen, Tools und Prompts bereit, die das KI-Modell nutzen kann. Ein Server könnte Zugriff auf eine Datenbank, ein Dateisystem, eine externe API oder jede andere Datenquelle bieten.
Die drei Primitiven: Resources, Tools und Prompts
MCP definiert drei grundlegende Typen von Fähigkeiten, die ein Server bereitstellen kann:
Resources (Ressourcen): Statische oder dynamisch generierte Daten, auf die das Modell lesend zugreifen kann. Beispiele: Dateien, Datenbankeinträge, API-Antworten, Log-Dateien.
Tools (Werkzeuge): Ausführbare Funktionen, die das Modell aufrufen kann, um Aktionen durchzuführen. Beispiele: Datei schreiben, API aufrufen, Berechnung durchführen, E-Mail senden.
Prompts (Eingabeaufforderungen): Vordefinierte Interaktionsmuster, die der Server bereitstellt. Sie helfen dabei, das Modell auf spezifische Aufgaben vorzubereiten.
MCP-Architektur im Detail
┌─────────────────────────────────────────────────┐
│ HOST │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ MCP │ │ MCP │ │ MCP │ │
│ │ Client 1 │ │ Client 2 │ │ Client 3 │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
└───────┼────────────────┼────────────────┼────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ MCP Server 1 │ │ MCP Server 2 │ │ MCP Server 3 │
│ (Dateisys.) │ │ (Datenbank) │ │ (Web-API) │
└──────────────┘ └──────────────┘ └──────────────┘
Das Protokoll nutzt JSON-RPC 2.0 für den Nachrichtenaustausch und unterstützt zwei Transport-Mechanismen:
- stdio (Standard Input/Output): Für lokale Prozesse – der Client startet den Server als Subprocess.
- HTTP mit SSE (Server-Sent Events): Für remote Server und skalierbare Deployments.
Für das Formatieren und Validieren von JSON-Nachrichten in deinen MCP-Implementierungen: JSON Formatter.
Einen einfachen MCP-Server bauen
Lass uns einen MCP-Server in Python implementieren, der Wetterdaten bereitstellt:
# weather_server.py
import asyncio
import json
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
# Server initialisieren
app = Server("weather-server")
# Tool definieren: Wetter abfragen
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="get_weather",
description="Gibt aktuelle Wetterdaten für eine Stadt zurück",
inputSchema={
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Der Name der Stadt"
},
"country_code": {
"type": "string",
"description": "ISO 3166-1 Alpha-2 Ländercode (z.B. 'DE')"
}
},
"required": ["city"]
}
)
]
# Tool implementieren
@app.call_tool()
async def call_tool(
name: str,
arguments: dict
) -> list[types.TextContent]:
if name == "get_weather":
city = arguments.get("city")
country_code = arguments.get("country_code", "DE")
# In einem realen Szenario würdest du hier eine
# echte Wetter-API aufrufen
weather_data = await fetch_weather(city, country_code)
return [
types.TextContent(
type="text",
text=json.dumps(weather_data, ensure_ascii=False, indent=2)
)
]
raise ValueError(f"Unbekanntes Tool: {name}")
async def fetch_weather(city: str, country_code: str) -> dict:
"""
Ruft Wetterdaten von einer externen API ab.
Für dieses Beispiel geben wir Mock-Daten zurück.
"""
return {
"stadt": city,
"land": country_code,
"temperatur": "18°C",
"beschreibung": "Teilweise bewölkt",
"luftfeuchtigkeit": "65%",
"wind": "15 km/h NW",
"aktualisiert": "2026-03-17T10:30:00Z"
}
# Server starten
async def main():
async with stdio_server() as (read_stream, write_stream):
await app.run(
read_stream,
write_stream,
app.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())
MCP-Server mit Resources
Hier ist ein Beispiel für einen Server, der Datei-Ressourcen bereitstellt:
# file_server.py
import asyncio
import os
from pathlib import Path
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
app = Server("file-server")
BASE_DIR = Path("/home/user/documents")
@app.list_resources()
async def list_resources() -> list[types.Resource]:
"""Listet alle verfügbaren Textdateien auf"""
resources = []
for file_path in BASE_DIR.rglob("*.txt"):
relative_path = file_path.relative_to(BASE_DIR)
resources.append(
types.Resource(
uri=f"file://{relative_path}",
name=str(relative_path),
description=f"Textdatei: {relative_path}",
mimeType="text/plain"
)
)
return resources
@app.read_resource()
async def read_resource(uri: str) -> str:
"""Liest den Inhalt einer Datei"""
# URI parsen und Pfad extrahieren
file_path = BASE_DIR / uri.removeprefix("file://")
# Sicherheitscheck: Pfad muss innerhalb BASE_DIR liegen
resolved = file_path.resolve()
if not str(resolved).startswith(str(BASE_DIR.resolve())):
raise ValueError("Zugriff außerhalb des erlaubten Verzeichnisses")
return resolved.read_text(encoding="utf-8")
async def main():
async with stdio_server() as (read_stream, write_stream):
await app.run(
read_stream,
write_stream,
app.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())
MCP in TypeScript/Node.js
Für JavaScript-Entwickler gibt es ebenfalls ein offizielles SDK:
// database_server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
// Simulierte Datenbank
const produktDatenbank = [
{ id: 1, name: "Laptop Pro X", preis: 1299.99, kategorie: "Elektronik" },
{ id: 2, name: "Wireless Maus", preis: 49.99, kategorie: "Zubehör" },
{ id: 3, name: "USB-C Hub", preis: 79.99, kategorie: "Zubehör" },
];
const server = new Server(
{
name: "produktdatenbank-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Tools registrieren
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "produkt_suchen",
description: "Sucht Produkte in der Datenbank anhand von Kriterien",
inputSchema: {
type: "object",
properties: {
suchbegriff: {
type: "string",
description: "Suchbegriff für Produktname oder Kategorie",
},
maxPreis: {
type: "number",
description: "Maximaler Preis in Euro (optional)",
},
},
required: ["suchbegriff"],
},
},
{
name: "produkt_details",
description: "Gibt detaillierte Informationen zu einem Produkt zurück",
inputSchema: {
type: "object",
properties: {
produktId: {
type: "number",
description: "Die ID des Produkts",
},
},
required: ["produktId"],
},
},
],
};
});
// Tool-Ausführung
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "produkt_suchen") {
const { suchbegriff, maxPreis } = args as {
suchbegriff: string;
maxPreis?: number;
};
let ergebnisse = produktDatenbank.filter(
(p) =>
p.name.toLowerCase().includes(suchbegriff.toLowerCase()) ||
p.kategorie.toLowerCase().includes(suchbegriff.toLowerCase())
);
if (maxPreis !== undefined) {
ergebnisse = ergebnisse.filter((p) => p.preis <= maxPreis);
}
return {
content: [
{
type: "text",
text: JSON.stringify(ergebnisse, null, 2),
},
],
};
}
if (name === "produkt_details") {
const { produktId } = args as { produktId: number };
const produkt = produktDatenbank.find((p) => p.id === produktId);
if (!produkt) {
return {
content: [
{
type: "text",
text: `Produkt mit ID ${produktId} nicht gefunden.`,
},
],
isError: true,
};
}
return {
content: [
{
type: "text",
text: JSON.stringify(produkt, null, 2),
},
],
};
}
throw new Error(`Unbekanntes Tool: ${name}`);
});
// Server starten
const transport = new StdioServerTransport();
await server.connect(transport);
MCP in Claude Desktop konfigurieren
Um deinen lokalen MCP-Server mit Claude Desktop zu verbinden, bearbeitest du die Konfigurationsdatei:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"wetter": {
"command": "python",
"args": ["/pfad/zu/deinem/weather_server.py"],
"env": {
"OPENWEATHER_API_KEY": "dein-api-schluessel"
}
},
"produktdatenbank": {
"command": "node",
"args": ["/pfad/zu/deinem/database_server.js"]
},
"dateisystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/dein-name/Dokumente"
]
}
}
}
Nach einem Neustart von Claude Desktop sind die konfigurierten Server verfügbar, und Claude kann die bereitgestellten Tools nutzen.
Praxisanwendungen und Use Cases
Use Case 1: Code-Review-Pipeline
Ein Entwicklungsteam baut einen MCP-Server, der auf ihr Git-Repository zugreift. Claude kann damit:
- Aktuelle Commit-History abrufen
- Spezifische Dateien lesen und reviewen
- Kommentare und Verbesserungsvorschläge in Pull Requests schreiben
- Automatisch auf häufige Anti-Patterns prüfen
Use Case 2: Unternehmens-Wissensmanagement
Ein Unternehmen verbindet seinen internen Confluence-Server über MCP. Mitarbeiter können Claude fragen:
- "Was ist unsere Urlaubsrichtlinie?"
- "Fasse die letzten 5 Produkt-Updates zusammen"
- "Welche Onboarding-Dokumente sind für neue Backend-Entwickler relevant?"
Der MCP-Server sucht in Confluence und liefert die relevanten Informationen – ohne dass sensible Daten das Unternehmens-Netzwerk verlassen.
Use Case 3: Monitoring und Alerting
Ein DevOps-Team baut einen MCP-Server, der auf ihre Prometheus/Grafana-Daten zugreift. Claude kann damit:
- Aktuelle Systemmetriken abfragen
- Ungewöhnliche Muster identifizieren
- Diagnose-Vorschläge bei Ausfällen machen
- Incident-Reports automatisch erstellen
Use Case 4: Datenanalyse-Assistent
Ein Data-Science-Team verbindet ihre PostgreSQL-Datenbank über MCP. Der Analyst kann Claude in natürlicher Sprache bitten:
- "Zeige mir die Umsatzzahlen der letzten 6 Monate aufgeschlüsselt nach Regionen"
- "Welche Produkte haben die höchste Retourenquote?"
- "Erstelle mir eine Zusammenfassung der Kundenakquisitionskosten"
Claude übersetzt die Anfragen in SQL, führt sie aus und präsentiert die Ergebnisse verständlich.
Sicherheitsaspekte bei MCP
Sicherheit ist bei MCP-Implementierungen entscheidend. Hier sind die wichtigsten Best Practices:
Authentifizierung und Autorisierung
# Beispiel: API-Key-Validierung in einem MCP-Server
from mcp.server import Server
from mcp import types
import os
import hashlib
app = Server("secure-server")
# Erlaubte API-Keys (in der Praxis aus sicherer Quelle laden)
ALLOWED_API_KEYS = {
hashlib.sha256(os.environ.get("API_KEY_1", "").encode()).hexdigest()
}
def validate_request(api_key: str) -> bool:
"""Validiert einen API-Key mit Constant-Time-Vergleich"""
key_hash = hashlib.sha256(api_key.encode()).hexdigest()
return key_hash in ALLOWED_API_KEYS
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
# API-Key aus den Argumenten oder dem Kontext extrahieren
api_key = arguments.pop("_api_key", "")
if not validate_request(api_key):
raise PermissionError("Ungültiger API-Key")
# Eigentliche Tool-Logik...
return [types.TextContent(type="text", text="Erfolg")]
Für die Arbeit mit Tokens und Hashes in deinen sicheren MCP-Servern: Hash Generator und JWT Decoder.
Input-Validierung
Validiere immer alle Eingaben von KI-Modellen:
from pydantic import BaseModel, validator
from typing import Optional
class WeatherRequest(BaseModel):
city: str
country_code: Optional[str] = "DE"
@validator("city")
def city_must_be_valid(cls, v):
if len(v) < 2 or len(v) > 100:
raise ValueError("Stadtname muss zwischen 2 und 100 Zeichen lang sein")
# Nur alphanumerische Zeichen und Leerzeichen erlauben
if not all(c.isalpha() or c.isspace() or c == '-' for c in v):
raise ValueError("Ungültige Zeichen im Stadtnamen")
return v
@validator("country_code")
def country_code_must_be_valid(cls, v):
if v and len(v) != 2:
raise ValueError("Ländercode muss 2 Zeichen lang sein")
return v.upper() if v else v
Least Privilege Principle
Ein MCP-Server sollte nur die minimal notwendigen Rechte haben:
- Datei-Server: Nur Lesezugriff, nur auf definierte Verzeichnisse
- Datenbank-Server: Read-only für Abfragen, separater schreibberechtigter Account für Updates
- API-Server: Separate API-Keys mit minimalen Berechtigungen
Das MCP-Ökosystem 2026
Das MCP-Ökosystem hat sich seit der Einführung rasant entwickelt. Es gibt mittlerweile Hunderte von Open-Source-MCP-Servern:
Offizielle Anthropic-Server:
@modelcontextprotocol/server-filesystem: Dateisystem-Zugriff@modelcontextprotocol/server-git: Git-Repository-Operationen@modelcontextprotocol/server-postgres: PostgreSQL-Zugriff@modelcontextprotocol/server-slack: Slack-Integration@modelcontextprotocol/server-brave-search: Web-Suche
Community-Server (Auswahl):
- MCP für GitHub, Jira, Linear
- MCP für AWS, GCP, Azure
- MCP für Notion, Obsidian
- MCP für Monitoring-Tools (Datadog, Grafana)
- MCP für Browser-Automatisierung
MCP vs. traditionelle Funktionsaufrufe (Function Calling)
| Merkmal | MCP | Function Calling |
|---|---|---|
| Standardisierung | Offen, universeller Standard | Anbieter-spezifisch |
| Wiederverwendbarkeit | Einmal gebaut, überall nutzbar | Muss für jeden Anbieter reimplementiert werden |
| Deployment | Separater Prozess/Server | Inline in der Anwendung |
| Sicherheit | Klar getrennte Verantwortlichkeiten | Muss individuell implementiert werden |
| Discovery | Dynamisch, zur Laufzeit | Statisch, muss vorkonfiguriert werden |
| Ökosystem | Wachsend, interoperabel | Fragmentiert, anbieterspezifisch |
Fazit und Ausblick
MCP ist mehr als ein weiteres Protokoll – es ist ein fundamentaler Schritt zur Standardisierung der KI-Integration. So wie REST-APIs die Web-Integration standardisiert haben, könnte MCP dasselbe für KI-Agenten tun.
Die wichtigsten Takeaways:
- MCP trennt sauber die Verantwortlichkeiten zwischen KI-Modell und externen Systemen
- Die Implementierung ist mit den offiziellen SDKs für Python und TypeScript zugänglich
- Sicherheit muss von Anfang an eingeplant werden
- Das Ökosystem wächst schnell – für viele Standard-Integrationen gibt es bereits fertige Server
Wenn du mit KI-Entwicklung anfängst oder eine bestehende Anwendung KI-fähig machen willst, ist MCP der richtige Ausgangspunkt.
Entwicklertools für deine MCP-Implementierung: JSON Formatter | Regex Tester | Hash Generator | JWT Decoder