create-mcp-craft
Version:
Create MCP TypeScript servers with Bun and Hono - the fastest way to scaffold Model Context Protocol servers
178 lines (158 loc) • 4.6 kB
text/typescript
#!/usr/bin/env bun
import { Hono } from 'hono'
import { streamSSE } from 'hono/streaming'
import { cors } from 'hono/cors'
import { serve } from '@hono/node-server'
import { logger } from '@/utils/logger.js'
import { serverConfig } from '@/config/server.js'
const app = new Hono()
// Set logger transport for HTTP
logger.setTransport('http')
// Enable CORS for browser clients
app.use(
'*',
cors({
origin: '*',
allowMethods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
allowHeaders: ['Content-Type', 'mcp-session-id'],
exposeHeaders: ['mcp-session-id'],
})
)
// Health check endpoint
app.get('/health', c => {
return c.json({
status: 'healthy',
server: serverConfig.name,
version: serverConfig.version,
timestamp: new Date().toISOString(),
})
})
// Server-Sent Events endpoint for MCP communication
app.get('/mcp/sse', c => {
logger.info('🔄 New SSE connection established')
return streamSSE(c, async stream => {
// Keep connection alive
stream.onAbort(() => {
logger.info('🔌 SSE connection closed')
})
// Send initial connection message
await stream.writeSSE({
data: JSON.stringify({
type: 'connection',
message: 'MCP Server connected via SSE',
timestamp: new Date().toISOString(),
}),
event: 'mcp-connection',
})
// Keep alive with periodic heartbeat
const heartbeat = setInterval(async () => {
if (!stream.aborted) {
await stream.writeSSE({
data: JSON.stringify({
type: 'heartbeat',
timestamp: new Date().toISOString(),
}),
event: 'mcp-heartbeat',
})
}
}, 30000) // 30 seconds
// Cleanup on abort
stream.onAbort(() => {
clearInterval(heartbeat)
})
// Keep connection open
while (!stream.aborted) {
await stream.sleep(1000)
}
})
})
// Basic MCP endpoint (for future HTTP transport implementation)
app.post('/mcp', async c => {
logger.info('📨 MCP HTTP request received')
try {
const body = await c.req.json()
logger.debug('📋 Request:', body)
// This is a placeholder for the HTTP transport implementation
// In a full implementation, this would integrate with the MCP server
return c.json({
jsonrpc: '2.0',
id: body.id,
result: {
message:
'HTTP transport not fully implemented yet. Use SSE endpoint for real-time communication.',
timestamp: new Date().toISOString(),
},
})
} catch (error) {
logger.error('❌ Error processing MCP request:', error)
return c.json(
{
jsonrpc: '2.0',
id: null,
error: {
code: -32603,
message: 'Internal server error',
},
},
500
)
}
})
// API info endpoint
app.get('/api/info', c => {
return c.json({
name: serverConfig.name,
version: serverConfig.version,
description:
serverConfig.description ||
'Model Context Protocol server built with Bun and Hono',
endpoints: {
'/health': 'Health check',
'/mcp/sse': 'Server-Sent Events for MCP communication',
'/mcp': 'HTTP endpoint for MCP requests (placeholder)',
'/api/info': 'This endpoint',
},
capabilities: serverConfig.capabilities,
supportedRequests: [
'initialize',
'discovery',
'tools/list',
'tools/call',
'resources/list',
'resources/read',
'resources/subscribe',
'resources/unsubscribe',
'resources/templates/list',
'prompts/list',
'prompts/get',
'sampling/createMessage',
'completion/complete',
'elicitation/create',
'roots/list',
'logging/setLevel',
'ping',
'session/create',
'session/destroy',
],
framework: 'Hono',
runtime: 'Bun',
timestamp: new Date().toISOString(),
})
})
// Start the server
const port = serverConfig.transports.http.port
logger.info('🚀 Starting MCP HTTP Server with Hono...')
logger.info(`📦 Server: ${serverConfig.name}`)
logger.info(`🌐 Framework: Hono`)
logger.info(`⚡ Runtime: Bun`)
logger.info(`🔗 Port: ${port}`)
serve({
fetch: app.fetch,
port: port,
})
logger.info(`✅ MCP HTTP Server running on http://localhost:${port}`)
logger.info(`🔗 Health check: http://localhost:${port}/health`)
logger.info(`📡 SSE endpoint: http://localhost:${port}/mcp/sse`)
logger.info(`📋 API info: http://localhost:${port}/api/info`)
logger.info('🎯 All MCP request types supported via HTTP transport')
logger.info(`🛑 Press Ctrl+C to stop`)