Files
servers/src/everything/transports/sse.ts
cliffhall 18ef6aa69b [WIP] Refactor everything server to be more modular and use recommended APIs.
Adding Trigger Elicitation Request and Get Roots List tools

* Updated architecture.md

* Added roots.ts
  - tracks roots by sessionId
  - setRootsListChangedHandler
    - listens for roots changed notification from the client
      - updates the roots map by sessionId
      - sends log notification or error to the client

* In server/index.ts
  - import setRootsListChangedHandler
  - in clientConnected callback
    - call setRootsListChangedHandler passing server and sessionId

* In sse.ts, stdio.ts, and streamableHttp.ts
  - receive clientConnected from server factory
  - call clientConnected when server is connected to transport
* Added get-roots-list.ts
  - registerGetRootsListTool
    - Registers the 'get-roots-list' tool with the given MCP server.

* Added trigger-elicitation-request.ts
  - registerTriggerElicitationRequestTool
    - registered tool sends an elicitation request that exercises all supported field types

* In tools/index.ts
  - imports registerTriggerElicitationRequestTool and registerGetRootsListTool
  - in registerTools
    - call registerTriggerElicitationRequestTool and registerGetRootsListTool, passing server
2025-12-11 20:25:37 -05:00

70 lines
2.1 KiB
TypeScript

import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
import { createServer } from "../server/index.js";
import cors from "cors";
console.error("Starting SSE server...");
const app = express();
app.use(
cors({
origin: "*", // use "*" with caution in production
methods: "GET,POST",
preflightContinue: false,
optionsSuccessStatus: 204,
})
); // Enable CORS for all routes so Inspector can connect
const transports: Map<string, SSEServerTransport> = new Map<
string,
SSEServerTransport
>();
app.get("/sse", async (req, res) => {
let transport: SSEServerTransport;
const { server, clientConnected, cleanup } = createServer();
if (req?.query?.sessionId) {
const sessionId = req?.query?.sessionId as string;
transport = transports.get(sessionId) as SSEServerTransport;
console.error(
"Client Reconnecting? This shouldn't happen; when client has a sessionId, GET /sse should not be called again.",
transport.sessionId
);
} else {
// Create and store transport for new session
transport = new SSEServerTransport("/message", res);
transports.set(transport.sessionId, transport);
// Connect server to transport
await server.connect(transport);
const sessionId = transport.sessionId;
clientConnected(sessionId);
console.error("Client Connected: ", sessionId);
// Handle close of connection
server.server.onclose = async () => {
const sessionId = transport.sessionId;
console.error("Client Disconnected: ", sessionId);
transports.delete(sessionId);
await cleanup(sessionId);
};
}
});
app.post("/message", async (req, res) => {
const sessionId = req?.query?.sessionId as string;
const transport = transports.get(sessionId);
if (transport) {
console.error("Client Message from", sessionId);
await transport.handlePostMessage(req, res);
} else {
console.error(`No transport found for sessionId ${sessionId}`);
}
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.error(`Server is running on port ${PORT}`);
});