mcpField
M
mcp-builder

mcp-builder

github.com/pixel-process-ugDevelopment380 installsUpdated 33w ago
Repository

About mcp-builder

A development skill for designing, implementing, testing, and hardening MCP servers. It guides developers through exposing tools, resources, and prompts, selecting transports, and preparing client integration.

Key features

  1. 01Plan MCP capabilities and transports
  2. 02Define validated tool schemas
  3. 03Implement tools, resources, and prompts
  4. 04Configure stdio and SSE transports
  5. 05Add error handling and security checks
  6. 06Test servers with an in-memory client

Use cases

  1. 01Design an MCP server for an application
  2. 02Expose data and actions to AI clients
  3. 03Choose a transport for local or remote access
  4. 04Test and review a server before deployment

MCP support

Guides the full development of MCP servers that expose tools, resources, and prompts to AI clients. Covers transport setup and client integration.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/pixel-process-ug/superkit-agents --skill mcp-builder

After installing, type/mcp-builderin Claude Code to confirm it loaded.

Details

From the project README

MCP Builder

Overview

Build production-quality MCP (Model Context Protocol) servers that expose tools, resources, and prompts to AI clients. This skill covers the full development lifecycle: tool definition, resource management, prompt templates, transport configuration (stdio, SSE), error handling, security hardening, testing, and client integration.

Phase 1: Design

  1. Identify capabilities to expose (tools, resources, prompts)
  2. Define tool schemas with Zod/JSON Schema
  3. Plan resource URI patterns
  4. Design error handling strategy
  5. Choose transport layer (stdio for CLI, SSE for web)

STOP — Present the capability inventory and transport choice to user for approval.

Capability Selection Decision Table
What You HaveMCP PrimitiveExample
Actions that modify stateToolcreate-issue, send-email, deploy-app
Actions that read/queryToolsearch-documents, get-status
Data the AI should readResourceconfig://settings, docs://api/endpoints
Reusable prompt patternsPromptcode-review, summarize-document
Real-time data feedsResource (subscribable)metrics://cpu/current
Transport Selection Decision Table
ContextTransportWhy
CLI tool, local client (Claude Desktop)StdioSimple, no network overhead
Web application, remote clientsSSENetwork-accessible, real-time
Both local and remoteStdio + SSESupport both use cases
High-throughput, bidirectionalWebSocket (custom)Lower latency than SSE

Phase 2: Implementation

  1. Set up MCP server project structure
  2. Implement tool handlers with input validation
  3. Implement resource providers
  4. Add prompt templates
  5. Configure transport and authentication

STOP — Run basic smoke tests before moving to hardening.

Project Structure
src/
  index.ts          # Server entry point
  tools/
    search.ts       # Tool implementations
    create.ts
  resources/
    documents.ts    # Resource providers
    config.ts
  prompts/
    review.ts       # Prompt templates
  lib/
    database.ts     # Shared utilities
    validation.ts
tests/
  tools.test.ts
  resources.test.ts
package.json
tsconfig.json
Tool Definition Pattern
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

const server = new McpServer({
  name: 'my-mcp-server',
  version: '1.0.0',
});

server.tool(
  'search-documents',
  'Search documents by query. Returns matching documents with relevance scores.',
  {
    query: z.string().describe('Search query string'),
    limit: z.number().min(1).max(100).default(10).describe('Maximum results to return'),
    filter: z.object({
      type: z.enum(['article', 'page', 'note']).optional(),
      dateAfter: z.string().datetime().optional(),
    }).optional().describe('Optional filters'),
  },
  async ({ query, limit, filter }) => {
    const results = await searchEngine.search(query, { limit, ...filter });
    return {
      content: [{
        type: 'text',
        text: JSON.stringify(results, null, 2),
      }],
    };
  }
);
Tool Design Principles
PrincipleRule
Clear namingverb-noun format: search-documents, create-issue
Descriptive descriptionsExplain what, when, and return value
Validated inputsZod schemas with .describe() on every field
Structured outputsWell-formatted text or JSON
Idempotent when possibleSame input produces same result
Actionable errorsSpecific error messages with isError: true
Tool Response Patterns
// Text response
return { content: [{ type: 'text', text: 'Operation completed successfully' }] };

// Structured data response
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };

// Multi-part response
return {
  content: [
    { type: 'text', text: `Found ${results.length} results:` },
    { type: 'text', text: results.map(r => `- ${r.title}: ${r.summary}`).join('\n') },
  ],
};

// Image response
return { content: [{ type: 'image', data: base64Data, mimeType: 'image/png' }] };

// Error response
return {
  content: [{ type: 'text', text: `Error: ${error.message}` }],
  isError: true,
};

Resource Management

Resource Definition
// Static resource
server.resource(
  'config',
  'config://app/settings',
  { mimeType: 'application/json' },
  async () => ({
    contents: [{
      uri: 'config://app/settings',
      mimeType: 'application/json',
      text: JSON.stringify(appConfig),
    }],
  })
);

// Dynamic resource with URI template
server.resource(
  'document',
  new ResourceTemplate('docs://{category}/{id}', { list: undefined }),
  { mimeType: 'text/markdown' },
  async (uri, { category, id }) => ({
    contents: [{
      uri: uri.href,
      mimeType: 'text/markdown',
      text: await getDocument(category, id),
    }],
  })
);
Resource URI Conventions
file:///path/to/file          — Local files
https://api.example.com/data  — Remote HTTP resources
db://database/table/id        — Database records
config://app/settings         — Configuration
docs://category/slug          — Documentation

Prompt Templates

server.prompt(
  'code-review',
  'Generate a code review for the given file',
  {
    filePath: z.string().describe('Path to the file to review'),
    severity: z.enum(['strict', 'normal', 'lenient']).default('normal'),
  },
  async ({ filePath, severity }) => {
    const code = await readFile(filePath, 'utf-8');
    return {
      messages: [{
        role: 'user',
        content: {
          type: 'text',
          text: `Review this code with ${severity} standards:\n\n\`\`\`\n${code}\n\`\`\``,
        },
      }],
    };
  }
);

Transport Layers

Stdio Transport (CLI tools, local development)
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

const transport = new StdioServerTransport();
await server.connect(transport);
SSE Transport (Web applications, remote servers)
import express from 'express';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';

const app = express();

app.get('/sse', async (req, res) => {
  const transport = new SSEServerTransport('/messages', res);
  await server.connect(transport);
});

app.post('/messages', async (req, res) => {
  // Handle incoming messages
});

app.listen(3001);

Phase 3: Hardening

  1. Add comprehensive error handling
  2. Implement rate limiting and timeouts
  3. Security review (input sanitization, permission checks)
  4. Write integration tests
  5. Document tools and resources for clients

STOP — All tests must pass and security review must be complete before deployment.

Error Handling
server.tool('risky-operation', 'Performs an operation that might fail', {
  input: z.string(),
}, async ({ input }) => {
  try {
    const result = await performOperation(input);
    return { content: [{ type: 'text', text: JSON.stringify(result) }] };
  } catch (error) {
    if (error instanceof ValidationError) {
      return {
        content: [{ type: 'text', text: `Invalid input: ${error.message}` }],
        isError: true,
      };
    }
    if (error instanceof NotFoundError) {
      return {
        content: [{ type: 'text', text: `Resource not found: ${error.message}` }],
        isError: true,
      };
    }
    console.error('Unexpected error:', error);
    return {
      content: [{ type: 'text', text: 'An unexpected error occurred. Please try again.' }],
      isError: true,
    };
  }
});
Error Handling Rules
RuleWhy
Never expose stack traces to clientsSecurity risk
Return isError: true for all errorsClient can distinguish success/failure
Log unexpected errors server-sideDebugging and monitoring
Provide actionable error messagesClient can self-correct
Handle timeouts for external callsPrevent hanging requests
Validate all inputs before processingReject bad data early
Security Considerations
CategoryRules
Input validationZod schemas, path traversal prevention, length limits
Permission modelLeast privilege, whitelist directories, separate read/write tools
SecretsEnv vars only, never in responses, mask in logs, rotate regularly
Rate limitingLimit tool invocations per client
AuditingLog all tool calls with timestamps

Testing MCP Servers

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';

describe('MCP Server', () => {
  let server: McpServer;
  let client: Client;

  beforeEach(async () => {
    server = createServer();
    client = new Client({ name: 'test-client', version: '1.0.0' });
    const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
    await Promise.all([
      server.connect(serverTransport),
      client.connect(clientTransport),
    ]);
  });

  test('search-documents returns results', async () => {
    const result = await client.callTool({
      name: 'search-documents',
      arguments: { query: 'test', limit: 5 },
    });
    expect(result.content[0].type).toBe('text');
    const data = JSON.parse(result.content[0].text);
    expect(data.length).toBeLessThanOrEqual(5);
  });

  test('handles invalid input gracefully', async () => {
    const result = await client.callTool({
      name: 'search-documents',
      arguments: { query: '', limit: -1 },
    });
    expect(result.isError).toBe(true);
  });
});

Client Integration

Claude Desktop Configuration
{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["/path/to/server/dist/index.js"],
      "env": {
        "API_KEY": "your-key-here"
      }
    }
  }
}

Anti-Patterns / Common Mistakes

Anti-PatternWhy It Is WrongWhat to Do Instead
Tools that do too many thingsHard to use, hard to testSplit into focused single-purpose tools
Missing input validationCrashes, security holesAlways use Zod schemas
Returning raw stack tracesSecurity risk, confusing for AIReturn isError: true with clean message
No timeout on external callsHangs indefinitelySet timeouts on all I/O
Hardcoded secrets in sourceCredential exposureUse environment variables
Tools without descriptionsClients cannot discover purposeWrite clear descriptions
Blocking event loop with sync opsServer becomes unresponsiveUse async/await for all I/O
No testsRegressions go undetectedTest with InMemoryTransport

Documentation Lookup (Context7)

Use mcp__context7__resolve-library-id then mcp__context7__query-docs for up-to-date docs. Returned docs override memorized knowledge.

  • @anthropic-ai/sdk — for Claude API client, tool definitions, or streaming

Integration Points

SkillIntegration
senior-devopsContainerize and deploy MCP servers
agent-developmentMCP servers provide tools for agents
security-reviewSecurity hardening of tool inputs/outputs
test-driven-developmentTDD for tool implementation
deploymentCI/CD pipeline for MCP server releases
planningMCP server design is part of the implementation plan

Skill Type

FLEXIBLE — Adapt project structure, transport choice, and tooling to the use case. Tool validation with Zod and error handling with isError are strongly recommended. Security review is recommended before production deployment.

Comments

Markdown supported