mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 14:10:24 +00:00
feat(mcp): add plugin tools MCP server for ACP sessions
Standalone MCP server that exposes OpenClaw plugin-registered tools (e.g. memory-lancedb's memory_recall, memory_store, memory_forget) to ACP sessions running Claude Code via acpx's MCP proxy mechanism. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Peter Steinberger
parent
3f5ed11266
commit
415899984e
106
src/mcp/plugin-tools-serve.ts
Normal file
106
src/mcp/plugin-tools-serve.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Standalone MCP server that exposes OpenClaw plugin-registered tools
|
||||
* (e.g. memory-lancedb's memory_recall, memory_store, memory_forget)
|
||||
* so ACP sessions running Claude Code can use them.
|
||||
*
|
||||
* Run via: node --import tsx src/mcp/plugin-tools-serve.ts
|
||||
* Or: bun src/mcp/plugin-tools-serve.ts
|
||||
*/
|
||||
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import {
|
||||
CallToolRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import type { AnyAgentTool } from "../agents/tools/common.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { resolvePluginTools } from "../plugins/tools.js";
|
||||
import { VERSION } from "../version.js";
|
||||
|
||||
function resolveJsonSchemaForTool(tool: AnyAgentTool): Record<string, unknown> {
|
||||
const params = tool.parameters;
|
||||
if (params && typeof params === "object" && "type" in params) {
|
||||
return params as Record<string, unknown>;
|
||||
}
|
||||
// Fallback: accept any object
|
||||
return { type: "object", properties: {} };
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const cfg = loadConfig();
|
||||
const tools = resolvePluginTools({
|
||||
context: { config: cfg },
|
||||
suppressNameConflicts: true,
|
||||
});
|
||||
|
||||
if (tools.length === 0) {
|
||||
process.stderr.write("plugin-tools-serve: no plugin tools found\n");
|
||||
}
|
||||
|
||||
const toolMap = new Map<string, AnyAgentTool>();
|
||||
for (const tool of tools) {
|
||||
toolMap.set(tool.name, tool);
|
||||
}
|
||||
|
||||
const server = new Server(
|
||||
{ name: "openclaw-plugin-tools", version: VERSION },
|
||||
{ capabilities: { tools: {} } },
|
||||
);
|
||||
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||
tools: tools.map((tool) => ({
|
||||
name: tool.name,
|
||||
description: tool.description ?? "",
|
||||
inputSchema: resolveJsonSchemaForTool(tool),
|
||||
})),
|
||||
}));
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const tool = toolMap.get(request.params.name);
|
||||
if (!tool) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Unknown tool: ${request.params.name}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
try {
|
||||
const result = await tool.execute(
|
||||
`mcp-${Date.now()}`,
|
||||
request.params.arguments ?? {},
|
||||
);
|
||||
return {
|
||||
content: Array.isArray(result.content) ? result.content : [{ type: "text", text: String(result) }],
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Tool error: ${err instanceof Error ? err.message : String(err)}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
|
||||
let shuttingDown = false;
|
||||
const shutdown = () => {
|
||||
if (shuttingDown) return;
|
||||
shuttingDown = true;
|
||||
process.stdin.off("end", shutdown);
|
||||
process.stdin.off("close", shutdown);
|
||||
process.off("SIGINT", shutdown);
|
||||
process.off("SIGTERM", shutdown);
|
||||
void server.close();
|
||||
};
|
||||
|
||||
process.stdin.once("end", shutdown);
|
||||
process.stdin.once("close", shutdown);
|
||||
process.once("SIGINT", shutdown);
|
||||
process.once("SIGTERM", shutdown);
|
||||
|
||||
await server.connect(transport);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
process.stderr.write(`plugin-tools-serve: ${err instanceof Error ? err.message : String(err)}\n`);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user