MCP Development
Skill Location: This file
Quick Start
Create MCP server: /toolkit:build:package → Full structure with SDK setup
Define tools: Follow <tool_schema> template with JSON Schema validation
Choose transport: stdio (local) or http (remote) based on use case
Validate server: scripts/test_mcp.sh → Quick integration testing
Why: MCP provides structured tool definitions with type safety—prevents runtime errors from malformed inputs.
Navigation
| If you need... | Read... |
|---|---|
| Create MCP server | ## Quick Start → /toolkit:build:package |
| Define tool schema | ## Implementation Patterns |
| Choose transport | ## Implementation Patterns |
| Context engineering | context-engineering skill |
| Authentication patterns | references/pattern_auth_patterns.md |
| Validate server | See validation section in this file |
| 2026 syntax features | references/pattern_2026_syntax.md |
| Plugin integration | references/pattern_plugin_integration.md |
Critical Reference Loading
Key Reference: Authentication patterns in pattern_auth_patterns.md contain JSON Schema validation rules critical for runtime stability. Read these patterns in full when implementing authentication—summarization causes validation failures.
System Requirements
- MCP SDK:
@modelcontextprotocol/sdk(latest via npm/pnpm) - TypeScript: >= 5.0
- Node.js: >= 18
- Validation:
scripts/test_mcp.sh(bundled CLI abstraction) - Transport: stdio (local) or http (remote)
- Schema: Valid JSON Schema for tool input/output
Operational Patterns
This skill follows these behavioral patterns:
- Planning: Switch to planning mode for architectural decisions
- Discovery: Locate files matching patterns and search file contents for MCP integration
- Delegation: Delegate planning and exploration to specialized workers
- Tracking: Maintain a visible task list for MCP development
Implementation Patterns
Pattern: Tool Definition Template
{
"name": "tool_name",
"description": "Tool to <verb> <resource>. Use when <condition>. Constraints: <limits>.",
"inputSchema": {
"type": "object",
"properties": {
"param_name": {
"type": "string",
"description": "Parameter description",
"enum": ["value1", "value2"],
"format": "email"
}
},
"required": ["param_name"],
"additionalProperties": false
},
"outputSchema": {
"type": "object",
"properties": {
"result": { "type": "string", "description": "Result description" }
}
}
}
Pattern: stdio Transport (Local)
{
"transport": {
"type": "stdio",
"command": "node",
"args": ["server.js"]
}
}
Pattern: http Transport (Remote)
{
"transport": {
"type": "http",
"url": "http://localhost:3000",
"authentication": {
"type": "bearer",
"token": "${API_TOKEN}"
}
}
}
Pattern: Error Message
"Invalid input: parameter must be a valid email address. Please provide a valid email like user@example.com."
Troubleshooting
| Issue | Symptom | Solution |
|---|---|---|
| Schema validation fails | Tool calls rejected | Ensure valid JSON Schema, add descriptions |
| Agent doesn't use tool | Poor description | Use formula: "Tool to X. Use when Y. Constraints: Z." |
| Missing required fields | Runtime errors | Add required array in inputSchema |
| Wrong parameter type | Type errors | Match type in schema (string/number/boolean/array/object) |
| Enum values not recognized | Unexpected behavior | Use enums for finite value sets |
| Transport connection fails | Server not reachable | Verify stdio command or http URL |
- Tool schema & transport → https://code.claude.com/docs/en/mcp.md
- Authentication patterns →
Glob: lookup_auth_patterns.md
MCP (Model Context Protocol) servers provide structured integration between Claude and external systems. They define tools, resources, and prompts that Claude can access through standardized protocols.
Core principle: Treat every MCP tool like a tiny, well-written prompt for the agent. The descriptions and schemas you write directly influence how well Claude understands and uses your tools.
Prompt Engineering for Tool Descriptions
Standard description format:
description: "Brief description, non spoiling of the content. Use when {trigger condition + keywords + key sentences user might say that should lead the tool to be invoked}. Not for {exclusions}."
Alternative formula (MCP-style):
Tool to <verb> <resource>. Use when <trigger condition>. Constraints: <key limits>.
{
"name": "create_channel",
"description": "Tool to create a new Slack channel. Use when the user asks to create a new channel, make a new channel, or set up a channel."
}
{
"name": "create_contact",
"description": "Tool to create a new Salesforce contact. Use when the user wants to create a contact, add a new contact, or input contact data. Required workflow: call discover_required_fields('Contact') first to identify mandatory fields and prevent creation errors. Not for bulk imports."
}
{
"name": "get_calls",
"description": "Tool to list all calls in a date range. Use when the user asks for calls across a workspace without user/workspace filtering. Constraint: this tool does not support user or workspace filters; for filtered calls, use search_calls_extensive instead. Not for real-time or live call data."
}
- Start with action: Verb + resource (what the tool does)
- Add trigger condition: Keywords and phrases that activate the tool
- Front-load constraints: Required workflows, hard limits, alternatives
- End with exclusions: "Not for" clause prevents misuse
- Keep descriptions 1-2 sentences
- Under 1024 characters in practice
- Avoid marketing fluff
- Focus on non-spoiling description
Schema-Level Prompt Engineering
Use enums instead of free text when:
- Finite set of values (status: ["pending", "approved", "rejected"])
- Predefined options (unit: ["celsius", "fahrenheit"])
- Boolean-like choices (severity: ["low", "medium", "high", "critical"])
Why: Enums remove ambiguity and act as strong constraints at the schema level.
Use format constraints:
"email": {
"type": "string",
"format": "email",
"description": "User's email address"
}
Common formats: email, date, date-time, uri, uuid
State mutual exclusivity or requirements in descriptions:
"agent_id": {
"type": "string",
"description": "Unique identifier for agent. At least one of agent_id, user_id, app_id, or run_id must be provided."
}
Define outputSchema for complex responses:
{
"name": "get_weather",
"description": "Get weather conditions for a city",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"enum": ["New York", "Chicago", "Los Angeles"],
"description": "Choose city"
}
}
},
"outputSchema": {
"type": "object",
"properties": {
"temperature": {
"type": "number",
"description": "Temperature in Fahrenheit"
},
"conditions": { "type": "string", "description": "Weather conditions" },
"humidity": { "type": "number", "description": "Humidity percentage" }
}
}
}
Why: outputSchema guides the agent about what shape to expect, improving reasoning about downstream tools.
When and How to Use Strong Language
Safety and guardrails:
"description": "Tool to delete a user record. Use only when the user has confirmed deletion and has appropriate permissions. Never allow deletion without explicit user consent."
Preconditions and ordering:
"description": "Tool to create a contact. Required workflow: call discover_required_fields('Contact') first to identify mandatory fields."
Format and cardinality constraints:
"title": {
"type": "string",
"description": "Title must be between 5 and 120 characters"
}
Error messages for agent recovery:
"Invalid date_range: date_from is after date_to. Please ensure date_from is earlier than date_to and try again."
Use strong language for:
- Safety requirements (never expose credentials)
- Required workflows (must call X first)
- Non-negotiable constraints (must be <= 100)
- Error recovery instructions
Avoid overusing strong language for:
- General behavior descriptions
- Optional features
- Documentation-level details (put these in schema)
Trade-off: Too much strong language causes token bloat and over-constraint. Use sparingly where it matters most.
Guiding Agents with Errors
Error message formula:
<Problem>. <Why>. <Fix>.
"Invalid date_from: date_from must be earlier than date_to. Please adjust date_from to a value before date_to and retry."
"Missing required parameter: repo_path is required. Please provide the path to the Git repository and retry."
"Access denied: token lacks write scope. Ask the user to grant write permissions or use a read-only tool."
"Result truncated: returned first 500 of 3,000 rows. To reduce truncation, specify a narrower date range or use filters (user_id, event_type)."
- Return structured errors with isError flag
- Use natural language, not stack traces
- Include concrete examples of correct parameters
- Suggest recovery actions explicitly
Balancing Power vs Complexity
- One clear, atomic action per tool
- 1-3 required parameters, small number of optional (total < 6-7)
- Short descriptions (1-2 sentences, < 1024 chars)
- Narrow scope: Don't create "manage" tools; prefer "create", "delete", "list", "search"
- Coherent domain: Group related tools by service (git**, slack*_, jira__)
- Small surface: Each server should do one thing well
- Consolidate chains: Merge tools that are almost always called together
Use MCP annotations for client guidance:
readOnlyHint: tool doesn't modify statedestructiveHint: may cause destructive changes (triggers confirmations)idempotentHint: repeating the call is safe
Community Patterns and Examples
Consistent snake_case with namespace prefixes:
{
"git_status": "Shows working tree status",
"git_diff_unstaged": "Shows unstaged changes",
"git_commit": "Records changes to the repository"
}
Concise descriptions following the formula:
{
"name": "get_structured_content",
"description": "Returns structured content along with an output schema for client data validation"
}
Strong typing with enums and descriptions:
{
"location": {
"type": "string",
"enum": ["New York", "Chicago", "Los Angeles"],
"description": "Choose city"
}
}
One dense description for server capability (not individual tools):
{
"description": "Integrates with Slack Lists to enable creation, retrieval, filtering, and management of list items with support for bulk operations, data export to JSON/CSV formats"
}
{
"name": "{{TOOL_NAME}}",
"description": "{{PROMPT_ENGINEERED_DESCRIPTION}}",
"inputSchema": {
"type": "object",
"properties": {
{{PARAMETER_DEFINITIONS}}
},
"required": [{{REQUIRED_PARAMS}}],
"additionalProperties": false
},
"outputSchema": {{OUTPUT_SCHEMA}}
}
"{{PARAM_NAME}}": {
"type": "{{PARAM_TYPE}}",
"description": "{{PARAM_DESCRIPTION}}",
"enum": [{{ENUM_VALUES}}],
"format": "{{FORMAT}}",
"default": "{{DEFAULT_VALUE}}"
}
- Tool names use snake_case with namespace prefix (e.g.,
service_action) - Descriptions achieve clarity when following: "Tool to . Use when . Constraints: ."
- Valid JSON Schema ensures runtime reliability for inputSchema
- Parameter descriptions prevent misuse by explaining meaning and constraints
- The required field lists all non-optional parameters
- Set additionalProperties to false for strict validation
- Enums improve reliability for finite value sets
- Format constraints (email, date, etc.) add automatic validation
- string - Text values
- number - Numeric values (int or float)
- boolean - true/false
- array - Lists of items
- object - Nested structures
- enum - Fixed set of values
- email - Valid email address
- date - ISO date (YYYY-MM-DD)
- date-time - ISO datetime
- uri - Valid URI
- uuid - Valid UUID
Transport Logic
- Local tool → stdio
- Remote service → http
- Multi-user → http
- Development → stdio
- Production → http
MCP servers provide:
- Tool definitions: Structured capabilities Claude can invoke
- Resource access: Shared data and context
- Prompt templates: Reusable interaction patterns
- Transport mechanisms: Communication protocols
- Portability: Work across different environments
MCP Architecture
Server-Based: MCPs run as separate servers that Claude connects to
Three Component Types:
- Tools: Invocable functions with parameters
- Resources: Data sources and context providers
- Prompts: Template-based interaction patterns
Core Structure
Server Configuration (McpServer)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "mcp-server-name",
version: "1.0.0",
});
// Tool registration with Zod
server.tool(
"tool-name",
"Tool description",
{
param: z.string().describe("Parameter description"),
},
async ({ param }) => {
// Implementation
return { content: [{ type: "text", text: result }] };
},
);
// Transport connection
const transport = new StdioServerTransport();
await server.connect(transport);
Validation Framework
Quick validation commands:
# Health check (verifies server starts and responds)
scripts/test_mcp.sh Custom_MCP/{project}/dist/server.js health
# List all tools (verify schema and descriptions)
scripts/test_mcp.sh Custom_MCP/{project}/dist/server.js tools
# List resources
scripts/test_mcp.sh Custom_MCP/{project}/dist/server.js resources
# List prompts
scripts/test_mcp.sh Custom_MCP/{project}/dist/server.js prompts
# Call a tool (happy path)
scripts/test_mcp.sh Custom_MCP/{project}/dist/server.js call myTool --arg key=value
# Call with JSON arguments
scripts/test_mcp.sh Custom_MCP/{project}/dist/server.js call myTool --json '{"key": "value"}'
# Run full test suite
scripts/test_mcp.sh Custom_MCP/{project}/dist/server.js all
CI/CD validation with exit codes:
#!/bin/bash
set -e
PROJECT="your-project-name"
SERVER_PATH="Custom_MCP/${PROJECT}/dist/server.js"
# Health check (exit 0 = server running)
scripts/test_mcp.sh "$SERVER_PATH" health || exit 1
# List tools (verify definitions)
TOOLS=$(scripts/test_mcp.sh "$SERVER_PATH" tools)
echo "$TOOLS" | jq -e '.tools | length > 0' || exit 1
# Test critical tool
scripts/test_mcp.sh "$SERVER_PATH" call myCriticalTool --arg input="test" || exit 1
echo "MCP server validation: PASS"
For deep spec compliance, combine with external validators like mcp-validator:
# STDIO compliance test
python -m mcp_testing.scripts.compliance_report \
--server-command "node Custom_MCP/${PROJECT}/dist/server.js" \
--protocol-version 2025-06-18
Relevance Heuristic
Freshness Gate: Before trusting any code snippets from a URL:
- Fetch the URL and check for last-updated metadata (Last-Modified header, date in content, or commit history)
- If content is > 6 months old, search for a more recent version before using the code
- Prefer recent commits over branch files for SDK documentation
- Document the freshness check result in the session
Skip freshness check when:
- Checking npm/GitHub version info (timestamp is the version)
- The URL is clearly versioned (e.g., /v1/, /2026/)
- Official SDK changelog confirms stability
# Check GitHub file last commit date
gh api repos/modelcontextprotocol/specification/commits?path=specification.md --jq '.[0].commit.author.date'
# Or fetch and parse for dates
curl -sI https://github.com/modelcontextprotocol/specification | grep -i last-modified
Protocol: Check Principles → Freshness Gate → Fetch Instance → Extract Delta → Dispose
Before fetching MCP specification URLs:
Check Principles - This skill covers tool schema design, transport selection, error guidance, and prompt engineering. The patterns here are stable.
Freshness Gate - Verify URL is < 6 months old. If older, search for updated version or use npm package version instead.
Fetch Instance Only When:
- MCP SDK version has breaking changes
- Transport mechanism differs from documented patterns
- New primitive types not covered
- Authentication flow has changed
Extract Delta - Keep only what this skill doesn't cover:
- SDK-specific initialization changes
- New transport options
- Updated security requirements
- Protocol version differences
Dispose Context - Remove fetched content after extracting delta
Delta Pattern: Fetch the URL, identify current tool-calling syntax, apply directly to the local file. Store patterns, not documentation. Documentation becomes stale; patterns remain actionable.
When NOT to fetch:
- Tool description formula (covered in skill)
- Schema patterns (covered with templates)
- Transport selection criteria (covered in tables)
- Error guidance (covered with examples)
- Strong language usage (covered in patterns)
Official Documentation (fetch for current MCP syntax):
- Tool schema & transport → https://code.claude.com/docs/en/mcp.md
- Inspector CLI (testing) → https://github.com/modelcontextprotocol/inspector
- MCP Validator (compliance) → https://github.com/Janix-ai/mcp-validator
- Protocol spec → https://modelcontextprotocol.io/
- Best practices → https://modelcontextprotocol.info/docs/best-practices
- SDK docs → https://modelcontextprotocol.io/quickstart/server
- Testing guide → https://www.stainless.com/mcp/how-to-test-mcp-servers
Workflow: Freshness check → Verify npm package version → Consult skill patterns → Fetch spec only if behavior differs → Extract delta → Dispose
Dynamic Sourcing Protocol
- https://code.claude.com/docs/en/mcp.md (tool schema and transport)
Note: For CLI testing, use scripts/test_mcp.sh instead of fetching inspector documentation.
Navigation
Official Documentation:
- Tool schema & transport → https://code.claude.com/docs/en/mcp.md
- Inspector CLI (testing) → https://github.com/modelcontextprotocol/inspector
- MCP Validator (compliance) → https://github.com/Janix-ai/mcp-validator
- Protocol spec → https://modelcontextprotocol.io/
- Best practices → https://modelcontextprotocol.info/docs/best-practices
- SDK docs → https://modelcontextprotocol.io/quickstart/server
- Testing guide → https://www.stainless.com/mcp/how-to-test-mcp-servers
| If you need... | Reference |
|---|---|
| Testing MCP server | scripts/test_mcp.sh |
| Authentication | references/lookup_auth_patterns.md |
| Plugin integration | references/lookup_plugin_integration.md |
| 2026 features | references/lookup_2026_syntax.md |
Genetic Code
This component carries essential Seed System principles for context: fork isolation:
Delta Standard: Good Component = Expert Knowledge − What Claude Already Knows
Recognition Questions
| Question | Recognition |
|---|---|
| Would Claude know this without being told? | Delete (zero delta) |
| Can this work standalone? | Fix if no (non-self-sufficient) |
| Did I read the actual file, or just see it in grep? | Verify before claiming |
Validation Checklist
Before claiming MCP development complete:
- Prompt-engineered descriptions used
- Schema-first design followed
- Transport layer properly configured
- Tool schemas validated
The Path to MCP Server Quality
1. Prompt-Engineered Descriptions
Tool descriptions achieve optimal agent behavior when written as micro-prompts.
- Description formula: "Tool to . Use when . Constraints: ."
- Front-load constraints: Place critical requirements and workflows first
- Conciseness: 1-2 sentences, under 1024 characters
2. Schema-First Design
Valid JSON Schema prevents runtime errors and guides agent behavior.
- Use
<tool_schema>template: Follow the tool definition structure exactly - Kebab-case names: Service_action format improves discoverability
- Property descriptions: Explain parameter meaning and constraints
- Enums over free text: Finite value sets reduce ambiguity
- Format constraints: Add automatic validation (email, date, uri, uuid)
3. Transport by Use Case
Choose stdio or http based on deployment requirements.
- stdio for local: Single client, direct integration, simpler setup
- http for remote: Multiple clients, network transport, load balancing
- Define clearly: Transport configuration affects deployment architecture
4. Error Messages as Micro-Prompts
Errors guide agents to self-correct when structured properly.
- Pattern: ". . ."
- Concrete suggestions: Include examples of correct parameters
- Recovery actions: Explicit next steps instead of stack traces
5. Portability First
Servers work across environments when configured properly.
- Environment variables: Externalize configuration
- Project-relative paths: Use
${CLAUDE_PROJECT_DIR}instead of hardcoding - Self-contained dependencies: Bundle required packages
- Graceful degradation: Handle missing configuration safely
6. Security by Design
Input validation and sanitization prevent common vulnerabilities.
- Validate all inputs: Against schema before processing
- Sanitize user input: Prevent injection attacks
- Parameterized queries: Use prepared statements for databases
- Rate limiting: Prevent abuse and resource exhaustion
- Mask sensitive data: Never log credentials or tokens
- HTTPS only: Encrypt external communications
7. Performance Patterns
Servers remain responsive under load when optimized appropriately.
- Cache repeated operations: Use
@lru_cacheor equivalent - Paginate large results: Return data in manageable chunks
- Batch operations: Group related calls when possible
- Set timeouts: Prevent indefinite waits
- Skill portability: The skill must function without CLAUDE.md, CLAUDE.local.md, or .claude/rules/ dependencies (core invariant)
- Optional tooling: External validators (
mcp-validator,mcp_testing) are recommended for deep compliance but not required—the skill provides manual review patterns as fallback - Use
McpServerfrom@modelcontextprotocol/sdk/server/mcp.js(legacyServerdeprecated) - Completion claims require verification evidence
- Prompt-engineered descriptions required for protocol correctness
Dynamic Knowledge Loading
This skill uses Blind Navigation for references. Load specialized knowledge ONLY when specific conditions are met:
| Context Condition | Resource | Action |
|---|---|---|
| Testing MCP server | Glob: test_mcp.sh |
Execute for validation |
| Authentication patterns | Glob: lookup_auth_patterns.md |
Grep for .env setup and header formats |
| Plugin integration | Glob: lookup_plugin_integration.md |
Grep for MCP client patterns |
| 2026 syntax features | Glob: lookup_2026_syntax.md |
Grep for new primitives |
| Tool schema templates | See tool_schema section | Grep for JSON Schema patterns |
| Enforcing tool invocation | Glob: lookup_anti_laziness_patterns.md |
Grep for confidence rating, CoT patterns |
Dynamic Sourcing
Syntax Source: This skill focuses on patterns and philosophy. For raw MCP tool schema syntax:
- Fetch:
https://code.claude.com/docs/en/mcp.md - Extract: The specific tool schema fields you need
- Discard: Do not retain the fetch in context
Common Mistakes to Avoid
Mistake 1: Missing additionalProperties
❌ Wrong:
"inputSchema": {
"type": "object",
"properties": { "param": { "type": "string" } }
}
✅ Correct:
"inputSchema": {
"type": "object",
"properties": { "param": { "type": "string" } },
"additionalProperties": false
}
Mistake 2: Weak Tool Descriptions
❌ Wrong:
"description": "Tool to get data"
✅ Correct:
"description": "Tool to retrieve user authentication status. Use when checking if a user is logged in or verifying session validity. Not for authentication operations (use auth-tool instead)."
Mistake 3: Missing Required Parameters
❌ Wrong:
"inputSchema": {
"type": "object",
"properties": { "user_id": { "type": "string" } }
}
✅ Correct:
"inputSchema": {
"type": "object",
"properties": { "user_id": { "type": "string", "description": "The user ID to look up" } },
"required": ["user_id"],
"additionalProperties": false
}
Mistake 4: Using Deprecated Server Class
❌ Wrong:
import { Server } from "@modelcontextprotocol/sdk/server/index.js"
✅ Correct:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
Mistake 5: Poor Error Messages
❌ Wrong:
"Invalid input"
✅ Correct:
"Invalid user_id: 'abc123' is not a valid UUID format. Please provide a valid UUID like '550e8400-e29b-41d4-a716-446655440000'."
Validation Checklist
Before claiming MCP development complete:
Tool Schema:
- Valid JSON Schema for inputSchema
- All required parameters listed in
requiredarray - additionalProperties set to false
- Parameter descriptions explain purpose and constraints
Tool Description:
- Follows formula: "Tool to X. Use when Y. Constraints: Z."
- Includes trigger conditions
- Excludes use cases clearly (Not for...)
Transport:
- stdio configured for local tools
- http configured for remote tools
- Authentication properly set up
Server:
- Uses McpServer (not deprecated Server class)
- Tools registered with valid schemas
- Server properly initialized
Testing:
- test_mcp.sh validation passes
- Tool calls work correctly
- Error messages are helpful
Best Practices Summary
✅ DO:
- Use
additionalProperties: falsefor strict validation - Write tool descriptions as micro-prompts
- Include all required parameters in schema
- Use enums for finite value sets
- Set format constraints (email, date, etc.)
- Use McpServer from correct import path
- Test with test_mcp.sh before claiming complete
❌ DON'T:
- Skip parameter descriptions
- Use loose schemas without additionalProperties
- Write vague tool descriptions
- Forget required array for mandatory params
- Use deprecated Server class
- Skip error message improvement
- Skip validation testing
- Zero external dependencies (portability invariant)
- Use
McpServerfrom@modelcontextprotocol/sdk/server/mcp.js(legacyServerdeprecated) - Completion claims require verification evidence
- Prompt-engineered descriptions required for protocol correctness
- additionalProperties: false required for tool schemas