1Zweck des MCP und Zugangsdaten
Der Local Solutions Design MCP ist die Single Source of Truth für Designregeln, Projektkontext, Cloud-Übergabe und Arbeits-Skills. Jeder Agent – ob Claude Code auf dem Rechner, ein Cloud-Chat in ChatGPT oder Claude – lädt zu Beginn denselben Kontext und kann Änderungen kontrolliert zurückschreiben.
- MCP-Endpunkt
<MCP_URL>=https://design-mcp.localsolutions.de/mcp- Transport
- MCP Streamable HTTP (Protokoll 2025-06-18), zustandslos, JSON-RPC 2.0 per
POST - Authentifizierung
- HTTP-Header
Authorization: Bearer <MCP_BEARER_TOKEN> - Healthcheck
https://design-mcp.localsolutions.de/health(öffentlich, ohne Token)- Diese Dokumentation
https://design-mcp.localsolutions.de/docs/
deploy/.env und in den Client-Konfigurationen der berechtigten Personen abgelegt – nie in Git, Chats, Screenshots oder auf dieser Seite.2Architekturübersicht
/mcp, /health, /docs/Streaming ohne Pufferung, 1 h Timeout@modelcontextprotocol/servernur 127.0.0.1:8788, User node/dataDokumente, Skills, manifest.json, history/, audit.ndjsonpersistent, Seed nur initialBeim ersten Start kopiert der Server die Einträge aus seed/ in das Volume. Bereits vorhandene Einträge werden bei späteren Starts nie überschrieben – serverseitige Änderungen bleiben erhalten, neue Seed-Einträge werden ergänzt. Es gibt keine Löschfunktion und keinen Zugriff außerhalb des Datenverzeichnisses; alle Pfade werden gegen /data validiert.
3Verfügbare Ressourcen und Tools
Jeder Eintrag ist zusätzlich als MCP-Ressource unter ls-design://entries/<id> erreichbar.
| Tool | Zweck | Wichtige Parameter | Schreibt |
|---|---|---|---|
design_list_entries | Alle Dokumente und Skills ohne Inhalt | kind (optional: document | skill) | nein |
design_read_entry | Vollständiger Eintrag inkl. SHA-256-Revision | id | nein |
design_search | Volltextsuche mit Fundstellen | query, kind, limit (1–20) | nein |
design_get_agent_context | Gebündelter Startkontext aller Core-Einträge | includeSkills (Standard true) | nein |
design_entry_history | Frühere Fassungen eines Eintrags | id, limit (1–50) | nein |
design_update_entry | Update nur bei passender Revision, legt Snapshot an | id, content, expectedRevision, changeSummary, confirmation: "UPDATE_ENTRY" | ja |
design_create_skill | Neuer Skill; vorhandene IDs werden nie überschrieben | id, title, description, content, confirmation: "CREATE_SKILL" | ja |
Seed-Einträge
project-context
Ziele, Technik, Arbeitsweise und Inhaltsregeln der Website.
design-architecture
Visuelle Regeln, Seitenrhythmus, Komponenten- und Interaktionsprinzipien.
cloud-chat-context
Übergabekontext für neue Cloud-Chats und Agents.
21st-design
Maschinenlesbare Design-Tokens und Komponenten-Workflow (JSON).
fluent-snippets
Globale CSS-, JS- und PHP-Assetverwaltung in WordPress.
local-solutions-website (Skill)
Arbeitsablauf für neue oder überarbeitete Seiten.
4Verbindung aus ChatGPT bzw. einem MCP-Client
Jeder Client, der MCP Streamable HTTP mit eigenen HTTP-Headern unterstützt, kann sich verbinden. Benötigt werden nur URL und Header:
URL: <MCP_URL>
Header: Authorization: Bearer <MCP_BEARER_TOKEN>
ChatGPT (Connector-Oberfläche)
- Einstellungen → Connectors (bzw. Apps & Connectors) → Entwicklermodus aktivieren.
- Erstellen → Name
Local Solutions Design, MCP-Server-URL<MCP_URL>. - Authentifizierung: Falls die Oberfläche einen statischen API-Schlüssel bzw. eigene Header anbietet, dort
Authorization: Bearer <MCP_BEARER_TOKEN>hinterlegen.
Rohe HTTP-Verbindung (curl)
curl -sS -X POST <MCP_URL> \
-H "Authorization: Bearer <MCP_BEARER_TOKEN>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'
Die Antwort kommt als text/event-stream (eine data:-Zeile mit dem JSON-RPC-Ergebnis). Der Server ist zustandslos – es ist keine Session-ID nötig.
5Verbindung aus Claude Desktop oder Claude Code
Claude Code (CLI)
claude mcp add --transport http local-solutions-design <MCP_URL> \
--header "Authorization: Bearer <MCP_BEARER_TOKEN>"
Mit --scope user gilt der Server für alle Projekte; ohne Angabe nur für das aktuelle. Prüfen mit claude mcp list, dann im Chat: „Lade den Agent-Kontext über design_get_agent_context.“
Claude Desktop
Claude Desktop verbindet Remote-Server über Einstellungen → Connectors → Custom Connector (OAuth-basiert). Für statische Bearer-Token wird stattdessen die lokale Konfiguration mit mcp-remote als Brücke genutzt (Datei claude_desktop_config.json):
{
"mcpServers": {
"local-solutions-design": {
"command": "npx",
"args": [
"-y", "mcp-remote", "<MCP_URL>",
"--header", "Authorization: Bearer <MCP_BEARER_TOKEN>"
]
}
}
}
Pfad der Datei: macOS ~/Library/Application Support/Claude/claude_desktop_config.json, Windows %APPDATA%\Claude\claude_desktop_config.json. Node.js ≥ 20 muss installiert sein.
6Beispielkonfigurationen
Claude Code – .mcp.json im Projekt
{
"mcpServers": {
"local-solutions-design": {
"type": "http",
"url": "<MCP_URL>",
"headers": { "Authorization": "Bearer <MCP_BEARER_TOKEN>" }
}
}
}
Cursor / Windsurf / generischer Client – mcp.json
{
"mcpServers": {
"local-solutions-design": {
"url": "<MCP_URL>",
"headers": { "Authorization": "Bearer <MCP_BEARER_TOKEN>" }
}
}
}
OpenAI Codex CLI – ~/.codex/config.toml
[mcp_servers.local-solutions-design]
url = "<MCP_URL>"
bearer_token_env_var = "LS_DESIGN_MCP_TOKEN" # Token nur als Umgebungsvariable setzen
OpenAI Responses API (Python)
from openai import OpenAI
client = OpenAI()
resp = client.responses.create(
model="gpt-5",
tools=[{
"type": "mcp",
"server_label": "local_solutions_design",
"server_url": "<MCP_URL>",
"headers": {"Authorization": "Bearer <MCP_BEARER_TOKEN>"},
"require_approval": "never",
}],
input="Lade den Agent-Kontext und fasse die Hero-Regeln zusammen.",
)
print(resp.output_text)
Anthropic Messages API – MCP-Connector
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" -H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: mcp-client-2025-04-04" -H "content-type: application/json" \
-d '{
"model": "claude-sonnet-5", "max_tokens": 2000,
"mcp_servers": [{ "type": "url", "url": "<MCP_URL>",
"name": "local-solutions-design",
"authorization_token": "<MCP_BEARER_TOKEN>" }],
"messages": [{ "role": "user", "content": "Rufe design_get_agent_context auf." }]
}'
7Authentifizierung mit Bearer-Token
Der Server prüft bei jedem Aufruf von /mcp den Header Authorization: Bearer <MCP_BEARER_TOKEN> in konstanter Zeit (timingSafeEqual). Ohne oder mit falschem Token antwortet er mit 401 {"error":"unauthorized"} und WWW-Authenticate: Bearer. Es gibt keine Nutzerkonten, Rollen oder OAuth – ein Token, volle Lese- und Schreibrechte.
- Token nur über HTTPS senden (HTTP wird von nginx auf HTTPS umgeleitet).
- Token nie in URLs oder Query-Parametern übergeben – nur im Header.
- In Client-Configs bevorzugt Umgebungsvariablen verwenden, wo der Client das erlaubt.
- Bei Verdacht auf Kompromittierung sofort rotieren (Abschnitt 15).
8Erstes Laden des Agent-Kontexts
Jeder neue Agent oder Chat beginnt mit design_get_agent_context. Das Ergebnis bündelt alle Core-Dokumente und Skills als ein Markdown-Dokument und liefert eine Gesamt-Revision, mit der sich später prüfen lässt, ob der Kontext noch aktuell ist.
curl -sS -X POST <MCP_URL> \
-H "Authorization: Bearer <MCP_BEARER_TOKEN>" \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"design_get_agent_context","arguments":{"includeSkills":true}}}'
Antwort (gekürzt):
{ "result": { "structuredContent": {
"revision": "9b00cb22…",
"entries": ["21st-design","cloud-chat-context","fluent-snippets","design-architecture","project-context","local-solutions-website"],
"content": "# 21st.dev Design-Konfiguration\n\n{ … }\n\n---\n\n# Cloud Chat Context …"
} } }
Empfohlener Einstiegs-Prompt in einem Chat: „Lade zuerst design_get_agent_context und halte dich an die dort enthaltenen Regeln.“
9Lesen und Suchen der Dokumentation
Einträge auflisten
{"jsonrpc":"2.0","id":3,"method":"tools/call",
"params":{"name":"design_list_entries","arguments":{}}}
Einen Eintrag lesen
{"jsonrpc":"2.0","id":4,"method":"tools/call",
"params":{"name":"design_read_entry","arguments":{"id":"design-architecture"}}}
Die Antwort enthält content, revision (SHA-256 des Inhalts) und updatedAt. Die Revision ist die Voraussetzung für jedes Update.
Suchen
{"jsonrpc":"2.0","id":5,"method":"tools/call",
"params":{"name":"design_search","arguments":{"query":"Hero Ticker","kind":"document","limit":5}}}
Die Suche ist akzent- und groß-/kleinschreibungsunabhängig, gewichtet nach Trefferanzahl und liefert je Eintrag einen kurzen Ausschnitt mit Revision.
10Kontrollierte Updates mit SHA-256-Revision
Updates folgen dem Muster lesen → ändern → mit erwarteter Revision schreiben. Stimmt die Revision nicht mehr (weil ein anderer Chat zwischenzeitlich geschrieben hat), lehnt der Server ab und nennt die aktuelle Revision. Nichts wird still überschrieben.
{"jsonrpc":"2.0","id":6,"method":"tools/call",
"params":{"name":"design_update_entry","arguments":{
"id": "cloud-chat-context",
"content": "<vollständiger neuer Inhalt>",
"expectedRevision": "592ac4787a6875e20066bf60e122bbdbde15fffdb1fe939577ef02afca297b6d",
"changeSummary": "Abschnitt Übergabe: Hinweis auf Staging-Domain ergänzt",
"confirmation": "UPDATE_ENTRY"
}}}
contentist immer der gesamte neue Inhalt, kein Diff.expectedRevisionist der 64-stellige Hex-Wert ausdesign_read_entry.confirmationmuss wörtlichUPDATE_ENTRYsein – ein Schutz gegen versehentliche Aufrufe.- Bei Konflikt:
isError: true, TextRevisionskonflikt. Aktuelle Revision: …→ erneut lesen, Änderung zusammenführen, erneut schreiben. - Ist
LS_DESIGN_WRITES_ENABLED=false, sind alle Schreibtools deaktiviert.
11Snapshots, Historie und Audit-Log
Vor jedem Update wird die bisherige Fassung als Snapshot abgelegt; jeder Schreibvorgang erzeugt außerdem eine Audit-Zeile.
- Snapshots
/data/history/<id>/<zeitstempel>-<revision12>.json– enthältrevision,savedAt,changeSummaryund den vollständigen altencontent- Audit-Log
/data/audit.ndjson– eine JSON-Zeile pro Vorgang:at,action(update|create-skill),id,fromRevision,toRevision,changeSummary
{"jsonrpc":"2.0","id":7,"method":"tools/call",
"params":{"name":"design_entry_history","arguments":{"id":"cloud-chat-context","limit":10}}}
Eine ältere Fassung wird wiederhergestellt, indem ihr content aus dem Snapshot per design_update_entry mit der aktuellen Revision erneut geschrieben wird – so bleibt auch die Wiederherstellung nachvollziehbar. Auf dem Server: docker exec local-solutions-design-mcp tail /data/audit.ndjson.
12Hinzufügen neuer Skills und Dokumente
Skill zur Laufzeit anlegen (per MCP)
{"jsonrpc":"2.0","id":8,"method":"tools/call",
"params":{"name":"design_create_skill","arguments":{
"id": "landingpage-review",
"title": "Landingpage Review",
"description": "Prüfschritte vor Freigabe einer Landingpage.",
"content": "# Landingpage Review\n\n1. Hero-Ticker prüfen …",
"confirmation": "CREATE_SKILL"
}}}
IDs bestehen aus Kleinbuchstaben, Ziffern und Bindestrichen (max. 81 Zeichen). Der Skill landet unter /data/skills/<id>/SKILL.md und ist sofort als Tool-Eintrag und Ressource verfügbar. Mit includeSkills:true (Standard) nimmt design_get_agent_context alle Skills auf, mit false nur die Core-Dokumente.
Dokument über den Seed hinzufügen (Deployment)
- Datei nach
seed/documents/legen und Eintrag inseed/manifest.jsonergänzen (id,kind,title,description,mimeType,relativePath, optionalcore: true). - Image neu bauen und Container neu starten (Abschnitt 13).
- Beim Start werden nur fehlende IDs ins Volume übernommen; bestehende Inhalte bleiben unangetastet.
document) lassen sich nur über den Seed anlegen – zur Laufzeit gibt es bewusst nur design_create_skill. Inhalte bestehender Dokumente werden per design_update_entry gepflegt.13Deployment und Aktualisierung
- Server
- Hetzner, Ubuntu 24.04, Docker 29 / Compose v5
- Verzeichnis
/opt/local-solutions-design-mcp- Compose
deploy/compose.yml, Projekt/Containerlocal-solutions-design-mcp- Secrets
deploy/.env(Rechte 600, nicht in Git)- Reverse Proxy
- nginx, vHost
/etc/nginx/sites-available/design-mcp.localsolutions.de.conf
Code-Update einspielen
cd /opt/local-solutions-design-mcp
# neue Version entpacken/pullen (deploy/.env bleibt unberührt), dann:
docker compose -f deploy/compose.yml up -d --build
docker compose -f deploy/compose.yml ps
curl -s https://design-mcp.localsolutions.de/health
Neustart und Logs
docker compose -f /opt/local-solutions-design-mcp/deploy/compose.yml restart
docker logs -f --tail 100 local-solutions-design-mcp
Details, Sicherheitsgrenzen und Checkliste: OPERATIONS.md im Projektverzeichnis.
14Backup und Wiederherstellung des Docker-Volumes
Alle veränderlichen Daten liegen im Volume local-solutions-design-mcp_design-mcp-data (Host-Pfad /var/lib/docker/volumes/local-solutions-design-mcp_design-mcp-data/_data). Ein Backup ist ein Tar-Archiv dieses Volumes.
Backup erstellen
/opt/local-solutions-design-mcp/deploy/backup-volume.sh
# → /var/backups/local-solutions-design-mcp/design-mcp-data-<datum>.tar.gz
Manuell, ohne Skript:
docker run --rm -v local-solutions-design-mcp_design-mcp-data:/data:ro -v /var/backups/local-solutions-design-mcp:/backup alpine \
tar czf /backup/design-mcp-data-$(date +%F-%H%M).tar.gz -C /data .
Wiederherstellen
/opt/local-solutions-design-mcp/deploy/restore-volume.sh /var/backups/local-solutions-design-mcp/design-mcp-data-<datum>.tar.gz
Das Skript stoppt den Container, sichert den aktuellen Stand als Sicherheitskopie, leert das Volume, entpackt das Archiv und startet den Container neu. Ablauf und Prüfschritte: BACKUP_RESTORE.md.
15Token-Rotation
- Neues Token erzeugen:
openssl rand -hex 32(64 Zeichen). deploy/.envbearbeiten:MCP_BEARER_TOKEN=<neues Token>; Rechte bleiben 600.- Container neu erstellen:
docker compose -f deploy/compose.yml up -d(Env wird nur beim Erstellen gelesen – ein reinesrestartgenügt nicht). - Prüfen: altes Token → 401, neues Token → Initialize erfolgreich.
- Alle Client-Konfigurationen aktualisieren (Claude Code, Codex, ChatGPT, …).
cd /opt/local-solutions-design-mcp
NEW=$(openssl rand -hex 32)
sed -i "s/^MCP_BEARER_TOKEN=.*/MCP_BEARER_TOKEN=$NEW/" deploy/.env
docker compose -f deploy/compose.yml up -d
unset NEW
Es gibt genau ein gültiges Token; eine Übergangsphase mit zwei Tokens unterstützt der Server nicht. Rotation daher mit den Nutzern abstimmen.
16Fehlerdiagnose
| Symptom | Ursache | Maßnahme |
|---|---|---|
401 unauthorized | Token fehlt, falsch oder Header falsch formatiert | Header exakt Authorization: Bearer <token>; Token in deploy/.env vergleichen (nicht ausgeben – Länge/Hash prüfen) |
404 not_found | Falscher Pfad | Nur /mcp und /health existieren im Backend |
502 Bad Gateway | Container läuft nicht / Port 8788 nicht erreichbar | docker ps, docker logs local-solutions-design-mcp, ss -tlnp | grep 8788 |
| Container startet nicht | Ungültige .env (Token < 32 Zeichen, Boolean falsch) | Logmeldung lesen: Umgebungsvariable … fehlt / muss true oder false sein |
Revisionskonflikt | Eintrag wurde zwischenzeitlich geändert | Eintrag neu lesen, Änderung zusammenführen, mit neuer Revision schreiben |
Schreibzugriffe sind serverseitig deaktiviert | LS_DESIGN_WRITES_ENABLED=false | Wert in deploy/.env auf true setzen, up -d |
| Client hängt bei langen Antworten | Proxy puffert oder Timeout | nginx-Location /mcp: proxy_buffering off, proxy_read_timeout 3600s |
| TLS-Fehler | Zertifikat abgelaufen | certbot certificates, certbot renew, systemctl reload nginx |
# Schnellcheck auf dem Server
docker compose -f /opt/local-solutions-design-mcp/deploy/compose.yml ps
docker logs --tail 50 local-solutions-design-mcp
curl -s http://127.0.0.1:8788/health
curl -s -o /dev/null -w "%{http_code}\n" -X POST http://127.0.0.1:8788/mcp # erwartet 401
nginx -t && tail -n 30 /var/log/nginx/error.log
17Healthcheck und Betriebsstatus
GET /health ist öffentlich, benötigt kein Token und gibt keine sensiblen Daten preis:
{"status":"ok","entries":6,"writesEnabled":true}
- Docker-Healthcheck: alle 30 s
wget http://127.0.0.1:8788/health; nach 3 Fehlschlägen gilt der Container alsunhealthy(docker pszeigt den Status). - Neustartverhalten:
restart: unless-stopped– der Container kommt nach Server-Reboot oder Absturz automatisch zurück. - Externes Monitoring: HTTPS-Check auf
/healthmit Erwartung200und"status":"ok".
Live-Status wird geladen …