mcp-standards.md
Standards for developing Model Context Protocol (MCP) servers including error handling, security, and best practices
mcp-standards.mdv1.2.09.2 KB
mcp-standards.md(markdown)
1# MCP Standards
2
3## Overview
4
5Comprehensive standards for developing Model Context Protocol (MCP) servers, covering naming conventions, error handling, and security considerations.
6
7## Tool Naming Conventions
8
9### Name Collision Prevention
10
11When developing MCP servers that expose tools, **always** use namespaced tool names to prevent conflicts when multiple servers are running simultaneously.
12
13**Format**: `mcp_<server>_<tool_name>`
14
15**Examples**:
16- `mcp_filesystem_get_logs` (instead of `get_logs`)
17- `mcp_shell_get_logs` (instead of `get_logs`)
18- `mcp_database_list_tables` (instead of `list_tables`)
19- `mcp_github_create_issue` (instead of `create_issue`)
20
21### Rationale
22
23The MCP specification currently has no official mechanism for handling duplicate tool names across multiple servers. Different MCP clients handle this inconsistently:
24
25- Some clients crash with errors like "Multiple tools with the same name"
26- Some route to the first/last registered server unpredictably
27- Some clients (like Cursor) automatically namespace tools
28
29**Problem**: When multiple MCP servers expose tools with identical names (e.g., both a filesystem server and shell server exposing `get_logs`), the behavior is undefined and can cause:
30- Tool calls routing to wrong servers
31- Application crashes
32- Unpredictable behavior for end users
33
34### Implementation Guidelines
35
361. **Always namespace tools** - Never expose bare tool names like `get_logs`, `list_files`, etc.
372. **Use descriptive server names** - Choose clear, unique identifiers for your server type
383. **Document tool names clearly** - Make the namespacing obvious in documentation
394. **Test with multiple servers** - Always test your MCP server alongside other common servers
40
41## Error Handling Standards
42
43### MCP Error Types
44```typescript
45enum McpErrorCode {
46 ParseError = -32700, // Invalid JSON received
47 InvalidRequest = -32600, // Request is not valid MCP
48 MethodNotFound = -32601, // Method does not exist
49 InvalidParams = -32602, // Invalid method parameters
50 InternalError = -32603 // Internal server error
51}
52```
53
54### Error Response Pattern
55```typescript
56class McpError extends Error {
57 constructor(
58 public code: McpErrorCode,
59 message: string,
60 public data?: any
61 ) {
62 super(message);
63 this.name = 'McpError';
64 }
65
66 toResponse(): McpErrorResponse {
67 return {
68 error: {
69 code: this.code,
70 message: this.message,
71 data: this.data
72 }
73 };
74 }
75}
76```
77
78### Error Handling Implementation
79```typescript
80// Request validation
81function validateRequest(request: any): void {
82 if (!request.method) {
83 throw new McpError(
84 McpErrorCode.InvalidRequest,
85 'Missing required field: method'
86 );
87 }
88
89 if (typeof request.method !== 'string') {
90 throw new McpError(
91 McpErrorCode.InvalidRequest,
92 'Field "method" must be a string'
93 );
94 }
95}
96
97// Tool execution error handling
98async function executeTool(toolName: string, params: any): Promise<any> {
99 try {
100 const result = await toolHandlers[toolName](params);
101 return { content: [{ type: 'text', text: JSON.stringify(result) }] };
102 } catch (error) {
103 if (error instanceof SecurityError) {
104 throw new McpError(
105 McpErrorCode.InvalidParams,
106 'Security validation failed',
107 { reason: error.message }
108 );
109 }
110
111 if (error instanceof ValidationError) {
112 throw new McpError(
113 McpErrorCode.InvalidParams,
114 'Parameter validation failed',
115 { details: error.details }
116 );
117 }
118
119 // Don't leak internal errors
120 logger.error('MCP-Server', 'Tool execution failed', toolName, { error: error.message });
121 throw new McpError(
122 McpErrorCode.InternalError,
123 'Tool execution failed'
124 );
125 }
126}
127```
128
129## Security Considerations
130
131### Input Validation
132```typescript
133// Schema-based parameter validation
134function validateToolParams(toolName: string, params: any): void {
135 const schema = getToolSchema(toolName);
136 const result = validateSchema(params, schema);
137
138 if (!result.valid) {
139 throw new McpError(
140 McpErrorCode.InvalidParams,
141 'Parameter validation failed',
142 { errors: result.errors }
143 );
144 }
145
146 // Additional security validation
147 if (toolName.includes('file') && params.path) {
148 SecurityValidator.validatePath(params.path);
149 }
150}
151
152// Sanitize output data
153function sanitizeResponse(data: any): any {
154 // Remove sensitive fields
155 const sensitiveFields = ['password', 'secret', 'token', 'key'];
156
157 if (typeof data === 'object' && data !== null) {
158 const sanitized = { ...data };
159
160 sensitiveFields.forEach(field => {
161 if (field in sanitized) {
162 sanitized[field] = '[REDACTED]';
163 }
164 });
165
166 return sanitized;
167 }
168
169 return data;
170}
171```
172
173## Logging and Monitoring
174
175### MCP-Specific Logging
176```typescript
177// Log MCP protocol events
178function logMcpEvent(
179 event: 'request' | 'response' | 'error',
180 method: string,
181 details: any
182): void {
183 logger.info('MCP-Server', `${event}: ${method}`, method, {
184 event,
185 method,
186 ...details
187 });
188}
189
190// Performance monitoring
191function logMcpPerformance(
192 method: string,
193 startTime: number,
194 success: boolean,
195 metadata?: any
196): void {
197 const duration = Date.now() - startTime;
198
199 logger.logPerformance(
200 'MCP-Server',
201 method,
202 undefined, // file size not applicable
203 duration,
204 undefined // quota not applicable
205 );
206
207 // Alert on slow operations
208 if (duration > 1000) {
209 logger.warn('MCP-Server', 'Slow operation detected', method, {
210 duration,
211 success,
212 ...metadata
213 });
214 }
215}
216```
217
218## Configuration Standards
219
220### Server Configuration
221```typescript
222interface McpServerConfig {
223 // Server identification
224 name: string;
225 version: string;
226 description: string;
227
228 // Security settings
229 security: {
230 requireAuth: boolean;
231 allowedOrigins: string[];
232 enableCors: boolean;
233 };
234
235 // Logging configuration
236 logging: {
237 level: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR';
238 enablePerformanceLogging: boolean;
239 enableSecurityLogging: boolean;
240 };
241
242 // Tool configuration
243 tools: {
244 [toolName: string]: {
245 enabled: boolean;
246 permissions: string[];
247 rateLimit?: number;
248 };
249 };
250}
251```
252
253### Environment Configuration
254```typescript
255// Load configuration from environment
256function loadMcpConfig(): McpServerConfig {
257 return {
258 name: process.env.MCP_SERVER_NAME || 'unnamed-server',
259 version: process.env.MCP_SERVER_VERSION || '1.0.0'
260 };
261}
262```
263
264## Testing MCP Servers
265
266### Mock Client for Testing
267```typescript
268class MockMcpClient {
269 async sendRequest(request: any): Promise<any> {
270 // Simulate MCP protocol request/response
271 return await this.server.handleRequest(request);
272 }
273
274 async callTool(name: string, params: any): Promise<any> {
275 return this.sendRequest({
276 method: 'tools/call',
277 params: { name, arguments: params }
278 });
279 }
280}
281```
282
283### Integration Testing
284```typescript
285describe('MCP Server Integration', () => {
286 it('should handle tool calls with proper namespacing', async () => {
287 const response = await client.callTool('mcp_filesystem_read_file', {
288 path: 'test.txt'
289 });
290
291 expect(response.error).toBeUndefined();
292 expect(response.content).toBeDefined();
293 });
294
295 it('should enforce rate limits', async () => {
296 // Make many requests rapidly
297 const promises = Array(150).fill(0).map(() =>
298 client.callTool('mcp_test_tool', {})
299 );
300
301 const results = await Promise.allSettled(promises);
302 const failures = results.filter(r => r.status === 'rejected');
303
304 expect(failures.length).toBeGreaterThan(0);
305 });
306});
307```
308
309## Cross-Cutting Concerns
310
311For shared functionality across multiple servers (logging, monitoring, health checks), coordination is essential:
312
313- **Agree on naming conventions** within your organization
314- **Consider consolidating** common tools into a single "utility" MCP server
315- **Document dependencies** between servers clearly
316- **Implement health checks** for server monitoring
317- **Use consistent error handling** patterns across servers
318
319## Best Practices Checklist
320
321### Development
322- [ ] Tools use proper `mcp_<server>_<tool>` naming convention
323- [ ] Comprehensive error handling with appropriate MCP error codes
324- [ ] Input validation and sanitization for all parameters
325- [ ] Security considerations documented and implemented
326
327### Testing
328- [ ] Unit tests cover all tool functionality
329- [ ] Integration tests with mock MCP clients
330- [ ] Security tests cover attack vectors
331
332### Documentation
333- [ ] Tool schemas clearly documented
334- [ ] Error conditions and responses documented
335- [ ] Configuration options explained
336- [ ] Security considerations outlined
337
338### Monitoring
339- [ ] MCP protocol events logged appropriately
340- [ ] Security events monitored
341- [ ] Health checks implemented
342
343## References
344
345- [MCP Tool Name Collision Discussion](https://github.com/orgs/modelcontextprotocol/discussions/291)
346- [Cursor Community: Tool Name Conflicts](https://forum.cursor.com/t/mcp-tools-with-name-conflicts-arent-routed-correctly/91168)
347- [Spring AI: Multiple Tools Issue](https://github.com/spring-projects/spring-ai/issues/2393)
348- [MCP Specification](https://modelcontextprotocol.io/docs/specification)
349
350---
351
352*Updated: 2025-05-31 - Comprehensive MCP development standards*
Metadata
- Path
- utaba/main/guidance/development/mcp-standards.md
- Namespace
- utaba/main/guidance/development
- Author
- utaba
- Category
- guidance
- Technology
- mcp
- Contract Version
- 1.2.0
- MIME Type
- text/markdown
- Published
- 18-Jul-2025
- Last Updated
- 18-Jul-2025