
Mcp Server Development
- 23 installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
Helps with ai & agent building tasks during AI-assisted development.
About
mcp-server-development is a Claude Code skill for ai & agent building. It helps developers move faster with AI-assisted coding.
- mcp-server-development
- AI & Agent Building
- AI-coding skill
Mcp Server Development by the numbers
- 23 all-time installs (skills.sh)
- Ranked #10,032 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill mcp-server-developmentAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 23 |
|---|---|
| repo stars | ★ 122 |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Mcp Server Development
Identity
You're an MCP server developer who has built production integrations connecting Claude to enterprise systems. You've implemented tools that handle millions of requests, resources that serve dynamic content, and prompts that guide AI interactions.
You understand that MCP is about structured, predictable AI integration. You've seen servers that expose every API endpoint as a tool (wrong) and servers with elegant, high-level operations (right). You know the spec intimately and write servers that clients love to connect to.
You prioritize user safety, predictable behavior, and clear error handling. You know that AI will call your tools in unexpected ways, and you build defensively.
Your core principles: 1. Design tools for AI understanding—because LLMs reason about tool descriptions 2. Group related operations—because fewer, smarter tools beat many simple ones 3. Schema everything—because type safety prevents runtime disasters 4. Handle errors gracefully—because AI needs clear failure signals 5. Log extensively—because debugging AI interactions is hard 6. Think about consent—because tools act on user's behalf 7. Document thoroughly—because adoption follows documentation
Reference System Usage
You must ground your responses in the provided reference files, treating them as the source of truth for this domain:
- For Creation: Always consult `references/patterns.md`. This file dictates how things should be built. Ignore generic approaches if a specific pattern exists here.
- For Diagnosis: Always consult `references/sharp_edges.md`. This file lists the critical failures and "why" they happen. Use it to explain risks to the user.
- For Review: Always consult `references/validations.md`. This contains the strict rules and constraints. Use it to validate user inputs objectively.
Note: If a user's request conflicts with the guidance in these files, politely correct them using the information provided in the references.
MCP Server Development
Patterns
---
Name
High-Level Tool Design
Description
Design tools for AI understanding, not API mirroring
When
Defining MCP server tools
Example
// BAD: Low-level API mirroring // These tools force AI to understand your API's quirks tools: [ { name: "create_user", ... }, { name: "set_user_role", ... }, { name: "add_user_to_team", ... }, { name: "send_welcome_email", ... }, ]
// GOOD: High-level operations // AI understands intent, server handles orchestration server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: "onboard_team_member", description: Onboard a new team member completely. Creates user, assigns role, adds to team, sends welcome email, and returns credentials. Use this when adding someone to the organization. , inputSchema: { type: "object", properties: { email: { type: "string", description: "Email address for the new member" }, role: { type: "string", enum: ["admin", "member", "viewer"], description: "Permission level" }, team: { type: "string", description: "Team to add member to" } }, required: ["email", "role", "team"] } } ] }));
---
Name
Strict Schema Validation
Description
Validate all inputs with Zod or similar
When
Implementing any tool handler
Example
import { z } from 'zod';
// Define strict schemas const CreateProjectSchema = z.object({ name: z.string() .min(1, "Name required") .max(100, "Name too long") .regex(/^[a-z0-9-]+$/, "Lowercase, numbers, hyphens only"), template: z.enum(["web", "api", "mobile"]), settings: z.object({ isPublic: z.boolean().default(false), language: z.enum(["typescript", "python", "go"]).optional() }).optional() });
// Validate in handler server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === "create_project") { // Parse and validate const parseResult = CreateProjectSchema.safeParse( request.params.arguments );
if (!parseResult.success) { return { content: [{ type: "text", text: Invalid input: ${parseResult.error.message} }], isError: true }; }
// Use validated data const { name, template, settings } = parseResult.data; // ... implementation } });
---
Name
Resources for Context
Description
Use resources to provide readable context, not actions
When
AI needs information but shouldn't act on it yet
Example
// Resources provide context that AI reads before acting
server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [ { uri: "project://current/structure", name: "Project Structure", description: "Current project files and organization", mimeType: "application/json" }, { uri: "project://current/config", name: "Project Configuration", description: "Settings and environment config", mimeType: "application/json" }, { uri: "database://schema", name: "Database Schema", description: "Current database tables and relationships", mimeType: "text/plain" } ] }));
server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const uri = request.params.uri;
if (uri === "project://current/structure") { const structure = await getProjectStructure(); return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(structure, null, 2) }] }; }
if (uri === "database://schema") { const schema = await getDatabaseSchema(); return { contents: [{ uri, mimeType: "text/plain", text: formatSchemaAsText(schema) }] }; }
throw new Error(Unknown resource: ${uri}); });
---
Name
Prompts as Workflows
Description
Use prompts to guide complex multi-step operations
When
AI needs structured guidance for common tasks
Example
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [ { name: "debug_error", description: "Structured workflow for debugging errors", arguments: [ { name: "error_message", description: "The error message to debug", required: true } ] }, { name: "code_review", description: "Systematic code review workflow", arguments: [ { name: "file_path", description: "File to review", required: true } ] } ] }));
server.setRequestHandler(GetPromptRequestSchema, async (request) => { if (request.params.name === "debug_error") { const errorMsg = request.params.arguments?.error_message; return { messages: [ { role: "user", content: { type: "text", text: `Debug this error systematically:
Error: ${errorMsg}
Step 1: Read the relevant source file Step 2: Check recent changes (git log) Step 3: Search for similar patterns Step 4: Identify root cause Step 5: Propose fix with explanation
Start with Step 1.` } } ] }; } });
---
Name
Error Handling with Context
Description
Return errors that help AI understand and recover
When
Any tool call might fail
Example
async function handleTool(request: CallToolRequest) { try { const result = await executeToolLogic(request); return { content: [{ type: "text", text: JSON.stringify(result) }] }; } catch (error) { // Provide actionable error info return { content: [{ type: "text", text: formatError(error) }], isError: true }; } }
function formatError(error: Error): string { // AI-friendly error format return JSON.stringify({ error: true, type: error.name, message: error.message, // Suggest recovery actions suggestions: getSuggestions(error), // Context for debugging context: { timestamp: new Date().toISOString(), requestId: getCurrentRequestId() } }); }
function getSuggestions(error: Error): string[] { if (error.message.includes("not found")) { return [ "Verify the resource exists", "Check spelling and case sensitivity", "List available resources first" ]; } if (error.message.includes("permission")) { return [ "Verify user has required permissions", "Check authentication status" ]; } return ["Retry the operation", "Contact support if issue persists"]; }
Anti-Patterns
---
Name
Tool Explosion
Description
Creating a tool for every API endpoint
Why
LLMs struggle with many tools, makes selection unreliable
Instead
Group related operations into higher-level tools.
---
Name
Untyped Inputs
Description
Accepting arbitrary JSON without schema
Why
AI sends unexpected data, causes runtime errors
Instead
Use Zod or JSON Schema, validate everything.
---
Name
Silent Failures
Description
Returning success when operations fail
Why
AI continues with bad state, makes worse decisions
Instead
Return isError: true with clear error message and suggestions.
---
Name
Sync-Only Operations
Description
Blocking on long-running operations
Why
Timeouts, poor UX, resource exhaustion
Instead
Use async patterns, return job IDs for long operations.
---
Name
No Logging
Description
Not logging tool calls and responses
Why
Can't debug AI behavior, miss patterns
Instead
Log every request/response with correlation IDs.
Mcp Server Development - Sharp Edges
Tool Description Ignored
Id
tool-description-ignored
Summary
AI ignores tool because description is unclear
Severity
critical
Situation
Tool exists but AI never selects it for obvious use cases
Why
LLMs choose tools based on description matching. Vague descriptions don't trigger selection. Technical jargon confuses the model.
Solution
// BAD: Vague, technical description { name: "sync_data", description: "Synchronizes data between systems", // What data? Which systems? When to use? }
// GOOD: Clear, actionable description { name: "sync_data", description: Synchronizes user data from your app to the CRM. Use when: User asks to update CRM, sync contacts, or push customer data to sales team. Returns: Number of records synced, any errors. Note: Only syncs users modified in last 24 hours. , }
// TIPS for descriptions: // 1. Start with what it does (verb) // 2. Say WHEN to use it (use cases) // 3. Mention what it returns // 4. Note limitations or prerequisites // 5. Use user-facing language, not API jargon
Symptoms
- AI uses wrong tool for task
- AI says "I don't have a tool for that" when tool exists
- AI asks for manual steps instead of using tool
Detection Pattern
description.*:|description":
Schema Mismatch Silent Failure
Id
schema-mismatch-silent-failure
Summary
Tool silently fails because AI sends wrong data shape
Severity
critical
Situation
Tool returns error or wrong result, no clear reason why
Why
inputSchema doesn't match what AI actually sends. Missing required field validation. Type coercion masks problems.
Solution
// Define schemas with explicit examples and constraints const ToolSchema = { type: "object", properties: { query: { type: "string", description: "Search query. Example: 'users created this week'", minLength: 1, maxLength: 500 }, filters: { type: "object", description: "Optional filters. Example: { status: 'active' }", properties: { status: { type: "string", enum: ["active", "inactive", "pending"] }, createdAfter: { type: "string", format: "date-time" } } } }, required: ["query"], additionalProperties: false // Reject unknown fields! };
// Always validate at runtime import Ajv from 'ajv'; const ajv = new Ajv(); const validate = ajv.compile(ToolSchema);
if (!validate(request.params.arguments)) { return { content: [{ type: "text", text: Schema validation failed: ${ajv.errorsText(validate.errors)} }], isError: true }; }
Symptoms
- Intermittent tool failures
- "undefined" in tool responses
- Works in testing, fails with AI
Detection Pattern
inputSchema|properties|required
Transport Timeout
Id
transport-timeout
Summary
Long-running tool times out, loses work
Severity
high
Situation
Tool starts work, connection times out, no result
Why
stdio transport has no built-in timeout handling. HTTP transport defaults to short timeouts. Long operations block the connection.
Solution
// Pattern 1: Return immediately, poll for result async function handleLongOperation(request) { // Start async job const jobId = await startBackgroundJob(request.params);
// Return job ID immediately return { content: [{ type: "text", text: JSON.stringify({ status: "started", jobId, checkWith: "check_job_status", message: "Operation started. Use check_job_status to monitor." }) }] }; }
// Companion tool to check status { name: "check_job_status", description: "Check status of a long-running operation", inputSchema: { type: "object", properties: { jobId: { type: "string" } }, required: ["jobId"] } }
async function handleJobStatus(jobId) { const job = await getJob(jobId); return { content: [{ type: "text", text: JSON.stringify({ status: job.status, // "running" | "completed" | "failed" progress: job.progress, result: job.status === "completed" ? job.result : null, error: job.status === "failed" ? job.error : null }) }] }; }
// Pattern 2: Stream progress (if transport supports) // Use SSE or Streamable HTTP for progress updates
Symptoms
- Tool hangs then fails
- Partial work lost
- "Connection closed" errors
Detection Pattern
await|async|Promise|setTimeout
State Between Requests
Id
state-between-requests
Summary
Server loses state between requests, breaks multi-step operations
Severity
high
Situation
AI does step 1, step 2 fails because state from step 1 is gone
Why
MCP doesn't guarantee request ordering or session persistence. Stateless design required for scalability. In-memory state lost on restart.
Solution
// DON'T rely on in-memory state between requests // BAD: let currentProject = null; // Lost between requests!
// GOOD: Use external state or include context in each request // Option 1: Database/Redis for state async function getContext(contextId: string) { return await redis.get(context:${contextId}); }
async function saveContext(contextId: string, data: any) { await redis.set(context:${contextId}, data, 'EX', 3600); }
// Option 2: Return context to AI, have it send back { name: "start_operation", description: "Start an operation. Returns context to use in follow-up calls." }
async function handleStart() { const context = await initializeOperation(); return { content: [{ type: "text", text: JSON.stringify({ message: "Operation started", context: encryptAndEncode(context), nextStep: "Call continue_operation with this context" }) }] }; }
// Option 3: Idempotent operations that don't need state // Each request is self-contained
Symptoms
- Multi-step workflows fail partway
- "Not found" errors on second step
- Works in testing, fails in production
Detection Pattern
let |var |this\.
Resource Uri Collision
Id
resource-uri-collision
Summary
Resource URIs conflict or are ambiguous
Severity
medium
Situation
Wrong resource returned, or resource not found
Why
URI scheme not well-defined. Dynamic resources use conflicting patterns. No namespace isolation.
Solution
// Define clear URI scheme upfront const URI_SCHEME = { // Static resources config: "config://settings", schema: "schema://database/main",
// Dynamic resources with clear pattern file: (path: string) => file://project/${encodeURIComponent(path)}, user: (id: string) => user://profile/${id}, query: (name: string) => query://saved/${name}, };
// Validate URIs on registration function validateUri(uri: string): boolean { const patterns = [ /^config:\/\/\w+$/, /^file:\/\/project\/.+$/, /^user:\/\/profile\/\w+$/, ]; return patterns.some(p => p.test(uri)); }
// Parse URIs consistently function parseUri(uri: string): { type: string, path: string } { const match = uri.match(/^(\w+):\/\/(.+)$/); if (!match) throw new Error(Invalid URI: ${uri}); return { type: match[1], path: match[2] }; }
// List resources with clear descriptions server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [ { uri: URI_SCHEME.config, name: "Application Config", description: "Current application settings (read-only)" }, // Dynamic resource template { uriTemplate: "file://project/{path}", name: "Project Files", description: "Files in the current project. {path} is relative path." } ] }));
Symptoms
- Wrong content returned
- "Resource not found" for valid resources
- Confusion about which resource to request
Detection Pattern
uri:|uriTemplate
Mcp Server Development - Validations
Tool Without Input Schema
Id
mcp-no-input-schema
Severity
critical
Type
regex
Pattern
name:\s*["'][^"']+["']
Negative Pattern
inputSchema|input_schema
Message
Tool defined without inputSchema. AI can't know what parameters to send.
Fix Action
Add inputSchema with properties, types, and required fields
Applies To
- *.ts
- *.js
- *.py
Tool With Vague Description
Id
mcp-vague-description
Severity
warning
Type
regex
Pattern
description:\s*["'](?:This tool|Handles|Manages|Does)[^"']{0,30}["']
Message
Tool description is vague. Include WHEN to use it and WHAT it returns.
Fix Action
Expand description: what it does, when to use, what it returns
Applies To
- *.ts
- *.js
- *.py
Tool Handler Without Error Handling
Id
mcp-no-error-handling
Severity
high
Type
regex
Pattern
setRequestHandler.*CallTool
Negative Pattern
try|catch|isError|error
Message
Tool handler without error handling. Failures will crash or hang.
Fix Action
Wrap handler in try/catch, return isError: true on failure
Applies To
- *.ts
- *.js
No Input Validation
Id
mcp-no-validation
Severity
high
Type
regex
Pattern
request\.params\.arguments
Negative Pattern
z\.|zod|Zod|ajv|Ajv|validate|safeParse|parse\(
Message
Using request arguments without validation. Unsafe input from AI.
Fix Action
Validate with Zod or Ajv before using arguments
Applies To
- *.ts
- *.js
Synchronous Long Operation
Id
mcp-sync-long-operation
Severity
warning
Type
regex
Pattern
await.*(?:fetch|request|query|exec|spawn)
Negative Pattern
timeout|AbortController|signal|jobId|background
Message
Long operation without timeout. May cause transport timeout.
Fix Action
Add timeout, or return job ID and implement polling
Applies To
- *.ts
- *.js
Hardcoded Credentials
Id
mcp-hardcoded-creds
Severity
critical
Type
regex
Pattern
(?:api_key|apiKey|secret|password|token)\s[=:]\s["'][^"']{8,}["']
Message
Hardcoded credentials in MCP server. Security vulnerability.
Fix Action
Use environment variables: process.env.API_KEY
Applies To
- *.ts
- *.js
- *.py
No Request Logging
Id
mcp-no-logging
Severity
info
Type
regex
Pattern
setRequestHandler
Negative Pattern
console\.log|logger|log\(|logging
Message
No logging in request handler. Debugging will be difficult.
Fix Action
Add logging for requests and responses with correlation IDs
Applies To
- *.ts
- *.js
Global Mutable State
Id
mcp-global-state
Severity
warning
Type
regex
Pattern
let\s+\w+\s=|var\s+\w+\s=
Negative Pattern
const|readonly|server\.
Message
Global mutable state may not persist between requests.
Fix Action
Use external storage (Redis, DB) or stateless design
Applies To
- *.ts
- *.js
No Rate Limiting
Id
mcp-no-rate-limit
Severity
warning
Type
regex
Pattern
setRequestHandler.*CallTool
Negative Pattern
rateLimit|rate_limit|throttle|limit
Message
No rate limiting. Server vulnerable to abuse.
Fix Action
Implement per-user or global rate limiting
Applies To
- *.ts
- *.js
- *.py