build-custom-mcp-server
Über
Diese Fähigkeit führt Entwickler beim Erstellen benutzerdefinierter MCP-Server an, um domänenspezifische Tools für KI-Assistenten wie Claude Code verfügbar zu machen. Sie behandelt die Implementierung in Node.js oder R, einschließlich Tool-Definitionen, Transportkonfiguration und Tests. Nutzen Sie sie, wenn Sie spezialisierte Integrationen über Standard-mcptools hinaus benötigen oder bestehende APIs/Dienste als MCP-Tools kapseln möchten.
Schnellinstallation
Claude Code
Empfohlennpx skills add pjt222/agent-almanac -a claude-code/plugin add https://github.com/pjt222/agent-almanacgit clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/build-custom-mcp-serverKopieren Sie diesen Befehl und fügen Sie ihn in Claude Code ein, um diese Fähigkeit zu installieren
Dokumentation
Build Custom MCP Server
Custom MCP server → expose domain-specific tools to AI assistants.
Use When
- Expose custom fn to Claude Code / Claude Desktop
- Specialized tools beyond mcptools
- Domain-specific AI assistant integration
- Wrap existing APIs/services as MCP tools
In
- Required: Tool list (name, desc, params, behavior)
- Required: Impl lang (Node.js or R)
- Required: Transport (stdio or HTTP)
- Optional: Auth reqs
- Optional: Docker packaging needs
Do
Step 1: Define Tool Specs
Before code, define each tool:
tools:
- name: query_database
description: Execute a read-only SQL query against the analysis database
parameters:
query:
type: string
description: SQL SELECT query to execute
required: true
limit:
type: integer
description: Maximum rows to return
default: 100
returns: JSON array of result rows
- name: run_analysis
description: Execute a predefined statistical analysis by name
parameters:
analysis_name:
type: string
description: Name of the analysis to run
enum: [descriptive, regression, survival]
dataset:
type: string
description: Dataset identifier
required: true
→ YAML/md spec per tool w/ name, desc, params (types, defaults, required), return type documented before code.
If err: Specs unclear → interview domain expert or review existing API docs for param types + return formats.
Step 2: Impl in Node.js (MCP SDK)
// server.js
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-analysis-server",
version: "1.0.0",
});
// Define tools
server.tool(
"query_database",
"Execute a read-only SQL query against the analysis database",
{
query: z.string().describe("SQL SELECT query"),
limit: z.number().default(100).describe("Max rows to return"),
},
async ({ query, limit }) => {
// Validate read-only
if (!/^\s*SELECT/i.test(query)) {
return {
content: [{ type: "text", text: "Error: Only SELECT queries allowed" }],
isError: true,
};
}
const results = await executeQuery(query, limit);
return {
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
};
}
);
server.tool(
"run_analysis",
"Execute a predefined statistical analysis",
{
analysis_name: z.enum(["descriptive", "regression", "survival"]),
dataset: z.string().describe("Dataset identifier"),
},
async ({ analysis_name, dataset }) => {
const result = await runAnalysis(analysis_name, dataset);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
);
// Start server with stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);
→ Working server.js imports MCP SDK, defines tools w/ Zod schemas, connects via stdio. node server.js starts w/o errs.
If err: Verify @modelcontextprotocol/sdk + zod installed (npm install). Check import paths match SDK ver (SDK reorganized exports between versions).
Step 3: Impl in R (mcptools)
# server.R
library(mcptools)
# Register custom tools
mcp_tool(
name = "query_database",
description = "Execute a read-only SQL query",
parameters = list(
query = list(type = "string", description = "SQL SELECT query"),
limit = list(type = "integer", description = "Max rows", default = 100)
),
handler = function(query, limit = 100) {
if (!grepl("^\\s*SELECT", query, ignore.case = TRUE)) {
stop("Only SELECT queries allowed")
}
result <- DBI::dbGetQuery(con, paste(query, "LIMIT", limit))
jsonlite::toJSON(result, auto_unbox = TRUE)
}
)
# Start server
mcptools::mcp_server()
→ Working server.R registers tools w/ mcp_tool(), starts via mcp_server(). Rscript server.R starts MCP server.
If err: mcptools installed from GitHub (remotes::install_github("posit-dev/mcptools")). Handler fn signatures match param defs.
Step 4: Project Structure
my-mcp-server/
├── package.json # Node.js dependencies
├── server.js # Server implementation
├── tools/ # Tool implementations
│ ├── database.js
│ └── analysis.js
├── test/ # Tests
│ └── tools.test.js
├── Dockerfile # Container packaging
└── README.md # Setup instructions
→ Project dir w/ server.js (or server.R), package.json, tools/ for modular tool impls, test/ for tests.
If err: Structure doesn't match impl lang → adjust. R servers may use R/ vs tools/ + tests/testthat/ vs test/.
Step 5: Test Server
Manual stdio test:
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node server.js
Register w/ Claude Code:
claude mcp add my-server stdio "node" "/path/to/server.js"
Verify tools appear:
Start Claude Code session, check custom tools listed + functional.
→ tools/list JSON-RPC returns all tools w/ correct names + schemas. claude mcp list shows server registered. Tools callable from session.
If err: tools/list returns empty → tools not registered before server.connect(). Claude Code can't find → verify cmd path in claude mcp add absolute + binary executable.
Step 6: Error Handling
server.tool("risky_operation", "...", schema, async (params) => {
try {
const result = await performOperation(params);
return {
content: [{ type: "text", text: JSON.stringify(result) }],
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${error.message}` }],
isError: true,
};
}
});
→ Each handler wrapped in try/catch. Invalid in → isError: true w/ desc msg, not crash.
If err: Still crashes on bad in → try/catch wraps full handler body incl async. Promises awaited in try block.
Step 7: Package for Distribution
Create package.json w/ bin entry:
{
"name": "my-mcp-server",
"version": "1.0.0",
"bin": {
"my-mcp-server": "./server.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"zod": "^3.22.0"
}
}
Users install + configure:
npm install -g my-mcp-server
claude mcp add my-server stdio "my-mcp-server"
→ package.json w/ bin entry pointing to entry point. Users install globally w/ npm install -g + register w/ claude mcp add.
If err: Bin entry doesn't work after global install → server.js has shebang (#!/usr/bin/env node) + marked executable. Pkg name doesn't conflict w/ existing npm.
Check
- Server starts w/o errs
-
tools/listreturns all tools w/ correct schemas - Each tool executes correctly w/ valid in
- Tools return appropriate errs for invalid in
- Works w/ Claude Code via stdio
- Tools discoverable + usable in Claude sessions
Traps
- Blocking ops: Handle req async. Long ops block other tool calls
- Missing err handling: Unhandled exceptions crash. Always wrap in try/catch
- Schema mismatch: Param schemas must exactly match handler expects
- stdio buffering: Ensure out flushed. Node.js buffers stdout by default
- Security: MCP servers have same access as process. Validate in carefully, esp shell cmds/DB queries
→
configure-mcp-server— connect built server to clientstroubleshoot-mcp-connection— debug connectivitycontainerize-mcp-server— package in Docker
GitHub Repository
Verwandte Skills
content-collections
MetaDiese Skill bietet eine produktionsgetestete Einrichtung für Content Collections – ein TypeScript-first-Tool, das Markdown/MDX-Dateien in typsichere Datensammlungen mit Zod-Validierung umwandelt. Verwenden Sie ihn beim Erstellen von Blogs, Dokumentationsseiten oder inhaltsstarken Vite + React-Anwendungen, um Typsicherheit und automatische Inhaltsvalidierung zu gewährleisten. Er behandelt alles von der Vite-Plugin-Konfiguration und MDX-Kompilierung bis hin zur Deployment-Optimierung und Schema-Validierung.
polymarket
MetaDiese Fähigkeit ermöglicht es Entwicklern, Anwendungen mit der Polymarket-Prognosemärkte-Plattform zu erstellen, einschließlich API-Integration für Handel und Marktdaten. Sie bietet außerdem Echtzeit-Datenstreaming über WebSocket, um Live-Trades und Marktaktivitäten zu überwachen. Nutzen Sie sie zur Implementierung von Handelsstrategien oder zur Erstellung von Tools, die Live-Marktaktualisierungen verarbeiten.
creating-opencode-plugins
MetaDiese Fähigkeit unterstützt Entwickler dabei, OpenCode-Plugins zu erstellen, die in über 25 Ereignistypen wie Befehle, Dateien und LSP-Operationen eingreifen. Sie bietet die Plugin-Struktur, Event-API-Spezifikationen und Implementierungsmuster für JavaScript/TypeScript-Module. Nutzen Sie sie, wenn Sie den Lebenszyklus des OpenCode KI-Assistenten mit benutzerdefinierter ereignisgesteuerter Logik abfangen, überwachen oder erweitern müssen.
sglang
MetaSGLang ist ein hochperformantes LLM-Serving-Framework, das sich auf schnelle, strukturierte Generierung für JSON, Regex und agentenbasierte Workflows unter Verwendung seines RadixAttention-Prefix-Cachings spezialisiert. Es bietet deutlich schnellere Inferenz, insbesondere für Aufgaben mit wiederholten Präfixen, was es ideal für komplexe, strukturierte Ausgaben und Mehrfachdialoge macht. Wählen Sie SGLang gegenüber Alternativen wie vLLM, wenn Sie constrained decoding benötigen oder Anwendungen mit umfangreicher Präfix-Weitergabe entwickeln.
