MCP-Server-Integrations-Leitfaden für Claude Code
Wie Sie Model Context Protocol Server entwickeln und integrieren, die Claude Code mit Ihren internen Systemen, Datenbanken und APIs verbinden.
MCP verstehen
Model Context Protocol (MCP) ist ein offener Standard, der KI-Assistenten wie Claude Code ermöglicht, auf strukturierte, sichere Weise mit externen Systemen zu interagieren. Stellen Sie sich MCP als die Brücke zwischen Claudes Intelligenz und den Daten und Tools Ihrer Organisation vor.
Ohne MCP operiert Claude Code mit dem Kontext, den Sie in die Konversation einfügen. Sie kopieren Code-Snippets, beschreiben Ihre Architektur, erklären Ihre Systeme. Es funktioniert, aber es ist manuell und begrenzt.
Mit MCP kann Claude Code:
- Ihre interne Dokumentationsdatenbank direkt abfragen
- Ihre Codebase-Struktur und -Muster lesen
- Auf Ihre API-Spezifikationen zugreifen
- Informationen in Ihrer Wissensdatenbank nachschlagen
- Genehmigte Operationen in Ihren Systemen ausführen
Die Schlüsselerkenntnis: MCP-Server geben Claude Code keinen direkten Zugang zu Systemen. Sie bieten ein kuratiertes, kontrolliertes Interface. Sie entscheiden genau, welche Fähigkeiten exponiert werden und wie.
MCP-Architektur
Die MCP-Architektur hat drei Komponenten:
Clients (Claude Code): Fordern Tools und Ressourcen von Servern an. Claude Code ist ein MCP-Client—es entdeckt, was Server anbieten, und nutzt diese Fähigkeiten.
Server: Exponieren spezifische Tools und Ressourcen. Ein Server könnte Zugang zu Ihrer Dokumentation, Ihren API-Specs, Ihrer Codebase-Struktur oder spezifischen Operationen bieten.
Transport: Die Kommunikationsschicht zwischen Clients und Servern. MCP unterstützt stdio (lokal) und HTTP/SSE (remote) Transports.
┌─────────────┐ MCP Protocol ┌─────────────┐ Internal ┌─────────────┐
│ │ ◄──────────────────► │ │ ◄──────────────► │ │
│ Claude Code │ (stdio/HTTP) │ MCP Server │ (API/SQL/etc) │ Ihre │
│ (Client) │ │ │ │ Systeme │
└─────────────┘ └─────────────┘ └─────────────┘
Tools vs. Ressourcen
MCP-Server exponieren zwei Arten von Fähigkeiten:
Tools sind Aktionen, die Claude Code ausführen kann. Sie sind wie Funktionen:
// Beispiel: Ein Tool zur Dokumentationssuche
tool: search_docs
inputs:
query: string // Suchanfrage
limit: number // Max Ergebnisse
output:
results: Array<{title, excerpt, url}>
Claude Code entscheidet basierend auf der Konversation, wann Tools genutzt werden. “Finde Dokumentation über Authentifizierung” würde das search_docs Tool auslösen.
Ressourcen sind Daten, auf die Claude Code zugreifen kann. Sie sind wie Dateien oder Datenbanken:
// Beispiel: Eine Ressource mit API-Specs
resource: api_spec
uri: api://specs/users
output:
openapi: string // OpenAPI-Spezifikation
Ressourcen bieten Kontext. Tools ermöglichen Aktionen. Die nützlichsten MCP-Server bieten beides.
MCP-Server entwickeln
Lassen Sie uns einen praktischen MCP-Server bauen. Wir erstellen einen Dokumentations-Server, der Claude Code Zugang zu Ihren internen Docs gibt.
Server-Struktur
Ein minimaler MCP-Server in TypeScript:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new Server(
{
name: 'docs-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
resources: {},
},
}
);
// Tools definieren
server.setRequestHandler('tools/list', async () => ({
tools: [
{
name: 'search_docs',
description: 'Durchsuche interne Dokumentation',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Suchanfrage' },
limit: { type: 'number', default: 10 },
},
required: ['query'],
},
},
],
}));
// Tool-Aufrufe behandeln
server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params;
if (name === 'search_docs') {
const results = await searchDocumentation(args.query, args.limit);
return { content: [{ type: 'text', text: JSON.stringify(results) }] };
}
throw new Error(`Unbekanntes Tool: ${name}`);
});
// Ressourcen definieren
server.setRequestHandler('resources/list', async () => ({
resources: [
{
uri: 'docs://architecture/overview',
name: 'Architektur-Übersicht',
description: 'System-Architektur-Dokumentation',
mimeType: 'text/markdown',
},
],
}));
// Ressourcen-Lesevorgänge behandeln
server.setRequestHandler('resources/read', async (request) => {
const { uri } = request.params;
if (uri === 'docs://architecture/overview') {
const content = await readDocFile('architecture/overview.md');
return { contents: [{ uri, text: content, mimeType: 'text/markdown' }] };
}
throw new Error(`Unbekannte Ressource: ${uri}`);
});
// Server starten
const transport = new StdioServerTransport();
await server.connect(transport);
Integrationsmuster
Verschiedene Systeme erfordern verschiedene Integrationsansätze.
Datenbank-Integration
Claude Code mit Ihren internen Datenbanken für Abfragen (nicht Modifikationen) verbinden:
// Datenbank-Server-Beispiel
server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params;
if (name === 'query_users') {
// Parametrisierte Abfragen verwenden um Injection zu verhindern
const result = await db.query(
'SELECT id, name, email FROM users WHERE department = $1 LIMIT $2',
[args.department, args.limit || 10]
);
return {
content: [{
type: 'text',
text: JSON.stringify(result.rows)
}]
};
}
});
API-Integration
Ihre internen APIs durch MCP exponieren:
// API-Gateway-Server
server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params;
if (name === 'get_user_details') {
const response = await fetch(
`${INTERNAL_API}/users/${args.userId}`,
{
headers: {
'Authorization': `Bearer ${SERVICE_TOKEN}`,
'X-Request-Source': 'mcp-server',
},
}
);
if (!response.ok) {
throw new Error(`API-Fehler: ${response.status}`);
}
const data = await response.json();
// Sensible Felder vor Rückgabe filtern
const safeData = {
id: data.id,
name: data.name,
department: data.department,
// Ausschließen: email, phone, address, etc.
};
return {
content: [{
type: 'text',
text: JSON.stringify(safeData)
}]
};
}
});
Dateisystem-Integration
Zugang zu bestimmten Verzeichnissen bereitstellen:
// Code-Kontext-Server
server.setRequestHandler('resources/list', async () => {
const files = await glob('src/**/*.ts');
return {
resources: files.map(file => ({
uri: `file://${file}`,
name: path.basename(file),
description: `Quelldatei: ${file}`,
mimeType: 'text/typescript',
})),
};
});
server.setRequestHandler('resources/read', async (request) => {
const { uri } = request.params;
// Sicherheit: Pfad validieren ob innerhalb erlaubtem Verzeichnis
const filePath = uri.replace('file://', '');
const resolvedPath = path.resolve(filePath);
if (!resolvedPath.startsWith(ALLOWED_DIR)) {
throw new Error('Zugriff verweigert: Pfad außerhalb erlaubtem Verzeichnis');
}
const content = await fs.readFile(resolvedPath, 'utf-8');
return {
contents: [{
uri,
text: content,
mimeType: 'text/typescript',
}],
};
});
Sicherheitsüberlegungen
MCP-Server können mächtige Angriffsvektoren sein, wenn sie nicht korrekt abgesichert sind. Sicherheit operiert auf mehreren Ebenen.
Authentifizierung
Wer kann sich mit Ihrem MCP-Server verbinden?
Lokale Server (stdio Transport):
- Prozess-Level-Sicherheit: Nur Claude Code-Prozess kann sich verbinden
- Benutzer-Level-Zugriff: Erbt Benutzerberechtigungen
- Niedriges Risiko für schreibgeschützte Operationen
Remote-Server (HTTP Transport):
- Authentifizierung für alle Verbindungen erforderlich
- OAuth2 oder JWT-Tokens verwenden
- Tokens bei jeder Anfrage validieren
// Authentifizierungs-Middleware für HTTP-Transport
async function authenticateRequest(request: Request): Promise<User> {
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
throw new AuthError('Fehlender Autorisierungstoken');
}
try {
const payload = await verifyJWT(token);
return await getUser(payload.sub);
} catch (error) {
throw new AuthError('Ungültiger Token');
}
}
Autorisierung
Was können authentifizierte Benutzer tun?
Tool-Level-Autorisierung:
const toolPermissions = {
'search_docs': ['all_users'],
'query_database': ['engineers', 'data_team'],
'modify_settings': ['admins'],
};
server.setRequestHandler('tools/call', async (request) => {
const user = await authenticateRequest(request);
const { name } = request.params;
const allowedRoles = toolPermissions[name] || [];
if (!allowedRoles.includes('all_users') &&
!user.roles.some(r => allowedRoles.includes(r))) {
throw new AuthError(`Benutzer hat keine Berechtigung für Tool: ${name}`);
}
// Mit Tool-Ausführung fortfahren
});
Eingabevalidierung
Niemals Eingaben von Claude Code vertrauen:
import { z } from 'zod';
const SearchDocsInput = z.object({
query: z.string().min(1).max(500),
limit: z.number().int().min(1).max(100).default(10),
department: z.enum(['engineering', 'product', 'design']).optional(),
});
server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params;
if (name === 'search_docs') {
// Eingaben validieren
const validatedArgs = SearchDocsInput.parse(args);
// Validierte Eingaben verwenden
const results = await searchDocs(validatedArgs);
return { content: [{ type: 'text', text: JSON.stringify(results) }] };
}
});
Rate Limiting
Missbrauch verhindern und Backend-Systeme schützen:
import { RateLimiter } from 'limiter';
const limiters = new Map<string, RateLimiter>();
function getRateLimiter(userId: string): RateLimiter {
if (!limiters.has(userId)) {
limiters.set(userId, new RateLimiter({
tokensPerInterval: 100,
interval: 'minute',
}));
}
return limiters.get(userId)!;
}
server.setRequestHandler('tools/call', async (request) => {
const user = await authenticateRequest(request);
const limiter = getRateLimiter(user.id);
if (!await limiter.removeTokens(1)) {
throw new RateLimitError('Rate-Limit überschritten');
}
// Mit Anfrage fortfahren
});
Audit-Logging
Alle MCP-Server-Aktivitäten protokollieren:
interface AuditLog {
timestamp: Date;
userId: string;
action: 'tool_call' | 'resource_read';
target: string;
inputs: object;
result: 'success' | 'error';
duration: number;
errorMessage?: string;
}
async function logAudit(log: AuditLog): Promise<void> {
await auditDatabase.insert(log);
// Bei verdächtigen Mustern alarmieren
if (await detectAnomalous(log)) {
await alertSecurityTeam(log);
}
}
Deployment-Muster
Wie Sie MCP-Server deployen beeinflusst Sicherheit, Performance und Wartbarkeit.
Lokales Deployment
Für Entwicklung und persönliche Nutzung:
// Claude Code Konfiguration
{
"mcpServers": {
"docs": {
"command": "node",
"args": ["/pfad/zu/docs-server/index.js"],
"env": {
"DOCS_PATH": "/pfad/zu/docs"
}
}
}
}
Vorteile: Einfach, schnell, kein Netzwerk-Overhead Nachteile: Funktioniert nur lokal, keine zentrale Verwaltung
Containerisiertes Deployment
Für teamweiten Zugang:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Kubernetes-Deployment
Für Enterprise-Skalierung:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-docs-server
spec:
replicas: 3
selector:
matchLabels:
app: mcp-docs
template:
metadata:
labels:
app: mcp-docs
spec:
containers:
- name: mcp-docs
image: company/mcp-docs:1.0.0
ports:
- containerPort: 3000
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: mcp-secrets
key: database-url
Performance-Optimierung
MCP-Server sitzen zwischen Claude Code und Ihren Systemen. Performance ist wichtig.
Connection Pooling
Keine neuen Verbindungen pro Anfrage erstellen:
import { Pool } from 'pg';
// Pool einmal beim Start erstellen
const pool = new Pool({
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
// Verbindungen für Anfragen wiederverwenden
server.setRequestHandler('tools/call', async (request) => {
const client = await pool.connect();
try {
const result = await client.query('SELECT ...');
return { content: [{ type: 'text', text: JSON.stringify(result.rows) }] };
} finally {
client.release();
}
});
Caching
Häufig abgerufene Daten cachen:
import { LRUCache } from 'lru-cache';
const cache = new LRUCache<string, any>({
max: 1000,
ttl: 1000 * 60 * 5, // 5 Minuten
});
async function getCachedDocs(query: string): Promise<any[]> {
const cacheKey = `docs:${query}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const results = await searchDocs(query);
cache.set(cacheKey, results);
return results;
}
Timeouts
Vernünftige Timeouts setzen:
const TOOL_TIMEOUT = 30000; // 30 Sekunden
server.setRequestHandler('tools/call', async (request) => {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), TOOL_TIMEOUT);
try {
const result = await executeToolWithSignal(
request,
controller.signal
);
return result;
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Tool-Ausführung Timeout');
}
throw error;
} finally {
clearTimeout(timeout);
}
});
Erste Schritte
Bereit, MCP-Server für Ihre Organisation zu entwickeln?
- Mit schreibgeschützt beginnen: Zuerst einen Dokumentations- oder Codebase-Server bauen
- Authentifizierung implementieren: Auch für interne Server
- Logging hinzufügen: Sie brauchen Sichtbarkeit darüber, was abgerufen wird
- Gründlich testen: MCP-Server brauchen Tests wie jeder Produktionscode
- Vorsichtig deployen: Mit begrenztem Zugang starten, schrittweise erweitern
Für Teams, die strukturierte Anleitung zur MCP-Server-Entwicklung wünschen, erfahren Sie mehr über unsere Beratungsleistungen oder lesen Sie unseren Leitfaden zur Claude Code Plugin-Architektur.
Dieser Artikel ist Teil unserer Plugin-Architektur-Serie. Für Skill-Entwicklung siehe Benutzerdefinierte Claude Code Skills erstellen. Für Sicherheitsüberlegungen lesen Sie KI-Governance und Sicherheit für Teams.