TypeScript framework for implementing MCP (Model Context Protocol) servers
npm install @agentdb/mcp-protocolA reusable TypeScript framework for implementing MCP (Model Context Protocol) servers that can be used in any application with different tools and backends.
This framework extracts the core MCP protocol handling logic into a reusable, application-agnostic framework. It handles:
- JSON-RPC 2.0 protocol compliance
- MCP-specific methods (initialize, ping, tools/list, tools/call, resources/list, resources/read, prompts/list, prompts/get, etc.)
- HTTP transport with CORS support
- Tool execution delegation
- Resource management with list, read, templates, and subscriptions
- Prompt management with list and get operations
- Notification system for tools, resources, prompts, and cancellation events
- Error handling and response formatting
- Server-Sent Events (SSE) for notifications
``bash`
npm install @agentdb/mcp-protocol
The framework consists of several main components:
1. MCPProtocolHandler - Core JSON-RPC protocol handling
2. MCPHttpTransport - HTTP-specific transport layer
3. ToolsProvider & ToolHandler - Interfaces for tool management and execution
4. ResourcesProvider - Interface for resource management (optional)
5. PromptsProvider - Interface for prompt management (optional)
6. NotificationSubscriber - Interface for handling cancellation notifications (optional)
`typescript
import { ToolsProvider, MCPTool } from '@agentdb/mcp-protocol';
class MyToolsProvider implements ToolsProvider {
getTools(): MCPTool[] {
return [
{
name: 'my_tool',
description: 'Description of what this tool does',
inputSchema: {
type: 'object',
properties: {
param1: { type: 'string', description: 'First parameter' }
},
required: ['param1']
}
}
];
}
}
`
`typescript
import { ToolHandler, ToolExecutionResult } from '@agentdb/mcp-protocol';
const myToolHandler: ToolHandler = async (name, args, context) => {
switch (name) {
case 'my_tool':
const result = await doSomething(args.param1);
return {
content: [{
type: 'text',
text: JSON.stringify({ success: true, result })
}]
};
default:
throw new Error(Unknown tool: ${name});`
}
};
`typescript
import { MCPProtocolHandler, MCPHttpTransport } from '@agentdb/mcp-protocol';
const config = {
serverInfo: {
name: 'My MCP Server',
version: '1.0.0'
}
};
const toolsProvider = new MyToolsProvider();
// Optional: Add resources and prompts providers
const resourcesProvider = new MyResourcesProvider(); // optional
const promptsProvider = new MyPromptsProvider(); // optional
const notificationSubscriber = new MyNotificationSubscriber(); // optional
const protocolHandler = new MCPProtocolHandler(
config,
toolsProvider,
myToolHandler,
resourcesProvider, // optional
promptsProvider, // optional
notificationSubscriber // optional
);
const httpTransport = new MCPHttpTransport(protocolHandler);
`
#### AWS Lambda
`typescript`
export async function handler(event: any, context: any) {
const method = event.requestContext?.http?.method || event.httpMethod;
const body = event.body;
const headers = event.headers || {};
return await httpTransport.handleHttpRequest(method, body, headers);
}
#### Express.js
`typescript`
app.post('/mcp', async (req, res) => {
const response = await httpTransport.handleHttpRequest(
req.method,
JSON.stringify(req.body),
req.headers
);
res.status(response.statusCode);
Object.entries(response.headers).forEach(([key, value]) => {
res.set(key, value);
});
res.send(response.body);
});
`typescript`
interface MCPTool {
name: string;
description: string;
inputSchema: {
type: 'object';
properties: Record
required: string[];
};
}
`typescript`
interface ToolsProvider {
getTools(context?: any): Promise
shouldNotifyToolsChanged?(toolName: string): boolean;
}
`typescript`
interface ToolHandler {
(name: string, args: Record
}
`typescript`
interface ToolExecutionResult {
content: Array<{
type: 'text' | 'image' | 'resource';
text?: string;
data?: string;
mimeType?: string;
}>;
isError?: boolean;
}
`typescript`
interface ResourcesProvider {
getResources(cursor?: string, context?: any): Promise<{ resources: MCPResource[]; nextCursor?: string }> | { resources: MCPResource[]; nextCursor?: string };
getResourceTemplates?(context?: any): Promise
readResource(uri: string, context?: any): Promise
subscribeToResource?(uri: string, context?: any): Promise
unsubscribeFromResource?(uri: string, context?: any): Promise
shouldNotifyResourcesChanged?(): boolean;
shouldNotifyResourceUpdated?(uri: string): boolean;
}
`typescript`
interface PromptsProvider {
getPrompts(cursor?: string, context?: any): Promise<{ prompts: MCPPrompt[]; nextCursor?: string }> | { prompts: MCPPrompt[]; nextCursor?: string };
getPrompt(name: string, args?: Record
shouldNotifyPromptsChanged?(): boolean;
}
`typescript`
interface NotificationSubscriber {
onCancelled?(requestId: string): void;
}
The framework supports all standard MCP protocol methods:
- Initialize the MCP session
- ping - Health check
- notifications/initialized - Initialization complete notification$3
- tools/list - List available tools
- tools/call - Execute a tool$3
- resources/list - List available resources
- resources/read - Read resource contents
- resources/templates/list - List resource templates
- resources/subscribe - Subscribe to resource updates
- resources/unsubscribe - Unsubscribe from resource updates$3
- prompts/list - List available prompts
- prompts/get - Get a specific prompt with arguments$3
- notifications/tools/list_changed - Tools list changed
- notifications/resources/list_changed - Resources list changed
- notifications/resources/updated - Resource updated
- notifications/prompts/list_changed - Prompts list changed
- notifications/cancelled - Request cancelledAdvanced Features
$3
Tools can be generated dynamically based on context:`typescript
class DynamicToolsProvider implements ToolsProvider {
async getTools(context?: any): Promise {
// Generate tools based on context
const tools = await fetchAvailableTools(context);
return tools.map(tool => ({
name: tool.name,
description: tool.description,
inputSchema: tool.schema
}));
}
}
`$3
Notify clients when tools change:`typescript
shouldNotifyToolsChanged(toolName: string): boolean {
// Return true for tools that modify the available tool set
return toolName === 'create_tool' || toolName === 'delete_tool';
}
`$3
Implement a resources provider:`typescript
class MyResourcesProvider implements ResourcesProvider {
getResources(cursor?: string) {
return {
resources: [
{
uri: 'file:///project/README.md',
name: 'README.md',
title: 'Project Documentation',
description: 'Main project documentation',
mimeType: 'text/markdown'
}
]
};
} readResource(uri: string) {
if (uri === 'file:///project/README.md') {
return [{
uri,
name: 'README.md',
mimeType: 'text/markdown',
text: '# My Project\n\nThis is the project documentation.'
}];
}
throw new Error('Resource not found');
}
}
`$3
Implement a prompts provider:`typescript
class MyPromptsProvider implements PromptsProvider {
getPrompts(cursor?: string) {
return {
prompts: [
{
name: 'code_review',
title: 'Code Review',
description: 'Review code for quality and improvements',
arguments: [
{
name: 'code',
description: 'The code to review',
required: true
}
]
}
]
};
} getPrompt(name: string, args?: Record) {
if (name === 'code_review') {
return {
description: 'Code review prompt',
messages: [
{
role: 'user',
content: {
type: 'text',
text:
Please review this code:\n\n${args?.code || 'No code provided'}
}
}
]
};
}
throw new Error('Prompt not found');
}
}
`$3
Pass custom context to tool handlers:`typescript
const executionContext = {
debug: true,
metadata: {
userId: 'user123',
sessionId: 'session456'
}
};const response = await httpTransport.handleHttpRequest(
method, body, headers, executionContext
);
`Error Handling
The framework automatically handles:
- JSON-RPC protocol errors
- Tool execution errors
- HTTP transport errors
- Request validation errors
Errors are returned in standard JSON-RPC error format:
`json
{
"jsonrpc": "2.0",
"id": "request-id",
"error": {
"code": -32603,
"message": "Tool execution failed: Error message"
}
}
`Examples
See the
examples/` directory for complete examples including:MIT License - see LICENSE file for details.
This package is part of the AgentDB project. Please see the main repository for contribution guidelines.