import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { serve } from '@hono/node-server';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { registerMobilityTools } from './tools/mobilityTools.js';
import { BecknClient } from './lib/becknClient.js';
import { WebhookManager } from './lib/webhookManager.js';
import dotenv from 'dotenv';
dotenv.config();
const app = new Hono();
app.use('/*', cors());
// Initialize MCP Server
const mcpServer = new Server({
name: 'mcp-beckn-mobility',
version: '1.0.0',
}, {
capabilities: {
tools: {},
resources: {},
prompts: {},
}
});
// Initialize Beckn Client
const becknClient = new BecknClient({
bppUri: process.env.BPP_URI || 'https://mobility.becknprotocol.io/sandbox/bpp',
bapId: process.env.BAP_ID || 'mcp-mobility-bap',
bapUri: process.env.BAP_URI || 'http://localhost:3000',
privateKey: process.env.SIGNING_PRIVATE_KEY || '',
publicKey: process.env.SIGNING_PUBLIC_KEY || '',
uniqueKeyId: process.env.UNIQUE_KEY_ID || 'key1',
});
// Register Tools
registerMobilityTools(mcpServer, becknClient);
import { streamSSE } from 'hono/streaming';
import { v4 as uuidv4 } from 'uuid';
import { HonoSSETransport } from './lib/honoTransport.js';
// Store transports map
const transports = new Map<string, HonoSSETransport>();
// SSE Transport Endpoint
app.get('/sse', async (c) => {
return streamSSE(c, async (stream) => {
const sessionId = uuidv4();
const transport = new HonoSSETransport(sessionId, stream);
transports.set(sessionId, transport);
await mcpServer.connect(transport);
// Keep connection alive
while (true) {
await stream.sleep(1000);
}
});
});
// Message Endpoint for SSE
app.post('/messages', async (c) => {
const sessionId = c.req.query('sessionId');
if (!sessionId) {
return c.text('Missing sessionId', 400);
}
const transport = transports.get(sessionId);
if (!transport) {
return c.text('Session not found', 404);
}
await transport.handlePostMessage(c);
return c.text('Accepted', 202);
});
// Webhook Endpoints
const webhookManager = WebhookManager.getInstance();
app.post('/on_search', async (c) => {
const body = await c.req.json();
console.log('Received on_search:', JSON.stringify(body));
webhookManager.handleCallback('on_search', body);
return c.json({ message: { ack: { status: 'ACK' } } });
});
app.post('/on_select', async (c) => {
const body = await c.req.json();
console.log('Received on_select:', JSON.stringify(body));
webhookManager.handleCallback('on_select', body);
return c.json({ message: { ack: { status: 'ACK' } } });
});
// Start Server
const port = 3000;
if (process.env.NODE_ENV !== 'test') {
console.log(`Server is running on port ${port}`);
serve({
fetch: app.fetch,
port
});
}
export { app };