MCP Server Setup for OpenClaw: Build and Connect Custom Tools (2026)
Anthropic released the Model Context Protocol (MCP) as an open standard in late 2024. It defines how AI models discover and call external tools. One protocol. Any tool. Any model.
For OpenClaw users, MCP servers are the fastest way to extend your agent. Database access, web scraping, GitHub integration, Slack notifications. Build or install an MCP server, point OpenClaw at it, and your agent gains new capabilities without custom integration code.
This tutorial walks through building an MCP server from scratch, connecting it to OpenClaw, and securing it with Clawctl.
What Is MCP and Why It Matters for OpenClaw
MCP stands for Model Context Protocol. It standardizes how AI agents communicate with external tools. Before MCP, every tool integration required custom glue code per vendor.
| Without MCP | With MCP | |
|---|---|---|
| Integration | Custom code per tool, per model | Universal protocol for all tools |
| Discovery | Hard-coded tool definitions | Agent discovers tools at runtime |
| Transport | REST, GraphQL, custom sockets | Standardized stdio or Streamable HTTP |
| Reuse | Rebuild per LLM vendor | Build once, connect anywhere |
| Validation | Roll your own schemas | JSON Schema built in |
OpenClaw natively supports MCP servers. You declare them in your openclaw.yaml config. OpenClaw spawns each server as a child process and routes tool calls through the MCP protocol. Your agent can use tools from multiple servers in a single conversation.
For a broader view of how OpenClaw compares to other agent runtimes, see our comparison of coding agents in 2026.
Prerequisites
You need these installed before starting:
- Node.js 18+ and npm (for the TypeScript server)
- Python 3.10+ and pip (for the Python server)
- An OpenClaw deployment — either self-hosted or via Clawctl
Verify your setup:
node --version # v18.x or higher
npm --version # 9.x or higher
python3 --version # 3.10 or higher
If you need help deploying OpenClaw itself, start with our self-hosted AI coding agent stack guide.
TypeScript MCP Server Tutorial
We will build a database query tool that lets your OpenClaw agent run read-only SQL queries. This is a real use case, not a toy example.
Step 1: Scaffold the Project
mkdir openclaw-db-server
cd openclaw-db-server
npm init -y
Install the MCP SDK and dependencies:
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
Create tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
Update package.json:
{
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}
Step 2: Define Tools with Zod
Create src/index.ts. The MCP SDK uses Zod schemas to define tool inputs. The SDK converts these to JSON Schema for the protocol.
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: "db-query-server",
version: "1.0.0",
});
server.tool(
"run-query",
"Run a read-only SQL query against the database",
{
sql: z
.string()
.min(1)
.describe("A SELECT query to run against the database"),
},
async ({ sql }) => {
// Enforce read-only queries
const normalized = sql.trim().toUpperCase();
if (!normalized.startsWith("SELECT")) {
return {
content: [
{ type: "text", text: "Only SELECT queries are allowed." },
],
isError: true,
};
}
try {
const results = await executeQuery(sql);
return {
content: [
{
type: "text",
text: JSON.stringify(results, null, 2),
},
],
};
} catch (error) {
return {
content: [
{ type: "text", text: `Query failed: ${String(error)}` },
],
isError: true,
};
}
}
);
server.tool(
"list-tables",
"List all tables in the database",
{},
async () => {
try {
const tables = await executeQuery(
"SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"
);
return {
content: [
{
type: "text",
text: JSON.stringify(tables, null, 2),
},
],
};
} catch (error) {
return {
content: [
{ type: "text", text: `Failed to list tables: ${String(error)}` },
],
isError: true,
};
}
}
);
The server.tool() method takes four arguments: name, description, Zod schema for inputs, and an async handler. The handler returns content blocks. Setting isError: true tells the model the tool call failed.
Step 3: Implement the Handler
Add the database connection and server startup. Still in src/index.ts:
import pg from "pg";
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
});
async function executeQuery(sql: string): Promise<Record<string, unknown>[]> {
const client = await pool.connect();
try {
const result = await client.query(sql);
return result.rows;
} finally {
client.release();
}
}
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("DB query MCP server running on stdio");
}
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});
Notice: log to stderr with console.error, not stdout. MCP uses stdout for protocol messages. Logging to stdout corrupts the protocol stream.
Step 4: Test with the Inspector
Build and run the MCP inspector:
npm run build
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb" \
npx @modelcontextprotocol/inspector node dist/index.js
The inspector opens a browser UI at http://localhost:5173. You will see your tools listed: run-query and list-tables. Click a tool, provide input, and run it. Use the inspector every time you change your server. It catches issues before they hit production.
Step 5: Connect to OpenClaw
This is where it gets specific to OpenClaw. Instead of Claude Desktop config, you declare MCP servers in your openclaw.yaml:
agents:
- id: main
model: anthropic/claude-sonnet-4-5
mcp_servers:
- name: db-query
command: node
args:
- /opt/mcp-servers/db-query/dist/index.js
env:
DATABASE_URL: postgresql://user:pass@db:5432/mydb
OpenClaw spawns the MCP server as a child process when the agent starts. The agent discovers available tools through the MCP protocol. When the model decides to call run-query, OpenClaw routes the request to your server over stdio.
No REST endpoints. No webhook URLs. No API gateway. The MCP server runs alongside the agent.
Python MCP Server
The Python SDK uses decorators and type hints. No manual schema definitions.
Install and Build
pip install mcp
Create server.py:
import os
import psycopg2
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("db-query-server")
@mcp.tool()
def run_query(sql: str) -> str:
"""Run a read-only SQL query against the database."""
normalized = sql.strip().upper()
if not normalized.startswith("SELECT"):
return "Error: Only SELECT queries are allowed."
conn = psycopg2.connect(os.environ["DATABASE_URL"])
try:
with conn.cursor() as cur:
cur.execute(sql)
columns = [desc[0] for desc in cur.description]
rows = cur.fetchall()
results = [dict(zip(columns, row)) for row in rows]
return str(results)
finally:
conn.close()
@mcp.tool()
def list_tables() -> str:
"""List all tables in the database."""
return run_query(
"SELECT table_name FROM information_schema.tables "
"WHERE table_schema = 'public'"
)
if __name__ == "__main__":
mcp.run()
FastMCP reads the function signature and docstring to generate the tool schema. The type hint sql: str becomes a required string parameter. The docstring becomes the tool description.
Test and Connect
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb" \
npx @modelcontextprotocol/inspector python3 server.py
Add to openclaw.yaml:
agents:
- id: main
model: anthropic/claude-sonnet-4-5
mcp_servers:
- name: db-query
command: python3
args:
- /opt/mcp-servers/db-query/server.py
env:
DATABASE_URL: postgresql://user:pass@db:5432/mydb
The protocol is language-agnostic. OpenClaw does not care if the server runs TypeScript or Python.
Connect Multiple MCP Servers to OpenClaw
A single OpenClaw agent can use multiple MCP servers. Each server adds a set of tools. Declare them all in your config:
agents:
- id: main
model: anthropic/claude-sonnet-4-5
mcp_servers:
- name: database
command: node
args:
- /opt/mcp-servers/db-query/dist/index.js
env:
DATABASE_URL: postgresql://user:pass@db:5432/mydb
- name: github
command: npx
args:
- -y
- "@modelcontextprotocol/server-github"
env:
GITHUB_PERSONAL_ACCESS_TOKEN: ghp_xxxxxxxxxxxx
- name: filesystem
command: npx
args:
- -y
- "@modelcontextprotocol/server-filesystem"
- /workspace
OpenClaw spawns all three servers at startup. The agent sees tools from all of them. It can query the database, create a GitHub issue, and write a file in a single conversation turn.
Real-World MCP Servers
You do not have to build everything. The MCP ecosystem has dozens of production-ready servers:
| Server | What It Does | Install |
|---|---|---|
| @modelcontextprotocol/server-filesystem | Read, write, search files | npx -y @modelcontextprotocol/server-filesystem /path |
| @modelcontextprotocol/server-postgres | Query PostgreSQL (read-only) | npx -y @modelcontextprotocol/server-postgres |
| @modelcontextprotocol/server-github | Issues, PRs, repos, branches | npx -y @modelcontextprotocol/server-github |
| @modelcontextprotocol/server-slack | Channels, messages, search | npx -y @modelcontextprotocol/server-slack |
| @modelcontextprotocol/server-puppeteer | Browse, screenshot, scrape | npx -y @modelcontextprotocol/server-puppeteer |
| mcp-server-sqlite | SQLite queries | pip install mcp-server-sqlite |
| mcp-server-fetch | Fetch URLs as markdown | pip install mcp-server-fetch |
Browse the full registry at github.com/modelcontextprotocol/servers. Most install with a single command and work with OpenClaw out of the box.
Common Mistakes
These trip up most people building their first MCP server.
1. Wrong Transport
MCP supports stdio and Streamable HTTP (formerly SSE). OpenClaw uses stdio by default. If your server listens on an HTTP port but OpenClaw expects stdio, nothing will connect.
// stdio — use this for OpenClaw
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// Streamable HTTP — use for remote/web clients
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
Stick with stdio for OpenClaw deployments. It is simpler and avoids network configuration.
2. No Error Handling
An unhandled exception crashes the server process. OpenClaw loses the connection. The agent gets no response. Always return errors through the protocol:
server.tool("my-tool", "Does something", { input: z.string() }, async ({ input }) => {
try {
const result = await doWork(input);
return { content: [{ type: "text", text: result }] };
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${String(error)}` }],
isError: true,
};
}
});
3. Logging to stdout
MCP uses stdout for protocol messages. If your server prints debug output to stdout, it corrupts the protocol stream. Use console.error in TypeScript or print(..., file=sys.stderr) in Python.
4. Vague Tool Names
The model picks tools based on the name and description. A tool called do-thing with description "does stuff" will confuse the model. Be specific:
// Bad — the model cannot figure out when to use this
server.tool("query", "Run a query", ...)
// Good — clear name and description
server.tool("run-sql-select", "Run a read-only SELECT query against PostgreSQL", ...)
Security: MCP Expands the Attack Surface
Every MCP server you add gives your agent more power. More power means more risk.
A filesystem MCP server can read any file the process has access to. A database server can run queries against production data. A shell server can execute arbitrary commands. If the model gets tricked by a prompt injection, those tools become weapons.
This is not theoretical. The Invariant Labs research team demonstrated MCP-specific attacks in early 2025, including tool poisoning and cross-server data exfiltration. When a model calls tools, it trusts the tool descriptions it receives. A malicious MCP server can embed hidden instructions in tool descriptions that override the model's behavior.
The attack surface grows linearly with each server you add. Three MCP servers means three sets of permissions, three processes with host access, three potential entry points.
For a detailed breakdown of agent security risks, read our AI agent security guide.
Clawctl Sandboxes MCP Servers
Clawctl solves the MCP security problem by isolating each server. Your agent keeps its capabilities. The blast radius shrinks.
Network policies per server. Each MCP server gets its own network rules. The database server can reach your PostgreSQL instance. It cannot reach the internet. The GitHub server can reach api.github.com. It cannot reach your database. You define this in your Clawctl config.
Filesystem restrictions. Clawctl mounts only the directories each server needs. The filesystem server gets /workspace. It cannot see /etc/shadow or ~/.ssh. No path traversal. No accidental credential exposure.
Process isolation. Each MCP server runs in its own sandboxed process. A crash in one server does not take down the others. A compromised server cannot access memory from another server or the host agent.
Audit logging. Every tool call is logged with timestamps, parameters, and results. You can trace what the agent did, which tool it called, and what data it accessed. This matters for compliance and incident response.
Without sandboxing, you are trusting every MCP server with full host access. With Clawctl, each server gets only what it needs.
# Clawctl applies these policies automatically
# You just declare your MCP servers in openclaw.yaml
# Clawctl handles the isolation
agents:
- id: main
model: anthropic/claude-sonnet-4-5
mcp_servers:
- name: database
command: node
args:
- /opt/mcp-servers/db-query/dist/index.js
env:
DATABASE_URL: postgresql://user:pass@db:5432/mydb
Clawctl provisions all of this in 60 seconds. No Docker compose files. No iptables rules. No manual audit config.
Get Started
You now know how to build an MCP server, connect it to OpenClaw, and secure it.
The fastest path to production: sign up for Clawctl at $49/month. It provisions OpenClaw with sandboxed MCP support, network policies, and audit logging. Deploy in 60 seconds.
Resources
- Claude Code vs Other AI Coding Agents (2026) — How MCP fits into the agent landscape
- Self-Hosted AI Coding Agent Stack — Run your full agent stack on your own hardware
- AI Agent Security: Complete Guide — Lock down your agents and their tools
- OpenClaw + Local LLM Guide — Run MCP servers with local models
- MCP Protocol Specification — Official protocol docs from Anthropic
- MCP Server Registry — Community-maintained server list