initialize project

This commit is contained in:
samanhappy
2025-03-31 15:38:30 +08:00
commit 5fdd0b0c7e
12 changed files with 11554 additions and 0 deletions

47
src/index.ts Normal file
View File

@@ -0,0 +1,47 @@
import express, { Request, Response } from 'express';
import dotenv from 'dotenv';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import { registerAllTools } from './server.js';
dotenv.config();
const server = new McpServer({
name: 'mcphub',
version: '0.0.1',
});
// Register all MCP tools from the modular structure
registerAllTools(server);
const app = express();
const PORT = process.env.PORT || 3000;
// to support multiple simultaneous connections we have a lookup object from sessionId to transport
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 {
console.error(`No transport found for sessionId: ${sessionId}`);
res.status(400).send('No transport found for sessionId');
}
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
export default app;

55
src/server.ts Normal file
View File

@@ -0,0 +1,55 @@
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
import { ZodType, ZodRawShape } from 'zod';
const transport = new SSEClientTransport(new URL('http://localhost:3001/sse'));
const client = new Client(
{
name: 'example-client',
version: '1.0.0',
},
{
capabilities: {
prompts: {},
resources: {},
tools: {},
},
},
);
export const registerAllTools = async (server: McpServer) => {
await client.connect(transport);
const tools = await client.listTools();
for (const tool of tools.tools) {
await server.tool(
tool.name,
tool.description || '',
cast(tool.inputSchema.properties),
async (params) => {
const result = await client.callTool({
name: tool.name,
arguments: params,
});
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
},
);
}
};
function cast(inputSchema: unknown): ZodRawShape {
if (typeof inputSchema !== 'object' || inputSchema === null) {
throw new Error('Invalid input schema');
}
const castedSchema = inputSchema as ZodRawShape;
for (const key in castedSchema) {
if (castedSchema[key] instanceof ZodType) {
castedSchema[key] = castedSchema[key].optional() as ZodType;
}
}
return castedSchema;
}