Skip to main content
Glama

MCP Gorev Asistani

ユーザーメッセージをGroqのLLMに送信し、LLMが5つのMCPツール(list_tasks、list_tasks_by_priority、create_task、update_task、delete_task)を使ってインメモリのタスクリストを管理できるようにする、単一のDocker Composeサービスとして動作するチュートリアルプロジェクト。各タスクにはpriority(緊急度: 低/中/高)フィールドがある。

何をするのか?

POST /chatエンドポイントに自然言語のメッセージを送信する(例: 「Dockerタスクを完了としてマークして」)。チャットサーバーはこのメッセージを、保持している5つのMCPツールのスキーマとともにGroqに送信する。モデルは必要に応じて(idを探すために最初にlist_tasksを呼ぶなど)連続してツールを呼び出せる。各呼び出しはJSON Schemaに対して検証され、実際のMCPサーバー経由で実行されて、結果が再びモデルに表示される。最後に、自然言語の応答とプロセス全体の可視化されたトレース(trace)が一緒に返される。

Related MCP server: MCP Project Manager

アーキテクチャ

2つの別々のNode.jsプロセスが、同じコンテナ内でstdio経由のJSON-RPCで通信する:

[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/

ファイル

責務

src/task-store

タスクCRUD、Mapベースのインメモリストア、シードデータ

src/mcp-server

task-storeをJSON Schema付きの5つのMCPツールに変換し、stdio+JSON-RPCを待ち受ける

src/mcp-client

mcp-serverを子プロセスとして起動し、単一(シングルトン)の接続を保持する

src/groq

Groqにリクエストを送信し、MCPスキーマ→Groqツール形式の変換を行う

src/app

/chatエンドポイント、ツール呼び出しループ、ajv検証、トレース生成

インストールと実行

1) Groq APIキーを取得する

  1. https://console.groq.com/keys にアクセスしてログインする。

  2. 「Create API Key」で新しいキーを作成し、名前は好きなように付ける(例: mcp-gorev-asistani)。

  3. 表示されたキー(gsk_...)をコピーする - 二度と表示されない。

2) .envファイルを作成する

cp .env.example .env

.envファイルを開き、GROQ_API_KEY=の行の末尾にキーを貼り付ける。

注: GROQ_MODELの値は時間とともに変わる可能性がある - Groqは時々モデルを削除して新しいものを追加している。最新のリストを確認するには: curl -s https://api.groq.com/openai/v1/models -H "Authorization: Bearer $GROQ_API_KEY"

3) Docker Composeで実行する

docker compose up --build -d

ログを監視するには:

docker compose logs -f

Chat sunucusu http://localhost:3000 adresinde calisiyor.という行が表示されたら準備完了(コンテナ内のポート3000、外部にはcompose.yaml経由で3001として公開 - 自分のマシンで3000が使用中の場合はcompose.yamlportsの行を変更できる)。

停止するには:

docker compose down

テストメッセージ

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."}'

応答例(3番目のメッセージ - idが最初にlist_tasksで見つけられ、その後update_taskに渡されていることに注目):

{
  "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 } }
  ]
}

4番目のメッセージ("Tamamlanan görevi sil.")はテスト中に興味深い動作を示した: シードデータにすでに完了済みのタスク(id=3)があり、5番目のメッセージの後にもう1つ完了済みのタスク(id=2)が作成されたため、モデルは2つの選択肢の間で迷い、推測する代わりにユーザーにどちらを指しているのか尋ねた - ツールを1つも呼び出さずに。これはプロジェクトの期待される/望ましい動作(誤ったタスクを削除しないこと)であり、バグではない。

よくある質問

新しいツール(例: list_tasks_by_priority)を追加するのに、なぜ2つのファイルを変更するだけでよかったのか? appmcp-clientgroqの各レイヤーはツールを一切ハードコードしていないからだ - appはリクエストのたびにlistMcpTools()でmcp-serverに「何を持っているか」を尋ね、返ってきたリストをそのままGroqに渡している。つまり、新しいツールを定義するには、(1) task-storeにロジックを、(2) mcp-serverにスキーマを追加するだけで十分で、残りはすべて自動的に流れていく。これがステップ1の「責務を分離する」という判断の具体的な成果だ。

なぜデータベースがなく、インメモリデータを使っているのか? 仕様が意図的にこれを求めている: このプロジェクトはMCPプロトコルとツール呼び出しの流れを教えることを目的としており、永続ストレージ(persistence)は別のトピックで、不要な複雑さを加えるだけだからだ。Map+シードデータは、「起動のたびにクリーンな状態から始める」という動作を追加コストなしで提供する。

なぜDocker Composeなのか、単一のnodeコマンドで十分ではなかったのか? Dockerは「自分の環境では動いていた」問題を排除し、プロジェクトがどのマシンでも同じように動作することを保証する。Composeはサービス(ここでは1つのサービスだけだが)を標準的で単一コマンドで起動可能にする - 実世界のセットアップに近い練習になる。

なぜJSON Schema検証があるのか、Groqを信頼するだけでは十分ではなかったのか? LLMの出力は決定的ではない - モデルは時々、欠落していたり誤った型の引数を生成することがある。ajvで検証せずに直接task-storeにアクセスすると、予期しないエラーや不整合なデータにつながる可能性がある。検証は、LLMに対する「信頼するな、検証せよ」という原則をコードで実現したものだ。

ツール定義はなぜシステムメッセージではなくtoolsフィールドに置かれるのか? toolsフィールドはGroq/OpenAI APIにおける構造化された契約であり、モデルはこれを実際に呼び出し可能な関数として認識し、応答も構造化されたtool_calls形式で生成する。システムメッセージにプレーンテキストとして書いた場合、モデルはそれを単なるコンテキストとして読むだけで、呼び出しの保証/構造は得られない。

mcp-clientはなぜリクエストのたびにmcp-serverを再起動しないのか? task-storeはmcp-serverプロセスのRAM上に存在している。リクエストのたびに新しいプロセスを起動すると、データは毎回シードに戻ってしまい、以前のメッセージで行った変更が失われてしまう。そのためmcp-clientは、appプロセスが稼働している限り、単一のmcp-server接続(シングルトン)を維持している。

なぜ1回のGroq呼び出しでは足りず、ループが必要なのか? ユーザーが「Dockerタスクをマークして」と言ったとき、モデルはそのidを知らない

  • 最初にlist_tasksを呼び出して正しいidを見つけ、その後、実際の操作を行うツールをそのidで呼び出す必要がある。これは1回のリクエストで複数の連続したツール呼び出しを意味する。固定の「問い合わせ→実行→説明」フローではこれをサポートできず、実際のループが必要だ。

既知の制限 / 本番運用にまだ対応していない点

  • 永続化がない: コンテナが再起動すると(あるいはクラッシュ/再デプロイされると)、すべてのタスクデータが失われる。実際の使用ではデータベース(Postgres、SQLiteなど)が必要だ。

  • 複数ユーザー/セッションの分離がない: すべてのユーザーが同じtask-storeを共有する。ユーザー間の分離(multi-tenancy)はない。

  • 会話の記憶がない: 各/chatリクエストは独立して開始される。ユーザーは以前のメッセージに言及できない(「それも削除して」など)- コンテキストは同じリクエスト内のツールループの間だけ保持される。

  • 同時ツール呼び出しが1つだけ: モデルが同じターンで複数のツールを要求しても(並列tool_calls)、最初の1つだけが処理される。

  • 認証/認可がない: /chatエンドポイントは誰にでも公開されており、アクセス制御は一切ない。

  • 入力サイズ/レート制限がない: 悪意のあるクライアントや誤動作したクライアントが無制限にリクエストを送信でき、Groqの請求額がそれに応じて膨らむ可能性がある。

  • Ajvスキーマがリクエストごとに再コンパイルされる: ajv.compile(...)はパフォーマンスのためにキャッシュ可能だった(小規模では違いはない)。

  • モデル名が時間とともに古くなる可能性がある: Groqのモデルカタログは変化している(このプロジェクトの間にllama-3.3-70b-versatileが削除された)- GROQ_MODELは定期的に確認すべきだ。

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.
    -