Comprehensive tutorial series for OpenClaw AI agent gateway
Plugins extend agent capabilities with external tools via MCP (Model Context Protocol).
A plugin is an MCP server that provides:
Unlike skills (instructions in context), plugins are external services that execute code.
MCP (Model Context Protocol) is a standard for connecting AI agents to external tools:
┌─────────────────────────────────────────────────────────┐
│ OpenClaw │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Agent 1 │ │ Agent 2 │ │ Agent 3 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ MCP Router │ │
│ └──────┬──────┘ │
└─────────────────────┼───────────────────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ GitHub │ │ Slack │ │ Custom │
│ Server │ │ Server │ │ Server │
└─────────┘ └─────────┘ └─────────┘
# Install globally
npm install -g @modelcontextprotocol/server-github
# Or locally in your project
npm install @modelcontextprotocol/server-github
# Clone and build
git clone https://github.com/example/mcp-server-custom
cd mcp-server-custom
npm install
npm run build
# Pull pre-built image
docker pull ghcr.io/example/mcp-server-custom:latest
// ~/.openclaw/openclaw.json
{
mcp: {
servers: {
// Server name (used in logs)
"github": {
// Command to run the server
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
// Environment variables
env: {
GITHUB_TOKEN: "${GITHUB_TOKEN}",
},
},
},
},
}
{
mcp: {
servers: {
"github": {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
env: { GITHUB_TOKEN: "${GITHUB_TOKEN}" },
},
"slack": {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-slack"],
env: { SLACK_TOKEN: "${SLACK_TOKEN}" },
},
"filesystem": {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"],
},
},
},
}
{
mcp: {
servers: {
"custom": {
command: "docker",
args: [
"run", "--rm", "-i",
"-e", "API_KEY=${API_KEY}",
"ghcr.io/example/mcp-server-custom:latest"
],
},
},
},
}
{
agents: {
main: {
// Uses all configured MCP servers
},
coder: {
// Only use specific servers
mcp: {
servers: ["github", "filesystem"],
},
},
researcher: {
// Different set of servers
mcp: {
servers: ["search", "web"],
},
},
},
mcp: {
servers: {
github: { /* ... */ },
filesystem: { /* ... */ },
search: { /* ... */ },
web: { /* ... */ },
},
},
}
Once configured, plugin tools appear alongside built-in tools:
// Agent can call GitHub tools
{
"tool": "github_create_issue",
"params": {
"owner": "example",
"repo": "myproject",
"title": "Bug: Login fails",
"body": "Steps to reproduce..."
}
}
// Or Slack tools
{
"tool": "slack_post_message",
"params": {
"channel": "#engineering",
"text": "Deployment complete!"
}
}
| Server | Purpose | Install |
|---|---|---|
server-github |
GitHub API | @modelcontextprotocol/server-github |
server-slack |
Slack API | @modelcontextprotocol/server-slack |
server-filesystem |
File access | @modelcontextprotocol/server-filesystem |
server-postgres |
PostgreSQL | @modelcontextprotocol/server-postgres |
server-sqlite |
SQLite | @modelcontextprotocol/server-sqlite |
| Server | Purpose | Source |
|---|---|---|
mcp-server-fetch |
HTTP requests | Community |
mcp-server-puppeteer |
Browser automation | Community |
mcp-server-docker |
Docker management | Community |
# Search npm
npm search @modelcontextprotocol
# Check MCP registry
https://github.com/modelcontextprotocol/servers
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server({
name: "my-custom-server",
version: "1.0.0",
}, {
capabilities: {
tools: {},
},
});
// Define a tool
server.setRequestHandler("tools/list", async () => ({
tools: [{
name: "my_tool",
description: "Does something useful",
inputSchema: {
type: "object",
properties: {
input: { type: "string", description: "Input value" },
},
required: ["input"],
},
}],
}));
// Handle tool calls
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "my_tool") {
const input = request.params.arguments.input;
const result = await doSomething(input);
return { content: [{ type: "text", text: result }] };
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
my-mcp-server/
├── package.json
├── tsconfig.json
├── src/
│ └── index.ts
└── dist/
└── index.js
// package.json
{
"name": "mcp-server-custom",
"version": "1.0.0",
"bin": {
"mcp-server-custom": "dist/index.js"
},
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0"
}
}
# Build
npm run build
# Test manually (stdio interface)
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/index.js
# Configure in OpenClaw
{
mcp: {
servers: {
"custom": {
command: "node",
args: ["/path/to/my-mcp-server/dist/index.js"],
},
},
},
}
MCP servers can also provide resources (read-only data):
{
mcp: {
servers: {
"docs": {
command: "npx",
args: ["-y", "mcp-server-docs", "/path/to/docs"],
},
},
},
}
// Agent can read resources
{
"tool": "mcp_read_resource",
"params": {
"server": "docs",
"uri": "docs://api-reference/auth"
}
}
MCP servers can provide prompt templates:
server.setRequestHandler("prompts/list", async () => ({
prompts: [{
name: "code_review",
description: "Review code for issues",
arguments: [{
name: "file",
description: "File to review",
required: true,
}],
}],
}));
server.setRequestHandler("prompts/get", async (request) => {
if (request.params.name === "code_review") {
return {
messages: [{
role: "user",
content: {
type: "text",
text: `Review this code for issues:\n\n${request.params.arguments.file}`,
},
}],
};
}
});
// Agent can use prompt templates
{
"tool": "mcp_get_prompt",
"params": {
"server": "review",
"name": "code_review",
"arguments": { "file": "src/auth.ts" }
}
}
Never hardcode secrets:
// Bad
{
mcp: {
servers: {
github: {
env: { GITHUB_TOKEN: "ghp_xxxxx" }, // Exposed!
},
},
},
}
// Good
{
mcp: {
servers: {
github: {
env: { GITHUB_TOKEN: "${GITHUB_TOKEN}" }, // From environment
},
},
},
}
Limit filesystem access:
{
mcp: {
servers: {
filesystem: {
command: "npx",
args: [
"-y", "@modelcontextprotocol/server-filesystem",
"/allowed/path", // Only this path is accessible
],
},
},
},
}
Use Docker for untrusted plugins:
{
mcp: {
servers: {
untrusted: {
command: "docker",
args: [
"run", "--rm", "-i",
"--network", "none", // No network
"--read-only", // Read-only filesystem
"--memory", "512m", // Memory limit
"mcp-server-untrusted"
],
},
},
},
}
# Test server manually
npx -y @modelcontextprotocol/server-github
# Check for missing env vars
echo $GITHUB_TOKEN
# Check logs
openclaw logs | grep mcp
# List available tools
openclaw tools list
# Check server is configured
openclaw config get mcp.servers
{
mcp: {
servers: {
slow: {
command: "...",
// Increase timeout
timeout: 30000, // 30 seconds
},
},
},
}
One server, one integration:
// Good: Separate servers
{
mcp: {
servers: {
github: { /* GitHub only */ },
jira: { /* Jira only */ },
},
},
}
// Avoid: One server doing everything
{
mcp: {
servers: {
everything: { /* GitHub + Jira + Slack + ... */ },
},
},
}
<!-- In agent instructions -->
## Available MCP Tools
### GitHub
- `github_create_issue`: Create new issues
- `github_list_prs`: List pull requests
- `github_merge_pr`: Merge a pull request
Use these for GitHub operations instead of `gh` CLI.
## MCP Tool Errors
If an MCP tool fails:
1. Check the error message
2. Try alternative approach (CLI, manual)
3. Report to user if critical
Now that you understand both skills and plugins, learn When to Use Which →