Skip to content
mcp-builder logo

Building Custom MCP Servers

mcp-builder

Build, register, and use custom MCP servers within this environment. Use this when asked to create a new tool, capability, or integration as an MCP server.

sskarz/nanoclawbster0installs2stars

SKILL.md

Full skill instructions

Building Custom MCP Servers

MCP (Model Context Protocol) servers extend what I can do by exposing new tools. I can write, register, and use them across sessions.

File locations

  • Server code: /​workspace/​group/​mcp-servers/<server-name>/​index.js
  • Shared dependencies: /​workspace/​group/​mcp-servers/​package.json
  • Registration: /​workspace/​group/​.mcp.json

All paths under /​workspace/​group/ persist across container restarts.


Step-by-step: Create and register an MCP server

1. Set up shared dependencies (first time only)

All MCP servers share a single node_modules at the mcp-servers/ root. Only do this once:

mkdir -p /​workspace/​group/​mcp-servers
cd /​workspace/​group/​mcp-servers
if [ ! -f package.json ]; then
  npm init -y
  # Add "type": "module" for ES module support
  node -e "const p=require('./​package.json'); p.type='module'; require('fs').writeFileSync('package.json', JSON.stringify(p, null, 2)+'\n')"
  npm install @modelcontextprotocol/​sdk zod
fi

2. Write the server

Create /​workspace/​group/​mcp-servers/<name>/​index.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: "<name>", version: "1.0.0" });

server.tool(
  "tool-name",
  "Description of what this tool does",
  { param: z.string().describe("A parameter") },
  async ({ param }) => ({
    content: [{ type: "text", text: `Result: ${param}` }],
  })
);

const transport = new StdioServerTransport();
await server.connect(transport);

Node resolves imports from the shared node_modules up the directory tree — no per-server install needed.

3. Test it

echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{},"clientInfo":{"name":"test"},"protocolVersion":"2024-11-05"}}' | node /​workspace/​group/​mcp-servers/<name>/​index.js

Should return a JSON response without errors.

4. Register in .mcp.json

Add the server to /​workspace/​group/​.mcp.json. If the file doesn't exist, create it:

{
  "mcpServers": {
    "<name>": {
      "command": "node",
      "args": ["/​workspace/​group/​mcp-servers/<name>/​index.js"]
    }
  }
}

If .mcp.json already exists, merge the new entry into the existing mcpServers object — don't overwrite other servers.

5. Tell the user

After creating and registering the server, tell the user:

I've created the <name> MCP server. It will be available as a tool next time you message me.

The current session cannot pick up newly registered MCP servers. They take effect on the next container invocation.


Common patterns

Tool with no params

server.tool("get-time", "Get current time", {}, async () => ({
  content: [{ type: "text", text: new Date().toISOString() }],
}));

Tool that calls an external API

server.tool(
  "fetch-weather",
  "Get weather for a city",
  { city: z.string() },
  async ({ city }) => {
    const res = await fetch(`https://wttr.in/​${city}?format=3`);
    return { content: [{ type: "text", text: await res.text() }] };
  }
);

Tool that reads/​writes files

import { readFileSync, writeFileSync } from "fs";

server.tool("read-notes", "Read saved notes", {}, async () => ({
  content: [{ type: "text", text: readFileSync("/​workspace/​group/​notes.md", "utf8") }],
}));

server.tool(
  "save-note",
  "Save a note",
  { text: z.string() },
  async ({ text }) => {
    writeFileSync("/​workspace/​group/​notes.md", text);
    return { content: [{ type: "text", text: "Saved." }] };
  }
);

Tool that runs a shell command

import { execSync } from "child_process";

server.tool(
  "run-command",
  "Run a shell command",
  { command: z.string() },
  async ({ command }) => {
    try {
      const output = execSync(command, { timeout: 30000 }).toString();
      return { content: [{ type: "text", text: output || "(no output)" }] };
    } catch (err) {
      return { content: [{ type: "text", text: `Error: ${err.message}` }] };
    }
  }
);

Multiple tools in one server

Group related tools into a single server rather than creating one server per tool:

const server = new McpServer({ name: "project-tools", version: "1.0.0" });

server.tool("lint", "Run linter", {}, async () => { /​* ... */ });
server.tool("test", "Run tests", {}, async () => { /​* ... */ });
server.tool("deploy", "Deploy to staging", {}, async () => { /​* ... */ });

Debugging

  • Server crashes silently: Wrap all tool handlers in try/​catch. Unhandled errors kill the process and Claude Code just sees "MCP server disconnected."
  • Tools don't appear: Check that /​workspace/​group/​.mcp.json is valid JSON. A trailing comma or syntax error silently breaks registration.
  • Import errors: Make sure the shared package.json has "type": "module" and that node_modules exists at /​workspace/​group/​mcp-servers/.
  • Logging: Use console.error() for debug output. Stdout is reserved for the JSON-RPC protocol — anything else on stdout breaks communication.

Python alternative

# /​workspace/​group/​mcp-servers/<name>/​server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("<name>")

@mcp.tool()
def my_tool(param: str) -> str:
    """Description of the tool"""
    return f"Result: {param}"

if __name__ == "__main__":
    mcp.run()

Install: pip install mcp

Register in /​workspace/​group/​.mcp.json:

{
  "mcpServers": {
    "<name>": {
      "command": "python3",
      "args": ["/​workspace/​group/​mcp-servers/<name>/​server.py"]
    }
  }
}

More skills from sskarz

self-improve logo
sskarz/nanoclawbster

self-improve

Write features, fix bugs, and create PRs for the NanoClawbster codebase. Works in an isolated dev workspace, pushes via Composio (CI validates on GitHub Actions), and deploys after user approval.

2 0
View
add-telegram-swarm logo
sskarz/nanoclawbster

add-telegram-swarm

Add Agent Swarm (Teams) support to Telegram. Each subagent gets its own bot identity in the group. Requires Telegram channel to be set up first (use /add-telegram). Triggers on "agent swarm", "agent teams telegram", "telegram swarm", "bot pool".

2 0
View
get-qodo-rules logo
sskarz/nanoclawbster

get-qodo-rules

Loads org- and repo-level coding rules from Qodo before code tasks begin, ensuring all generation and modification follows team standards. Use before any code generation or modification task when rules are not already loaded. Invoke when user asks to write, edit, refactor, or review code, or when...

2 0
View
customize logo
sskarz/nanoclawbster

customize

Add new capabilities or modify NanoClawbster behavior. Use when user wants to add channels (Telegram, Slack, email input), change triggers, add integrations, modify the router, or make any other customizations. This is an interactive skill that asks questions to understand what the user wants.

2 0
View
update logo
sskarz/nanoclawbster

update

Update NanoClawbster from upstream. Fetches latest changes, merges with your customizations and skills, runs migrations. Triggers on \"update\", \"pull upstream\", \"sync with upstream\", \"get latest changes\".

2 0
View
debug logo
sskarz/nanoclawbster

debug

Debug container agent issues. Use when things aren't working, container fails, authentication problems, or to understand how the container system works. Covers logs, environment variables, mounts, and common issues.

2 0
View
x-integration logo
sskarz/nanoclawbster

x-integration

X (Twitter) integration for NanoClawbster. Post tweets, like, reply, retweet, and quote. Use for setup, testing, or troubleshooting X functionality. Triggers on "setup x", "x integration", "twitter", "post tweet", "tweet".

2 0
View
agent-browser logo
sskarz/nanoclawbster

agent-browser

Browse the web for any task — research topics, read articles, interact with web apps, fill forms, take screenshots, extract data, and test web pages. Use whenever a browser would be useful, not just when the user explicitly asks.

2 0
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View