Built-in MCP Server
Native MCP Server per organization to connect ChatGPT, Claude, and developer tools to your SaaS via Igniter.js controllers.
By the end of this guide, you'll understand what the Model Context Protocol (MCP) is, how the SaaS Boilerplate's built-in MCP Server works, and how to extend it with custom tools, prompts, and resources. You'll learn how Igniter.js controllers are automatically converted into MCP tools and how to configure various AI clients to connect to your SaaS.
Before You Begin
Basic Knowledge: Familiarity with REST APIs, TypeScript, and basic understanding of AI assistants Environment Setup: A running SaaS Boilerplate instance with Redis configured Prerequisites: Understanding of the Authentication & Sessions and Organizations & Tenancy concepts Optional: Experience with AI tools like Cursor, Claude, or ChatGPT
What is the Model Context Protocol (MCP)?
The Model Context Protocol (MCP) is an open-source standard that enables AI applications to connect to external systems in a standardized way. Think of MCP like a USB-C port for AI applications - just as USB-C provides a universal way to connect electronic devices, MCP provides a standardized way to connect AI models to data sources, tools, and workflows.
Why MCP Matters
MCP solves a fundamental problem: AI applications were previously limited to their built-in capabilities and couldn't easily access external data or perform actions on behalf of users. With MCP:
- AI assistants can access your data: ChatGPT can read your database, Claude can analyze your files
- AI can take actions: Cursor can create GitHub issues, Claude can send emails through your system
- Standardized integration: One protocol works across all MCP-compatible tools
- Security-first: Each connection is authenticated and scoped to specific organizations
MCP Architecture
MCP follows a client-server architecture where:
- MCP Servers expose tools, resources, and prompts
- MCP Clients (AI assistants) connect to servers and use their capabilities
- Transports handle the communication between clients and servers
The SaaS Boilerplate acts as an MCP Server, exposing your business logic as tools that AI assistants can use.
Core Concepts
The SaaS Boilerplate includes a built-in MCP Server that transforms your Igniter.js application into an AI-native platform. This server exposes your API endpoints as MCP tools, scoped to each organization.
Automatic Tool Generation
The most powerful feature is automatic conversion of Igniter.js controllers into MCP tools. Every igniter.query() and igniter.mutation() in your controllers becomes an executable tool for AI assistants.
Multi-Tenant Security
Each organization gets its own MCP endpoint with organization-scoped authentication. API keys ensure that AI assistants can only access data for the specific organization they belong to.
Transport Flexibility
The server supports both Server-Sent Events (SSE) and HTTP Streamable transports, enabling real-time bidirectional communication with AI clients.
The @igniter-js/adapter-mcp-server Package
The @igniter-js/adapter-mcp-server package is the bridge between Igniter.js and the MCP protocol. It provides:
- Automatic introspection of your router to discover controllers and actions
- Schema conversion from Zod to JSON Schema for tool parameters
- Type-safe execution using Igniter's
$callerfor direct server-side invocation - Event-driven architecture with hooks for monitoring and customization
- Multi-transport support for different client requirements
Key Capabilities
Prop
Type
Architecture
The MCP Server integrates deeply with the SaaS Boilerplate's existing architecture:
- Route Handler:
src/app/(mcp)/mcp/[orgId]/[transport]/route.ts - Server Factory:
src/igniter.mcp.ts - Settings UI:
src/app/(private)/app/(organization)/(dashboard)/settings/organization/mcp/page.tsx - Key Management: API key procedures and controllers
- CORS & Headers: Next.js configuration for cross-origin requests
Request Flow
Client Connection
An AI client (like Cursor or Claude) connects to your MCP endpoint using an organization-specific API key:
// Example: https://yourapp.com/mcp/sk_xxx.../sse
{
"mcpServers": {
"your-saas": {
"type": "http",
"url": "https://yourapp.com/mcp/sk_xxx.../sse"
}
}
}Authentication & Context
The route handler extracts the organization ID from the API key and creates an authenticated context:
// src/app/(mcp)/mcp/[orgId]/[transport]/route.ts
const authMcpHandler = async (request: NextRequest, { params }: RouteContext) => {
const { orgId } = await params
request.headers.set('Authorization', `Bearer ${orgId}`)
return McpServer(orgId).handler(request)
}Tool Execution
When an AI requests a tool, the adapter:
- Validates the request against your Zod schemas
- Executes the corresponding Igniter.js action
- Returns the result in MCP format
// Tool call: leads.list({})
// Becomes: router.caller.leads.list.query({})Transports
The MCP Server supports two transport mechanisms for different client needs:
Server-Sent Events (SSE)
SSE is the primary transport for most AI clients, providing real-time bidirectional communication.
HTTP Streamable
HTTP Streamable transport uses standard HTTP POST requests for all communication, suitable for environments with restricted connectivity.
How Controllers Become MCP Tools
The adapter automatically converts Igniter.js controllers into MCP tools through a sophisticated introspection process.
Tool Generation Process
Router Traversal
The adapter walks through your AppRouter.controllers map, discovering all controllers and their actions:
// From your router
const AppRouter = igniter.router({
controllers: {
lead: LeadController,
user: UserController,
// ...
}
})Action Discovery
For each controller, it extracts all igniter.query() and igniter.mutation() actions:
// LeadController
export const LeadController = igniter.controller({
name: 'Lead',
path: '/leads',
actions: {
list: igniter.query({ /* ... */ }),
create: igniter.mutation({ /* ... */ }),
// ...
}
})Schema Conversion
Zod schemas are converted to JSON Schema format that MCP clients understand:
// Igniter action with Zod
list: igniter.query({
body: z.object({
limit: z.number().optional(),
offset: z.number().optional()
})
})
// Becomes MCP tool schema
{
"type": "object",
"properties": {
"limit": { "type": "number" },
"offset": { "type": "number" }
}
}Tool Registration
Each action becomes a named tool (e.g., leads.list, leads.create) that AI clients can invoke.
Tool Naming Convention
Tools are named using the pattern {controller}.{action} in lowercase:
lead.list→leads_listuser.create→users_createbilling.getSubscription→billing_getSubscription
Parameter Mapping
Igniter.js parameters are mapped to MCP tool arguments:
Prop
Type
Custom Tools with .addTool()
Custom tools in MCP allow you to extend your server's capabilities beyond the automatically generated tools from your Igniter.js controllers. These are specialized functions that AI clients can invoke to perform specific tasks that aren't covered by your standard API endpoints.
Custom tools are particularly useful when you need to implement complex business logic, integrate with external services, or provide AI-specific functionality that doesn't fit the traditional REST API pattern. For example, you might create a custom tool for generating analytics reports, processing files with AI models, or orchestrating multi-step workflows that involve multiple API calls.
When to use custom tools:
- Complex computations: Machine learning predictions, data analysis, or algorithmic processing
- External integrations: Connecting to third-party APIs or services
- Composite operations: Workflows that combine multiple API calls into a single AI-invokable action
- AI-specific features: Specialized prompts, content generation, or intelligent automation
For detailed information about MCP tools and their capabilities, refer to the official MCP specification.
Adding Custom Tools
// src/igniter.mcp.ts
export const McpServer = (organizationId: string) => {
return IgniterMCPServer.create()
.router(igniter.router({ controllers: { /* ... */ } }))
.addTool({
name: 'analyze_user_behavior',
description: 'Analyze user behavior patterns using advanced ML models',
schema: {
userId: z.string(),
timeframe: z.enum(['week', 'month', 'year'])
},
handler: async (args, context) => {
// Custom logic here
const analysis = await analyzeUserBehavior(args.userId, args.timeframe)
return { result: analysis }
}
})
.build()
}Custom Tool Capabilities
Prompts with .addPrompt()
MCP prompts are structured message templates that guide AI interactions by providing context, instructions, and examples for specific tasks. Unlike tools that perform actions, prompts shape how AI models respond and behave in conversations.
Prompts are essential for creating consistent AI experiences across different clients and use cases. They allow you to define reusable conversation patterns, provide domain-specific guidance, or create specialized workflows that help AI assistants work more effectively with your application.
When to use prompts:
- Task-specific guidance: Direct AI behavior for customer support, content creation, or technical analysis
- Context provision: Supply background information, examples, or reference materials
- Workflow automation: Create standardized processes for common user interactions
- Personalization: Customize AI responses based on user roles, organization context, or specific requirements
For comprehensive details about MCP prompts, including message formats and best practices, see the official MCP prompts specification.
Adding Prompts
// src/igniter.mcp.ts
export const McpServer = (organizationId: string) => {
return IgniterMCPServer.create()
.router(igniter.router({ controllers: { /* ... */ } }))
.addPrompt({
name: 'customer_support',
description: 'Guide for handling customer support inquiries',
schema: {
userId: z.string(),
timeframe: z.enum(['week', 'month', 'year'])
},
handler: async (args, context) => {
return {
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Please help with this ${args.issue_type} issue. Use the available tools to investigate and resolve.`
}
}
]
}
}
})
.build()
}Prompt Features
Resources with .addResource()
MCP resources allow you to expose data, documents, and content that AI models can reference and incorporate into their responses. Resources provide a way for AI assistants to access contextual information like documentation, policies, user guides, or dynamically generated content.
Resources are particularly valuable for providing AI assistants with access to information that enhances their ability to help users effectively. Instead of hardcoding knowledge into prompts, resources allow you to keep information current and provide AI with access to the latest documentation, policies, or application-specific data.
When to use resources:
- Documentation access: API docs, user guides, or help content
- Policy and compliance: Company policies, terms of service, or regulatory information
- Dynamic data: Real-time metrics, user-specific information, or generated reports
- Reference materials: Code examples, configuration templates, or knowledge base articles
For complete information about MCP resources, including URI schemes and content types, refer to the official MCP resources specification.
Adding Resources
// src/igniter.mcp.ts
export const McpServer = (organizationId: string) => {
return IgniterMCPServer.create()
.router(igniter.router({ controllers: { /* ... */ } }))
.addResource({
uri: `org://${organizationId}/company-policy`,
name: 'Company Policy',
description: 'Current company policies and guidelines',
mimeType: 'text/markdown',
handler: async () => {
const policy = await getCompanyPolicy(organizationId)
return { text: policy }
}
})
.addResource({
uri: `org://${organizationId}/user-guide`,
name: 'User Guide',
description: 'Application user guide and documentation',
mimeType: 'text/html',
handler: async () => {
const guide = await generateUserGuide(organizationId)
return { text: guide }
}
})
.build()
}Resource Types
Implementation Details
Now let's see how to implement and configure the MCP Server in your SaaS Boilerplate.
Configure the MCP Server Factory
Create the server factory in src/igniter.mcp.ts:
// src/igniter.mcp.ts
import { IgniterMCPServer } from '@igniter-js/adapter-mcp-server'
import { igniter } from '@/igniter.router'
import { lead, SubmissionController } from '@/features'
export const McpServer = (organizationId: string) => {
return IgniterMCPServer.create()
.withServerInfo({
name: 'SaaS Boilerplate',
version: '1.0.0'
})
.router(igniter.router({
controllers: {
lead: LeadController,
submission: SubmissionController
}
}))
.withAdapter({
redisUrl: process.env.REDIS_URL!,
basePath: `/mcp/${organizationId}`
})
.build()
}Set Up the Route Handler
Create the Next.js route handler for MCP requests:
// src/app/(mcp)/mcp/[orgId]/[transport]/route.ts
import { McpServer } from '@/igniter.mcp'
import type { NextRequest } from 'next/server';
// Handles authentication for MCP (Model Context Protocol)
const authMcpHandler = async (request: NextRequest, { params }: RouteContext<'/mcp/[orgId]/[transport]'>) => {
const { orgId } = await params;
// Set the organization ID in the request headers
request.headers.set('Authorization', `Bearer ${orgId}`);
// Call the MCP server handler with the modified request
return McpServer(orgId).handler(request);
}
// Export the handler for Next.js to handle both GET and POST requests
export const GET = authMcpHandler
export const POST = authMcpHandler
export const DELETE = authMcpHandlerConfigure Authentication
Ensure your AuthFeatureProcedure supports API key authentication:
// In your auth procedure
getSession: async (options?: GetSessionInput<TRequirements, TRoles>) => {
// ... existing session logic ...
// Check for API key authentication
if (!session) {
const authHeader = request.headers.get('Authorization')
if (authHeader?.startsWith('Bearer ')) {
const token = authHeader.substring(7)
// Validate API key and create virtual session
}
}
}Configuration
Environment Variables
Prop
Type
Client Configuration Examples
Cursor
// .cursor/mcp.json
{
"mcpServers": {
"your-saas": {
"type": "http",
"url": "https://yourapp.com/mcp/sk_xxx.../sse"
}
}
}Claude Desktop
// ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"your-saas": {
"type": "http",
"url": "https://yourapp.com/mcp/sk_xxx.../sse"
}
}
}VS Code Copilot
// .vscode/mcp.json
{
"servers": {
"your-saas": {
"command": "npx",
"args": ["@vercel/mcp-adapter", "http://localhost:3000/mcp/sk_xxx.../sse"]
}
}
}Troubleshooting
Best Practices
See Also
- API Keys - Managing MCP authentication keys
- Controllers & Procedures - Building the APIs that become MCP tools
- Organizations & Tenancy - Understanding multi-tenant architecture
- Authentication & Sessions - How MCP integrates with your auth system