[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-hajimete-mcp-server-kouchiku-kaihatsusha-jissen-guide":3},{"article":4,"author":50},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":7,"meta_description":16,"focus_keyword":17,"og_image":18,"canonical_url":18,"robots_meta":19,"created_at":15,"updated_at":15,"tags":20,"category_name":30,"related_articles":31},"d0000000-0000-0000-0000-000000000507","a0000000-0000-0000-0000-000000000046","初めてのMCPサーバー構築：開発者向け実践ガイド","hajimete-mcp-server-kouchiku-kaihatsusha-jissen-guide","TypeScriptとPythonでModel Context Protocol（MCP）サーバーを構築し、Claude DesktopやCursorに接続して本番環境にデプロイするまでの実践チュートリアル。","## Model Context Protocol（MCP）とは？\n\n**Model Context Protocol（MCP）** は、Anthropicが開発したオープンスタンダードであり、AIアプリケーションが外部データソースやツールと通信する方法を定義します。MCPは **AI統合のためのUSB-C** と考えてください。USB-Cが充電、データ転送、ディスプレイ出力のための単一のユニバーサルコネクタを提供するように、MCPはAIモデルをデータベース、API、ファイルシステム、その他のサービスに接続するための単一のユニバーサルプロトコルを提供します。MCPにより、すべてのAIアプリケーションとすべてのツール間のカスタム統合が不要になります。\n\n2026年現在、MCPはAIツール通信の主要なスタンダードとなっています。Gartnerは **2026年末までにエンタープライズアプリケーションの40%がAIエージェントを含む** と予測しており、MCPはこれを大規模に実現可能にするプロトコルです。MCP以前は、AIモデルをN個のツールに接続するにはN個のカスタム統合が必要でした。M個のAIアプリケーションとN個のツールでは、M x N個の統合アダプタが必要でした。MCPはこれをM + Nに削減します。すべてのAIアプリが1つのMCP clientを実装し、すべてのツールが1つのMCP serverを実装します。\n\n## MCPアーキテクチャ：Host、Client、Server、Transport\n\nコードを書く前にMCPアーキテクチャを理解することが重要です。プロトコルは4つの主要な役割を定義しています：\n\n| コンポーネント | 役割 | 例 |\n|---------------|------|-----|\n| **Host** | エンドユーザーが操作するAIアプリケーション | Claude Desktop、Cursor、VS Code Copilot |\n| **Client** | Host内のMCPプロトコルハンドラー | Hostアプリケーションに組み込み |\n| **Server** | MCPを通じてtools、resources、promptsを公開 | カスタムサーバー（これから構築するもの） |\n| **Transport** | ClientとServer間の通信レイヤー | stdio、HTTP+SSE、Streamable HTTP |\n\n通信フローは次のように動作します：\n\n1. **Host**が起動し、設定された各サーバーに対してMCP **Client**を初期化します。\n2. **Client**が**Transport**（ローカルはstdio、リモートはHTTP）を介して**Server**に接続します。\n3. **Client**が`initialize`リクエストを送信し、プロトコルバージョンと機能をネゴシエートします。\n4. **Server**が利用可能な**tools**、**resources**、**prompts**を応答します。\n5. AIモデルが外部データやアクションを必要とする場合、**Client**が適切なサーバーメソッドを呼び出します。\n6. **Server**が操作を実行し、JSON-RPC 2.0メッセージで結果を返します。\n\n### Tools vs Resources vs Prompts\n\nMCP serverは3種類の機能を公開できます：\n\n- **Tools** はAIモデルが呼び出せる関数です。構造化された入力を受け取り、構造化された出力を返します。例：`query_database(sql: string)` や `send_email(to: string, subject: string, body: string)`。\n- **Resources** はAIモデルが読み取れるデータです。URIで識別され、コンテンツを返します。例：`file:\u002F\u002F\u002Fpath\u002Fto\u002Fdocument.md` や `postgres:\u002F\u002Flocalhost\u002Fmydb\u002Fusers`。\n- **Prompts** はサーバーが提供する再利用可能なプロンプトテンプレートです。AIがサーバーのドメインとやり取りする方法を標準化します。例：Jira MCP serverの`summarize_ticket`プロンプトテンプレート。\n\n## ステップバイステップ：TypeScriptでMCPサーバーを構築\n\nSQLiteデータベースにクエリを実行するツールを提供する実用的なMCPサーバーを構築しましょう。これは一般的なユースケースで、AIモデルにアプリケーションデータへの安全な読み取り専用アクセスを提供します。\n\n### ステップ1：プロジェクトの初期化\n\n```bash\nmkdir mcp-sqlite-server && cd mcp-sqlite-server\nnpm init -y\nnpm install @modelcontextprotocol\u002Fsdk better-sqlite3\nnpm install -D typescript @types\u002Fnode @types\u002Fbetter-sqlite3\nnpx tsc --init\n```\n\n`tsconfig.json`を設定：\n\n```json\n{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"Node16\",\n    \"moduleResolution\": \"Node16\",\n    \"outDir\": \".\u002Fdist\",\n    \"rootDir\": \".\u002Fsrc\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"declaration\": true\n  },\n  \"include\": [\"src\u002F**\u002F*\"]\n}\n```\n\n### ステップ2：サーバーの実装\n\n`src\u002Findex.ts`を作成：\n\n```typescript\nimport { McpServer } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fmcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fstdio.js\";\nimport Database from \"better-sqlite3\";\nimport { z } from \"zod\";\n\nconst db = new Database(process.env.DB_PATH || \".\u002Fdata.db\", {\n  readonly: true,\n});\n\nconst server = new McpServer({\n  name: \"sqlite-query\",\n  version: \"1.0.0\",\n});\n\nserver.tool(\n  \"query\",\n  \"SQLiteデータベースに対して読み取り専用SQLクエリを実行\",\n  {\n    sql: z.string().describe(\"実行するSQL SELECTクエリ\"),\n  },\n  async ({ sql }) => {\n    const normalized = sql.trim().toUpperCase();\n    if (!normalized.startsWith(\"SELECT\")) {\n      return {\n        content: [{ type: \"text\", text: \"エラー：SELECTクエリのみ許可されています。\" }],\n        isError: true,\n      };\n    }\n    try {\n      const rows = db.prepare(sql).all();\n      return {\n        content: [{ type: \"text\", text: JSON.stringify(rows, null, 2) }],\n      };\n    } catch (error) {\n      return {\n        content: [{ type: \"text\", text: `クエリエラー：${(error as Error).message}` }],\n        isError: true,\n      };\n    }\n  }\n);\n\nserver.tool(\n  \"list_tables\",\n  \"データベース内のすべてのテーブルとスキーマを一覧表示\",\n  {},\n  async () => {\n    const tables = db\n      .prepare(\n        `SELECT name, sql FROM sqlite_master\n         WHERE type='table' AND name NOT LIKE 'sqlite_%'\n         ORDER BY name`\n      )\n      .all();\n    return {\n      content: [{ type: \"text\", text: JSON.stringify(tables, null, 2) }],\n    };\n  }\n);\n```\n\n### ステップ3：ビルドとローカルテスト\n\n```bash\nnpx tsc\necho '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-03-26\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0\"}}}' | node dist\u002Findex.js\n```\n\nサーバーの機能を含むJSON-RPCレスポンスが表示されるはずです。\n\n### ステップ4：Pythonで同じサーバーを構築\n\nPython開発者向けに、公式MCP Python SDKを使用した同等の実装を示します：\n\n```python\nimport sqlite3\nimport json\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP(\"sqlite-query\")\nDB_PATH = \"data.db\"\n\n@mcp.tool()\ndef query(sql: str) -> str:\n    \"\"\"SQLiteデータベースに対して読み取り専用SQLクエリを実行。\"\"\"\n    normalized = sql.strip().upper()\n    if not normalized.startswith(\"SELECT\"):\n        raise ValueError(\"SELECTクエリのみ許可されています。\")\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(sql)\n        rows = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(rows, indent=2, default=str)\n    finally:\n        conn.close()\n\n@mcp.tool()\ndef list_tables() -> str:\n    \"\"\"データベース内のすべてのテーブルとスキーマを一覧表示。\"\"\"\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(\n            \"SELECT name, sql FROM sqlite_master \"\n            \"WHERE type='table' AND name NOT LIKE 'sqlite_%'\"\n        )\n        tables = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(tables, indent=2)\n    finally:\n        conn.close()\n\nif __name__ == \"__main__\":\n    mcp.run(transport=\"stdio\")\n```\n\n## Claude Desktopへの接続\n\nClaude DesktopはMCPサーバーをネイティブにサポートしています。サーバーを接続するには、Claude Desktop設定ファイルを編集します：\n\n**macOS:** `~\u002FLibrary\u002FApplication Support\u002FClaude\u002Fclaude_desktop_config.json`\n**Windows:** `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n```json\n{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\"\u002Fabsolute\u002Fpath\u002Fto\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \"\u002Fabsolute\u002Fpath\u002Fto\u002Fyour\u002Fdata.db\"\n      }\n    }\n  }\n}\n```\n\nClaude Desktopを再起動します。チャットインターフェースにハンマーアイコンが表示され、利用可能なMCPツールが示されます。「データベースにはどんなテーブルがありますか？」や「登録日順にトップ10のユーザーを表示して」のようにClaudeに質問すると、MCPサーバーを使用してデータベースに直接クエリを実行します。\n\n## Cursorへの接続\n\nCursorもMCPサーバーをサポートしています。プロジェクトルートの`.cursor\u002Fmcp.json`に設定を追加します：\n\n```json\n{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\".\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \".\u002Fdata.db\"\n      }\n    }\n  }\n}\n```\n\n保存後、Cursorを再起動します。MCPツールがAIアシスタントパネルに表示され、コード生成やデバッグセッション中に呼び出すことができます。\n\n## MCPサーバーのテストとデバッグ\n\n### MCP Inspectorの使用\n\nMCP Inspectorは公式デバッグツールです。サーバーとやり取りするためのWebインターフェースを提供します：\n\n```bash\nnpx @modelcontextprotocol\u002Finspector node dist\u002Findex.js\n```\n\n`http:\u002F\u002Flocalhost:5173`でブラウザインターフェースが開き、以下のことができます：\n\n- 登録されたすべてのtools、resources、promptsを表示\n- カスタム入力でツールを呼び出し、レスポンスを検査\n- JSON-RPCメッセージストリームをリアルタイムで監視\n- 不正なリクエストを送信してエラー処理をテスト\n\n### 一般的なデバッグの問題\n\n| 問題 | 原因 | 解決策 |\n|------|------|--------|\n| サーバーがClaude Desktopに表示されない | 設定パスまたはJSON構文エラー | JSONを検証し、絶対パスを確認 |\n| 「Tool not found」エラー | 接続前にツールが登録されていない | `server.connect()`を呼ぶ前にツールを登録 |\n| ツール呼び出しのタイムアウト | 進捗なしの長時間実行操作 | `server.sendProgress()`で進捗通知を追加 |\n| stderrの出力がプロトコルを破損 | console.logがstdoutに書き込み（stdioトランスポート） | stdioではロギングに`console.error()`を使用 |\n| アイドル後の接続切断 | トランスポートのタイムアウト | ハートビートの実装またはHTTPトランスポートを使用 |\n\n## 本番環境のベストプラクティス\n\n1. **入力バリデーション**：常にツールの入力を検証しサニタイズします。TypeScriptではZodスキーマ、PythonではPydanticモデルを使用して厳密な型チェックを行います。\n\n2. **デフォルトで読み取り専用**：読み取り専用アクセスから始めます。書き込み機能は本当に必要な場合にのみ追加し、破壊的な操作には必ず確認を求めます。\n\n3. **エラー処理**：`isError: true`で構造化されたエラーメッセージを返します。内部スタックトレースやデータベース接続文字列を公開しないでください。\n\n4. **ロギング**：すべてのツール呼び出しをタイムスタンプ、入力、実行時間とともにログに記録します。stdioトランスポート使用時はログにstderr（stdoutではなく）を使用します。\n\n5. **レート制限**：制御されないAIループがバックエンドサービスを過負荷にすることを防ぐため、ツールごとのレート制限を実装します。\n\n6. **タイムアウト**：すべてのツールハンドラーに実行タイムアウトを設定します。AIモデルが高コストなクエリをトリガーするツールを呼び出す可能性があります。インフラストラクチャを保護しましょう。\n\n7. **環境の分離**：すべての設定に環境変数を使用します。データベースURL、APIキー、ファイルパスをハードコードしないでください。\n\n8. **バージョニング**：MCPサーバーにセマンティックバージョニングを使用します。`initialize`ハンドシェイクにはバージョンネゴシエーションが含まれ、破壊的変更にはメジャーバージョンのバンプが必要です。\n\n## FAQ\n\n**Q：MCPとfunction callingの違いは何ですか？**\nA：Function calling（OpenAI、Anthropicなどが使用）は、各APIリクエストにインラインでツールを定義します。MCPはツール定義をスタンドアロンサーバーに外部化し、MCP互換ホストが発見して使用できるようにします。Function callingはリクエストごと、MCPはステートフルなセッションを持つ永続的なプロトコルです。\n\n**Q：Claude以外のモデルでMCPを使用できますか？**\nA：はい。MCPはオープンプロトコルです。OpenAI、Google DeepMind、Microsoftは2026年初頭からプラットフォームにMCPサポートを採用しています。MCP clientを実装するすべてのAIアプリケーションは、あらゆるMCP serverに接続できます。\n\n**Q：MCPはローカルツール専用ですか？**\nA：いいえ。stdioトランスポートはローカルサーバー向けに設計されていますが、HTTP+SSEおよびStreamable HTTPトランスポートはリモートMCPサーバーをサポートします。MCPサーバーをネットワーク経由でアクセス可能なクラウドサービスとしてデプロイできます。\n\n**Q：MCPは認証をどのように処理しますか？**\nA：リモートサーバーにはOAuth 2.0をサポートします。ローカルstdioサーバーはホストプロセスのセキュリティコンテキストを継承します。エンタープライズデプロイメントでは、MCPゲートウェイが認証と認可を一元化できます。\n\n**Q：MCPサーバーを構築するにはどの言語を使用できますか？**\nA：公式SDKはTypeScript、Python、Java、Kotlin、C#、Swiftで利用できます。コミュニティSDKにはRust、Go、Ruby、PHPが含まれます。プロトコルは言語に依存しません。stdioまたはHTTPを介してJSON-RPCの読み書きができる言語であれば、MCPサーバーを実装できます。\n\n**Q：ホストを再起動せずにMCPサーバーを更新する方法は？**\nA：MCPは機能変更通知をサポートしています。サーバーのツールが変更された場合、サーバーは`notifications\u002Ftools\u002Flist_changed`メッセージを送信し、clientにツールリストの再取得を促します。stdioサーバーの場合、通常ホストの再起動が必要です。HTTPサーバーはホストの再起動なしで更新できます。","\u003Ch2 id=\"model-context-protocol-mcp\">Model Context Protocol（MCP）とは？\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Model Context Protocol（MCP）\u003C\u002Fstrong> は、Anthropicが開発したオープンスタンダードであり、AIアプリケーションが外部データソースやツールと通信する方法を定義します。MCPは \u003Cstrong>AI統合のためのUSB-C\u003C\u002Fstrong> と考えてください。USB-Cが充電、データ転送、ディスプレイ出力のための単一のユニバーサルコネクタを提供するように、MCPはAIモデルをデータベース、API、ファイルシステム、その他のサービスに接続するための単一のユニバーサルプロトコルを提供します。MCPにより、すべてのAIアプリケーションとすべてのツール間のカスタム統合が不要になります。\u003C\u002Fp>\n\u003Cp>2026年現在、MCPはAIツール通信の主要なスタンダードとなっています。Gartnerは \u003Cstrong>2026年末までにエンタープライズアプリケーションの40%がAIエージェントを含む\u003C\u002Fstrong> と予測しており、MCPはこれを大規模に実現可能にするプロトコルです。MCP以前は、AIモデルをN個のツールに接続するにはN個のカスタム統合が必要でした。M個のAIアプリケーションとN個のツールでは、M x N個の統合アダプタが必要でした。MCPはこれをM + Nに削減します。すべてのAIアプリが1つのMCP clientを実装し、すべてのツールが1つのMCP serverを実装します。\u003C\u002Fp>\n\u003Ch2 id=\"mcp-host-client-server-transport\">MCPアーキテクチャ：Host、Client、Server、Transport\u003C\u002Fh2>\n\u003Cp>コードを書く前にMCPアーキテクチャを理解することが重要です。プロトコルは4つの主要な役割を定義しています：\u003C\u002Fp>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>コンポーネント\u003C\u002Fth>\u003Cth>役割\u003C\u002Fth>\u003Cth>例\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>\u003Cstrong>Host\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>エンドユーザーが操作するAIアプリケーション\u003C\u002Ftd>\u003Ctd>Claude Desktop、Cursor、VS Code Copilot\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>Client\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>Host内のMCPプロトコルハンドラー\u003C\u002Ftd>\u003Ctd>Hostアプリケーションに組み込み\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>Server\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>MCPを通じてtools、resources、promptsを公開\u003C\u002Ftd>\u003Ctd>カスタムサーバー（これから構築するもの）\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>\u003Cstrong>Transport\u003C\u002Fstrong>\u003C\u002Ftd>\u003Ctd>ClientとServer間の通信レイヤー\u003C\u002Ftd>\u003Ctd>stdio、HTTP+SSE、Streamable HTTP\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>通信フローは次のように動作します：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Host\u003C\u002Fstrong>が起動し、設定された各サーバーに対してMCP \u003Cstrong>Client\u003C\u002Fstrong>を初期化します。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Client\u003C\u002Fstrong>が\u003Cstrong>Transport\u003C\u002Fstrong>（ローカルはstdio、リモートはHTTP）を介して\u003Cstrong>Server\u003C\u002Fstrong>に接続します。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Client\u003C\u002Fstrong>が\u003Ccode>initialize\u003C\u002Fcode>リクエストを送信し、プロトコルバージョンと機能をネゴシエートします。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Server\u003C\u002Fstrong>が利用可能な\u003Cstrong>tools\u003C\u002Fstrong>、\u003Cstrong>resources\u003C\u002Fstrong>、\u003Cstrong>prompts\u003C\u002Fstrong>を応答します。\u003C\u002Fli>\n\u003Cli>AIモデルが外部データやアクションを必要とする場合、\u003Cstrong>Client\u003C\u002Fstrong>が適切なサーバーメソッドを呼び出します。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Server\u003C\u002Fstrong>が操作を実行し、JSON-RPC 2.0メッセージで結果を返します。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3>Tools vs Resources vs Prompts\u003C\u002Fh3>\n\u003Cp>MCP serverは3種類の機能を公開できます：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Tools\u003C\u002Fstrong> はAIモデルが呼び出せる関数です。構造化された入力を受け取り、構造化された出力を返します。例：\u003Ccode>query_database(sql: string)\u003C\u002Fcode> や \u003Ccode>send_email(to: string, subject: string, body: string)\u003C\u002Fcode>。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Resources\u003C\u002Fstrong> はAIモデルが読み取れるデータです。URIで識別され、コンテンツを返します。例：\u003Ccode>file:\u002F\u002F\u002Fpath\u002Fto\u002Fdocument.md\u003C\u002Fcode> や \u003Ccode>postgres:\u002F\u002Flocalhost\u002Fmydb\u002Fusers\u003C\u002Fcode>。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Prompts\u003C\u002Fstrong> はサーバーが提供する再利用可能なプロンプトテンプレートです。AIがサーバーのドメインとやり取りする方法を標準化します。例：Jira MCP serverの\u003Ccode>summarize_ticket\u003C\u002Fcode>プロンプトテンプレート。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"typescript-mcp\">ステップバイステップ：TypeScriptでMCPサーバーを構築\u003C\u002Fh2>\n\u003Cp>SQLiteデータベースにクエリを実行するツールを提供する実用的なMCPサーバーを構築しましょう。これは一般的なユースケースで、AIモデルにアプリケーションデータへの安全な読み取り専用アクセスを提供します。\u003C\u002Fp>\n\u003Ch3>ステップ1：プロジェクトの初期化\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">mkdir mcp-sqlite-server &amp;&amp; cd mcp-sqlite-server\nnpm init -y\nnpm install @modelcontextprotocol\u002Fsdk better-sqlite3\nnpm install -D typescript @types\u002Fnode @types\u002Fbetter-sqlite3\nnpx tsc --init\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>tsconfig.json\u003C\u002Fcode>を設定：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"Node16\",\n    \"moduleResolution\": \"Node16\",\n    \"outDir\": \".\u002Fdist\",\n    \"rootDir\": \".\u002Fsrc\",\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"declaration\": true\n  },\n  \"include\": [\"src\u002F**\u002F*\"]\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>ステップ2：サーバーの実装\u003C\u002Fh3>\n\u003Cp>\u003Ccode>src\u002Findex.ts\u003C\u002Fcode>を作成：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-typescript\">import { McpServer } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fmcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol\u002Fsdk\u002Fserver\u002Fstdio.js\";\nimport Database from \"better-sqlite3\";\nimport { z } from \"zod\";\n\nconst db = new Database(process.env.DB_PATH || \".\u002Fdata.db\", {\n  readonly: true,\n});\n\nconst server = new McpServer({\n  name: \"sqlite-query\",\n  version: \"1.0.0\",\n});\n\nserver.tool(\n  \"query\",\n  \"SQLiteデータベースに対して読み取り専用SQLクエリを実行\",\n  {\n    sql: z.string().describe(\"実行するSQL SELECTクエリ\"),\n  },\n  async ({ sql }) =&gt; {\n    const normalized = sql.trim().toUpperCase();\n    if (!normalized.startsWith(\"SELECT\")) {\n      return {\n        content: [{ type: \"text\", text: \"エラー：SELECTクエリのみ許可されています。\" }],\n        isError: true,\n      };\n    }\n    try {\n      const rows = db.prepare(sql).all();\n      return {\n        content: [{ type: \"text\", text: JSON.stringify(rows, null, 2) }],\n      };\n    } catch (error) {\n      return {\n        content: [{ type: \"text\", text: `クエリエラー：${(error as Error).message}` }],\n        isError: true,\n      };\n    }\n  }\n);\n\nserver.tool(\n  \"list_tables\",\n  \"データベース内のすべてのテーブルとスキーマを一覧表示\",\n  {},\n  async () =&gt; {\n    const tables = db\n      .prepare(\n        `SELECT name, sql FROM sqlite_master\n         WHERE type='table' AND name NOT LIKE 'sqlite_%'\n         ORDER BY name`\n      )\n      .all();\n    return {\n      content: [{ type: \"text\", text: JSON.stringify(tables, null, 2) }],\n    };\n  }\n);\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>ステップ3：ビルドとローカルテスト\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">npx tsc\necho '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-03-26\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0\"}}}' | node dist\u002Findex.js\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>サーバーの機能を含むJSON-RPCレスポンスが表示されるはずです。\u003C\u002Fp>\n\u003Ch3>ステップ4：Pythonで同じサーバーを構築\u003C\u002Fh3>\n\u003Cp>Python開発者向けに、公式MCP Python SDKを使用した同等の実装を示します：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-python\">import sqlite3\nimport json\nfrom mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP(\"sqlite-query\")\nDB_PATH = \"data.db\"\n\n@mcp.tool()\ndef query(sql: str) -&gt; str:\n    \"\"\"SQLiteデータベースに対して読み取り専用SQLクエリを実行。\"\"\"\n    normalized = sql.strip().upper()\n    if not normalized.startswith(\"SELECT\"):\n        raise ValueError(\"SELECTクエリのみ許可されています。\")\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(sql)\n        rows = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(rows, indent=2, default=str)\n    finally:\n        conn.close()\n\n@mcp.tool()\ndef list_tables() -&gt; str:\n    \"\"\"データベース内のすべてのテーブルとスキーマを一覧表示。\"\"\"\n    conn = sqlite3.connect(DB_PATH)\n    conn.row_factory = sqlite3.Row\n    try:\n        cursor = conn.execute(\n            \"SELECT name, sql FROM sqlite_master \"\n            \"WHERE type='table' AND name NOT LIKE 'sqlite_%'\"\n        )\n        tables = [dict(row) for row in cursor.fetchall()]\n        return json.dumps(tables, indent=2)\n    finally:\n        conn.close()\n\nif __name__ == \"__main__\":\n    mcp.run(transport=\"stdio\")\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"claude-desktop\">Claude Desktopへの接続\u003C\u002Fh2>\n\u003Cp>Claude DesktopはMCPサーバーをネイティブにサポートしています。サーバーを接続するには、Claude Desktop設定ファイルを編集します：\u003C\u002Fp>\n\u003Cp>\u003Cstrong>macOS:\u003C\u002Fstrong> \u003Ccode>~\u002FLibrary\u002FApplication Support\u002FClaude\u002Fclaude_desktop_config.json\u003C\u002Fcode>\n\u003Cstrong>Windows:\u003C\u002Fstrong> \u003Ccode>%APPDATA%\\Claude\\claude_desktop_config.json\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\"\u002Fabsolute\u002Fpath\u002Fto\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \"\u002Fabsolute\u002Fpath\u002Fto\u002Fyour\u002Fdata.db\"\n      }\n    }\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Claude Desktopを再起動します。チャットインターフェースにハンマーアイコンが表示され、利用可能なMCPツールが示されます。「データベースにはどんなテーブルがありますか？」や「登録日順にトップ10のユーザーを表示して」のようにClaudeに質問すると、MCPサーバーを使用してデータベースに直接クエリを実行します。\u003C\u002Fp>\n\u003Ch2 id=\"cursor\">Cursorへの接続\u003C\u002Fh2>\n\u003Cp>CursorもMCPサーバーをサポートしています。プロジェクトルートの\u003Ccode>.cursor\u002Fmcp.json\u003C\u002Fcode>に設定を追加します：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  \"mcpServers\": {\n    \"sqlite-query\": {\n      \"command\": \"node\",\n      \"args\": [\".\u002Fdist\u002Findex.js\"],\n      \"env\": {\n        \"DB_PATH\": \".\u002Fdata.db\"\n      }\n    }\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>保存後、Cursorを再起動します。MCPツールがAIアシスタントパネルに表示され、コード生成やデバッグセッション中に呼び出すことができます。\u003C\u002Fp>\n\u003Ch2 id=\"mcp\">MCPサーバーのテストとデバッグ\u003C\u002Fh2>\n\u003Ch3>MCP Inspectorの使用\u003C\u002Fh3>\n\u003Cp>MCP Inspectorは公式デバッグツールです。サーバーとやり取りするためのWebインターフェースを提供します：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">npx @modelcontextprotocol\u002Finspector node dist\u002Findex.js\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>http:\u002F\u002Flocalhost:5173\u003C\u002Fcode>でブラウザインターフェースが開き、以下のことができます：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>登録されたすべてのtools、resources、promptsを表示\u003C\u002Fli>\n\u003Cli>カスタム入力でツールを呼び出し、レスポンスを検査\u003C\u002Fli>\n\u003Cli>JSON-RPCメッセージストリームをリアルタイムで監視\u003C\u002Fli>\n\u003Cli>不正なリクエストを送信してエラー処理をテスト\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>一般的なデバッグの問題\u003C\u002Fh3>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>問題\u003C\u002Fth>\u003Cth>原因\u003C\u002Fth>\u003Cth>解決策\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>サーバーがClaude Desktopに表示されない\u003C\u002Ftd>\u003Ctd>設定パスまたはJSON構文エラー\u003C\u002Ftd>\u003Ctd>JSONを検証し、絶対パスを確認\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>「Tool not found」エラー\u003C\u002Ftd>\u003Ctd>接続前にツールが登録されていない\u003C\u002Ftd>\u003Ctd>\u003Ccode>server.connect()\u003C\u002Fcode>を呼ぶ前にツールを登録\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>ツール呼び出しのタイムアウト\u003C\u002Ftd>\u003Ctd>進捗なしの長時間実行操作\u003C\u002Ftd>\u003Ctd>\u003Ccode>server.sendProgress()\u003C\u002Fcode>で進捗通知を追加\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>stderrの出力がプロトコルを破損\u003C\u002Ftd>\u003Ctd>console.logがstdoutに書き込み（stdioトランスポート）\u003C\u002Ftd>\u003Ctd>stdioではロギングに\u003Ccode>console.error()\u003C\u002Fcode>を使用\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>アイドル後の接続切断\u003C\u002Ftd>\u003Ctd>トランスポートのタイムアウト\u003C\u002Ftd>\u003Ctd>ハートビートの実装またはHTTPトランスポートを使用\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Ch2 id=\"\">本番環境のベストプラクティス\u003C\u002Fh2>\n\u003Col>\n\u003Cli>\n\u003Cp>\u003Cstrong>入力バリデーション\u003C\u002Fstrong>：常にツールの入力を検証しサニタイズします。TypeScriptではZodスキーマ、PythonではPydanticモデルを使用して厳密な型チェックを行います。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>デフォルトで読み取り専用\u003C\u002Fstrong>：読み取り専用アクセスから始めます。書き込み機能は本当に必要な場合にのみ追加し、破壊的な操作には必ず確認を求めます。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>エラー処理\u003C\u002Fstrong>：\u003Ccode>isError: true\u003C\u002Fcode>で構造化されたエラーメッセージを返します。内部スタックトレースやデータベース接続文字列を公開しないでください。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>ロギング\u003C\u002Fstrong>：すべてのツール呼び出しをタイムスタンプ、入力、実行時間とともにログに記録します。stdioトランスポート使用時はログにstderr（stdoutではなく）を使用します。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>レート制限\u003C\u002Fstrong>：制御されないAIループがバックエンドサービスを過負荷にすることを防ぐため、ツールごとのレート制限を実装します。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>タイムアウト\u003C\u002Fstrong>：すべてのツールハンドラーに実行タイムアウトを設定します。AIモデルが高コストなクエリをトリガーするツールを呼び出す可能性があります。インフラストラクチャを保護しましょう。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>環境の分離\u003C\u002Fstrong>：すべての設定に環境変数を使用します。データベースURL、APIキー、ファイルパスをハードコードしないでください。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>バージョニング\u003C\u002Fstrong>：MCPサーバーにセマンティックバージョニングを使用します。\u003Ccode>initialize\u003C\u002Fcode>ハンドシェイクにはバージョンネゴシエーションが含まれ、破壊的変更にはメジャーバージョンのバンプが必要です。\u003C\u002Fp>\n\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"faq\">FAQ\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Q：MCPとfunction callingの違いは何ですか？\u003C\u002Fstrong>\nA：Function calling（OpenAI、Anthropicなどが使用）は、各APIリクエストにインラインでツールを定義します。MCPはツール定義をスタンドアロンサーバーに外部化し、MCP互換ホストが発見して使用できるようにします。Function callingはリクエストごと、MCPはステートフルなセッションを持つ永続的なプロトコルです。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Q：Claude以外のモデルでMCPを使用できますか？\u003C\u002Fstrong>\nA：はい。MCPはオープンプロトコルです。OpenAI、Google DeepMind、Microsoftは2026年初頭からプラットフォームにMCPサポートを採用しています。MCP clientを実装するすべてのAIアプリケーションは、あらゆるMCP serverに接続できます。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Q：MCPはローカルツール専用ですか？\u003C\u002Fstrong>\nA：いいえ。stdioトランスポートはローカルサーバー向けに設計されていますが、HTTP+SSEおよびStreamable HTTPトランスポートはリモートMCPサーバーをサポートします。MCPサーバーをネットワーク経由でアクセス可能なクラウドサービスとしてデプロイできます。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Q：MCPは認証をどのように処理しますか？\u003C\u002Fstrong>\nA：リモートサーバーにはOAuth 2.0をサポートします。ローカルstdioサーバーはホストプロセスのセキュリティコンテキストを継承します。エンタープライズデプロイメントでは、MCPゲートウェイが認証と認可を一元化できます。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Q：MCPサーバーを構築するにはどの言語を使用できますか？\u003C\u002Fstrong>\nA：公式SDKはTypeScript、Python、Java、Kotlin、C#、Swiftで利用できます。コミュニティSDKにはRust、Go、Ruby、PHPが含まれます。プロトコルは言語に依存しません。stdioまたはHTTPを介してJSON-RPCの読み書きができる言語であれば、MCPサーバーを実装できます。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Q：ホストを再起動せずにMCPサーバーを更新する方法は？\u003C\u002Fstrong>\nA：MCPは機能変更通知をサポートしています。サーバーのツールが変更された場合、サーバーは\u003Ccode>notifications\u002Ftools\u002Flist_changed\u003C\u002Fcode>メッセージを送信し、clientにツールリストの再取得を促します。stdioサーバーの場合、通常ホストの再起動が必要です。HTTPサーバーはホストの再起動なしで更新できます。\u003C\u002Fp>\n","ja","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:38.722633Z","TypeScriptとPythonでMCPサーバーを構築するステップバイステップチュートリアル。Claude DesktopやCursorに接続し、MCP Inspectorでテストし、本番環境にデプロイ。","mcp server チュートリアル",null,"index, follow",[21,26],{"id":22,"name":23,"slug":24,"created_at":25},"c0000000-0000-0000-0000-000000000008","AI","ai","2026-03-28T10:44:21.513630Z",{"id":27,"name":28,"slug":29,"created_at":25},"c0000000-0000-0000-0000-000000000002","TypeScript","typescript","エンジニアリング",[32,38,44],{"id":33,"title":34,"slug":35,"excerpt":36,"locale":12,"category_name":30,"published_at":37},"d0000000-0000-0000-0000-000000000671","2026年、なぜBaliは東南アジアのインパクトテックハブになりつつあるのか","naze-bali-2026-tonan-ajia-inpakuto-tekku-habu","Baliは東南アジアのスタートアップエコシステムで第16位にランクイン。Web3ビルダー、AIサステナビリティスタートアップ、エコトラベルテック企業が集積し、この島は地域のインパクトテック首都としてのニッチを確立しつつあります。","2026-03-28T10:44:49.081179Z",{"id":39,"title":40,"slug":41,"excerpt":42,"locale":12,"category_name":30,"published_at":43},"d0000000-0000-0000-0000-000000000670","ASEANデータ保護パッチワーク：開発者のためのコンプライアンスチェックリスト","asean-deta-hogo-pacchiwaku-kaihatsusha-kompuraiansu-chekkurisuto","7つのASEAN諸国が包括的なデータ保護法を有し、それぞれ異なる同意モデル、ローカライゼーション要件、罰則構造を持っています。マルチカントリーアプリケーションを構築する開発者のための実用的なコンプライアンスチェックリストです。","2026-03-28T10:44:49.074910Z",{"id":45,"title":46,"slug":47,"excerpt":48,"locale":12,"category_name":30,"published_at":49},"d0000000-0000-0000-0000-000000000669","Indonesiaの290億ドルデジタルトランスフォーメーション：ソフトウェア企業のチャンス","indonesia-290oku-doru-dejitaru-toransufomeshon-sofutowea-kigyo-chansu","IndonesiaのITサービス市場は2026年に290.3億ドルに達すると予測されており、2025年の243.7億ドルから増加します。クラウドインフラ、AI、電子商取引、データセンターが東南アジアで最も速い成長を牽引しています。","2026-03-28T10:44:49.055660Z",{"id":13,"name":51,"slug":52,"bio":53,"photo_url":18,"linkedin":18,"role":54,"created_at":55,"updated_at":55},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]