Skip to content

Latest commit

 

History

History
504 lines (348 loc) · 13.8 KB

File metadata and controls

504 lines (348 loc) · 13.8 KB

SSE 伺服器

SSE(Server Sent Events)是一種伺服器到客戶端的串流標準,允許伺服器透過 HTTP 推送即時更新給客戶端。這對於需要即時更新的應用程式特別有用,例如聊天應用、通知或即時資料串流。此外,您的伺服器可以同時被多個客戶端使用,因為它可以部署在雲端等任何地方。

概覽

本課程將介紹如何建立與使用 SSE 伺服器。

學習目標

完成本課程後,您將能夠:

  • 建立 SSE 伺服器。
  • 使用 Inspector 偵錯 SSE 伺服器。
  • 使用 Visual Studio Code 使用 SSE 伺服器。

SSE 運作原理

SSE 是兩種支援的傳輸類型之一。您之前的課程已經看過第一種 stdio 的使用方式。兩者的差異如下:

  • SSE 需要您處理兩件事:連線與訊息。
  • 由於這是可以部署在任何地方的伺服器,您需要在使用 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();
    ```

    有兩個方法幫助我們從一般的網頁伺服器轉成支援 SSE 的網頁伺服器:

    - `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 /sse route that instantiates a transport of type SSE and ends up calling connect on the MCP server.
  • A /messages route that takes care of incoming messages.

Python

app = Starlette(
    routes=[
        Mount('/', app=mcp.sse_app()),
    ]
)

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 /sse and /messages route 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

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers"""
    return a + b

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";

// Create an MCP server
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("No transport found for sessionId");
  }
});

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,
      },
    ],
  };
});

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:
    """Add two numbers"""
    return a + b

# Mount the SSE server to the existing ASGI server
app = Starlette(
    routes=[
        Mount('/', app=mcp.sse_app()),
    ]
)

.NET

  1. 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("Add two numbers together.")]
    public async Task<string> AddNumbers(
        [Description("The first number")] int a,
        [Description("The second number")] int b)
    {
        return (a + b).ToString();
    }

}

Here we've added the following:

  • Created a class Tools with the decorator McpServerToolType.
  • Defined a tool AddNumbers by decorating the method with McpServerTool. We've also provided parameters and an implementation.
  1. Let's leverage the Tools class 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:

  1. Run the server

    TypeScript

    tsx && node ./build/server-sse.ts

    Python

    uvicorn server:app

    Note how we use the executable uvicorn that's installed when we typed pip install "mcp[cli]". Typing server:app means we're trying to run a file server.py and for it to have a Starlette instance called app.

    .NET

    dotnet run

    This should start the server. To interface with it you need a new terminal.

  2. 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.

    npx @modelcontextprotocol/inspector --cli http://localhost:8000/sse --method tools/list

    執行 inspector 在所有執行環境中看起來都相同。請注意,我們不是傳入伺服器的啟動路徑和指令,而是傳入伺服器運行的 URL,並且指定 /sse 路由。

-2- 試用工具

在下拉選單中選擇 SSE,並填入伺服器運行的 URL,例如 http:localhost:4321/sse。接著點擊「Connect」按鈕。和之前一樣,選擇列出工具、選擇工具並提供輸入值。您應該會看到如下結果:

SSE Server running in inspector

太好了,您已經能使用 inspector,接下來讓我們看看如何使用 Visual Studio Code。

作業

嘗試為您的伺服器增加更多功能。參考此頁面來新增一個呼叫 API 的工具。伺服器的樣貌由您決定。祝您玩得愉快 :)

解答

解答 這裡有一個可行的解答範例,包含可運作的程式碼。

重要重點

本章節的重點如下:

  • SSE 是繼 stdio 之後第二種支援的傳輸方式。
  • 支援 SSE 需要使用網頁框架管理傳入的連線與訊息。
  • 您可以使用 Inspector 和 Visual Studio Code 來使用 SSE 伺服器,就像 stdio 伺服器一樣。請注意 stdio 與 SSE 之間有些微差異。SSE 需要您先啟動伺服器,然後再執行 inspector 工具。使用 inspector 時,也需要指定 URL。

範例

其他資源

接下來

免責聲明
本文件係使用 AI 翻譯服務 Co-op Translator 進行翻譯。雖然我們致力於確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議採用專業人工翻譯。我們不對因使用本翻譯而產生的任何誤解或誤釋負責。