#!/usr/bin/env node
// OpenClaw MCP Server - Expose OpenClaw agent tools via MCP
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool
} from '@modelcontextprotocol/sdk/types.js';
import { OpenClawClient } from './openclaw.js';
import type {
SendMessageArgs,
SpawnTaskArgs,
CheckStatusArgs,
ListSessionsArgs
} from './types.js';
// Configuration from environment
const GATEWAY_URL = process.env.OPENCLAW_GATEWAY_URL || 'ws://localhost:18789';
const AGENT = process.env.OPENCLAW_AGENT || 'phoenix';
const TOKEN = process.env.OPENCLAW_TOKEN;
// Initialize OpenClaw client
const client = new OpenClawClient({
gatewayUrl: GATEWAY_URL,
agent: AGENT,
token: TOKEN
});
// Define MCP tools
const TOOLS: Tool[] = [
{
name: 'send_message',
description: 'Send a message to an OpenClaw agent (immediate, conversational). Use for quick questions or immediate tasks.',
inputSchema: {
type: 'object',
properties: {
agent: {
type: 'string',
description: 'Agent ID (e.g., "phoenix")',
default: AGENT
},
message: {
type: 'string',
description: 'Message to send to the agent'
},
timeout: {
type: 'number',
description: 'Timeout in seconds (default: 60)',
default: 60
}
},
required: ['message']
}
},
{
name: 'spawn_task',
description: 'Spawn an isolated background task for an OpenClaw agent. Use for long-running work, research, or monitoring. The agent will work asynchronously and notify when done.',
inputSchema: {
type: 'object',
properties: {
agent: {
type: 'string',
description: 'Agent ID (e.g., "phoenix")',
default: AGENT
},
task: {
type: 'string',
description: 'Task description for the agent to work on'
},
label: {
type: 'string',
description: 'Optional label for the task'
},
cleanup: {
type: 'string',
enum: ['delete', 'keep'],
description: 'Whether to keep or delete the session after completion (default: keep)',
default: 'keep'
},
model: {
type: 'string',
description: 'Optional model override (e.g., "sonnet", "opus")'
},
timeout: {
type: 'number',
description: 'Timeout in seconds (default: 300)',
default: 300
}
},
required: ['task']
}
},
{
name: 'check_status',
description: 'Check the status of a spawned background task',
inputSchema: {
type: 'object',
properties: {
taskId: {
type: 'string',
description: 'Task ID returned from spawn_task'
}
},
required: ['taskId']
}
},
{
name: 'list_sessions',
description: 'List active sessions for an OpenClaw agent',
inputSchema: {
type: 'object',
properties: {
agent: {
type: 'string',
description: 'Agent ID (e.g., "phoenix")',
default: AGENT
},
activeMinutes: {
type: 'number',
description: 'Only show sessions active in last N minutes (default: 60)',
default: 60
}
},
required: []
}
}
];
// Create MCP server
const server = new Server(
{
name: 'openclaw-mcp-server',
version: '0.1.0'
},
{
capabilities: {
tools: {}
}
}
);
// Handle tool list requests
server.setRequestHandler(ListToolsRequestSchema, async () => {
return { tools: TOOLS };
});
// Handle tool call requests
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
if (!args) {
throw new Error('Missing arguments');
}
switch (name) {
case 'send_message': {
const result = await client.sendMessage({
agent: (args.agent as string) || AGENT,
...args
} as SendMessageArgs);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2)
}
]
};
}
case 'spawn_task': {
const result = await client.spawnTask({
agent: (args.agent as string) || AGENT,
...args
} as SpawnTaskArgs);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2)
}
]
};
}
case 'check_status': {
const result = await client.checkStatus(args as unknown as CheckStatusArgs);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2)
}
]
};
}
case 'list_sessions': {
const result = await client.listSessions({
agent: (args.agent as string) || AGENT,
...args
} as ListSessionsArgs);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2)
}
]
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: 'text',
text: JSON.stringify({ error: errorMessage }, null, 2)
}
],
isError: true
};
}
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
// Log to stderr (stdout is for MCP protocol)
console.error('OpenClaw MCP Server started');
console.error(`Agent: ${AGENT}`);
console.error(`Gateway: ${GATEWAY_URL}`);
}
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});