メインコンテンツまでスキップ

MCPサーバーの起動

🚀 MCP サーバーの起動

このレッスンでは、前の2つのレッスンで作成したJPYC SDK WrapperとMCPツールを公開するMCPサーバーを実装します。

MCPサーバーは、HTTP/SSE(Server-Sent Events)プロトコルを使用して、AI Agentと通信します。

📝 実装するファイル

external/mcp/src/index.tsファイルを作成し、以下のコードを記述します。

/**
* JPYC MCP Server - Standalone MCP Server
*
* このサーバーはJPYC SDKの機能をMCPプロトコル経由で公開します。
* HTTP/SSEを使用して、MCPClientから接続できるようにします。
*
* 学習用ポイント:
* 1. MCPServerを独立したプロセスとして実行
* 2. HTTP/SSE経由でMCPClientと通信
* 3. 実際のMCPプロトコルの使い方を学習
*/

import "dotenv/config";
import { MCPServer } from "@mastra/mcp";
import http from "node:http";
import { jpycTools } from "./tools";

const PORT = process.env.MCP_PORT || 3001;

/**
* JPYC MCP Server インスタンス
*/
const server = new MCPServer({
name: "jpyc-sdk",
version: "1.0.0",
tools: jpycTools,
});

/**
* HTTPサーバーを作成してMCPServerを統合
*/
const httpServer = http.createServer(async (req, res) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);

// CORSヘッダーを設定(Next.jsアプリからのアクセスを許可)
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");

if (req.method === "OPTIONS") {
res.writeHead(200);
res.end();
return;
}

// ヘルスチェックエンドポイント
if (req.url === "/health" && req.method === "GET") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ status: "ok", server: "jpyc-mcp-server" }));
return;
}

// MCP SSEエンドポイント
try {
await server.startSSE({
url: new URL(req.url || "", `http://localhost:${PORT}`),
ssePath: "/sse",
messagePath: "/message",
req,
res,
});
} catch (error) {
console.error("MCP Server error:", error);
if (!res.headersSent) {
res.writeHead(500, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "Internal server error" }));
}
}
});

// サーバー起動
httpServer.listen(PORT, () => {
console.log(`
╔════════════════════════════════════════════════════════════╗
║ ║
║ 🚀 JPYC MCP Server is running! ║
║ ║
║ SSE Endpoint: http://localhost:${PORT}/sse ║
║ Message Endpoint: http://localhost:${PORT}/message ║
║ Health Check: http://localhost:${PORT}/health ║
║ ║
║ Available Tools: ║
║ - jpyc_balance (残高照会) ║
║ - jpyc_transfer (送信) ║
║ - jpyc_switch_chain (チェーン切り替え) ║
║ - jpyc_get_current_chain (現在のチェーン取得) ║
║ - jpyc_total_supply (総供給量照会) ║
║ ║
╚════════════════════════════════════════════════════════════╝
`);
});

// エラーハンドリング
httpServer.on("error", (error) => {
console.error("HTTP Server error:", error);
process.exit(1);
});

// graceful shutdown
process.on("SIGTERM", () => {
console.log("SIGTERM signal received: closing HTTP server");
httpServer.close(() => {
console.log("HTTP server closed");
process.exit(0);
});
});

process.on("SIGINT", () => {
console.log("\nSIGINT signal received: closing HTTP server");
httpServer.close(() => {
console.log("HTTP server closed");
process.exit(0);
});
});

💡 コードの解説

このファイルは、MCPサーバーのエントリーポイントです。主要なポイントを見ていきましょう。

1. 環境変数の読み込み

import "dotenv/config";

最初にdotenv/configをインポートすることで、.envファイルから環境変数を読み込みます。これにより、PRIVATE_KEYなどの機密情報を安全に管理できます。

2. MCPServerインスタンスの作成

const server = new MCPServer({
name: "jpyc-sdk",
version: "1.0.0",
tools: jpycTools,
});

MastraのMCPServerを作成します。前のレッスンで作成したjpycToolsをツールとして登録することで、AI Agentがこれらのツールを使用できるようになります。

3. HTTPサーバーの作成

const httpServer = http.createServer(async (req, res) => {
// リクエスト処理
});

Node.jsの標準モジュールhttpを使って、HTTPサーバーを作成します。MCPサーバーは、このHTTPサーバーを通じてクライアントと通信します。

4. CORSヘッダーの設定

res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");

CORS(Cross-Origin Resource Sharing)ヘッダーを設定することで、Next.jsアプリケーション(別のポートで動作)からMCPサーバーにアクセスできるようにします。

5. ヘルスチェックエンドポイント

if (req.url === "/health" && req.method === "GET") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ status: "ok", server: "jpyc-mcp-server" }));
return;
}

/healthエンドポイントは、サーバーが正常に動作しているかを確認するために使用します。ブラウザでhttp://localhost:3001/healthにアクセスすると、サーバーの状態を確認できます。

6. SSE(Server-Sent Events)エンドポイント

await server.startSSE({
url: new URL(req.url || "", `http://localhost:${PORT}`),
ssePath: "/sse",
messagePath: "/message",
req,
res,
});

MCPプロトコルは、SSE(Server-Sent Events)を使用してリアルタイム通信を行います。

  • /sse: クライアントがサーバーからのイベントを受信するエンドポイント
  • /message: クライアントがサーバーにメッセージを送信するエンドポイント

7. Graceful Shutdown

process.on("SIGTERM", () => {
console.log("SIGTERM signal received: closing HTTP server");
httpServer.close(() => {
console.log("HTTP server closed");
process.exit(0);
});
});

SIGTERMSIGINT(Ctrl+C)シグナルを受信したとき、サーバーを適切に終了します。これにより、リクエスト処理中にサーバーが強制終了されることを防ぎます。

🧪 動作確認

MCPサーバーを起動して、動作を確認しましょう。

1. MCPサーバーの起動

ターミナルで以下のコマンドを実行します:

pnpm mcp:dev

以下のような出力が表示されれば、MCPサーバーが正常に起動しています:

╔════════════════════════════════════════════════════════════╗
║ ║
║ 🚀 JPYC MCP Server is running! ║
║ ║
║ SSE Endpoint: http://localhost:3001/sse ║
║ Message Endpoint: http://localhost:3001/message ║
║ Health Check: http://localhost:3001/health ║
║ ║
║ Available Tools: ║
║ - jpyc_balance (残高照会) ║
║ - jpyc_transfer (送信) ║
║ - jpyc_switch_chain (チェーン切り替え) ║
║ - jpyc_get_current_chain (現在のチェーン取得) ║
║ - jpyc_total_supply (総供給量照会) ║
║ ║
╚════════════════════════════════════════════════════════════╝

2. ヘルスチェック

ブラウザでhttp://localhost:3001/healthにアクセスします。

以下のJSONレスポンスが表示されれば、サーバーが正常に動作しています:

{
"status": "ok",
"server": "jpyc-mcp-server"
}

3. サーバーの停止

MCPサーバーを停止するには、ターミナルでCtrl+Cを押します。

SIGINT signal received: closing HTTP server
HTTP server closed

これで、Section 3(MCPサーバーの実装)が完了しました!

次のセクションでは、このMCPサーバーと通信するMastra Agentを実装していきます。

🙋‍♂️ 質問する

ここまでの作業で何かわからないことがある場合は、Discordの#jpycで質問をしてください。

ヘルプをするときのフローが円滑になるので、エラーレポートには下記の4点を記載してください ✨

  1. 質問が関連しているセクション番号とレッスン番号
  2. 何をしようとしていたか
  3. エラー文をコピー&ペースト
  4. エラー画面のスクリーンショット

次のセクションでは、Mastra Agentを実装します!