Builder Pattern API
Use the fluent builder pattern API for full type inference and step-by-step configuration of your MCP server.
Overview
The Builder Pattern API (IgniterMcpServer) provides a fluent, chainable interface for configuring your MCP server. It offers full TypeScript type inference, better IDE autocomplete, and explicit separation of concerns.
Recommended
The builder pattern is recommended for TypeScript projects where you want maximum type safety and a clear, progressive configuration flow.
Basic Usage
Creating a Server
Start by calling IgniterMcpServer.create() and chain configuration methods:
import { IgniterMcpServer } from '@igniter-js/adapter-mcp-server';
import { AppRouter } from '@/igniter.router';
const { handler, auth } = IgniterMcpServer
.create()
.router(AppRouter)
.withServerInfo({
name: 'My MCP Server',
version: '1.0.0',
})
.build();
export const GET = handler;
export const POST = handler;Configuration Methods
router()
Sets the Igniter.js router to expose as MCP tools. This method is required.
const { handler } = IgniterMcpServer
.create()
.router(AppRouter) // Required: Your Igniter router
.build();Type Safety: After calling .router(), all subsequent methods benefit from type inference based on your router's context type.
withServerInfo()
Sets metadata about your MCP server:
const { handler } = IgniterMcpServer
.create()
.router(AppRouter)
.withServerInfo({
name: 'Acme Corporation API',
version: '1.2.0',
})
.build();Parameters:
name(string): Server name displayed to MCP clientsversion(string): Server version (semantic versioning recommended)
withInstructions()
Provides instructions to AI agents about how to use your server:
const { handler } = IgniterMcpServer
.create()
.router(AppRouter)
.withInstructions(
"This server provides tools to manage users and products. " +
"Use users.list to get all users, users.create to add new users, " +
"and products.search to find products by keyword."
)
.build();Best Practices:
- Describe what your API does
- List key tools and their purposes
- Mention any important usage patterns or constraints
withCapabilities()
Configure server capabilities to advertise to MCP clients:
import { ServerCapabilities } from '@modelcontextprotocol/sdk/types';
const { handler } = IgniterMcpServer
.create()
.router(AppRouter)
.withCapabilities({
tools: {},
prompts: {},
resources: {},
})
.build();addTool()
Add custom tools beyond your router actions. Each tool gets full type inference:
import { z } from 'zod';
const { handler } = IgniterMcpServer
.create()
.router(AppRouter)
.addTool({
name: 'calculateTax',
description: 'Calculate tax for a given amount',
args: {
amount: z.number(),
taxRate: z.number(),
},
handler: async (args, context) => {
// context is automatically typed from your router!
const tax = args.amount * args.taxRate;
return {
content: [{
type: 'text',
text: `Tax: $${tax.toFixed(2)}`
}]
};
},
})
.build();Type Inference:
argsis automatically typed based on your Zod schemacontextis automatically typed from your router's context type- Return type is validated to match MCP's
CallToolResult
Multiple Tools:
const { handler } = IgniterMcpServer
.create()
.router(AppRouter)
.addTool({ /* tool 1 */ })
.addTool({ /* tool 2 */ })
.addTool({ /* tool 3 */ })
.build();addPrompt()
Register prompts that AI agents can use to guide interactions:
import { z } from 'zod';
const { handler } = IgniterMcpServer
.create()
.router(AppRouter)
.addPrompt({
name: 'debugUser',
description: 'Debug user account issues',
args: {
userId: z.string(),
},
handler: async (args, context) => {
// context is automatically typed!
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Please debug the account for user ${args.userId}. ` +
`Check their permissions, recent activity, and any error logs.`
}
}]
};
},
})
.build();Use Cases:
- Create debugging prompts that combine multiple steps
- Guide AI agents through complex workflows
- Provide context-aware instructions
addResource()
Expose resources that AI agents can read:
const { handler } = IgniterMcpServer
.create()
.router(AppRouter)
.addResource({
uri: 'config://app/settings',
name: 'Application Settings',
description: 'Current application configuration',
mimeType: 'application/json',
handler: async (context) => {
// context is automatically typed!
const settings = await getAppSettings();
return {
contents: [{
uri: 'config://app/settings',
mimeType: 'application/json',
text: JSON.stringify(settings, null, 2)
}]
};
}
})
.build();Resource URI Patterns:
config://- Configuration resourcesfile://- File-based resourcesdb://- Database resources- Custom schemes are also supported
withOAuth()
Secure your MCP server with OAuth authentication:
const { handler, auth } = IgniterMcpServer
.create()
.router(AppRouter)
.withOAuth({
issuer: 'https://auth.example.com',
resourceMetadataPath: '/.well-known/oauth-protected-resource',
scopes: ['mcp:read', 'mcp:write'],
verifyToken: async ({ request, bearerToken, context }) => {
// context is automatically typed!
const result = await verifyJWT(bearerToken);
return {
valid: result.valid,
user: result.user
};
}
})
.build();
// Export the handler and OAuth endpoints
export const GET = handler;
export const POST = handler;
export const OPTIONS = auth.cors;
// In your OAuth metadata endpoint route (e.g., /.well-known/oauth-protected-resource)
// export { auth.resource as GET };OAuth Features:
- Automatic Bearer token validation
- Custom token verification logic
- OAuth metadata endpoint
- CORS support for OAuth flows
withEvents()
Monitor and log MCP operations:
const { handler } = IgniterMcpServer
.create()
.router(AppRouter)
.withEvents({
onRequest: async (request, context) => {
console.log('MCP request received:', request.url);
},
onResponse: async (response, context) => {
console.log('MCP response sent');
},
onToolCall: async (toolName, args, context) => {
console.log(`Tool called: ${toolName}`, args);
},
onToolSuccess: async (toolName, result, duration, context) => {
console.log(`Tool ${toolName} completed in ${duration}ms`);
},
onToolError: async (toolName, error, context) => {
console.error(`Tool ${toolName} failed:`, error);
},
onError: async (error, context) => {
console.error('MCP adapter error:', error);
}
})
.build();Available Events:
onRequest- Called when any MCP request is receivedonResponse- Called when a response is sentonToolCall- Called when a tool is invokedonToolSuccess- Called when a tool completes successfullyonToolError- Called when a tool failsonError- Called on general adapter errors
withToolTransform()
Customize how router actions are transformed into tools:
const { handler } = IgniterMcpServer
.create()
.router(AppRouter)
.withToolTransform((controller, action, actionConfig) => {
// Customize tool name
const name = `${controller}_${action}`;
// Use summary if available, fallback to description
const description = actionConfig.summary ||
actionConfig.description ||
`Execute ${controller} ${action}`;
// Include tags from action config
const tags = actionConfig.tags || [];
return {
name,
description,
schema: actionConfig.body || actionConfig.query || {},
tags: [...tags, controller, actionConfig.method?.toLowerCase()].filter(Boolean)
};
})
.build();Use Cases:
- Custom naming strategies
- Enhanced descriptions
- Additional metadata (tags, categories)
- Schema transformations
withLogger()
Configure custom logging:
const { handler } = IgniterMcpServer
.create()
.router(AppRouter)
.withLogger({
log: (message) => console.log(`[MCP] ${message}`),
error: (message) => console.error(`[MCP ERROR] ${message}`),
warn: (message) => console.warn(`[MCP WARN] ${message}`),
debug: (message) => console.debug(`[MCP DEBUG] ${message}`),
})
.build();withResponse()
Customize response transformation and error handling:
const { handler } = IgniterMcpServer
.create()
.router(AppRouter)
.withResponse({
transform: async (igniterResponse, toolName, context) => {
// Customize how Igniter responses are formatted for MCP
return {
content: [{
type: 'text',
text: JSON.stringify(igniterResponse, null, 2)
}]
};
},
onError: async (error, toolName, context) => {
// Custom error handling
return {
content: [{
type: 'text',
text: `Error in ${toolName}: ${error.message}`
}]
};
}
})
.build();Complete Example
Here's a complete example combining multiple features:
import { IgniterMcpServer } from '@igniter-js/adapter-mcp-server';
import { AppRouter } from '@/igniter.router';
import { z } from 'zod';
const { handler, auth } = IgniterMcpServer
.create()
.router(AppRouter)
.withServerInfo({
name: 'Acme Corporation API',
version: '1.0.0',
})
.withInstructions(
"This server provides tools to manage users, products, and orders. " +
"Use the appropriate tools to interact with each resource."
)
.addTool({
name: 'calculateTax',
description: 'Calculate tax for an amount',
args: {
amount: z.number(),
taxRate: z.number(),
},
handler: async (args, context) => {
const tax = args.amount * args.taxRate;
return {
content: [{
type: 'text',
text: `Tax: $${tax.toFixed(2)}`
}]
};
},
})
.addPrompt({
name: 'debugUser',
description: 'Debug user account',
args: { userId: z.string() },
handler: async (args, context) => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Debug user ${args.userId}`
}
}]
};
},
})
.addResource({
uri: 'config://app/settings',
name: 'App Settings',
description: 'Application configuration',
mimeType: 'application/json',
handler: async (context) => {
const settings = await getAppSettings();
return {
contents: [{
uri: 'config://app/settings',
mimeType: 'application/json',
text: JSON.stringify(settings, null, 2)
}]
};
}
})
.withOAuth({
issuer: 'https://auth.example.com',
verifyToken: async ({ bearerToken, context }) => {
const result = await verifyJWT(bearerToken);
return { valid: result.valid, user: result.user };
}
})
.withEvents({
onToolCall: async (toolName, args, context) => {
console.log(`Tool called: ${toolName}`);
},
onToolError: async (toolName, error, context) => {
console.error(`Tool error: ${toolName}`, error);
}
})
.build();
export const GET = handler;
export const POST = handler;
export const OPTIONS = auth.cors;Type Safety Benefits
The builder pattern provides excellent type safety:
1. Router Context Inference:
// Context type is automatically inferred from AppRouter
.handler(async (args, context) => {
// ✅ context.db is typed
// ✅ context.services is typed
// ✅ All your context properties are available
})2. Tool Arguments:
.addTool({
args: {
amount: z.number(),
email: z.string().email(),
},
handler: async (args, context) => {
// ✅ args.amount is number
// ✅ args.email is string
// ✅ TypeScript enforces correct usage
}
})3. Chain Type Safety:
// Each method returns a properly typed builder
const builder = IgniterMcpServer.create()
.router(AppRouter) // Now knows about AppRouter's context
.addTool({ /* Full type inference */ })
.addPrompt({ /* Full type inference */ })
.build(); // Returns properly typed handlerNext Steps
- Learn about the Function API for simpler setups
- Explore Custom Tools in detail
- Check out Advanced Configurations