Skip to main content
Glama

MCP Aufgabenassistent

Ein lehrreiches Projekt, das als einzelner Docker-Compose-Dienst läuft: Es sendet Benutzernachrichten an ein LLM bei Groq, das mithilfe von fünf MCP-Tools (list_tasks, list_tasks_by_priority, create_task, update_task, delete_task) eine In-Memory-Aufgabenliste verwaltet. Jede Aufgabe hat ein priority-Feld (Dringlichkeit: niedrig/mittel/hoch).

Was macht es?

Du sendest eine Nachricht in natürlicher Sprache an den Endpunkt POST /chat (z. B. „Docker-Aufgabe als erledigt markieren"). Der Chat-Server sendet diese Nachricht zusammen mit den Schemata der 5 verfügbaren MCP-Tools an Groq. Das Modell kann bei Bedarf nacheinander Tools aufrufen (z. B. zuerst list_tasks, um die ID zu finden); jeder Aufruf wird gegen das JSON-Schema validiert, über den echten MCP-Server ausgeführt und das Ergebnis dem Modell erneut angezeigt. Am Ende werden die Antwort in natürlicher Sprache und eine sichtbare Spur (trace) des gesamten Ablaufs gemeinsam zurückgegeben.

Related MCP server: MCP Project Manager

Architektur

Zwei separate Node.js-Prozesse kommunizieren im selben Container über JSON-RPC per stdio:

[app sureci]                          [mcp-server sureci]
Express (/chat)                       (child process, stdio ile baslatiliyor)
  |- groq/           --HTTP-->  Groq API
  `- mcp-client/     --stdio/JSON-RPC-->  mcp-server/  -->  task-store/

Datei

Verantwortung

src/task-store

Aufgaben-CRUD, Map-basierter In-Memory-Speicher, Seed-Daten

src/mcp-server

Wandelt task-store in 5 MCP-Tools mit JSON-Schema um, lauscht auf stdio+JSON-RPC

src/mcp-client

Startet mcp-server als Child-Prozess, hält eine einzige (Singleton-)Verbindung

src/groq

Sendet Anfragen an Groq, Konvertierung MCP-Schema → Groq-Tool-Format

src/app

/chat-Endpunkt, Tool-Aufruf-Schleife, ajv-Validierung, Trace-Erzeugung

Installation und Ausführung

1) Groq-API-Schlüssel besorgen

  1. Gehe auf https://console.groq.com/keys und melde dich an.

  2. Erstelle mit „Create API Key" einen neuen Schlüssel und gib ihm einen beliebigen Namen (z. B. mcp-aufgabenassistent).

  3. Kopiere den angezeigten Schlüssel (gsk_...) – er wird nicht noch einmal angezeigt.

2) .env-Datei erstellen

cp .env.example .env

Öffne die .env-Datei und füge deinen Schlüssel am Ende der Zeile GROQ_API_KEY= ein.

Hinweis: Der Wert von GROQ_MODEL kann sich im Laufe der Zeit ändern – Groq entfernt und fügt regelmäßig Modelle hinzu. So siehst du die aktuelle Liste: curl -s https://api.groq.com/openai/v1/models -H "Authorization: Bearer $GROQ_API_KEY"

3) Mit Docker Compose ausführen

docker compose up --build -d

So verfolgst du die Logs:

docker compose logs -f

Sobald du die Zeile Chat-Server läuft auf http://localhost:3000 siehst, ist alles bereit (Container-Port 3000, nach außen über compose.yaml als 3001 geöffnet – falls Port 3000 auf deinem Rechner belegt ist, kannst du die ports-Zeile in compose.yaml ändern).

Zum Beenden:

docker compose down

Testnachrichten

curl -X POST http://localhost:3001/chat -H "Content-Type: application/json" \
  -d '{"message": "Hangi görevlerim var?"}'

curl -X POST http://localhost:3001/chat -H "Content-Type: application/json" \
  -d '{"message": "JSON Schema öğrenmek için bir görev ekle."}'

curl -X POST http://localhost:3001/chat -H "Content-Type: application/json" \
  -d '{"message": "Docker görevini tamamlandı olarak işaretle."}'

curl -X POST http://localhost:3001/chat -H "Content-Type: application/json" \
  -d '{"message": "Tamamlanan görevi sil."}'

Beispielantwort (3. Nachricht – beachte, dass die ID zuerst mit list_tasks ermittelt und dann an update_task übergeben wird):

{
  "answer": "\"Docker Compose kur\" görevi tamamlandı olarak işaretlendi.",
  "trace": [
    { "tool": "list_tasks", "arguments": {}, "validation": "passed",
      "result": { "tasks": [ { "id": 1, "title": "MCP sartnamesini oku", "completed": false },
        { "id": 2, "title": "Docker Compose kur", "completed": false },
        { "id": 3, "title": "Groq API anahtarini al", "completed": true } ] } },
    { "tool": "update_task", "arguments": { "completed": true, "id": 2 }, "validation": "passed",
      "result": { "id": 2, "title": "Docker Compose kur", "completed": true } }
  ]
}

Die 4. Nachricht („Lösche die erledigte Aufgabe.") zeigte während des Tests ein interessantes Verhalten: Da in den Seed-Daten bereits eine erledigte Aufgabe existierte (id=3) und nach der 5. Nachricht eine weitere erledigte Aufgabe (id=2) hinzukam, stand das Modell vor zwei Optionen und fragte den Benutzer, welche gemeint sei, statt zu raten – ohne ein einziges Tool aufzurufen. Das ist ein erwartetes/gewünschtes Verhalten des Projekts (nicht die falsche Aufgabe löschen), kein Fehler.

Häufig gestellte Fragen

Warum musste ich für ein neues Tool (z. B. list_tasks_by_priority) nur 2 Dateien ändern? Weil die Schichten app, mcp-client und groq die Tools überhaupt nicht fest verdrahten (hardcoden) – app fragt bei jeder Anfrage mit listMcpTools() beim mcp-server nach, „was hast du", und übergibt die zurückgegebene Liste unverändert an Groq. Um ein neues Tool zu definieren, genügt es also, (1) die Logik in task-store und (2) das Schema in mcp-server zu ergänzen – alles andere fließt automatisch. Das ist die konkrete Umsetzung der Entscheidung „Verantwortlichkeiten trennen" aus Schritt 1.

Warum gibt es keine Datenbank, sondern In-Memory-Daten? Die Spezifikation verlangt das bewusst: Das Projekt soll das MCP-Protokoll und den Tool-Aufruf-Ablauf vermitteln; persistente Speicherung ist ein separates Thema und hätte unnötige Komplexität hinzugefügt. Map + Seed-Daten liefern das Verhalten „bei jedem Start mit einem sauberen Zustand beginnen" kostenlos mit.

Warum Docker Compose – hätte nicht ein einzelner node-Befehl gereicht? Docker beseitigt das „bei mir funktioniert es"-Problem und garantiert, dass das Projekt auf jeder Maschine identisch läuft. Compose macht die Dienste (auch wenn es hier nur einer ist) standardmäßig mit einem einzigen Befehl startbar – eine Übung, die realen Installationen nahekommt.

Warum gibt es eine JSON-Schema-Validierung – hätte man Groq nicht vertrauen können? Die LLM-Ausgabe ist nicht deterministisch – das Modell kann manchmal unvollständige oder falsch typisierte Argumente erzeugen. Ohne Validierung mit ajv direkt auf task-store zuzugreifen, könnte zu unerwarteten Fehlern oder inkonsistenten Daten führen. Die Validierung ist die Code-Entsprechung des Prinzips „traue dem LLM nicht, prüfe es".

Warum stehen die Tool-Definitionen im tools-Feld und nicht in der Systemnachricht? Das tools-Feld ist ein strukturierter Vertrag in der Groq/OpenAI-API – das Modell behandelt es als echte, aufrufbare Funktionen und erzeugt die Antwort im strukturierten tool_calls-Format. Würden wir sie als Klartext in die Systemnachricht schreiben, würde das Modell sie nur als Kontext lesen, ohne Garantie oder Struktur für den Aufruf.

Warum startet mcp-client den mcp-server nicht bei jeder Anfrage neu? task-store lebt im RAM des mcp-server-Prozesses. Würde bei jeder Anfrage ein neuer Prozess gestartet, würden die Daten jedes Mal auf den Seed-Zustand zurückgesetzt – Änderungen aus früheren Nachrichten gingen verloren. Deshalb hält mcp-client, solange der app-Prozess läuft, genau EINE mcp-server-Verbindung (Singleton).

Warum reicht ein einziger Groq-Aufruf nicht – warum ist eine Schleife nötig? Wenn der Benutzer „Markiere die Docker-Aufgabe" sagt, kennt das Modell deren ID nicht – es muss zuerst list_tasks aufrufen, um die richtige ID zu finden, und dann das Tool, das die eigentliche Aktion ausführt, mit dieser ID aufrufen. Das bedeutet mehrere aufeinanderfolgende Tool-Aufrufe in einer einzigen Anfrage; der starre Ablauf „fragen–ausführen–erklären" unterstützt das nicht, eine echte Schleife ist nötig.

Bekannte Einschränkungen / nicht produktionsreife Punkte

  • Keine Persistenz: Wenn der Container neu gestartet wird (oder abstürzt/neu deployed wird), gehen alle Aufgabendaten verloren. Für den echten Einsatz wäre eine Datenbank (Postgres, SQLite usw.) nötig.

  • Keine Mehrbenutzer-/Sitzungstrennung: Alle Benutzer teilen sich denselben task-store; es gibt keine Isolierung zwischen Benutzern (Multi-Tenancy).

  • Kein Gesprächsgedächtnis: Jede /chat-Anfrage startet unabhängig. Der Benutzer kann nicht auf frühere Nachrichten Bezug nehmen (z. B. „lösche auch die") – Kontext bleibt nur innerhalb der Tool-Schleife derselben Anfrage erhalten.

  • Nur ein gleichzeitiger Tool-Aufruf: Selbst wenn das Modell in derselben Runde mehrere Tools anfordert (parallele tool_calls), wird nur der erste verarbeitet.

  • Keine Authentifizierung/Autorisierung: Der /chat-Endpunkt ist für alle offen, ohne jegliche Zugriffskontrolle.

  • Keine Eingabegrößen-/Ratenbegrenzung (Rate Limiting): Bösartige oder fehlerhafte Clients können unbegrenzt Anfragen senden; die Groq-Rechnung kann entsprechend ansteigen.

  • Das Ajv-Schema wird bei jeder Anfrage neu kompiliert: ajv.compile(...) könnte für die Leistung zwischengespeichert werden (im kleinen Maßstab fällt es nicht auf).

  • Der Modellname kann mit der Zeit veralten: Der Modellkatalog von Groq ändert sich (während dieses Projekts wurde llama-3.3-70b-versatile entfernt) – GROQ_MODEL sollte regelmäßig überprüft werden.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    A simple, powerful Todo list manager for Claude Desktop and other MCP-compatible AI assistants. Organize your tasks across different projects with priorities and never lose track of what needs to be done!
    15 npm
    2
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    A task manager MCP server that demonstrates all three MCP primitives (tools, resources, prompts). Enables users to manage tasks, read task summaries and details, and run structured planning/review prompts through natural language.
    -