Skip to main content
Glama

MCP Template

by rhit-bhuwalk
MCPServer.ts9.21 kB
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, CallToolResult, ListToolsResult, ReadResourceResult, ListResourcesResult, } from '@modelcontextprotocol/sdk/types.js'; import { z } from 'zod'; import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; import { IDataService, IMCPServer, ListRecordsArgsSchema, GetRecordArgsSchema, CreateRecordArgsSchema, UpdateRecordArgsSchema, DeleteRecordArgsSchema, SearchRecordsArgsSchema } from '../types/index.js'; import { formatErrorResponse, getInputSchema, formatResourceContent, logger, validateInput, safeExecute } from '../utils/index.js'; /** * Base MCP Server implementation that can be extended for specific use cases */ export class MCPServer implements IMCPServer { private server: Server; private dataService: IDataService; /** * Resource URI prefix used for all resources in this server */ protected resourcePrefix: string; /** * Creates a new MCP Server * * @param dataService - Service for data operations * @param options - Server configuration options */ constructor( dataService: IDataService, options: { name: string; version: string; resourcePrefix?: string; } ) { this.dataService = dataService; this.resourcePrefix = options.resourcePrefix || 'mcp://'; this.server = new Server( { name: options.name, version: options.version, }, { capabilities: { resources: {}, tools: {}, }, }, ); this.initializeHandlers(); } /** * Sets up the request handlers for the MCP protocol */ private initializeHandlers(): void { this.server.setRequestHandler(ListResourcesRequestSchema, this.handleListResources.bind(this)); this.server.setRequestHandler(ReadResourceRequestSchema, this.handleReadResource.bind(this)); this.server.setRequestHandler(ListToolsRequestSchema, this.handleListTools.bind(this)); this.server.setRequestHandler(CallToolRequestSchema, this.handleCallTool.bind(this)); } /** * Handles listing available resources */ protected async handleListResources(): Promise<ListResourcesResult> { try { const resources = await this.dataService.listResources(); return { resources: resources.map(resource => ({ uri: resource.uri, mimeType: 'application/json', name: resource.name, })), }; } catch (error) { logger.error('Error listing resources:', error); return { resources: [] }; } } /** * Handles reading a specific resource */ protected async handleReadResource(request: z.infer<typeof ReadResourceRequestSchema>): Promise<ReadResourceResult> { try { const { uri } = request.params; const resource = await this.dataService.getResource(uri); return { contents: [ formatResourceContent(uri, resource), ], }; } catch (error) { logger.error(`Error reading resource ${request.params.uri}:`, error); throw new Error(`Resource not found: ${request.params.uri}`); } } /** * Handles listing available tools */ protected async handleListTools(): Promise<ListToolsResult> { return { tools: [ { name: 'list_records', description: 'List records from a resource', inputSchema: getInputSchema(ListRecordsArgsSchema), }, { name: 'search_records', description: 'Search for records containing specific text', inputSchema: getInputSchema(SearchRecordsArgsSchema), }, { name: 'get_record', description: 'Get a specific record by ID', inputSchema: getInputSchema(GetRecordArgsSchema), }, { name: 'create_record', description: 'Create a new record in a resource', inputSchema: getInputSchema(CreateRecordArgsSchema), }, { name: 'update_record', description: 'Update a record in a resource', inputSchema: getInputSchema(UpdateRecordArgsSchema), }, { name: 'delete_record', description: 'Delete a record from a resource', inputSchema: getInputSchema(DeleteRecordArgsSchema), }, ], }; } /** * Handles calling a specific tool */ protected async handleCallTool(request: z.infer<typeof CallToolRequestSchema>): Promise<CallToolResult> { const toolName = request.params.name; try { switch (toolName) { case 'list_records': { return await safeExecute(toolName, async () => { const args = validateInput(ListRecordsArgsSchema, request.params.arguments); const records = await this.dataService.queryResource( args.resourceUri, { maxRecords: args.maxRecords, filter: args.filter, sort: args.sort, } ); return records; }); } case 'search_records': { return await safeExecute(toolName, async () => { const args = validateInput(SearchRecordsArgsSchema, request.params.arguments); const records = await this.dataService.queryResource( args.resourceUri, { searchTerm: args.searchTerm, fields: args.fields, maxRecords: args.maxRecords, } ); return records; }); } case 'get_record': { return await safeExecute(toolName, async () => { const args = validateInput(GetRecordArgsSchema, request.params.arguments); const resource = await this.dataService.getResource(args.resourceUri); // In a real implementation, you would fetch the specific record // This is a placeholder for the template return { id: args.recordId, resource: resource.name }; }); } case 'create_record': { return await safeExecute(toolName, async () => { const args = validateInput(CreateRecordArgsSchema, request.params.arguments); const record = await this.dataService.createRecord(args.resourceUri, args.data); return record; }); } case 'update_record': { return await safeExecute(toolName, async () => { const args = validateInput(UpdateRecordArgsSchema, request.params.arguments); const record = await this.dataService.updateRecord(args.resourceUri, args.recordId, args.data); return record; }); } case 'delete_record': { return await safeExecute(toolName, async () => { const args = validateInput(DeleteRecordArgsSchema, request.params.arguments); const success = await this.dataService.deleteRecord(args.resourceUri, args.recordId); return { success, id: args.recordId }; }); } default: { return formatErrorResponse(toolName, `Unknown tool: ${toolName}`); } } } catch (error) { return formatErrorResponse(toolName, error); } } /** * Registers a custom tool with the server * * @param toolName - Name of the tool * @param description - Description of what the tool does * @param inputSchema - Zod schema for the tool's input * @param handler - Function to handle tool calls */ public registerTool( toolName: string, description: string, inputSchema: z.ZodType<object>, handler: (_args: unknown) => Promise<unknown> ): void { // Add the tool to the list_tools response const existingHandler = this.handleListTools; this.handleListTools = async () => { const result = await existingHandler.call(this); result.tools.push({ name: toolName, description, inputSchema: getInputSchema(inputSchema), }); return result; }; // Add the tool handler to call_tool const existingCallHandler = this.handleCallTool; this.handleCallTool = async (request) => { if (request.params.name === toolName) { return await safeExecute(toolName, async () => { const args = validateInput(inputSchema, request.params.arguments); return await handler(args); }); } return existingCallHandler.call(this, request); }; } /** * Connects the server to a transport * * @param transport - Transport to connect to */ async connect(transport: Transport): Promise<void> { logger.info(`Connecting MCP server...`); await this.server.connect(transport); logger.info(`MCP server connected`); } /** * Closes the server connection */ async close(): Promise<void> { logger.info(`Closing MCP server...`); await this.server.close(); logger.info(`MCP server closed`); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rhit-bhuwalk/MCP_TEMPLATE'

If you have feedback or need assistance with the MCP directory API, please join our Discord server