E

Expert MCP Server Builder Toolkit

Professional-grade skill designed for guide for creating Model Context Protocol servers. Built for Claude Code with best practices and real-world patterns.

SkillCommunitydevelopmentv1.0.0MIT
0 views0 copies

MCP Server Builder Toolkit

A development skill for building Model Context Protocol (MCP) servers that extend AI assistants with custom tools, resources, and prompts using the official MCP SDK.

When to Use

Choose MCP Server Builder when:

  • Creating custom tools that AI assistants can invoke during conversations
  • Building integrations that expose databases, APIs, or services to AI agents
  • Implementing resource providers that give AI assistants access to dynamic data
  • Developing MCP servers for local development tools, cloud services, or data sources

Consider alternatives when:

  • Building a standalone API — use a standard web framework
  • Creating a chatbot — use a chatbot framework directly
  • Simple prompt engineering — use system prompts without MCP

Quick Start

# Create MCP server with TypeScript npx @modelcontextprotocol/create-server my-mcp-server cd my-mcp-server && npm install
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: 'my-tools', version: '1.0.0' }); // Define a tool server.tool( 'search-database', 'Search the product database by query', { query: z.string().describe('Search query string'), limit: z.number().optional().default(10).describe('Max results'), category: z.enum(['electronics', 'clothing', 'books']).optional() }, async ({ query, limit, category }) => { const results = await searchProducts(query, { limit, category }); return { content: [ { type: 'text', text: JSON.stringify(results, null, 2) } ] }; } ); // Define a resource server.resource( 'product://{id}', 'Get product details by ID', async (uri) => { const id = uri.pathname.split('/').pop(); const product = await getProduct(id); return { contents: [ { uri: uri.toString(), mimeType: 'application/json', text: JSON.stringify(product) } ] }; } ); // Define a prompt template server.prompt( 'analyze-sales', 'Analyze sales data for a given period', { startDate: z.string().describe('Start date (YYYY-MM-DD)'), endDate: z.string().describe('End date (YYYY-MM-DD)') }, async ({ startDate, endDate }) => ({ messages: [ { role: 'user', content: { type: 'text', text: `Analyze sales data from ${startDate} to ${endDate}. Focus on trends, top products, and anomalies.` } } ] }) ); // Start server const transport = new StdioServerTransport(); await server.connect(transport);

Core Concepts

MCP Architecture

ComponentPurposeProtocol
ServerProvides tools, resources, promptsstdio or HTTP/SSE
ClientAI assistant that connects to serversSends requests
ToolsFunctions the AI can invokeJSON-RPC
ResourcesData the AI can readURI-based
PromptsTemplate prompts with parametersPre-defined
TransportCommunication layerstdio / HTTP

Building Complex Tools

// Tool with error handling and streaming server.tool( 'run-query', 'Execute a read-only SQL query against the database', { sql: z.string().describe('SQL SELECT query to execute'), database: z.enum(['analytics', 'products']).describe('Target database') }, async ({ sql, database }) => { // Validate query is read-only const normalized = sql.trim().toUpperCase(); if (!normalized.startsWith('SELECT')) { return { content: [{ type: 'text', text: 'Error: Only SELECT queries are allowed' }], isError: true }; } try { const db = getConnection(database); const results = await db.query(sql); return { content: [{ type: 'text', text: `Query returned ${results.length} rows:\n\n` + JSON.stringify(results.slice(0, 100), null, 2) + (results.length > 100 ? `\n\n... and ${results.length - 100} more rows` : '') }] }; } catch (error) { return { content: [{ type: 'text', text: `Query error: ${error.message}` }], isError: true }; } } ); // Resource with dynamic listing server.resource( 'config://{key}', 'Application configuration values', async (uri) => { const key = uri.pathname.split('/').pop(); const value = await getConfig(key); return { contents: [{ uri: uri.toString(), mimeType: 'text/plain', text: String(value) }] }; } );

Configuration

OptionDescriptionDefault
nameServer name for identificationRequired
versionServer version string"1.0.0"
transportCommunication: stdio, http"stdio"
capabilitiesEnabled features: tools, resources, promptsAll enabled
tool_timeoutMaximum tool execution time (ms)30000
max_response_sizeMaximum response content size1MB
loggingLog level: debug, info, warn, error"info"
authAuthentication configurationNone

Best Practices

  1. Make tools focused and single-purpose — a tool that "searches, filters, and exports data" should be three separate tools so the AI can compose them flexibly based on what the user actually needs
  2. Return structured data in tool responses so the AI can parse, filter, and present results appropriately; raw text dumps force the AI to guess at data structure
  3. Validate all inputs with Zod schemas that include .describe() on every parameter so the AI understands what values are expected and can provide accurate arguments
  4. Handle errors gracefully by returning isError: true with a descriptive error message rather than throwing exceptions that crash the server
  5. Use resources for read-only data access and tools for actions that have side effects — this separation helps the AI understand when it is reading data versus performing an action

Common Issues

Server not discovered by AI assistant: The MCP server must be registered in the assistant's configuration file (e.g., Claude's claude_desktop_config.json). Verify the server command path is correct, test the server standalone with stdio, and check that the server outputs valid JSON-RPC on stdout.

Tool responses too large: Returning entire database tables or large file contents exceeds context limits. Implement pagination in tools, truncate results with a clear indicator of omitted data, and let the AI request specific subsets rather than returning everything.

Transport connection dropping: Stdio-based servers that write debug output to stdout corrupt the JSON-RPC protocol. Always write debug and log messages to stderr, never to stdout, and handle SIGPIPE gracefully for long-running servers.

Community

Reviews

Write a review

No reviews yet. Be the first to review this template!

Similar Templates