出典: https://github.com/microsoft/mcp-for-beginners フォーク元リポジトリ: https://github.com/microsoft/mcp-for-beginners (Microsoft) ライセンス: MIT License
SSEサーバー¶
SSE(Server Sent Events)は、サーバーからクライアントへのストリーミングの標準であり、サーバーがHTTPを通じてリアルタイムの更新をクライアントにプッシュすることを可能にします。これはチャットアプリケーション、通知、リアルタイムデータフィードなど、ライブアップデートが必要なアプリケーションに特に有用です。また、サーバーはクラウドなどのどこかで実行されるサーバー上に存在するため、複数のクライアントが同時に利用できます。
概要¶
このレッスンでは、SSEサーバーの構築と利用方法について説明します。
学習目標¶
このレッスンの終わりまでに、以下ができるようになります:
- SSEサーバーを構築する。
- Inspectorを使ってSSEサーバーをデバッグする。
- Visual Studio Codeを使ってSSEサーバーを利用する。
SSEの仕組み¶
SSEはサポートされている2つのトランスポートタイプのうちの一つです。前のレッスンで使ったstdioが最初のタイプでした。違いは以下の通りです:
- SSEでは接続とメッセージの2つを扱う必要があります。
- このサーバーはどこにでも存在できるため、InspectorやVisual Studio Codeのようなツールの使い方にもそれが反映されます。つまり、サーバーの起動方法を指定する代わりに、接続を確立できるエンドポイントを指定します。以下の例コードを参照してください。
TypeScript¶
app.get("/sse", async (_: Request, res: Response) => {
const transport = new SSEServerTransport('/messages', res);
transports[transport.sessionId] = transport;
res.on("close", () => {
delete transports[transport.sessionId];
});
await server.connect(transport);
});
app.post("/messages", async (req: Request, res: Response) => {
const sessionId = req.query.sessionId as string;
const transport = transports[sessionId];
if (transport) {
await transport.handlePostMessage(req, res);
} else {
res.status(400).send('No transport found for sessionId');
}
});
上記のコードでは:
/sseがルートとして設定されています。このルートにリクエストが来ると、新しいトランスポートインスタンスが作成され、サーバーはこのトランスポートを使って接続します。/messagesは受信メッセージを処理するルートです。
Python¶
mcp = FastMCP("My App")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
# Mount the SSE server to the existing ASGI server
app = Starlette(
routes=[
Mount('/', app=mcp.sse_app()),
]
)
上記のコードでは:
- ASGIサーバーのインスタンス(特にStarletteを使用)を作成し、デフォルトルート
/にマウントしています。
裏側では、/sseと/messagesのルートがそれぞれ接続とメッセージの処理に設定されています。その他の機能追加はstdioサーバーと同様に行われます。
.NET¶
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddMcpServer()
.WithTools<Tools>();
builder.Services.AddHttpClient();
var app = builder.Build();
app.MapMcp();
```
WebサーバーからSSE対応のWebサーバーに移行するのに役立つ2つのメソッドがあります:
- `AddMcpServer`:機能を追加します。
- `MapMcp`:`/SSE`や`/messages`のようなルートを追加します。
Now that we know a little bit more about SSE, let's build an SSE server next.
Exercise: Creating an SSE Server¶
To create our server, we need to keep two things in mind:
- We need to use a web server to expose endpoints for connection and messages.
- Build our server like we normally do with tools, resources and prompts when we were using stdio.
-1- Create a server instance¶
To create our server, we use the same types as with stdio. However, for the transport, we need to choose SSE.
TypeScript¶
import { Request, Response } from "express";
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
const server = new McpServer({
name: "example-server",
version: "1.0.0"
});
const app = express();
const transports: {[sessionId: string]: SSEServerTransport} = {};
In the preceding code we've:
- Created a server instance.
- Defined an app using the web framework express.
- Created a transports variable that we will use to store incoming connections.
Python¶
from starlette.applications import Starlette
from starlette.routing import Mount, Host
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My App")
In the preceding code we've:
- Imported the libraries we're going to need with Starlette (an ASGI framework) being pulled in.
- Created an MCP server instance
mcp.
.NET¶
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddMcpServer();
builder.Services.AddHttpClient();
var app = builder.Build();
// TODO: add routes
At this point, we've:
- Created a web app
- Added support for MCP features through
AddMcpServer.
Let's add the needed routes next.
-2- Add routes¶
Let's add routes next that handle the connection and incoming messages:
TypeScript¶
app.get("/sse", async (_: Request, res: Response) => {
const transport = new SSEServerTransport('/messages', res);
transports[transport.sessionId] = transport;
res.on("close", () => {
delete transports[transport.sessionId];
});
await server.connect(transport);
});
app.post("/messages", async (req: Request, res: Response) => {
const sessionId = req.query.sessionId as string;
const transport = transports[sessionId];
if (transport) {
await transport.handlePostMessage(req, res);
} else {
res.status(400).send('No transport found for sessionId');
}
});
app.listen(3001);
In the preceding code we've defined:
- An
/sseroute that instantiates a transport of type SSE and ends up callingconnecton the MCP server. - A
/messagesroute that takes care of incoming messages.
Python¶
In the preceding code we've:
- Created an ASGI app instance using the Starlette framework. As part of that we passes
mcp.sse_app()to it's list of routes. That ends up mounting an/sseand/messagesroute on the app instance.
.NET¶
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddMcpServer();
builder.Services.AddHttpClient();
var app = builder.Build();
app.MapMcp();
We've added one line of code at the end add.MapMcp() this means we now have routes /SSE and /messages.
Let's add capabilties to the server next.
-3- Adding server capabilities¶
Now that we've got everything SSE specific defined, let's add server capabilities like tools, prompts and resources.
TypeScript¶
server.tool("random-joke", "A joke returned by the chuck norris api", {},
async () => {
const response = await fetch("https://api.chucknorris.io/jokes/random");
const data = await response.json();
return {
content: [
{
type: "text",
text: data.value
}
]
};
}
);
Here's how you can add a tool for example. This specific tool creates a tool call "random-joke" that calls a Chuck Norris API and returns a JSON response.
Python¶
Now your server has one tool.
TypeScript¶
// server-sse.ts
import { Request, Response } from "express";
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
// MCPサーバーを作成
const server = new McpServer({
name: "example-server",
version: "1.0.0",
});
const app = express();
const transports: { [sessionId: string]: SSEServerTransport } = {};
app.get("/sse", async (_: Request, res: Response) => {
const transport = new SSEServerTransport("/messages", res);
transports[transport.sessionId] = transport;
res.on("close", () => {
delete transports[transport.sessionId];
});
await server.connect(transport);
});
app.post("/messages", async (req: Request, res: Response) => {
const sessionId = req.query.sessionId as string;
const transport = transports[sessionId];
if (transport) {
await transport.handlePostMessage(req, res);
} else {
res.status(400).send("sessionIdに対応するトランスポートが見つかりません");
}
});
server.tool("random-joke", "chuck norris APIから取得したジョーク", {}, async () => {
const response = await fetch("https://api.chucknorris.io/jokes/random");
const data = await response.json();
return {
content: [
{
type: "text",
text: data.value,
},
],
};
});
app.listen(3001);
Python¶
from starlette.applications import Starlette
from starlette.routing import Mount, Host
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My App")
@mcp.tool()
def add(a: int, b: int) -> int:
"""2つの数字を足す"""
return a + b
# 既存のASGIサーバーにSSEサーバーをマウント
app = Starlette(
routes=[
Mount('/', app=mcp.sse_app()),
]
)
.NET¶
- Let's create some tools first, for this we will create a file Tools.cs with the following content:
using System.ComponentModel;
using System.Text.Json;
using ModelContextProtocol.Server;
namespace server;
[McpServerToolType]
public sealed class Tools
{
public Tools()
{
}
[McpServerTool, Description("2つの数字を足し合わせます。")]
public async Task<string> AddNumbers(
[Description("最初の数字")] int a,
[Description("2番目の数字")] int b)
{
return (a + b).ToString();
}
}
Here we've added the following:
- Created a class
Toolswith the decoratorMcpServerToolType. -
Defined a tool
AddNumbersby decorating the method withMcpServerTool. We've also provided parameters and an implementation. -
Let's leverage the
Toolsclass we just created:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddMcpServer()
.WithTools<Tools>();
builder.Services.AddHttpClient();
var app = builder.Build();
app.MapMcp();
We've added a call to WithTools that specifies Tools as the class containing the tools. That's it, we're ready.
Great, we have a server using SSE, let's take it for a spin next.
Exercise: Debugging an SSE Server with Inspector¶
Inspector is a great tool that we saw in a previous lesson Creating your first server. Let's see if we can use the Inspector even here:
-1- Running the inspector¶
To run the inspector, you first must have an SSE server running, so let's do that next:
-
Run the server
TypeScript¶
Python¶
Note how we use the executable
uvicornthat's installed when we typedpip install "mcp[cli]". Typingserver:appmeans we're trying to run a fileserver.pyand for it to have a Starlette instance calledapp..NET¶
This should start the server. To interface with it you need a new terminal.
-
Run the inspector
![NOTE] Run this in a separate terminal window than the server is running in. Also note, you need to adjust the below command to fit the URL where your server runs.
Inspectorの実行はすべてのランタイムで同じように見えます。サーバーのパスや起動コマンドを渡す代わりに、サーバーが動作しているURLと
/sseルートを指定している点に注目してください。
-2- ツールの試用¶
ドロップリストからSSEを選択し、サーバーが動作しているURL(例: http:localhost:4321/sse)を入力してサーバーに接続します。前回と同様にツールの一覧を取得し、ツールを選択して入力値を提供してください。以下のような結果が表示されるはずです:

素晴らしいです。Inspectorでの操作ができました。次にVisual Studio Codeでの操作方法を見てみましょう。
課題¶
サーバーにさらに機能を追加してみてください。例えば、このページを参考にAPIを呼び出すツールを追加するなど、サーバーの形は自由に決めて構いません。楽しんでください :)
解答例¶
解答例 動作するコードを含む一例です。
まとめ¶
この章の重要なポイントは以下の通りです:
- SSEはstdioに次ぐ2番目のサポートされているトランスポートです。
- SSEをサポートするには、Webフレームワークを使って接続とメッセージの管理が必要です。
- InspectorやVisual Studio Codeを使ってstdioサーバーと同様にSSEサーバーを利用できます。ただしstdioとSSEでは少し違いがあります。SSEではサーバーを別途起動してからInspectorツールを実行します。またInspectorツールではURLを指定する必要があります。
サンプル¶
追加リソース¶
次に進む¶
免責事項:
本書類はAI翻訳サービス「Co-op Translator」を使用して翻訳されました。正確性を期しておりますが、自動翻訳には誤りや不正確な部分が含まれる可能性があります。原文の言語によるオリジナル文書が正式な情報源とみなされるべきです。重要な情報については、専門の人間による翻訳を推奨します。本翻訳の利用により生じた誤解や誤訳について、当方は一切の責任を負いかねます。