Eigenen MCP-Server auf einem VPS bauen und selbst hosten
Bauen Sie einen TypeScript MCP-Server von Grund auf, stellen Sie ihn auf einem VPS mit systemd bereit und betreiben Sie ihn hinter Nginx mit TLS. Verbinden Sie Claude Desktop, Claude Code und Cursor mit Ihrem selbst gehosteten Server.
Jedes MCP-Server-Tutorial hoert bei "lokal mit stdio ausfuehren" auf. Dieses hier deckt den gesamten Weg ab: einen TypeScript MCP-Server bauen, auf einem VPS hinter Nginx mit TLS bereitstellen, mit Authentifizierung und Firewall-Regeln absichern und MCP-Clients ueber das Internet verbinden.
Am Ende haben Sie einen produktionsfaehigen MCP-Server, den Claude Desktop, Claude Code, Cursor oder jeder andere MCP-kompatible Client aus der Ferne erreichen kann.
Was werden Sie bauen?
MCP-Clients senden JSON-RPC-Anfragen ueber HTTPS. Nginx terminiert TLS und leitet Anfragen an Ihren MCP-Server-Prozess weiter, der auf einem lokalen Port laeuft. Der MCP-Server validiert ein Bearer-Token, verarbeitet die Anfrage und gibt Ergebnisse zurueck.
MCP Client (Claude, Cursor, ...)
|
| HTTPS (port 443)
v
Nginx (TLS termination + reverse proxy)
|
| HTTP (port 3000, localhost only)
v
MCP Server (Node.js + Express)
|
v
SQLite database
Das Arbeitsbeispiel ist ein Notizen-Server. Er stellt zwei Tools (Notizen suchen, Notiz erstellen) und eine Resource (alle Notizen auflisten) bereit. Einfach genug zum Nachvollziehen, nuetzlich genug zum Erweitern.
Welchen Transport sollten Sie fuer einen Remote-MCP-Server verwenden?
Streamable HTTP ist der aktuelle MCP-Spezifikationsstandard fuer Remote-Deployments. Er hat den veralteten SSE-Transport ersetzt und funktioniert ueber normale HTTP-POST-Anfragen. Stdio bleibt der Transport fuer rein lokale Server, bei denen der Client den Server als Subprozess startet.
| Transport | Anwendungsfall | Remote-faehig | Client-Unterstuetzung |
|---|---|---|---|
| stdio | Lokale Tools, CLI-Integrationen | Nein | Alle Clients |
| SSE (veraltet) | Aeltere Remote-Server | Ja | Die meisten Clients (wird ausgemustert) |
| Streamable HTTP | Remote-Server, Produktion | Ja | Claude Desktop, Claude Code, Cursor |
Streamable HTTP sendet jede Client-Nachricht als HTTP POST an einen einzelnen Endpunkt (z.B. /mcp). Der Server antwortet mit application/json fuer einfache Antworten oder text/event-stream, wenn er mehrere Nachrichten zurueckstreamen muss. Das funktioniert mit Standard-HTTP-Infrastruktur, einschliesslich Reverse Proxies und Load Balancern.
Voraussetzungen
- Ein VPS mit Debian 12 oder Ubuntu 24.04 (diese Anleitung verwendet Debian 12)
- Ein Domainname mit DNS, der auf Ihren VPS zeigt (z.B.
mcp.example.com) - Node.js 20+ auf dem VPS installiert
- Grundkenntnisse in TypeScript und Linux
Wie richten Sie ein TypeScript-MCP-Server-Projekt ein?
Beginnen Sie auf Ihrem lokalen Rechner oder direkt auf dem VPS. Initialisieren Sie das Projekt, installieren Sie das MCP SDK und richten Sie TypeScript ein.
mkdir mcp-notes-server && cd mcp-notes-server
npm init -y
npm install @modelcontextprotocol/sdk zod express better-sqlite3
npm install -D typescript @types/node @types/express @types/better-sqlite3
Erstellen Sie die TypeScript-Konfiguration:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
Speichern Sie dies als tsconfig.json im Projektstammverzeichnis.
Aktualisieren Sie package.json, um die Build- und Start-Skripte hinzuzufuegen und den Modultyp festzulegen:
{
"type": "module",
"scripts": {
"build": "tsc",
"start": "node build/index.js"
}
}
Erstellen Sie das Quellverzeichnis:
mkdir src
Wie implementieren Sie MCP-Tools mit Eingabevalidierung?
Ein MCP-Server stellt Tools, Resources und Prompts fuer KI-Clients bereit. Tools sind Funktionen, die die KI aufrufen kann. Resources sind schreibgeschuetzte Daten, auf die die KI zugreifen kann. Wir implementieren beides.
Erstellen Sie src/index.ts mit dem Datenbank-Setup und einer Factory-Funktion, die fuer jede Anfrage einen neuen MCP-Server erstellt. Zustandsloses Streamable HTTP erfordert eine neue McpServer-Instanz pro Anfrage, da das SDK es nicht erlaubt, denselben Server mit einem anderen Transport erneut zu verbinden.
import express from "express";
import { randomUUID } from "crypto";
import Database from "better-sqlite3";
import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
// --- Database setup ---
const db = new Database("notes.db");
db.pragma("journal_mode = WAL");
db.exec(`
CREATE TABLE IF NOT EXISTS notes (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
)
`);
// --- MCP Server factory ---
// Each stateless request needs its own McpServer + transport pair.
// The SDK binds a server to a single transport on connect() and
// throws if you try to reuse it. A factory function keeps it clean.
function createServer(): McpServer {
const server = new McpServer({
name: "notes-server",
version: "1.0.0",
});
Registrieren Sie nun die Tools innerhalb der Factory. Jedes Tool verwendet Zod fuer die Eingabevalidierung. Das SDK validiert Eingaben automatisch, bevor Ihr Handler ausgefuehrt wird.
// --- Tools ---
server.tool(
"search_notes",
"Search notes by keyword in title or content",
{
query: z.string().min(1).describe("Search keyword"),
},
async ({ query }) => {
const stmt = db.prepare(
"SELECT id, title, content, created_at FROM notes WHERE title LIKE ? OR content LIKE ? ORDER BY created_at DESC LIMIT 20"
);
const rows = stmt.all(`%${query}%`, `%${query}%`);
return {
content: [
{
type: "text" as const,
text: JSON.stringify(rows, null, 2),
},
],
};
}
);
server.tool(
"create_note",
"Create a new note with a title and content",
{
title: z.string().min(1).max(200).describe("Note title"),
content: z.string().min(1).describe("Note content"),
},
async ({ title, content }) => {
const id = randomUUID();
const stmt = db.prepare(
"INSERT INTO notes (id, title, content) VALUES (?, ?, ?)"
);
stmt.run(id, title, content);
return {
content: [
{
type: "text" as const,
text: `Note created with id: ${id}`,
},
],
};
}
);
Jede Eingabe wird von Zod validiert, bevor sie den Handler erreicht. Wenn ein Client { query: "" } sendet, gibt das SDK automatisch einen Validierungsfehler zurueck. Kein manuelles Parsen noetig.
Wie fuegen Sie Resources zu einem MCP-Server hinzu?
Resources geben KI-Clients schreibgeschuetzten Zugriff auf Daten. Sie werden ueber URI referenziert. Fuegen Sie eine Resource hinzu, die alle Notizen auflistet, weiterhin innerhalb der createServer-Factory:
// --- Resources ---
server.resource(
"all-notes",
"notes://all",
{
description: "List all notes in the database",
mimeType: "application/json",
},
async (uri) => {
const rows = db.prepare(
"SELECT id, title, created_at FROM notes ORDER BY created_at DESC LIMIT 100"
).all();
return {
contents: [
{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify(rows, null, 2),
},
],
};
}
);
return server;
}
MCP-Clients koennen diese Resource entdecken und lesen, ohne ein Tool aufzurufen. Claude Desktop zeigt Resources im Anhaenge-Menue an. Claude Code stellt sie ueber @-Erwahnungen bereit.
Wie sichern Sie einen MCP-Server mit Token-Authentifizierung ab?
Ein produktiver MCP-Server braucht Authentifizierung. Ohne sie kann jeder, der Ihren Endpunkt entdeckt, Ihre Tools aufrufen. Verwenden Sie ein Bearer-Token, das in einer Express-Middleware geprueft wird, bevor Anfragen den MCP-Transport erreichen.
Generieren Sie ein starkes Token:
openssl rand -base64 32
Speichern Sie die Ausgabe. Sie benoetigen sie sowohl fuer die Server- als auch fuer die Client-Konfiguration.
Fuegen Sie die Authentifizierungs-Middleware und Express-Routen zu src/index.ts hinzu:
// --- Auth middleware ---
const AUTH_TOKEN = process.env.MCP_AUTH_TOKEN;
if (!AUTH_TOKEN) {
console.error("MCP_AUTH_TOKEN environment variable is required");
process.exit(1);
}
function authenticateRequest(
req: express.Request,
res: express.Response,
next: express.NextFunction
): void {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
res.status(401).json({
jsonrpc: "2.0",
error: { code: -32001, message: "Missing or invalid authorization" },
id: null,
});
return;
}
const token = authHeader.slice(7);
if (token !== AUTH_TOKEN) {
res.status(403).json({
jsonrpc: "2.0",
error: { code: -32001, message: "Invalid token" },
id: null,
});
return;
}
next();
}
// --- Express app ---
const app = express();
app.use(express.json());
app.use("/mcp", authenticateRequest);
Die Middleware wird vor jeder Anfrage an /mcp ausgefuehrt. Sie prueft auf ein Bearer-Token im Authorization-Header und lehnt unautorisierte Anfragen mit einer JSON-RPC-Fehlerantwort ab.
Wie richten Sie den Streamable-HTTP-Transport ein?
Fuegen Sie das Transport-Setup und die Route-Handler hinzu. Jede Anfrage erstellt einen neuen McpServer aus der Factory und eine neue Transport-Instanz:
// --- Streamable HTTP transport ---
app.post("/mcp", async (req, res) => {
const server = createServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
await server.connect(transport);
try {
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error("MCP request error:", error);
if (!res.headersSent) {
res.status(500).json({
jsonrpc: "2.0",
error: { code: -32603, message: "Internal server error" },
id: null,
});
}
}
});
app.get("/mcp", (_req, res) => {
res.status(405).json({
jsonrpc: "2.0",
error: { code: -32000, message: "Method not allowed. Use POST." },
id: null,
});
});
app.delete("/mcp", (_req, res) => {
res.status(405).json({
jsonrpc: "2.0",
error: { code: -32000, message: "Method not allowed." },
id: null,
});
});
// --- Health check ---
app.get("/health", (_req, res) => {
res.json({ status: "ok" });
});
// --- Start ---
const PORT = parseInt(process.env.PORT || "3000", 10);
app.listen(PORT, "127.0.0.1", () => {
console.log(`MCP server listening on http://127.0.0.1:${PORT}/mcp`);
});
sessionIdGenerator: undefined erstellt einen zustandslosen Server. Jede Anfrage ist unabhaengig, mit eigenem McpServer und Transport-Paar. Das ist einfacher zu deployen und funktioniert gut hinter einem Reverse Proxy ohne Sticky Sessions. Die Datenbankverbindung (db) lebt auf Modulebene und wird anfrageuebergreifend geteilt, sodass Daten zwischen Aufrufen bestehen bleiben.
Der Server bindet an 127.0.0.1, nicht an 0.0.0.0. Er akzeptiert nur Verbindungen von localhost. Nginx ist der einzige Weg, ihn von aussen zu erreichen.
Bauen und lokal testen:
npm run build
Pruefen Sie, ob der Build ohne Fehler durchlaeuft.
Wie testen Sie einen MCP-Server lokal?
Der MCP Inspector ist das offizielle Test-Tool. Er verbindet sich mit Ihrem Server und laesst Sie Tools interaktiv aufrufen.
Starten Sie Ihren Server in einem Terminal:
MCP_AUTH_TOKEN=test-token-local npm start
In einem anderen Terminal mit curl testen:
curl -X POST http://127.0.0.1:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer test-token-local" \
-d '{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": { "name": "test-client", "version": "1.0.0" }
},
"id": 1
}'
Sie sollten eine SSE-Antwort sehen (event: message gefolgt von einer data:-Zeile), die die Faehigkeiten des Servers enthaelt, einschliesslich der tools- und resources-Objekte. Das bestaetigt, dass der Server Verbindungen annimmt und auf den MCP-Initialisierungs-Handshake antwortet.
Testen Sie das create_note-Tool:
curl -X POST http://127.0.0.1:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer test-token-local" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "create_note",
"arguments": { "title": "Test Note", "content": "Hello from curl" }
},
"id": 2
}'
Die Antwort sollte "Note created with id: ..." enthalten. Testen Sie nun das Such-Tool:
curl -X POST http://127.0.0.1:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer test-token-local" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "search_notes",
"arguments": { "query": "Test" }
},
"id": 3
}'
Sie sollten die gerade erstellte Notiz in den Ergebnissen sehen. Pruefen Sie, ob eine Anfrage ohne Authorization-Header einen 401-Fehler zurueckgibt:
curl -s -o /dev/null -w "%{http_code}" -X POST http://127.0.0.1:3000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{},"id":1}'
Das sollte 401 ausgeben. Wenn ja, funktioniert Ihre Auth-Middleware.
Sie koennen auch den MCP Inspector fuer interaktives Testen mit Web-UI verwenden:
npx @modelcontextprotocol/inspector
Der Inspector oeffnet eine Browser-Oberflaeche, in der Sie sich mit Ihrem Server verbinden, Tools und Resources durchsuchen und Aufrufe testen koennen.
Wie stellen Sie einen MCP-Server auf einem VPS bereit?
Uebertragen Sie Ihr Projekt mit rsync, scp oder pushen Sie es in ein Git-Repository und klonen Sie es auf dem Server. Die folgenden Schritte setzen voraus, dass Sie auf dem VPS eingeloggt sind.
Dedizierten Benutzer erstellen
Fuehren Sie Anwendungsprozesse nie als root aus. Erstellen Sie einen Dienstbenutzer:
sudo useradd -r -m -s /usr/sbin/nologin mcpserver
Node.js installieren
Falls Node.js 20+ noch nicht installiert ist:
curl -fsSL https://deb.nodesource.com/setup_20.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
sudo apt-get install -y nodejs
node --version
npm --version
Der erste Befehl laedt das Setup-Skript in eine Datei herunter, damit Sie es vor der Ausfuehrung pruefen koennen. Die Ausgabe sollte v20.x.x oder hoeher fuer Node.js und 10.x.x fuer npm anzeigen. Das MCP SDK benoetigt Node.js 18+, aber 20 LTS wird fuer den Produktionseinsatz empfohlen.
Projektverzeichnis einrichten
sudo mkdir -p /opt/mcp-notes-server
sudo cp -r /path/to/your/project/* /opt/mcp-notes-server/
cd /opt/mcp-notes-server
sudo npm ci --omit=dev
sudo npm run build
sudo chown -R mcpserver:mcpserver /opt/mcp-notes-server
Eigentuemer pruefen:
ls -la /opt/mcp-notes-server/
Alle Dateien sollten mcpserver:mcpserver gehoeren.
Umgebungsdatei erstellen
Speichern Sie das Auth-Token in einer geschuetzten Umgebungsdatei:
sudo mkdir -p /etc/mcp-notes-server
echo "MCP_AUTH_TOKEN=$(openssl rand -base64 32)" | sudo tee /etc/mcp-notes-server/env > /dev/null
sudo chmod 600 /etc/mcp-notes-server/env
sudo chown mcpserver:mcpserver /etc/mcp-notes-server/env
Lesen Sie das generierte Token aus und speichern Sie es sicher. Sie benoetigen es fuer die Client-Konfiguration:
sudo cat /etc/mcp-notes-server/env
systemd-Service erstellen
sudo tee /etc/systemd/system/mcp-notes-server.service > /dev/null << 'EOF'
[Unit]
Description=MCP Notes Server
After=network.target
[Service]
Type=simple
User=mcpserver
Group=mcpserver
WorkingDirectory=/opt/mcp-notes-server
EnvironmentFile=/etc/mcp-notes-server/env
ExecStart=/usr/bin/node build/index.js
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
# Hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/mcp-notes-server
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
EnvironmentFile laedt MCP_AUTH_TOKEN aus der geschuetzten Datei. Der Hardening-Abschnitt begrenzt, was der Prozess tun kann. NoNewPrivileges verhindert Rechteeskalation. ProtectSystem=strict macht das Dateisystem schreibgeschuetzt, ausser fuer Pfade in ReadWritePaths. ProtectHome blockiert den Zugriff auf Home-Verzeichnisse.
Aktivieren und starten Sie den Service:
sudo systemctl enable --now mcp-notes-server
enable sorgt dafuer, dass der Service beim Booten startet. --now startet ihn sofort.
Status pruefen:
sudo systemctl status mcp-notes-server
Sie sollten active (running) sehen. Falls der Service fehlgeschlagen ist, pruefen Sie die Logs:
journalctl -u mcp-notes-server -n 50 --no-pager
Pruefen Sie, ob der Server lokal antwortet:
TOKEN=$(sudo grep MCP_AUTH_TOKEN /etc/mcp-notes-server/env | cut -d= -f2)
curl -s http://127.0.0.1:3000/health
Das sollte {"status":"ok"} zurueckgeben.
Wie betreiben Sie einen MCP-Server hinter Nginx mit TLS?
Nginx terminiert TLS und leitet Anfragen an den MCP-Server weiter. Der MCP-Server behandelt TLS nie direkt. Das ist das Standardmuster fuer produktive Node.js-Deployments.
Nginx und Certbot installieren
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx
Nginx-Serverblock konfigurieren
sudo tee /etc/nginx/sites-available/mcp.example.com.conf > /dev/null << 'NGINX'
server {
listen 80;
listen [::]:80;
server_name mcp.example.com;
# Certbot will add the redirect to HTTPS
location / {
return 444;
}
}
NGINX
Ersetzen Sie mcp.example.com durch Ihre tatsaechliche Domain.
Aktivieren Sie die Seite und holen Sie ein TLS-Zertifikat:
sudo ln -s /etc/nginx/sites-available/mcp.example.com.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d mcp.example.com
Certbot aendert die Konfiguration, um TLS-Einstellungen und eine HTTP-zu-HTTPS-Weiterleitung hinzuzufuegen. Aktualisieren Sie nun die Konfiguration fuer den Reverse Proxy zum MCP-Endpunkt:
sudo tee /etc/nginx/sites-available/mcp.example.com.conf > /dev/null << 'NGINX'
server {
listen 80;
listen [::]:80;
server_name mcp.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name mcp.example.com;
ssl_certificate /etc/letsencrypt/live/mcp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mcp.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Hide Nginx version
server_tokens off;
# MCP endpoint
location /mcp {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
# Required for Streamable HTTP / SSE responses
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding off;
# Keep connection open for streaming responses
proxy_set_header Connection "";
# Extended timeouts for long-running tool calls
proxy_read_timeout 300s;
proxy_send_timeout 300s;
# Forward client info
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Pass through Authorization header
proxy_set_header Authorization $http_authorization;
}
# Health check (no auth required)
location /health {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
# Block everything else
location / {
return 444;
}
}
NGINX
Die wichtigen Direktiven fuer MCP-Kompatibilitaet:
proxy_buffering offverhindert, dass Nginx SSE-Antworten puffert. Ohne diese Einstellung haengen Streaming-Antworten, bis die Verbindung geschlossen wird.proxy_cache offstellt sicher, dass dynamische Antworten nicht gecacht werden.chunked_transfer_encoding offvermeidet Chunking-Probleme bei SSE-Event-Streams.Connection ""haelt die Verbindung offen, ohne WebSocket-Upgrade-Verhalten auszuloesen.proxy_read_timeout 300serlaubt Tool-Aufrufe, die bis zu 5 Minuten dauern. Die Standard-60s sind fuer aufwendige Operationen zu kurz.
Testen und neu laden:
sudo nginx -t
sudo systemctl reload nginx
Von Ihrem lokalen Rechner (nicht dem Server) pruefen:
curl -s https://mcp.example.com/health
Das sollte {"status":"ok"} zurueckgeben. Wenn ja, leitet Nginx korrekt an Ihren MCP-Server ueber TLS weiter.
Firewall-Regeln
Blockieren Sie direkten Zugriff auf Port 3000 von aussen. Nur Nginx (auf localhost) sollte den MCP-Server erreichen:
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Pruefen Sie, ob Port 3000 von aussen nicht erreichbar ist:
# Von Ihrem lokalen Rechner, das sollte einen Timeout ergeben oder abgelehnt werden:
curl -s --connect-timeout 5 http://YOUR_VPS_IP:3000/health
Die Verbindung sollte fehlschlagen. Ihr MCP-Server ist nur ueber Nginx auf Port 443 erreichbar.
Wie verbinden Sie MCP-Clients mit einem Remote-Server?
Nachdem der Server hinter Nginx mit TLS laeuft, konfigurieren Sie Ihre MCP-Clients fuer die Verbindung.
Claude Code
Fuegen Sie den Remote-Server ueber die CLI hinzu:
claude mcp add --transport http \
--header "Authorization: Bearer YOUR_TOKEN_HERE" \
notes-server https://mcp.example.com/mcp
Ersetzen Sie YOUR_TOKEN_HERE durch das Token aus /etc/mcp-notes-server/env.
Verbindung pruefen:
claude mcp list
In Claude Code tippen Sie /mcp, um den Serverstatus zu pruefen. Der notes-server sollte als verbunden angezeigt werden, mit den Tools search_notes und create_note.
Claude Desktop
Claude Desktop verbindet sich ueber die Einstellungs-UI mit Remote-MCP-Servern. Gehen Sie zu Settings > Connectors und fuegen Sie einen neuen Remote-Server mit der URL https://mcp.example.com/mcp hinzu.
Wenn Sie das Bearer-Token uebergeben muessen, koennen Sie es auch ueber eine lokale .mcp.json-Datei oder den oben gezeigten claude mcp add-CLI-Befehl konfigurieren und den Server in Claude Desktop importieren:
claude mcp add-from-claude-desktop
Cursor
Oeffnen Sie die Cursor-Einstellungen und navigieren Sie zur MCP-Konfiguration. Fuegen Sie einen neuen Server in der mcp.json-Datei hinzu (oder ueber die Cursor-Einstellungs-UI):
{
"mcpServers": {
"notes-server": {
"url": "https://mcp.example.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_TOKEN_HERE"
}
}
}
}
Cursor versucht bei einer URL-Verbindung zuerst Streamable HTTP, daher ist keine zusaetzliche Transport-Konfiguration noetig.
End-to-End-Verbindung pruefen
Bitten Sie die KI von jedem verbundenen Client aus:
- Eine Notiz erstellen: "Create a note titled 'Server Test' with content 'Connected successfully'"
- Danach suchen: "Search my notes for 'Server Test'"
Wenn beide Operationen Ergebnisse liefern, funktioniert die gesamte Kette: Client zu Nginx zu MCP-Server zu SQLite und zurueck.
MCP-Primitives-Referenz
| Primitive | Zweck | Beispiel |
|---|---|---|
| Tools | Funktionen, die die KI mit strukturierten Eingaben aufrufen kann | search_notes, create_note |
| Resources | Schreibgeschuetzte Daten, auf die die KI zugreifen kann | notes://all Notizliste |
| Prompts | Vorgefertigte Templates fuer haeufige Aufgaben | In diesem Tutorial nicht verwendet |
Tools sind das am haeufigsten implementierte Primitive. Resources eignen sich, um Referenzdaten ohne Berechnung bereitzustellen. Prompts erstellen wiederverwendbare Templates, die die KI durch bestimmte Workflows fuehren.
Etwas funktioniert nicht?
Server startet nicht: Pruefen Sie das Journal auf Fehler:
journalctl -u mcp-notes-server -n 50 --no-pager
Haeufige Ursachen: fehlende Umgebungsvariable MCP_AUTH_TOKEN, falsche Dateiberechtigungen auf notes.db, zu alte Node.js-Version.
Nginx gibt 502 Bad Gateway zurueck: Der MCP-Server laeuft nicht oder hoert nicht auf Port 3000. Pruefen Sie:
sudo systemctl status mcp-notes-server
curl -s http://127.0.0.1:3000/health
Client bekommt 401 Unauthorized: Das Token in der Client-Konfiguration stimmt nicht mit dem Token auf dem Server ueberein. Pruefen Sie:
sudo cat /etc/mcp-notes-server/env
Streaming-Antworten laufen in einen Timeout: Erhoehen Sie proxy_read_timeout in der Nginx-Konfiguration. Die Standard-300s reichen fuer die meisten Faelle, aber lang laufende Tools brauchen moeglicherweise mehr.
Zertifikatsfehler: Pruefen Sie, ob das Zertifikat gueltig ist und Ihre Domain abdeckt:
sudo certbot certificates
Falls das Zertifikat abgelaufen ist, erneuern Sie es:
sudo certbot renew
Database-locked-Fehler: SQLite im WAL-Modus verarbeitet gleichzeitige Lesezugriffe gut, aber gleichzeitige Schreibzugriffe koennen kollidieren. Fuer Server mit hohem Verkehrsaufkommen sollten Sie PostgreSQL in Betracht ziehen.
Permission denied bei notes.db: Der Benutzer mcpserver braucht Schreibzugriff auf das Arbeitsverzeichnis fuer SQLite. Pruefen Sie:
ls -la /opt/mcp-notes-server/notes.db
Die Datei sollte mcpserver:mcpserver gehoeren. Falls sie beim Testen von root erstellt wurde, korrigieren Sie das:
sudo chown mcpserver:mcpserver /opt/mcp-notes-server/notes.db
sudo chmod 640 /opt/mcp-notes-server/notes.db
Client zeigt "connection refused" oder "timeout": Pruefen Sie, ob Ihr DNS-A-Record auf die VPS-IP zeigt und die Ports 80 und 443 in der Firewall geoeffnet sind:
sudo ufw status
dig +short mcp.example.com
Die dig-Ausgabe sollte Ihre VPS-IP-Adresse anzeigen. Falls nicht, aktualisieren Sie Ihre DNS-Eintraege und warten Sie auf die Propagierung.
Naechste Schritte
Ihr MCP-Server ist live und abgesichert. Moegliche Erweiterungen:
- Weitere Tools hinzufuegen, die sich mit Ihren Datenbanken, APIs oder Dateisystemen verbinden
- OAuth 2.1 statt Bearer-Tokens fuer Mehrbenutzerzugriff implementieren
- Rate Limiting in Express oder Nginx zum Schutz vor Missbrauch einrichten
- Monitoring mit
journalctl -u mcp-notes-server -fund Alerting aufsetzen - Mehrere MCP-Server hinter derselben Nginx-Instanz auf verschiedenen Pfaden bereitstellen
Die offizielle MCP-SDK-Dokumentation behandelt fortgeschrittene Muster wie zustandsbehaftete Sitzungen, Fortschrittsberichte und strukturierte Ausgabe-Schemas.
Copyright 2026 Virtua.Cloud. Alle Rechte vorbehalten. Dieser Inhalt ist ein Originalwerk des Virtua.Cloud-Teams. Vervielfaeltigung, Wiederveroeffentlichung oder Weiterverbreitung ohne schriftliche Genehmigung ist untersagt.
Bereit, es selbst auszuprobieren?
Stellen Sie Ihren eigenen Server in Sekunden bereit. Linux, Windows oder FreeBSD.
VPS-Angebote ansehen