MCP Server Development Skill
Context
This skill applies when:
- Implementing MCP protocol tools, resources, and prompts
- Creating MCP server handlers for European Parliament data
- Designing tool input/output schemas
- Implementing resource URI patterns
- Creating prompt templates for AI assistants
- Handling MCP protocol errors and edge cases
- Testing MCP server implementations
- Optimizing MCP tool performance
MCP (Model Context Protocol) is the foundation of this server. All data access must follow MCP specification patterns to ensure compatibility with MCP clients.
Rules
- Follow MCP Specification: Implement all handlers according to MCP protocol specification
- Validate Tool Inputs: Use Zod schemas to validate all tool inputs before processing
- Return Structured Responses: Always return MCP-compliant response structures with
content array
- Handle Errors Gracefully: Catch errors and return safe error messages (never expose internal details)
- Use Type Safety: Leverage TypeScript types for all MCP handlers and request/response objects
- Implement Resources with URIs: Use consistent URI patterns (e.g.,
ep://meps/{id})
- Provide Tool Descriptions: Write clear, concise descriptions for all tools, resources, and prompts
- Document Input Schemas: Use JSON Schema in tool listings to document expected inputs
- Log MCP Operations: Log all tool invocations, resource accesses for audit trails
- Test MCP Handlers: Write comprehensive tests for all MCP tools and resources
Examples
✅ Good Pattern: MCP Tool Implementation
import { z } from 'zod';
import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
// Input schema with validation
const SearchMEPsInputSchema = z.object({
country: z.string().length(2).regex(/^[A-Z]{2}$/).optional(),
limit: z.number().int().min(1).max(100).default(20),
}).strict();
// Tool handler
export async function handleSearchMEPs(request: typeof CallToolRequestSchema._type) {
const input = SearchMEPsInputSchema.parse(request.params.arguments);
try {
const meps = await searchMEPs(input);
return {
content: [{
type: "text",
text: JSON.stringify({ count: meps.length, meps }, null, 2)
}]
};
} catch (error) {
console.error('[MCP Tool Error] search_meps:', error);
throw new Error('Failed to search MEPs. Please try again.');
}
}
// Tool registration
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: "search_meps",
description: "Search Members of the European Parliament by filters",
inputSchema: {
type: "object",
properties: {
country: { type: "string", pattern: "^[A-Z]{2}$" },
limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
},
},
}],
}));
✅ Good Pattern: MCP Resource Implementation
// Resource URI pattern
const MEP_RESOURCE_TEMPLATE = "ep://meps/{id}";
// List resources
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
resources: [{
uri: MEP_RESOURCE_TEMPLATE,
name: "European Parliament Member",
description: "Detailed MEP information",
mimeType: "application/json",
}],
}));
// Read resource
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const uri = request.params.uri;
const match = uri.match(/^ep:\/\/meps\/(\d+)$/);
if (!match) {
throw new Error(`Invalid MEP resource URI: ${uri}`);
}
const mepId = parseInt(match[1], 10);
const mep = await getMEPById(mepId);
return {
contents: [{
uri,
mimeType: "application/json",
text: JSON.stringify(mep, null, 2),
}],
};
});
Anti-Patterns
❌ Bad: No Input Validation
// NEVER - no validation!
async function bad(request: any) {
const data = await fetch(request.params.arguments.url); // Injection risk!
return { content: [{ type: "text", text: data }] };
}
❌ Bad: Exposing Internal Errors
// NEVER - exposes internals!
async function bad(request: any) {
try {
return await process(request);
} catch (error) {
throw error; // Exposes stack trace!
}
}
ISMS Compliance
- SC-002: Input validation for all tool parameters (Zod schemas)
- AU-002: Audit logging for tool invocations (personal-data tools especially)
- AC-003: Rate limiting and access control (
EP_RATE_LIMIT)
- SC-001: Safe output — no raw upstream errors, sanitised responses
Policy References
Primary:
Related:
1---2name: mcp-server-development3description: MCP protocol patterns, tool implementation, resource handlers, prompt templates, and error handling for Model Context Protocol servers4license: MIT5---67# MCP Server Development Skill89## Context1011This skill applies when:12- Implementing MCP protocol tools, resources, and prompts13- Creating MCP server handlers for European Parliament data14- Designing tool input/output schemas15- Implementing resource URI patterns16- Creating prompt templates for AI assistants17- Handling MCP protocol errors and edge cases18- Testing MCP server implementations19- Optimizing MCP tool performance2021MCP (Model Context Protocol) is the foundation of this server. All data access must follow MCP specification patterns to ensure compatibility with MCP clients.2223## Rules24251. **Follow MCP Specification**: Implement all handlers according to [MCP protocol specification](https://spec.modelcontextprotocol.io/)262. **Validate Tool Inputs**: Use Zod schemas to validate all tool inputs before processing273. **Return Structured Responses**: Always return MCP-compliant response structures with `content` array284. **Handle Errors Gracefully**: Catch errors and return safe error messages (never expose internal details)295. **Use Type Safety**: Leverage TypeScript types for all MCP handlers and request/response objects306. **Implement Resources with URIs**: Use consistent URI patterns (e.g., `ep://meps/{id}`)317. **Provide Tool Descriptions**: Write clear, concise descriptions for all tools, resources, and prompts328. **Document Input Schemas**: Use JSON Schema in tool listings to document expected inputs339. **Log MCP Operations**: Log all tool invocations, resource accesses for audit trails3410. **Test MCP Handlers**: Write comprehensive tests for all MCP tools and resources3536## Examples3738### ✅ Good Pattern: MCP Tool Implementation3940```typescript41import { z } from 'zod';42import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';4344// Input schema with validation45const SearchMEPsInputSchema = z.object({46 country: z.string().length(2).regex(/^[A-Z]{2}$/).optional(),47 limit: z.number().int().min(1).max(100).default(20),48}).strict();4950// Tool handler51export async function handleSearchMEPs(request: typeof CallToolRequestSchema._type) {52 const input = SearchMEPsInputSchema.parse(request.params.arguments);53 54 try {55 const meps = await searchMEPs(input);56 57 return {58 content: [{59 type: "text",60 text: JSON.stringify({ count: meps.length, meps }, null, 2)61 }]62 };63 } catch (error) {64 console.error('[MCP Tool Error] search_meps:', error);65 throw new Error('Failed to search MEPs. Please try again.');66 }67}6869// Tool registration70server.setRequestHandler(ListToolsRequestSchema, async () => ({71 tools: [{72 name: "search_meps",73 description: "Search Members of the European Parliament by filters",74 inputSchema: {75 type: "object",76 properties: {77 country: { type: "string", pattern: "^[A-Z]{2}$" },78 limit: { type: "number", minimum: 1, maximum: 100, default: 20 },79 },80 },81 }],82}));83```8485### ✅ Good Pattern: MCP Resource Implementation8687```typescript88// Resource URI pattern89const MEP_RESOURCE_TEMPLATE = "ep://meps/{id}";9091// List resources92server.setRequestHandler(ListResourcesRequestSchema, async () => ({93 resources: [{94 uri: MEP_RESOURCE_TEMPLATE,95 name: "European Parliament Member",96 description: "Detailed MEP information",97 mimeType: "application/json",98 }],99}));100101// Read resource102server.setRequestHandler(ReadResourceRequestSchema, async (request) => {103 const uri = request.params.uri;104 const match = uri.match(/^ep:\/\/meps\/(\d+)$/);105 106 if (!match) {107 throw new Error(`Invalid MEP resource URI: ${uri}`);108 }109 110 const mepId = parseInt(match[1], 10);111 const mep = await getMEPById(mepId);112 113 return {114 contents: [{115 uri,116 mimeType: "application/json",117 text: JSON.stringify(mep, null, 2),118 }],119 };120});121```122123## Anti-Patterns124125### ❌ Bad: No Input Validation126```typescript127// NEVER - no validation!128async function bad(request: any) {129 const data = await fetch(request.params.arguments.url); // Injection risk!130 return { content: [{ type: "text", text: data }] };131}132```133134### ❌ Bad: Exposing Internal Errors135```typescript136// NEVER - exposes internals!137async function bad(request: any) {138 try {139 return await process(request);140 } catch (error) {141 throw error; // Exposes stack trace!142 }143}144```145146## ISMS Compliance147148- **SC-002**: Input validation for all tool parameters (Zod schemas)149- **AU-002**: Audit logging for tool invocations (personal-data tools especially)150- **AC-003**: Rate limiting and access control (`EP_RATE_LIMIT`)151- **SC-001**: Safe output — no raw upstream errors, sanitised responses152153### Policy References154155**Primary:**156157- [Secure Development Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Secure_Development_Policy.md) — Input validation, audit logging, secure error handling158- [OWASP LLM Security Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/OWASP_LLM_Security_Policy.md) — Prompt-injection resistance in tool descriptions, output filtering159160**Related:**161162- [Information Security Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Information_Security_Policy.md)163- [Privacy Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Privacy_Policy.md) — MEP personal-data tools164- [Access Control Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Access_Control_Policy.md) — Least privilege on tool capabilities165- [AI Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/AI_Policy.md) — Responsible MCP-tool integration with LLMs166- [Open Source Policy](https://github.com/Hack23/ISMS-PUBLIC/blob/main/Open_Source_Policy.md) — Attribution in tool output