皆が便利だと言っているMCPについて今更ながら入門したので備忘録として残しておく(自分用メモ)
MCPとは?なぜ話題になっている?
MCP(Model Context Protocol)とは、LLM(大規模言語モデル)と外部ツール・データソースをつなぐための統一的なプロトコルを提供する仕組み。
AIエージェントとあらゆる外部システムをつなぐ「USB-Cポート」のようなものをイメージすると良い。
通常、AIの外部連携(ファイル、リポジトリ、APIアクセスなど)を行うにはケースごとに専用スクリプトが必要になるが、MCPによって統一インターフェースを介した拡張が可能になる。
これにより、エディタがAIに「実際に外部の何かを実行させる」ことが格段に容易になり、たとえばファイルの読み書き、DBクエリ、API呼び出し、テストの実行、デプロイ、CI/CDとの連携などが、MCP対応のプラグイン(MCPサーバ)として一元化される。
MCPの基本
MCPと一言で言っても、
というのがある
MCP ClientはCursorやClaude Desktop、Clineのようなものを指す。
また、自前でMCP Clientを書くこともできる
構成的にはざっくり以下のようになっている。構成としてはとてもシンプルで分かりやすい。
MCP Client <---> MCP Server
世間一般的に言われているMCPというのは大抵MCP Serverのことを指しており、ここでCursorなどのMCP Client(AIエージェント)から渡ってきた指示をもとにMCP Server側であらゆることを実行することになる。
(この "あらゆること" に様々な可能性が詰められており、今話題になっているという形)
ちなみにMCP関連については以下を見ておけば最低限の理解は可能そう。
また、Cursorで使う場合はCursor側が出しているドキュメントを参照
Clineで使う場合はClineが出しているドキュメントを参照
MCPサーバーの最小限の実装例
MCP Serverの基本的な挙動を理解するには、まずは「どんなコードが必要なのか」をざっくり見てみるのが早い。以下のサンプルでは、外部ライブラリを使わず、標準入出力(STDIN/STDOUT)を介してMCPクライアント(Cursorなど)と通信する仕組みを、約100行程度で実装している。
「JSON-RPC 2.0を扱う」「MCPハンドシェイクを処理する」「ツール呼び出しに応答する」という3点が押さえられれば、ひとまず動くMCPサーバーになるのが分かると思う。
※TypeScriptで書いているので、実際に動かす際はbuildして js にする。node /foo/bar/simple-mcp-server.js という形でCursor側ではプロセスを起動して動かす想定
/** * シンプルなMCPサーバー (外部ライブラリ未使用版) * * このサーバーはCursorなどのMCPクライアントと標準入出力(STDIN/STDOUT)を通じて * JSON-RPC形式で通信し、MCPの最低限の機能を提供します。 * * 今回は「echoツール」を1つだけ実装し、ユーザーが送った文字列をそのまま返すサンプルです。 */ import * as readline from 'readline'; /** MCPツールの出力コンテンツの型定義 */ interface ToolContent { type: string; text?: string; } /** MCPツールの基本構造 */ interface MCPTool { name: string; // ツール名 description: string; // ツールの簡単な説明 inputSchema: object; // 入力パラメータのJSON Schema execute: (args: any) => ToolContent | ToolContent[]; } /** 例: Echoツール - 入力文字列をそのまま返す */ const echoTool: MCPTool = { name: "echo", description: "指定されたメッセージをそのまま返すツール", inputSchema: { type: "object", properties: { message: { type: "string", description: "エコーしたい文字列" } }, required: ["message"] }, execute: (args) => { const msg = args.message; return { type: "text", text: `Echo: ${msg}` }; } }; /** 提供するツールの一覧 */ const tools: MCPTool[] = [ echoTool ]; /** JSON-RPCレスポンスを標準出力に送るユーティリティ関数 */ function sendResponse(obj: any) { process.stdout.write(JSON.stringify(obj) + "\n"); } /** JSON-RPCエラー形式で返すユーティリティ関数 */ function sendError(id: any, code: number, message: string) { const errorResponse = { jsonrpc: "2.0", id: id, error: { code, message } }; sendResponse(errorResponse); } /** 標準入力(STDIN)を行単位で読むための設定 */ const rl = readline.createInterface({ input: process.stdin }); /** * 標準入力で受け取ったJSON-RPCリクエストをパースし、 * MCPプロトコルに沿った処理を行う。 */ rl.on('line', (data: string) => { if (!data.trim()) return; // 空行は無視 let request; try { request = JSON.parse(data); } catch (err) { // JSONのパースに失敗した場合は、-32700(Parse error)を返す sendError(null, -32700, "Parse error"); return; } // JSON-RPC 2.0のフォーマットを満たしているか確認 if (request.jsonrpc !== "2.0" || !request.method) { sendError(request.id ?? null, -32600, "Invalid Request"); return; } const method = request.method; const id = request.id; const isNotification = (id === null || id === undefined); // 1. 初期化ハンドシェイク (initialize → initialized) if (method === "initialize") { // クライアントからプロトコルバージョンを受け取り、サーバーの情報などを返す const clientProtocol = request.params?.protocolVersion; const protocolVersion = (typeof clientProtocol === "string") ? clientProtocol : "2025-03-08"; // デフォルト、あるいは最新の日時などを適当に設定 const initResponse = { jsonrpc: "2.0", id: id, result: { protocolVersion: protocolVersion, serverInfo: { name: "simple-mcp-server", version: "0.1.0" }, // このサーバーが提供する機能の概要 (toolsをサポート) capabilities: { tools: {} } } }; sendResponse(initResponse); return; } if (method === "initialized" || method === "notifications/initialized") { // クライアント側がサーバーの返答を受け取り、初期化完了したことを通知する // ここでは特にレスポンスは返さない return; } // 2. (任意) キャンセル通知など if (method === "cancelled") { // ツール呼び出し途中でクライアントがキャンセルを発行した場合など // 本サンプルでは何もしない return; } // 3. ツール一覧の取得 if (method === "tools/list") { // 現在サーバーに登録されているツール一覧を返す const toolList = tools.map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })); const listResponse = { jsonrpc: "2.0", id: id, result: { tools: toolList } }; sendResponse(listResponse); return; } // リソースやプロンプトなどは空リストで返す if (method === "resources/list") { sendResponse({ jsonrpc: "2.0", id: id, result: { resources: [] } }); return; } if (method === "prompts/list") { sendResponse({ jsonrpc: "2.0", id: id, result: { prompts: [] } }); return; } // 4. ツール呼び出し要求 (tools/call) if (method === "tools/call") { const params = request.params; if (!params || typeof params !== "object") { sendError(id, -32602, "Invalid parameters"); return; } const toolName = params.name; const args = params.arguments; if (typeof toolName !== "string" || args === undefined) { sendError(id, -32602, "Invalid parameters: missing tool name or arguments"); return; } // ツール名に対応するツールを探す const tool = tools.find(t => t.name === toolName); if (!tool) { sendError(id, -32601, `Method not found: tool '${toolName}' is not available`); return; } // ツールのinputSchemaで必須フィールドがあればチェック const schema: any = tool.inputSchema; if (schema.required && Array.isArray(schema.required)) { for (const field of schema.required) { if (!(field in args)) { sendError(id, -32602, `Missing required parameter: '${field}'`); return; } } } // ツールのexecuteを呼び出す try { const resultContent = tool.execute(args); // MCPプロトコル上、contentは配列で返す必要がある const contentArray: ToolContent[] = Array.isArray(resultContent) ? resultContent : [ resultContent ]; // 結果をJSON-RPC形式で返却 const callResponse = { jsonrpc: "2.0", id: id, result: { content: contentArray } }; sendResponse(callResponse); } catch (error) { // ツール実行中に予期せぬエラーが発生した場合 sendError(id, -32603, "Internal error during tool execution"); } return; } // 5. 上記以外のメソッドが呼ばれた場合 if (!isNotification) { sendError(id, -32601, `Method not found: ${method}`); } }); /** MCPクライアントが切断したらプロセスを終了 */ rl.on('close', () => { process.exit(0); });
実際にCursorにMCP Serverとして登録して実行したものが以下。
(TestServerという名称で登録している)

コード解説
以上が、「最もシンプルなMCPサーバー実装例」となる。
標準入出力でJSON-RPCを読み書きする
- 冒頭で 
readlineを使ってprocess.stdinを行単位で読み込み、process.stdout.writeによってレスポンスを返している。 - MCPクライアント(例えばCursor)がこのプロセスを起動し、標準入出力を通じてメッセージを送受信するイメージ。
 
- 冒頭で 
 echoツールのみを実装
toolsという配列に、echoという名前のツールを1つだけ追加している。tools/listが呼ばれると、このツール情報(名前、説明、inputSchema)が返される。tools/callに{ name: "echo", arguments: { message: "Hello" } }のような引数が渡されると、execute関数が呼ばれて"Echo: Hello"というテキストを返す。
エラー処理
最低限の機能
- リソース一覧(
resources/list)やプロンプト一覧(prompts/list)は空を返すだけなので、特に機能としては実装していない。 - もう少し拡張したいときは同様に
if (method===\"resources/list\") { ... }の中で色々書けばOK。 
- リソース一覧(
 
このように、MCPサーバーの最小限の実装は「JSON-RPCメソッドの受け取りと分岐処理」を自力で書くだけでも可能だし、そこに外部ライブラリや外部API呼び出しを加えていけば、いわゆる“MCPプラグイン”が作れるようになるわけだ。
今回は自身の理解のためにも最低限の実装を動かしてみたが、基本的にMCP Serverを自前で実装するケースはなく、
公式のNode向けライブラリ(@modelcontextprotocol/sdk)などを使って作成していくケースが多いかと思われる。
また今回の最小限の実装では標準入出力のみの対応だが、公式ライブラリではSSEなどにも対応していたり、OAuth認証やZodによるスキーマ検証なども入っている。
ただ逆に、JSON-RPC 2.0の仕様に基づいていればライブラリがなくても実装できるため、好きな言語でMCPサーバーを書くことが可能であるとも言える。
こういった、ある程度開発者に自由がある環境というのは、今後MCPが盛り上がっていきそうな要因の一つになるかなと思う。
(どの言語でも実装できるメリットとして他にも、GoやRustなどで実装してシングルバイナリで実行できる形で配布すれば、ユーザー側でNode.jsやPython環境を用意してもらう必要もない)
MCP Marketplaceの存在(Cline)
ClineにはMCP Marketplaceという存在があるらしい。 VSCodeのExtension marketplaceのようなもの。
リリースするにはGitHub経由で提出して審査を受ける必要がある。
ちなみにこのMCP Marketplaceに登録するにはGitHubのissueに提出して承認を受ける必要がある
— Yuki Shin🎥💫AITuber配信サービス作ってます (@shinshin86) March 8, 2025
まあ、確かにこれはちゃんと審査されたものでないと怖いよねhttps://t.co/4xjsrCIzvM
その他、MCP Serverがまとめられているサイト
他にもこういうサイトがあるみたい
MCP Serverはやろうと思えば様々なことができてしまうが、外部のMCPを使う場合の安全性の線引がどこらへんにあるのか、私はまだ温度感を掴めていない。
MCPが話題になっている理由が分かった気がした
MCP Clinetが操縦席だとすると、実際の実務を行なうのがMCP Serverとなるだろうか。たしかにこの仕組みを使えばAIに指示を出してやれることが一気に増えるし、可能性も溢れていそう。
MCPが話題になっているのが少しだけ分かった気がした。