Skip to content
This repository was archived by the owner on May 21, 2026. It is now read-only.

Commit f5e3ddf

Browse files
committed
feat: server great design from express extension
1 parent ee0aaae commit f5e3ddf

1 file changed

Lines changed: 70 additions & 72 deletions

File tree

packages/mcp-use/src/server/mcp-server.ts

Lines changed: 70 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,40 @@ import type {
77
} from './types.js'
88
import { McpServer as OfficialMcpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
99
import { z } from 'zod'
10+
import express, { type Express } from 'express'
1011

1112
export class McpServer {
1213
private server: OfficialMcpServer
1314
private config: ServerConfig
14-
private expressApp?: any
15+
private app: Express
16+
private mcpMounted = false
1517

1618
constructor(config: ServerConfig) {
1719
this.config = config
1820
this.server = new OfficialMcpServer({
1921
name: config.name,
2022
version: config.version,
2123
})
24+
this.app = express()
25+
26+
// Enable CORS by default
27+
this.app.use((req, res, next) => {
28+
res.header('Access-Control-Allow-Origin', '*')
29+
res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS')
30+
res.header('Access-Control-Allow-Headers', 'Content-Type')
31+
next()
32+
})
33+
34+
// Proxy all Express methods to the underlying app
35+
return new Proxy(this, {
36+
get(target, prop) {
37+
if (prop in target) {
38+
return (target as any)[prop]
39+
}
40+
const value = (target.app as any)[prop]
41+
return typeof value === 'function' ? value.bind(target.app) : value
42+
}
43+
}) as McpServer
2244
}
2345

2446
/**
@@ -126,83 +148,56 @@ export class McpServer {
126148
}
127149

128150
/**
129-
* Set the Express app for SSE transport
130-
*/
131-
setExpressApp(app: any): this {
132-
this.expressApp = app
133-
return this
134-
}
135-
136-
/**
137-
* Start the MCP server with configurable transport
138-
* @param options - Transport options (defaults to HTTP)
151+
* Mount MCP server endpoints at /mcp
139152
*/
140-
async serve(options?: { transport?: 'http' | 'stdio', port?: number, endpoint?: string }): Promise<void> {
141-
const transport = options?.transport || (process.env.MCP_TRANSPORT as 'http' | 'stdio') || 'http'
142-
143-
if (transport === 'stdio') {
144-
// Use stdio transport (for traditional MCP clients)
145-
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js')
146-
const stdioTransport = new StdioServerTransport()
147-
await this.server.connect(stdioTransport)
148-
console.log('📡 MCP server connected via stdio transport')
149-
} else {
150-
// Default to StreamableHTTPServerTransport (for HTTP/web access)
151-
const { StreamableHTTPServerTransport } = await import('@modelcontextprotocol/sdk/server/streamableHttp.js')
152-
const endpoint = options?.endpoint || '/mcp'
153-
154-
if (!this.expressApp) {
155-
// Create a standalone Express server if no app provided
156-
const express = await import('express')
157-
this.expressApp = express.default()
158-
159-
// Enable CORS
160-
this.expressApp.use((req: any, res: any, next: any) => {
161-
res.header('Access-Control-Allow-Origin', '*')
162-
res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS')
163-
res.header('Access-Control-Allow-Headers', 'Content-Type')
164-
next()
165-
})
166-
167-
const port = options?.port || 3001
168-
this.expressApp.listen(port, () => {
169-
console.log(`📡 MCP HTTP server listening on http://localhost:${port}${endpoint}`)
170-
})
171-
}
172-
173-
console.log(`📡 Registering HTTP endpoints on Express app...`)
153+
private async mountMcp(): Promise<void> {
154+
if (this.mcpMounted) return
155+
156+
const { StreamableHTTPServerTransport } = await import('@modelcontextprotocol/sdk/server/streamableHttp.js')
157+
158+
// Create StreamableHTTPServerTransport in stateless mode
159+
const httpTransport = new StreamableHTTPServerTransport({
160+
sessionIdGenerator: undefined // Stateless mode
161+
})
174162

175-
// Create StreamableHTTPServerTransport in stateless mode
176-
const httpTransport = new StreamableHTTPServerTransport({
177-
sessionIdGenerator: undefined // Stateless mode
178-
})
163+
// Connect the MCP server to the transport
164+
await this.server.connect(httpTransport)
179165

180-
// Connect the MCP server to the transport
181-
await this.server.connect(httpTransport)
166+
const endpoint = '/mcp'
182167

183-
// Add HTTP endpoints for StreamableHTTPServerTransport
184-
const express = await import('express')
168+
// GET endpoint for SSE streaming
169+
this.app.get(endpoint, async (req, res) => {
170+
console.log(`📡 HTTP GET request received at ${endpoint}`)
171+
await httpTransport.handleRequest(req, res)
172+
})
185173

186-
// GET endpoint for SSE streaming
187-
this.expressApp.get(endpoint, async (req: any, res: any) => {
188-
console.log(`📡 HTTP GET request received at ${endpoint}`)
189-
await httpTransport.handleRequest(req, res)
190-
})
174+
// POST endpoint for messages
175+
this.app.post(endpoint, express.json(), async (req, res) => {
176+
console.log(`📡 HTTP POST request received at ${endpoint}`)
177+
await httpTransport.handleRequest(req, res, req.body)
178+
})
191179

192-
// POST endpoint for messages
193-
this.expressApp.post(endpoint, express.json(), async (req: any, res: any) => {
194-
console.log(`📡 HTTP POST request received at ${endpoint}`)
195-
await httpTransport.handleRequest(req, res, req.body)
196-
})
180+
// DELETE endpoint for session cleanup
181+
this.app.delete(endpoint, async (req, res) => {
182+
console.log(`📡 HTTP DELETE request received at ${endpoint}`)
183+
await httpTransport.handleRequest(req, res)
184+
})
197185

198-
// DELETE endpoint for session cleanup (if using stateful mode)
199-
this.expressApp.delete(endpoint, async (req: any, res: any) => {
200-
console.log(`📡 HTTP DELETE request received at ${endpoint}`)
201-
await httpTransport.handleRequest(req, res)
202-
})
186+
this.mcpMounted = true
187+
console.log(`📡 MCP server mounted at ${endpoint}`)
188+
}
203189

204-
console.log(`📡 MCP server HTTP endpoints registered at ${endpoint}`)
205-
}
190+
/**
191+
* Start the Express server with MCP endpoints
192+
* @param port - Port to listen on (defaults to 3001)
193+
*/
194+
async listen(port?: number): Promise<void> {
195+
await this.mountMcp()
196+
const serverPort = port || 3001
197+
this.app.listen(serverPort, () => {
198+
console.log(`📡 Server listening on http://localhost:${serverPort}`)
199+
console.log(`📡 MCP endpoints available at http://localhost:${serverPort}/mcp`)
200+
})
206201
}
207202

208203
/**
@@ -304,13 +299,16 @@ export class McpServer {
304299
}
305300
}
306301

302+
export type McpServerInstance = Omit<McpServer, keyof Express> & Express
303+
307304
/**
308305
* Create a new MCP server instance
309306
*/
310-
export function createMCPServer(name: string, config: Partial<ServerConfig> = {}): McpServer {
311-
return new McpServer({
307+
export function createMCPServer(name: string, config: Partial<ServerConfig> = {}): McpServerInstance {
308+
const instance = new McpServer({
312309
name,
313310
version: config.version || '1.0.0',
314311
description: config.description,
315312
})
313+
return instance as unknown as McpServerInstance
316314
}

0 commit comments

Comments
 (0)