Skip to main content
Glama
index.ts9.33 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from "@modelcontextprotocol/sdk/types.js"; import express from "express"; import cors from "cors"; import path from "path"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // MCPサーバーの設定 const server = new Server( { name: "network-diagram-mcp", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // データストア(後でMCPリクエストに応じて更新される) const dataStore = { nodes: [] as Array<{ id: string; label: string; x: number; y: number; color?: string; solution?: string; filePath?: string; lineNumber?: number; code?: string; group?: string }>, edges: [] as Array<{ from: string; to: string; label?: string }>, groups: [] as Array<{ id: string; label: string; color?: string }>, lastUpdate: new Date().toISOString(), }; // ツールの定義 const tools: Tool[] = [ { name: "set_diagram", description: "ネットワークダイアグラム全体を設定します。既存のダイアグラムは完全に上書きされます。", inputSchema: { type: "object", properties: { nodes: { type: "array", description: "ノードの配列", items: { type: "object", properties: { id: { type: "string", description: "ノードのID", }, label: { type: "string", description: "ノードのラベル", }, color: { type: "string", description: "ノードの背景色(オプション、16進数カラーコード)。白文字なので濃い色を推奨(例: #2563eb, #dc2626, #059669, #7c3aed)", }, solution: { type: "string", description: "ソリューション名(オプション)", }, filePath: { type: "string", description: "プロジェクトルートからのファイルパス(オプション)", }, lineNumber: { type: "number", description: "対象コードの行番号(オプション)", }, code: { type: "string", description: "表示する生コード(オプション)", }, group: { type: "string", description: "所属するグループのID(オプション)", }, x: { type: "number", description: "X座標(オプション)", }, y: { type: "number", description: "Y座標(オプション)", }, }, required: ["id", "label"], }, }, groups: { type: "array", description: "グループの配列(オプション)", items: { type: "object", properties: { id: { type: "string", description: "グループのID", }, label: { type: "string", description: "グループのラベル", }, color: { type: "string", description: "グループの背景色(オプション、16進数カラーコード、例: #e0f2fe, #fef3c7, #e0e7ff)", }, }, required: ["id", "label"], }, }, edges: { type: "array", description: "エッジの配列", items: { type: "object", properties: { from: { type: "string", description: "開始ノードのID", }, to: { type: "string", description: "終了ノードのID", }, label: { type: "string", description: "エッジのラベル(オプション)", }, }, required: ["from", "to"], }, }, }, required: ["nodes", "edges"], }, }, ]; // ツール一覧を返すハンドラ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools, })); // ツール実行ハンドラ server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; switch (name) { case "set_diagram": { const { nodes, edges, groups } = args as { nodes: Array<{ id: string; label: string; x?: number; y?: number; color?: string; solution?: string; filePath?: string; lineNumber?: number; code?: string; group?: string }>; edges: Array<{ from: string; to: string; label?: string }>; groups?: Array<{ id: string; label: string; color?: string }>; }; // ノードのデフォルト座標を設定 dataStore.nodes = nodes.map(node => ({ id: node.id, label: node.label, x: node.x ?? Math.random() * 800, y: node.y ?? Math.random() * 600, color: node.color, solution: node.solution, filePath: node.filePath, lineNumber: node.lineNumber, code: node.code, group: node.group, })); // エッジをそのまま設定 dataStore.edges = edges; // グループを設定 dataStore.groups = groups || []; dataStore.lastUpdate = new Date().toISOString(); const url = `http://localhost:${actualPort}`; return { content: [ { type: "text", text: `ダイアグラムを設定しました: ${nodes.length}個のノード、${edges.length}個のエッジ\n\nWebインターフェース: ${url}`, }, ], }; } default: throw new Error(`Unknown tool: ${name}`); } }); // Expressサーバーの設定 const app = express(); const INITIAL_PORT = parseInt(process.env.PORT || "3000", 10); let actualPort = INITIAL_PORT; app.use(cors()); app.use(express.json()); // 静的ファイルの配信 app.use(express.static(path.join(__dirname, "public"))); // APIエンドポイント app.get("/api/diagram", (req, res) => { res.json(dataStore); }); // ダイアグラム全体を設定(上書き) app.put("/api/diagram", (req, res) => { const { nodes, edges, groups } = req.body; if (!Array.isArray(nodes) || !Array.isArray(edges)) { return res.status(400).json({ error: "nodes and edges must be arrays" }); } // ノードのデフォルト座標を設定 dataStore.nodes = nodes.map((node: any) => ({ id: node.id, label: node.label, x: node.x ?? Math.random() * 800, y: node.y ?? Math.random() * 600, color: node.color, solution: node.solution, filePath: node.filePath, lineNumber: node.lineNumber, code: node.code, group: node.group, })); dataStore.edges = edges; dataStore.groups = groups || []; dataStore.lastUpdate = new Date().toISOString(); res.json({ success: true, nodes: dataStore.nodes.length, edges: dataStore.edges.length, groups: dataStore.groups.length }); }); app.get("/api/health", (req, res) => { res.json({ status: "ok", timestamp: new Date().toISOString() }); }); // ポートを試行してサーバーを起動する関数 function startServer(port: number, maxRetries: number = 10): Promise<any> { return new Promise((resolve, reject) => { const server = app.listen(port, () => { actualPort = port; console.error(`HTTP server running on http://localhost:${port}`); console.error(`Web interface: http://localhost:${port}`); console.error(`API available at: http://localhost:${port}/api`); resolve(server); }); server.on("error", (err: any) => { if (err.code === "EADDRINUSE") { if (maxRetries > 0) { console.error(`Port ${port} is in use, trying ${port + 1}...`); server.close(); resolve(startServer(port + 1, maxRetries - 1)); } else { reject(new Error(`Could not find available port after ${10 - maxRetries} attempts`)); } } else { reject(err); } }); }); } // HTTPサーバーの起動 let httpServer: any; startServer(INITIAL_PORT).then((server) => { httpServer = server; }).catch((error) => { console.error("Failed to start HTTP server:", error); process.exit(1); }); // MCPサーバーの起動 async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("MCP server running on stdio"); } main().catch((error) => { console.error("Server error:", error); process.exit(1); }); // クリーンアップ process.on("SIGINT", () => { console.error("\nShutting down..."); httpServer.close(); process.exit(0); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Fogrexon/network-diagram-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server