Skip to content

Commit f7bf48a

Browse files
committed
docs: fix Typescriot walkthrough
1 parent 3d8a60c commit f7bf48a

1 file changed

Lines changed: 92 additions & 56 deletions

File tree

docs/src/guide/server/typescript/walkthrough.md

Lines changed: 92 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -45,85 +45,121 @@ npm install @modelcontextprotocol/sdk @mcp-ui/server
4545

4646
The `@modelcontextprotocol/sdk` package provides the core functionality for creating an MCP server, while `@mcp-ui/server` includes helpers specifically for creating UI resources.
4747

48-
## 3. Create an MCP Tool
48+
## 3. Set up the MCP Server Handler
4949

5050
In MCP, tools are functions that the client can invoke. For this example, we'll create a tool that returns a `UIResource`.
51+
Now, let's create an MCP server instance and an endpoint to handle MCP requests using a streaming transport. This allows for more complex, stateful interactions.
5152

52-
Create a new file, `tools.ts`, and add the following code:
53+
Modify your `server.ts` file to include the following:
5354

5455
```typescript
55-
import { Tool, ToolResponse } from '@modelcontextprotocol/sdk';
56+
import express from 'express';
57+
import cors from 'cors';
58+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
59+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
60+
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
5661
import { createUIResource } from '@mcp-ui/server';
62+
import { randomUUID } from 'crypto';
63+
64+
const app = express();
65+
const port = 3000;
66+
67+
app.use(cors({
68+
origin: '*',
69+
exposedHeaders: ['Mcp-Session-Id'],
70+
allowedHeaders: ['Content-Type', 'mcp-session-id'],
71+
}));
72+
app.use(express.json());
73+
74+
// Map to store transports by session ID
75+
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
5776

58-
export class GreetTool extends Tool {
59-
constructor() {
60-
super({
61-
name: 'greet',
62-
description: 'A simple tool that returns a UI resource',
63-
inputSchema: {
64-
type: 'object',
65-
properties: {},
77+
// Handle POST requests for client-to-server communication.
78+
app.post('/mcp', async (req, res) => {
79+
const sessionId = req.headers['mcp-session-id'] as string | undefined;
80+
let transport: StreamableHTTPServerTransport;
81+
82+
if (sessionId && transports[sessionId]) {
83+
// A session already exists; reuse the existing transport.
84+
transport = transports[sessionId];
85+
} else if (!sessionId && isInitializeRequest(req.body)) {
86+
// This is a new initialization request. Create a new transport.
87+
transport = new StreamableHTTPServerTransport({
88+
sessionIdGenerator: () => randomUUID(),
89+
onsessioninitialized: (sid) => {
90+
transports[sid] = transport;
91+
console.log(`MCP Session initialized: ${sid}`);
6692
},
6793
});
68-
}
6994

70-
async call(serverContext: any): Promise<ToolResponse> {
71-
const uiResource = createUIResource({
72-
uri: 'ui://greeting',
73-
content: {
74-
type: 'externalUrl',
75-
iframeUrl: 'https://example.com',
76-
},
77-
encoding: 'text',
95+
// Clean up the transport from our map when the session closes.
96+
transport.onclose = () => {
97+
if (transport.sessionId) {
98+
console.log(`MCP Session closed: ${transport.sessionId}`);
99+
delete transports[transport.sessionId];
100+
}
101+
};
102+
103+
// Create a new server instance for this specific session.
104+
const server = new McpServer({
105+
name: "typescript-server-walkthrough",
106+
version: "1.0.0"
78107
});
79108

80-
return new ToolResponse([uiResource]);
109+
// Register our MCP-UI tool on the new server instance.
110+
server.registerTool('greet', {
111+
title: 'Greet',
112+
description: 'A simple tool that returns a UI resource.',
113+
inputSchema: {},
114+
}, async () => {
115+
// Create the UI resource to be returned to the client (this is the only part specific to MCP-UI)
116+
const uiResource = createUIResource({
117+
uri: 'ui://greeting',
118+
content: { type: 'externalUrl', iframeUrl: 'https://example.com' },
119+
encoding: 'text',
120+
});
121+
122+
return {
123+
content: [uiResource],
124+
};
125+
});
126+
127+
// Connect the server instance to the transport for this session.
128+
await server.connect(transport);
129+
} else {
130+
return res.status(400).json({
131+
error: { message: 'Bad Request: No valid session ID provided' },
132+
});
81133
}
82-
}
83-
```
84-
85-
This tool, when called, will generate a simple HTML UI resource. The `import { createUIResource } from '@mcp-ui/server'` line imports the `mcp-ui` helper. The `GreetTool` is a standard MCP `Tool`, but it uses `createUIResource` to generate a `UIResource`, which is the primary integration point with `mcp-ui`. The following section describes how to set up a standard MCP server and expose it over HTTP.
86-
87-
## 4. Set up the MCP Server Handler
88-
89-
Now, let's create an MCP server instance and an endpoint to handle MCP requests.
90134

91-
Modify your `server.ts` file to include the following:
92-
93-
```typescript
94-
import express from 'express';
95-
import cors from 'cors';
96-
import { Server } from '@modelcontextprotocol/sdk';
97-
import { GreetTool } from './tools';
98-
99-
const app = express();
100-
const port = 3000;
101-
102-
// Set up the MCP server with your tool
103-
const mcpServer = new Server({
104-
tools: [new GreetTool()],
135+
// Handle the client's request using the session's transport.
136+
await transport.handleRequest(req, res, req.body);
105137
});
106138

107-
app.use(cors());
108-
app.use(express.json());
139+
// A separate, reusable handler for GET and DELETE requests.
140+
const handleSessionRequest = async (req: express.Request, res: express.Response) => {
141+
const sessionId = req.headers['mcp-session-id'] as string | undefined;
142+
if (!sessionId || !transports[sessionId]) {
143+
return res.status(404).send('Session not found');
144+
}
145+
146+
const transport = transports[sessionId];
147+
await transport.handleRequest(req, res);
148+
};
109149

110-
app.get('/', (req, res) => {
111-
res.send('Hello, world!');
112-
});
150+
// GET handles the long-lived stream for server-to-client messages.
151+
app.get('/mcp', handleSessionRequest);
113152

114-
// Add the /mcp endpoint
115-
app.post('/mcp', async (req, res) => {
116-
const response = await mcpServer.handle(req.body);
117-
res.json(response);
118-
});
153+
// DELETE handles explicit session termination from the client.
154+
app.delete('/mcp', handleSessionRequest);
119155

120156
app.listen(port, () => {
121157
console.log(`Server listening at http://localhost:${port}`);
122158
console.log(`MCP endpoint available at http://localhost:${port}/mcp`);
123159
});
124160
```
125161

126-
## 5. Run and Test
162+
## 4. Run and Test
127163

128164
You can now run your server:
129165

@@ -139,6 +175,6 @@ To test your new endpoint, you can use the [`ui-inspector`](https://github.com/i
139175
4. Enter your server's MCP endpoint URL: `http://localhost:3000/mcp`.
140176
5. Click "Connect".
141177

142-
The inspector will show tools for the different content types. When you call them, the UI resource will be rendered in the inspector's Tool Results.
178+
The inspector will show the "Greet" tool. When you call it, the UI resource will be rendered in the inspector's Tool Results.
143179

144-
You've now successfully integrated `mcp-ui` into your TypeScript server! You can now create more complex tools that return different types of UI resources.
180+
You've now successfully integrated `mcp-ui` into your TypeScript server! You can now create more complex tools that return different types of UI resources.

0 commit comments

Comments
 (0)