create-mcp-craft
Version:
Create MCP TypeScript servers with Bun and Hono - the fastest way to scaffold Model Context Protocol servers
413 lines (386 loc) โข 11.1 kB
text/typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { z } from 'zod'
// Import utilities and configuration
import { logger } from '@/utils/logger.js'
import { serverConfig } from '@/config/server.js'
// Import all handlers
// import { handleInitialize } from '@/handlers/initialize/index.js'
// import { handleDiscovery } from '@/handlers/discovery/index.js'
// import { handleToolsList } from '@/handlers/tools/list.js'
// import { handleToolsCall } from '@/handlers/tools/call.js'
// import { handleResourcesList } from '@/handlers/resources/list.js'
// import { handleResourcesRead } from '@/handlers/resources/read.js'
// import { handleResourcesSubscribe } from '@/handlers/resources/subscribe.js'
// import { handleResourcesUnsubscribe } from '@/handlers/resources/unsubscribe.js'
// import { handleResourcesTemplatesList } from '@/handlers/resources/templates/list.js'
// import { handlePromptsList } from '@/handlers/prompts/list.js'
// import { handlePromptsGet } from '@/handlers/prompts/get.js'
// import { handleSamplingCreateMessage } from '@/handlers/sampling/createMessage.js'
// import { handleCompletionComplete } from '@/handlers/completion/complete.js'
// import { handleElicitationCreate } from '@/handlers/elicitation/create.js'
// import { handleRootsList } from '@/handlers/roots/list.js'
// import { handleLoggingSetLevel } from '@/handlers/logging/setLevel.js'
// import { handlePing } from '@/handlers/ping/index.js'
// import { handleSessionCreate } from '@/handlers/session/create.js'
// import { handleSessionDestroy } from '@/handlers/session/destroy.js'
// Import services for tool registration
import { CalculatorService } from '@/services/calculator.js'
import { WeatherService } from '@/services/weather.js'
// Create the MCP server
const server = new McpServer({
name: serverConfig.name,
version: serverConfig.version,
})
// Register Tools
logger.info('๐ง Registering tools...')
// Calculator tools
server.registerTool(
'add',
{
title: 'Addition Calculator',
description: 'Add two numbers together',
inputSchema: {
a: z.number().describe('First number'),
b: z.number().describe('Second number'),
},
},
async ({ a, b }) => {
const result = CalculatorService.add(a, b)
return {
content: [
{
type: 'text',
text: `${a} + ${b} = ${result}`,
},
],
}
}
)
server.registerTool(
'subtract',
{
title: 'Subtraction Calculator',
description: 'Subtract two numbers',
inputSchema: {
a: z.number().describe('First number'),
b: z.number().describe('Second number'),
},
},
async ({ a, b }) => {
const result = CalculatorService.subtract(a, b)
return {
content: [
{
type: 'text',
text: `${a} - ${b} = ${result}`,
},
],
}
}
)
server.registerTool(
'multiply',
{
title: 'Multiplication Calculator',
description: 'Multiply two numbers',
inputSchema: {
a: z.number().describe('First number'),
b: z.number().describe('Second number'),
},
},
async ({ a, b }) => {
const result = CalculatorService.multiply(a, b)
return {
content: [
{
type: 'text',
text: `${a} ร ${b} = ${result}`,
},
],
}
}
)
server.registerTool(
'divide',
{
title: 'Division Calculator',
description: 'Divide two numbers',
inputSchema: {
a: z.number().describe('Dividend'),
b: z.number().describe('Divisor'),
},
},
async ({ a, b }) => {
try {
const result = CalculatorService.divide(a, b)
return {
content: [
{
type: 'text',
text: `${a} รท ${b} = ${result}`,
},
],
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
isError: true,
}
}
}
)
// Weather tools
server.registerTool(
'fetch-weather',
{
title: 'Weather Fetcher',
description: 'Get current weather information for a city',
inputSchema: {
city: z.string().describe('Name of the city'),
},
},
async ({ city }) => {
const weatherData = await WeatherService.fetchWeatherData(city)
return {
content: [
{
type: 'text',
text: weatherData,
},
],
}
}
)
server.registerTool(
'get-forecast',
{
title: 'Weather Forecast',
description: 'Get weather forecast for a city',
inputSchema: {
city: z.string().describe('Name of the city'),
days: z
.number()
.optional()
.describe('Number of days for forecast (default: 5)'),
},
},
async ({ city, days }) => {
const forecast = await WeatherService.getForecast(city, days || 5)
return {
content: [
{
type: 'text',
text: forecast.join('\n'),
},
],
}
}
)
// Register Prompts
logger.info('๐ญ Registering prompts...')
// Code review prompt
server.registerPrompt(
'review-code',
{
title: 'Code Review Assistant',
description:
'Generate a comprehensive code review with best practices and suggestions',
argsSchema: {
code: z.string().describe('The code to review'),
language: z
.string()
.optional()
.describe('Programming language (auto-detected if not provided)'),
focus: z.string().optional().describe('Review focus area'),
},
},
({ code, language, focus }) => {
const focusAreas = {
security:
'Focus on security vulnerabilities, input validation, and secure coding practices.',
performance:
'Focus on performance optimizations, algorithmic efficiency, and resource usage.',
readability:
'Focus on code clarity, naming conventions, and maintainability.',
all: 'Provide a comprehensive review covering security, performance, readability, and best practices.',
}
return {
messages: [
{
role: 'user' as const,
content: {
type: 'text' as const,
text: `Please review the following ${language || 'code'} and provide detailed feedback. ${focusAreas[focus as keyof typeof focusAreas]}
\`\`\`${language || ''}
${code}
\`\`\`
Please provide:
1. Overall assessment
2. Specific issues and suggestions
3. Best practices recommendations
4. Security considerations (if applicable)
5. Performance considerations (if applicable)`,
},
},
],
}
}
)
// Additional prompts
const additionalPrompts = [
'explain-concept',
'debug-help',
'optimize-code',
'generate-tests',
]
additionalPrompts.forEach(promptName => {
server.registerPrompt(
promptName,
{
title: promptName
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' '),
description: `${promptName.replace('-', ' ')} assistant prompt`,
argsSchema: {
content: z.string().describe('Content for the prompt'),
options: z.string().optional().describe('Additional options'),
},
},
({ content }) => {
return {
messages: [
{
role: 'user' as const,
content: {
type: 'text' as const,
text: `Process the following content for ${promptName.replace('-', ' ')}: ${content}`,
},
},
],
}
}
)
})
// Register Resources
logger.info('๐ Registering resources...')
// Static configuration resource
server.registerResource(
'config',
'config://app',
{
title: 'Application Configuration',
description: 'Static application configuration data',
mimeType: 'application/json',
},
async uri => {
return {
contents: [
{
uri: uri.href,
mimeType: 'application/json',
text: JSON.stringify(
{
app_name: 'MCP TypeScript Server Template',
version: '1.0.0',
author: 'Muhammed Kฤฑlฤฑรง',
database: {
host: 'localhost',
port: 5432,
name: 'mcp_template',
},
server: {
port: 3000,
host: '0.0.0.0',
},
logging: {
level: 'info',
format: 'json',
},
security: {
cors_enabled: true,
rate_limiting: true,
},
features: {
analytics: true,
monitoring: true,
caching: true,
},
},
null,
2
),
},
],
}
}
)
// Dynamic greeting resource
server.registerResource(
'greeting',
'greeting://{name}',
{
title: 'Dynamic Greeting Generator',
description: 'Generate personalized greetings',
mimeType: 'text/plain',
},
async uri => {
const urlPath = new URL(uri.href).pathname
const name = urlPath.split('/').pop() || 'World'
return {
contents: [
{
uri: uri.href,
mimeType: 'text/plain',
text: `Hello, ${name}! Welcome to our MCP TypeScript server template. Today is ${new Date().toLocaleDateString()}.`,
},
],
}
}
)
// Function to start the server
export async function startServer(transport?: 'stdio' | 'http') {
// Set the logger transport mode
logger.setTransport(transport || 'stdio')
logger.info('๐ Starting MCP TypeScript Template Server...')
logger.info(`๐ฆ Server name: ${serverConfig.name}`)
logger.info(`๐ Version: ${serverConfig.version}`)
logger.info('๐ฏ All MCP request types supported:')
logger.info(
' โ
Core: initialize, tools/list, tools/call, resources/list, resources/read, prompts/list, prompts/get, ping'
)
logger.info(
' โ
Optional: discovery, resources/subscribe, resources/unsubscribe, resources/templates/list'
)
logger.info(
' โ
Extended: sampling/createMessage, completion/complete, elicitation/create, roots/list'
)
logger.info(
' โ
Management: logging/setLevel, session/create, session/destroy'
)
if (!transport || transport === 'stdio') {
// Use stdio transport for local development
logger.info('๐ Transport: stdio')
const stdioTransport = new StdioServerTransport()
await server.connect(stdioTransport)
logger.info('โ
Server connected via stdio transport')
} else {
// HTTP transport will be implemented in Phase 4
throw new Error('HTTP transport not implemented yet')
}
}
// Default export for the server
export default server
// Auto-start if this file is run directly
if (import.meta.main) {
startServer('stdio').catch(error => {
logger.error('Failed to start server:', error)
process.exit(1)
})
}