KI-Architektur·

Aufbau eines MCP-Servers für Ihr Unternehmen: Ein praktischer Leitfaden zum Model Context Protocol

Lernen Sie, wie Sie produktionsreife MCP-Server erstellen, die Ihre Unternehmenssysteme mit KI-Modellen verbinden — mit Authentifizierungsmustern, Werkzeugregistrierungsstrategien und realen Implementierungsmustern, die leistungsstarke, kontextbewusste KI-Anwendungen ermöglichen.

Aufbau eines MCP-Servers für Ihr Unternehmen: Ein praktischer Leitfaden zum Model Context Protocol

Das Model Context Protocol (MCP) hat sich als De-facto-Standard für die Verbindung von KI-Modellen mit Unternehmenssystemen etabliert. Ursprünglich von Anthropic entwickelt, wird MCP mittlerweile im gesamten KI-Ökosystem breit angenommen und ermöglicht eine nahtlose Integration zwischen Large Language Models und den Daten, Tools und Arbeitsabläufen Ihres Unternehmens.

In diesem umfassenden Leitfaden zeigen wir Ihnen, wie Sie produktionsreife MCP-Server erstellen, die das volle Potenzial von KI in Ihren Geschäftsabläufen freisetzen.

Was ist MCP und warum ist es wichtig?

Das Model Context Protocol definiert eine standardisierte Methode, mit der KI-Systeme externe Fähigkeiten erkennen und nutzen können. Stellen Sie es sich als Universalkonnektor vor, der KI-Modellen ermöglicht, sich mit Ihren Datenbanken, APIs, Dateisystemen und Geschäftslogiken zu verbinden — ohne benutzerdefinierten Integrationscode für jede Verbindung.

Zentrale Vorteile von MCP:

  • Standardisierte Integration — Ein Protokoll, unbegrenzte Konnektoren
  • Typsichere Interaktionen — JSON-RPC 2.0 mit JSON-Schema-Validierung
  • Sicherheitsorientierter Entwurf — Eingebaute Authentifizierung und Berechtigungsbereiche
  • Bidirektionale Kommunikation — KI kann sowohl Daten lesen als auch Aktionen ausführen
  • Herstellerunabhängig — Funktioniert mit Claude, GPT, Gemini und anderen LLMs

Die MCP-Architektur verstehen

Im Kern folgt MCP einem Client-Server-Muster:

Transportschicht

  • stdio: Standard-Eingabe/Ausgabe für lokale Prozesskommunikation
  • HTTP mit SSE: Server-Sent Events für entfernte, zustandsbehaftete Verbindungen
  • WebSocket: Vollduplex-Kommunikation für Echtzeitanwendungen

Protokollschicht

  • JSON-RPC 2.0 Nachrichtenformat
  • Anfrage/Benachrichtigungsmuster für Kommunikation
  • Strukturiertes Logging und Fehlerbehandlung

Fähigkeitsschicht

  • Tools: Funktionen, die die KI aufrufen kann, um Aktionen auszuführen
  • Ressourcen: Schreibgeschützte Datenquellen, auf die die KI zugreifen kann
  • Prompts: Vorlagengestützte Interaktionen für häufige Aufgaben

Ihren ersten MCP-Server erstellen

Lassen Sie uns einen praktischen MCP-Server erstellen, der sich mit dem CRM Ihres Unternehmens verbindet und Verkaufseinblicke für KI-Assistenten bereitstellt.

Einrichten des Projekts

# Neues MCP-Server-Projekt erstellen
mkdir mcp-crm-server
cd mcp-crm-server
npm init -y
npm install @modelcontextprotocol/sdk zod

# Zusätzliche Abhängigkeiten für Ihren Anwendungsfall installieren
npm install axios dotenv

Server-Struktur

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListResourcesRequestSchema,
  ListToolsRequestSchema,
  ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";

// Tool-Schemas definieren
const GetSalesDataSchema = z.object({
  startDate: z.string().datetime(),
  endDate: z.string().datetime(),
  region: z.string().optional(),
});

class CRMMCPServer {
  private server: Server;
  private crmAPI: CRMClient;

  constructor() {
    this.server = new Server(
      {
        name: "crm-mcp-server",
        version: "1.0.0",
      },
      {
        capabilities: {
          tools: {},
          resources: {},
        },
      }
    );

    this.crmAPI = new CRMClient({
      apiKey: process.env.CRM_API_KEY,
      baseURL: process.env.CRM_BASE_URL,
    });

    this.setupHandlers();
  }

  private setupHandlers() {
    // Verfügbare Tools auflisten
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          name: "get_sales_data",
          description: "Verkaufsleistungsdaten für einen bestimmten Zeitraum abrufen",
          inputSchema: {
            type: "object",
            properties: {
              startDate: {
                type: "string",
                format: "date-time",
                description: "Startdatum im ISO 8601-Format",
              },
              endDate: {
                type: "string",
                format: "date-time",
                description: "Enddatum im ISO 8601-Format",
              },
              region: {
                type: "string",
                description: "Optionaler Regionsfilter (z.B. 'EMEA', 'APAC')",
              },
            },
            required: ["startDate", "endDate"],
          },
        },
        {
          name: "update_contact_status",
          description: "Den Status eines CRM-Kontakts aktualisieren",
          inputSchema: {
            type: "object",
            properties: {
              contactId: {
                type: "string",
                description: "Eindeutige Kontaktkennung",
              },
              status: {
                type: "string",
                enum: ["lead", "qualified", "customer", "churned"],
                description: "Neuer Status für den Kontakt",
              },
              notes: {
                type: "string",
                description: "Optionale Notizen zur Statusänderung",
              },
            },
            required: ["contactId", "status"],
          },
        },
      ],
    }));

    // Tool-Aufrufe ausführen
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;

      try {
        switch (name) {
          case "get_sales_data": {
            const validated = GetSalesDataSchema.parse(args);
            const data = await this.crmAPI.getSalesData(validated);
            return {
              content: [
                {
                  type: "text",
                  text: JSON.stringify(data, null, 2),
                },
              ],
            };
          }

          case "update_contact_status": {
            const { contactId, status, notes } = args as {
              contactId: string;
              status: string;
              notes?: string;
            };
            await this.crmAPI.updateContact(contactId, { status, notes });
            return {
              content: [
                {
                  type: "text",
                  text: `Kontakt ${contactId} erfolgreich auf Status aktualisiert: ${status}`,
                },
              ],
            };
          }

          default:
            throw new Error(`Unbekanntes Tool: ${name}`);
        }
      } catch (error) {
        return {
          content: [
            {
              type: "text",
              text: `Fehler: ${error instanceof Error ? error.message : String(error)}`,
            },
          ],
          isError: true,
        };
      }
    });

    // Verfügbare Ressourcen auflisten
    this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
      resources: [
        {
          uri: "crm://contacts",
          name: "Alle CRM-Kontakte",
          description: "Vollständige Liste der Kontakte im CRM",
          mimeType: "application/json",
        },
        {
          uri: "crm://deals/pipeline",
          name: "Aktive Verkaufspipeline",
          description: "Aktuelle Verkaufspipeline mit Deal-Phasen",
          mimeType: "application/json",
        },
      ],
    }));

    // Ressourcen-Lesevorgänge verarbeiten
    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
      const { uri } = request.params;

      try {
        switch (uri) {
          case "crm://contacts": {
            const contacts = await this.crmAPI.getAllContacts();
            return {
              contents: [
                {
                  uri,
                  mimeType: "application/json",
                  text: JSON.stringify(contacts, null, 2),
                },
              ],
            };
          }

          case "crm://deals/pipeline": {
            const pipeline = await this.crmAPI.getPipeline();
            return {
              contents: [
                {
                  uri,
                  mimeType: "application/json",
                  text: JSON.stringify(pipeline, null, 2),
                },
              ],
            };
          }

          default:
            throw new Error(`Unbekannte Ressource: ${uri}`);
        }
      } catch (error) {
        throw new Error(`Fehler beim Lesen der Ressource: ${error}`);
      }
    });
  }

  async run() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.error("CRM MCP Server läuft über stdio");
  }
}

// Server starten
const server = new CRMMCPServer();
server.run().catch(console.error);

Authentifizierungs- und Sicherheitsmuster

Umgebungsbasierte Anmeldeinformationen

// config/security.ts
import { z } from "zod";

const SecurityConfigSchema = z.object({
  apiKey: z.string().min(32),
  allowedOrigins: z.array(z.string().url()),
  rateLimit: z.object({
    requestsPerMinute: z.number().default(60),
  }),
  jwtSecret: z.string().optional(),
});

export const securityConfig = SecurityConfigSchema.parse({
  apiKey: process.env.MCP_API_KEY,
  allowedOrigins: process.env.MCP_ALLOWED_ORIGINS?.split(",") || [],
  rateLimit: {
    requestsPerMinute: parseInt(process.env.MCP_RATE_LIMIT || "60"),
  },
  jwtSecret: process.env.MCP_JWT_SECRET,
});

Anfragevalidierungs-Middleware

// middleware/validation.ts
import { z, ZodTypeAny } from "zod";

export function validateToolInput<T extends ZodTypeAny>(
  schema: T,
  input: unknown
): z.infer<T> {
  try {
    return schema.parse(input);
  } catch (error) {
    if (error instanceof z.ZodError) {
      const messages = error.errors
        .map((e) => `${e.path.join(".")}: ${e.message}`)
        .join("; ");
      throw new Error(`Validierung fehlgeschlagen: ${messages}`);
    }
    throw error;
  }
}

// Ausgaben bereinigen, um Prompt-Injection zu verhindern
export function sanitizeOutput(text: string): string {
  return text
    .replace(/<script[^>]*>.*?<\/script>/gi, "")
    .replace(/javascript:/gi, "")
    .replace(/on\w+\s*=/gi, "");
}

Produktionsbereitstellungsstrategien

Docker-Containerisierung

# Dockerfile
FROM node:20-alpine

WORKDIR /app

# Paketdateien kopieren
COPY package*.json ./
RUN npm ci --only=production

# Quellcode kopieren
COPY dist/ ./dist/

// Sicherheit: Als nicht-Root-Benutzer ausführen
USER node

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD node dist/healthcheck.js

CMD ["node", "dist/index.js"]

HTTP-Transport mit SSE

Für Remote-Bereitstellungen verwenden Sie HTTP mit Server-Sent Events:

import express from "express";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";

const app = express();
const transport = new SSEServerTransport("/mcp", app);

// MCP-Verbindungen verarbeiten
app.use("/mcp", async (req, res) => {
  const sessionId = req.headers["x-session-id"] as string;
  await server.connect(transport, { sessionId });
});

// Authentifizierungs-Middleware
app.use((req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !verifyToken(authHeader)) {
    return res.status(401).json({ error: "Nicht autorisiert" });
  }
  next();
});

app.listen(3000, () => {
  console.log("MCP Server lauscht auf Port 3000");
});

Testen Ihres MCP-Servers

Unit-Test-Beispiel

// tests/crm-server.test.ts
import { describe, it, expect, beforeEach } from "vitest";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";

describe("CRM MCP Server", () => {
  let client: Client;

  beforeEach(async () => {
    const { client: c, server: s } = InMemoryTransport.createLinkedPair();
    client = new Client({ name: "test-client", version: "1.0.0" });
    await client.connect(c);
  });

  it("sollte verfügbare Tools auflisten", async () => {
    const tools = await client.listTools();
    expect(tools.tools).toContainEqual(
      expect.objectContaining({ name: "get_sales_data" })
    );
  });

  it("sollte Tool-Eingaben validieren", async () => {
    await expect(
      client.callTool({
        name: "get_sales_data",
        arguments: { startDate: "ungültiges-datum" },
      })
    ).rejects.toThrow();
  });
});

Integrationstests

// tests/integration.test.ts
import { spawn } from "child_process";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

describe("Integrationstests", () => {
  it("sollte Verbindung mit laufendem Server herstellen", async () => {
    const transport = new StdioClientTransport({
      command: "node",
      args: ["dist/index.js"],
      env: { CRM_API_KEY: "test-schlüssel" },
    });

    const client = new Client({
      name: "integration-test",
      version: "1.0.0",
    });

    await client.connect(transport);

    const resources = await client.listResources();
    expect(resources.resources.length).toBeGreaterThan(0);

    await client.close();
  });
});

Häufige Muster und Best Practices

1. Progressive Fähigkeitsbereitstellung

Geben Sie nicht alle Fähigkeiten auf einmal frei. Implementieren Sie Zugriffskontrolle:

getCapabilities() {
  const capabilities: Capabilities = {};
  
  if (this.hasPermission("tools:read")) {
    capabilities.tools = this.getToolsForUser();
  }
  
  if (this.hasPermission("resources:read")) {
    capabilities.resources = this.getResourcesForUser();
  }
  
  if (this.hasPermission("prompts:read")) {
    capabilities.prompts = this.getPromptsForUser();
  }
  
  return capabilities;
}

2. Paginierung für große Datenmengen

{
  name: "search_contacts",
  inputSchema: {
    type: "object",
    properties: {
      query: { type: "string" },
      limit: { type: "number", maximum: 100, default: 20 },
      cursor: { type: "string", description: "Paginierungs-Cursor" },
    },
  },
}

3. Fehlerbehandlung mit Kontext

class MCPToolError extends Error {
  constructor(
    message: string,
    public code: string,
    public details?: Record<string, unknown>
  ) {
    super(message);
  }
}

// Im Tool-Handler
try {
  await riskyOperation();
} catch (error) {
  throw new MCPToolError(
    "Fehler beim Aktualisieren des Kontakts",
    "CONTACT_UPDATE_FAILED",
    { contactId: args.contactId, reason: error.message }
  );
}

Überwachung und Beobachtbarkeit

Strukturiertes Logging

import { pino } from "pino";

const logger = pino({
  level: process.env.LOG_LEVEL || "info",
  formatters: {
    bindings: () => ({ service: "mcp-server" }),
  },
});

// In Ihren Handlern
logger.info({ tool: name, duration_ms: elapsed }, "Tool erfolgreich ausgeführt");
logger.error({ error, tool: name }, "Tool-Ausführung fehlgeschlagen");

Health Checks

// healthcheck.ts
import { checkDatabaseConnection } from "./db";
import { checkExternalAPIs } from "./apis";

export async function healthCheck(): Promise<HealthStatus> {
  const [db, apis] = await Promise.all([
    checkDatabaseConnection(),
    checkExternalAPIs(),
  ]);

  const healthy = db.healthy && apis.every((a) => a.healthy);

  return {
    status: healthy ? "healthy" : "unhealthy",
    timestamp: new Date().toISOString(),
    checks: {
      database: db,
      external_apis: apis,
    },
  };
}

Anwendungsfälle aus der Praxis

Multi-System-Integration

// CRM, ERP und Support-Systeme verbinden
class EnterpriseMCPServer {
  private integrations = {
    crm: new CRMIntegration(),
    erp: new ERPIntegration(),
    support: new SupportIntegration(),
  };

  async getCustomer360View(customerId: string) {
    const [crmData, erpData, supportHistory] = await Promise.all([
      this.integrations.crm.getCustomer(customerId),
      this.integrations.erp.getOrders(customerId),
      this.integrations.support.getTickets(customerId),
    ]);

    return {
      profile: crmData,
      purchaseHistory: erpData,
      supportInteractions: supportHistory,
      healthScore: this.calculateHealthScore(crmData, erpData, supportHistory),
    };
  }
}

Dateiverarbeitungspipeline

{
  name: "process_document",
  description: "Ein Dokument hochladen und verarbeiten",
  inputSchema: {
    type: "object",
    properties: {
      file_path: { type: "string" },
      operations: {
        type: "array",
        items: {
          enum: ["ocr", "summarize", "extract_entities", "translate"],
        },
      },
    },
  },
}

Fazit

Der Aufbau von MCP-Servern schaltet das volle Potenzial von KI in Ihrem Unternehmen frei, indem er sicheren, standardisierten Zugriff auf Ihre Systeme bietet. Beginnen Sie mit einer einzelnen Integration, folgen Sie Sicherheits-Best-Practices und erweitern Sie Ihre Fähigkeiten schrittweise. Die Investition in eine richtige Architektur zahlt sich durch schnellere KI-Funktionsentwicklung und wartbare Integrationen aus.

Denken Sie daran: MCP geht darum, KI dabei zu unterstützen, mit Ihrem Unternehmen zu arbeiten, nicht darum, Ihre bestehenden Systeme zu ersetzen. Konzentrieren Sie sich auf klare Anwendungsfälle, robuste Fehlerbehandlung und umfassende Überwachung für Produktionserfolg.


Bereit, MCP in Ihrem Unternehmen zu implementieren? Kontaktieren Sie Tropical Media unter tropical-media.work für professionelle Unterstützung bei KI-Architektur und Integration.