From c473a69f2d4807ab476a14555b3fe554289af825 Mon Sep 17 00:00:00 2001 From: Ananta Tamboli Date: Fri, 30 Jan 2026 13:00:01 +0530 Subject: [PATCH 1/4] feat: add OpenClaw MCP integration for Cursor IDE This commit introduces the OpenClaw Model Context Protocol (MCP) server integration for Cursor IDE, enabling users to leverage OpenClaw's AI capabilities directly within the IDE. Key features include session management, multi-channel messaging, and built-in code assistance prompts. The integration is documented in the new `cursor-mcp.md` file and includes a changelog for version 2026.1.29. New files added: - `cursor-mcp.md`: Documentation for MCP integration. - `CHANGELOG.md`: Changelog for the integration. - `index.ts`: Main plugin file for MCP server. - `openclaw.plugin.json`: Plugin configuration. - `README.md`: Detailed usage instructions. - `src/gateway-client.ts`: WebSocket client for gateway communication. - `src/server.ts`: MCP server implementation. - `src/types.ts`: Type definitions for the MCP server. - `tsconfig.json`: TypeScript configuration. - `package.json`: Plugin dependencies and scripts. --- docs/plugins/cursor-mcp.md | 215 +++++ extensions/cursor-mcp/CHANGELOG.md | 33 + extensions/cursor-mcp/README.md | 221 ++++++ extensions/cursor-mcp/index.ts | 148 ++++ extensions/cursor-mcp/openclaw.plugin.json | 25 + extensions/cursor-mcp/package.json | 29 + extensions/cursor-mcp/src/gateway-client.ts | 302 ++++++++ extensions/cursor-mcp/src/server.ts | 818 ++++++++++++++++++++ extensions/cursor-mcp/src/types.ts | 55 ++ extensions/cursor-mcp/tsconfig.json | 18 + 10 files changed, 1864 insertions(+) create mode 100644 docs/plugins/cursor-mcp.md create mode 100644 extensions/cursor-mcp/CHANGELOG.md create mode 100644 extensions/cursor-mcp/README.md create mode 100644 extensions/cursor-mcp/index.ts create mode 100644 extensions/cursor-mcp/openclaw.plugin.json create mode 100644 extensions/cursor-mcp/package.json create mode 100644 extensions/cursor-mcp/src/gateway-client.ts create mode 100644 extensions/cursor-mcp/src/server.ts create mode 100644 extensions/cursor-mcp/src/types.ts create mode 100644 extensions/cursor-mcp/tsconfig.json diff --git a/docs/plugins/cursor-mcp.md b/docs/plugins/cursor-mcp.md new file mode 100644 index 000000000..7cfefdd3f --- /dev/null +++ b/docs/plugins/cursor-mcp.md @@ -0,0 +1,215 @@ +--- +title: Cursor IDE Integration +description: Use OpenClaw as an AI backend in Cursor IDE via MCP +--- + +# Cursor IDE Integration + +OpenClaw provides a Model Context Protocol (MCP) server that integrates with [Cursor IDE](https://cursor.com), enabling you to use OpenClaw's AI capabilities directly in Cursor's Composer Agent. + +## Overview + +The Cursor MCP integration allows you to: + +- **Chat with OpenClaw**: Use OpenClaw's AI agent directly in Cursor +- **Manage Sessions**: Create, list, and manage conversation sessions +- **Send Messages**: Route messages through WhatsApp, Telegram, Discord, and more +- **Access Models**: Use any AI model configured in OpenClaw +- **Code Assistance**: Built-in prompts for code review, debugging, and testing + +## Quick Setup + +### Prerequisites + +1. [Install OpenClaw](/install) +2. Start the OpenClaw gateway: + ```bash + openclaw gateway run + ``` +3. Install [Cursor IDE](https://cursor.com) + +### Configure Cursor + +#### Option 1: Cursor Settings UI + +1. Open **Cursor Settings** → **Features** → **MCP** +2. Click **"+ Add New MCP Server"** +3. Configure: + - **Name**: `openclaw` + - **Type**: `stdio` + - **Command**: `openclaw` + - **Arguments**: `mcp serve` + +#### Option 2: Manual Configuration + +Create or edit `~/.cursor/mcp.json`: + +```json +{ + "mcpServers": { + "openclaw": { + "command": "openclaw", + "args": ["mcp", "serve"], + "env": { + "OPENCLAW_GATEWAY_URL": "ws://127.0.0.1:18789" + } + } + } +} +``` + +### Authentication + +If your gateway requires authentication: + +```json +{ + "mcpServers": { + "openclaw": { + "command": "openclaw", + "args": ["mcp", "serve"], + "env": { + "OPENCLAW_GATEWAY_URL": "ws://127.0.0.1:18789", + "OPENCLAW_GATEWAY_TOKEN": "your-token-here", + "OPENCLAW_GATEWAY_PASSWORD": "your-password-here" + } + } + } +} +``` + +## Available Tools + +The MCP server exposes these tools to Cursor: + +| Tool | Description | +|------|-------------| +| `openclaw_chat` | Chat with the OpenClaw AI agent | +| `openclaw_list_sessions` | List all active chat sessions | +| `openclaw_get_session` | Get details about a specific session | +| `openclaw_clear_session` | Clear a session's conversation history | +| `openclaw_execute_command` | Execute OpenClaw control commands | +| `openclaw_send_message` | Send messages through channels | +| `openclaw_get_status` | Get gateway and channel status | +| `openclaw_list_models` | List available AI models | + +### Tool Examples + +#### Chat with OpenClaw + +``` +User: Ask OpenClaw to explain this Python code +Cursor Agent: [Uses openclaw_chat tool] +``` + +#### Send a Message + +``` +User: Send "Build completed" to my Telegram channel +Cursor Agent: [Uses openclaw_send_message tool] +``` + +## Available Resources + +Access OpenClaw data via MCP resources: + +| URI | Description | +|-----|-------------| +| `openclaw://status` | Gateway and channel status | +| `openclaw://models` | Available AI models | +| `openclaw://sessions` | Active chat sessions | +| `openclaw://config` | Current configuration (sanitized) | + +## Available Prompts + +Built-in prompts for common development tasks: + +| Prompt | Description | +|--------|-------------| +| `code_review` | Review code for issues and improvements | +| `explain_code` | Explain how code works | +| `generate_tests` | Generate tests for code | +| `refactor_code` | Suggest refactoring improvements | +| `debug_help` | Help debug issues | +| `send_notification` | Send notification via channels | + +## CLI Commands + +```bash +# Start MCP server manually (usually done by Cursor) +openclaw mcp serve + +# Show configuration help +openclaw mcp info + +# Custom options +openclaw mcp serve --url ws://localhost:18789 --session agent:main:cursor +``` + +## Environment Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `OPENCLAW_GATEWAY_URL` | Gateway WebSocket URL | `ws://127.0.0.1:18789` | +| `OPENCLAW_GATEWAY_TOKEN` | Authentication token | - | +| `OPENCLAW_GATEWAY_PASSWORD` | Authentication password | - | +| `OPENCLAW_SESSION_KEY` | Default session key | `agent:main:cursor` | + +## Architecture + +``` +┌─────────────────┐ MCP Protocol ┌──────────────────┐ +│ Cursor IDE │◄───────────────────►│ OpenClaw MCP │ +│ (MCP Client) │ (stdio) │ Server │ +└─────────────────┘ └────────┬─────────┘ + │ + │ WebSocket + ▼ + ┌──────────────────┐ + │ OpenClaw │ + │ Gateway │ + └────────┬─────────┘ + │ + ┌─────────────────────────────┼─────────────────────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ + │ AI Models │ │ Channels │ │ Sessions │ + │ (Anthropic, │ │ (WhatsApp, │ │ │ + │ OpenAI...) │ │ Telegram...) │ │ │ + └───────────────┘ └───────────────┘ └───────────────┘ +``` + +## Troubleshooting + +### Gateway Connection Failed + +1. Ensure the OpenClaw gateway is running: + ```bash + openclaw gateway run + ``` + +2. Check the gateway URL in your configuration + +3. Verify authentication credentials if required + +### Tools Not Appearing + +1. Restart Cursor after adding the MCP server +2. Check Cursor's MCP logs for errors +3. Ensure `openclaw` is in your system PATH + +### Session Issues + +Clear and restart a session using the `openclaw_clear_session` tool or: + +```bash +openclaw sessions clear agent:main:cursor +``` + +## See Also + +- [Gateway Configuration](/gateway/configuration) +- [Model Providers](/concepts/model-providers) +- [Sessions](/concepts/sessions) +- [Messaging Channels](/channels) diff --git a/extensions/cursor-mcp/CHANGELOG.md b/extensions/cursor-mcp/CHANGELOG.md new file mode 100644 index 000000000..88ae55f44 --- /dev/null +++ b/extensions/cursor-mcp/CHANGELOG.md @@ -0,0 +1,33 @@ +# Changelog + +## 2026.1.29 + +Initial release of OpenClaw Cursor MCP integration. + +### Features + +- **MCP Server**: Full Model Context Protocol server implementation for Cursor IDE +- **Tools**: + - `openclaw_chat`: Chat with OpenClaw AI agent + - `openclaw_list_sessions`: List active sessions + - `openclaw_get_session`: Get session details + - `openclaw_clear_session`: Clear session history + - `openclaw_execute_command`: Execute OpenClaw commands + - `openclaw_send_message`: Send messages through channels + - `openclaw_get_status`: Get gateway status + - `openclaw_list_models`: List available models +- **Resources**: + - `openclaw://status`: Gateway status + - `openclaw://models`: Available models + - `openclaw://sessions`: Active sessions + - `openclaw://config`: Configuration (sanitized) +- **Prompts**: + - `code_review`: Code review assistance + - `explain_code`: Code explanation + - `generate_tests`: Test generation + - `refactor_code`: Refactoring suggestions + - `debug_help`: Debugging assistance + - `send_notification`: Channel notifications +- **CLI Commands**: + - `openclaw mcp serve`: Start MCP server + - `openclaw mcp info`: Show configuration help diff --git a/extensions/cursor-mcp/README.md b/extensions/cursor-mcp/README.md new file mode 100644 index 000000000..52e205f98 --- /dev/null +++ b/extensions/cursor-mcp/README.md @@ -0,0 +1,221 @@ +# OpenClaw Cursor MCP Integration + +This extension provides Model Context Protocol (MCP) server integration for [Cursor IDE](https://cursor.com), enabling OpenClaw as an AI backend within Cursor's Composer Agent. + +## Features + +- **Chat with OpenClaw Agent**: Use OpenClaw's AI capabilities directly in Cursor +- **Session Management**: Create, list, and manage conversation sessions +- **Multi-Channel Messaging**: Send messages through WhatsApp, Telegram, Discord, Slack, and more +- **Model Selection**: Choose from any AI model configured in OpenClaw +- **Code Assistance Prompts**: Built-in prompts for code review, debugging, test generation, and more + +## Quick Start + +### Prerequisites + +1. OpenClaw installed and gateway running: + ```bash + npm install -g openclaw + openclaw gateway run + ``` + +2. Cursor IDE installed + +### Setup in Cursor + +#### Option 1: Cursor Settings UI + +1. Open **Cursor Settings** → **Features** → **MCP** +2. Click **"+ Add New MCP Server"** +3. Configure: + - **Name**: `openclaw` + - **Type**: `stdio` + - **Command**: `openclaw` + - **Arguments**: `mcp serve` + +#### Option 2: Manual Configuration + +Create or edit `~/.cursor/mcp.json`: + +```json +{ + "mcpServers": { + "openclaw": { + "command": "openclaw", + "args": ["mcp", "serve"], + "env": { + "OPENCLAW_GATEWAY_URL": "ws://127.0.0.1:18789" + } + } + } +} +``` + +### Authentication (Optional) + +If your gateway requires authentication, add credentials to the environment: + +```json +{ + "mcpServers": { + "openclaw": { + "command": "openclaw", + "args": ["mcp", "serve"], + "env": { + "OPENCLAW_GATEWAY_URL": "ws://127.0.0.1:18789", + "OPENCLAW_GATEWAY_TOKEN": "your-token-here", + "OPENCLAW_GATEWAY_PASSWORD": "your-password-here" + } + } + } +} +``` + +## Available Tools + +| Tool | Description | +|------|-------------| +| `openclaw_chat` | Chat with the OpenClaw AI agent | +| `openclaw_list_sessions` | List all active chat sessions | +| `openclaw_get_session` | Get details about a specific session | +| `openclaw_clear_session` | Clear a session's conversation history | +| `openclaw_execute_command` | Execute OpenClaw control commands | +| `openclaw_send_message` | Send messages through channels | +| `openclaw_get_status` | Get gateway and channel status | +| `openclaw_list_models` | List available AI models | + +## Available Resources + +| URI | Description | +|-----|-------------| +| `openclaw://status` | Gateway and channel status | +| `openclaw://models` | Available AI models | +| `openclaw://sessions` | Active chat sessions | +| `openclaw://config` | Current configuration (sanitized) | + +## Available Prompts + +| Prompt | Description | +|--------|-------------| +| `code_review` | Review code for issues and improvements | +| `explain_code` | Explain how code works | +| `generate_tests` | Generate tests for code | +| `refactor_code` | Suggest refactoring improvements | +| `debug_help` | Help debug issues | +| `send_notification` | Send notification via channels | + +## Usage Examples + +### Chat with OpenClaw + +In Cursor's Composer, the Agent can use OpenClaw tools: + +``` +User: Use openclaw to help me debug this Python code +Agent: [Uses openclaw_chat tool to send your code to OpenClaw] +``` + +### Send a Message + +``` +User: Send "Build completed successfully" to my Telegram +Agent: [Uses openclaw_send_message with target and channel] +``` + +### Check Status + +``` +User: What's the status of my OpenClaw channels? +Agent: [Uses openclaw_get_status tool] +``` + +## CLI Commands + +```bash +# Start MCP server (usually done automatically by Cursor) +openclaw mcp serve + +# Show configuration help +openclaw mcp info + +# With custom options +openclaw mcp serve --url ws://localhost:18789 --session agent:main:cursor +``` + +## Environment Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `OPENCLAW_GATEWAY_URL` | Gateway WebSocket URL | `ws://127.0.0.1:18789` | +| `OPENCLAW_GATEWAY_TOKEN` | Authentication token | - | +| `OPENCLAW_GATEWAY_PASSWORD` | Authentication password | - | +| `OPENCLAW_SESSION_KEY` | Default session key | `agent:main:cursor` | + +## Troubleshooting + +### Gateway Connection Failed + +1. Ensure the OpenClaw gateway is running: + ```bash + openclaw gateway run + ``` + +2. Check the gateway URL in your configuration + +3. Verify authentication credentials if required + +### Tools Not Appearing in Cursor + +1. Restart Cursor after adding the MCP server +2. Check Cursor's MCP logs for errors +3. Ensure `openclaw` is in your PATH + +### Session Issues + +Clear and restart a session: +``` +User: Clear my OpenClaw session +Agent: [Uses openclaw_clear_session tool] +``` + +## Development + +```bash +# Install dependencies +cd extensions/cursor-mcp +pnpm install + +# Build +pnpm build + +# Test locally +node bin/server.js +``` + +## Architecture + +``` +┌─────────────────┐ MCP Protocol ┌──────────────────┐ +│ Cursor IDE │◄───────────────────►│ OpenClaw MCP │ +│ (MCP Client) │ (stdio) │ Server │ +└─────────────────┘ └────────┬─────────┘ + │ + │ WebSocket + ▼ + ┌──────────────────┐ + │ OpenClaw │ + │ Gateway │ + └────────┬─────────┘ + │ + ┌─────────────────────────────┼─────────────────────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ + │ WhatsApp │ │ Telegram │ │ Discord │ + └───────────────┘ └───────────────┘ └───────────────┘ +``` + +## License + +MIT - Part of the OpenClaw project. diff --git a/extensions/cursor-mcp/index.ts b/extensions/cursor-mcp/index.ts new file mode 100644 index 000000000..fbfd89a4a --- /dev/null +++ b/extensions/cursor-mcp/index.ts @@ -0,0 +1,148 @@ +/** + * OpenClaw Cursor MCP Plugin + * + * This plugin integrates OpenClaw with Cursor IDE through the Model Context Protocol (MCP). + * It allows Cursor to use OpenClaw as an AI backend and provides tools for managing + * conversations, sessions, and messaging channels. + * + * Usage in Cursor: + * 1. Add to Cursor's MCP configuration (~/.cursor/mcp.json or Cursor Settings > Features > MCP) + * 2. Use the openclaw_* tools in Cursor's Composer Agent + * + * Configuration example for mcp.json: + * { + * "mcpServers": { + * "openclaw": { + * "command": "openclaw", + * "args": ["mcp", "serve"], + * "env": { + * "OPENCLAW_GATEWAY_URL": "ws://127.0.0.1:18789", + * "OPENCLAW_GATEWAY_TOKEN": "your-token" + * } + * } + * } + * } + */ + +import { emptyPluginConfigSchema } from "openclaw/plugin-sdk"; +import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; + +export type CursorMcpPluginConfig = { + enabled?: boolean; + port?: number; + autoApproveTools?: string[]; +}; + +const cursorMcpPlugin = { + id: "cursor-mcp", + name: "Cursor MCP Server", + description: "MCP server integration for Cursor IDE - enables OpenClaw as an AI agent in Cursor", + configSchema: emptyPluginConfigSchema(), + + register(api: OpenClawPluginApi) { + // Register the CLI command for starting the MCP server + api.registerCli( + async (ctx) => { + const mcpCommand = ctx.program + .command("mcp") + .description("MCP server for IDE integration"); + + mcpCommand + .command("serve") + .description("Start the MCP server for Cursor IDE integration") + .option("--url ", "Gateway WebSocket URL", "ws://127.0.0.1:18789") + .option("--token ", "Gateway auth token") + .option("--password ", "Gateway auth password") + .option("--session ", "Default session key", "agent:main:cursor") + .action(async (opts) => { + // Dynamic import to avoid loading MCP SDK unless needed + const { OpenClawMcpServer } = await import("./src/server.js"); + + const server = new OpenClawMcpServer({ + gatewayUrl: opts.url, + gatewayToken: opts.token, + gatewayPassword: opts.password, + defaultSessionKey: opts.session, + }); + + process.on("SIGINT", async () => { + await server.stop(); + process.exit(0); + }); + + process.on("SIGTERM", async () => { + await server.stop(); + process.exit(0); + }); + + await server.start(); + }); + + mcpCommand + .command("info") + .description("Show MCP server configuration information for Cursor") + .action(() => { + console.log(` +OpenClaw MCP Server - Cursor IDE Integration + +To use OpenClaw in Cursor, add the following to your Cursor MCP configuration: + +1. Open Cursor Settings > Features > MCP +2. Click "+ Add New MCP Server" +3. Configure: + - Name: openclaw + - Type: stdio + - Command: openclaw mcp serve + +Or manually edit ~/.cursor/mcp.json: + +{ + "mcpServers": { + "openclaw": { + "command": "openclaw", + "args": ["mcp", "serve"], + "env": { + "OPENCLAW_GATEWAY_URL": "ws://127.0.0.1:18789" + } + } + } +} + +Environment Variables: +- OPENCLAW_GATEWAY_URL: Gateway WebSocket URL (default: ws://127.0.0.1:18789) +- OPENCLAW_GATEWAY_TOKEN: Authentication token +- OPENCLAW_GATEWAY_PASSWORD: Authentication password +- OPENCLAW_SESSION_KEY: Default session key (default: agent:main:cursor) + +Available Tools: +- openclaw_chat: Chat with the OpenClaw AI agent +- openclaw_list_sessions: List active sessions +- openclaw_get_session: Get session details +- openclaw_clear_session: Clear session history +- openclaw_execute_command: Execute OpenClaw commands +- openclaw_send_message: Send messages through channels +- openclaw_get_status: Get gateway status +- openclaw_list_models: List available models + +Available Resources: +- openclaw://status: Gateway status +- openclaw://models: Available models +- openclaw://sessions: Active sessions +- openclaw://config: Configuration (sanitized) + +Available Prompts: +- code_review: Review code for issues +- explain_code: Explain how code works +- generate_tests: Generate tests +- refactor_code: Suggest refactoring +- debug_help: Help debug issues +- send_notification: Send notification via channels +`); + }); + }, + { commands: ["mcp"] }, + ); + }, +}; + +export default cursorMcpPlugin; diff --git a/extensions/cursor-mcp/openclaw.plugin.json b/extensions/cursor-mcp/openclaw.plugin.json new file mode 100644 index 000000000..9e6d24541 --- /dev/null +++ b/extensions/cursor-mcp/openclaw.plugin.json @@ -0,0 +1,25 @@ +{ + "id": "cursor-mcp", + "name": "Cursor MCP Server", + "description": "MCP server integration for Cursor IDE - enables OpenClaw as an AI agent in Cursor", + "providers": [], + "configSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable the Cursor MCP server" + }, + "port": { + "type": "number", + "description": "Port for SSE transport (optional, uses stdio by default)" + }, + "autoApproveTools": { + "type": "array", + "items": { "type": "string" }, + "description": "List of tool names to auto-approve without user confirmation" + } + } + } +} diff --git a/extensions/cursor-mcp/package.json b/extensions/cursor-mcp/package.json new file mode 100644 index 000000000..bd6bf4bbd --- /dev/null +++ b/extensions/cursor-mcp/package.json @@ -0,0 +1,29 @@ +{ + "name": "@openclaw/cursor-mcp", + "version": "2026.1.29", + "type": "module", + "description": "OpenClaw MCP server for Cursor IDE integration", + "main": "index.ts", + "openclaw": { + "extensions": [ + "./index.ts" + ] + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.25.3", + "ws": "^8.19.0", + "zod": "^3.25.0" + }, + "peerDependencies": { + "openclaw": "*" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "@types/ws": "^8.18.1", + "typescript": "^5.9.0" + }, + "scripts": { + "build": "tsc", + "start": "node --import tsx src/server.ts" + } +} diff --git a/extensions/cursor-mcp/src/gateway-client.ts b/extensions/cursor-mcp/src/gateway-client.ts new file mode 100644 index 000000000..0c2e95d0e --- /dev/null +++ b/extensions/cursor-mcp/src/gateway-client.ts @@ -0,0 +1,302 @@ +/** + * Gateway client for MCP server + * + * This module provides a standalone WebSocket client for communicating + * with the OpenClaw gateway. It implements the gateway's JSON-RPC protocol. + */ + +import { randomUUID } from "node:crypto"; +import { WebSocket } from "ws"; + +import type { CursorMcpConfig } from "./types.js"; + +type RequestFrame = { + type: "req"; + id: string; + method: string; + params?: unknown; +}; + +type ResponseFrame = { + type: "res"; + id: string; + ok: boolean; + payload?: unknown; + error?: { message?: string }; +}; + +type EventFrame = { + type: "evt"; + event: string; + payload?: unknown; + seq?: number; +}; + +type Pending = { + resolve: (value: unknown) => void; + reject: (err: Error) => void; +}; + +const PROTOCOL_VERSION = 6; + +export class McpGatewayClient { + private ws: WebSocket | null = null; + private connected = false; + private config: CursorMcpConfig; + private pending = new Map(); + private eventHandlers: Map void)[]> = new Map(); + private connectResolve: ((value: void) => void) | null = null; + private connectReject: ((err: Error) => void) | null = null; + private connectNonce: string | null = null; + private connectSent = false; + private connectTimer: ReturnType | null = null; + + constructor(config: CursorMcpConfig) { + this.config = config; + } + + async connect(): Promise { + return new Promise((resolve, reject) => { + this.connectResolve = resolve; + this.connectReject = reject; + + const gatewayUrl = this.config.gatewayUrl ?? "ws://127.0.0.1:18789"; + + try { + this.ws = new WebSocket(gatewayUrl, { + maxPayload: 25 * 1024 * 1024, + }); + } catch (err) { + reject(err instanceof Error ? err : new Error(String(err))); + return; + } + + this.ws.on("open", () => { + this.queueConnect(); + }); + + this.ws.on("message", (data) => { + const raw = typeof data === "string" ? data : data.toString("utf8"); + this.handleMessage(raw); + }); + + this.ws.on("close", (code, reason) => { + const reasonText = typeof reason === "string" ? reason : reason.toString("utf8"); + this.ws = null; + this.connected = false; + this.flushPendingErrors(new Error(`Gateway closed (${code}): ${reasonText}`)); + }); + + this.ws.on("error", (err) => { + if (!this.connectSent && this.connectReject) { + this.connectReject(err instanceof Error ? err : new Error(String(err))); + this.connectResolve = null; + this.connectReject = null; + } + }); + }); + } + + disconnect(): void { + if (this.connectTimer) { + clearTimeout(this.connectTimer); + this.connectTimer = null; + } + if (this.ws) { + this.ws.close(); + this.ws = null; + } + this.connected = false; + this.flushPendingErrors(new Error("Client disconnected")); + } + + isConnected(): boolean { + return this.connected; + } + + private queueConnect(): void { + this.connectNonce = null; + this.connectSent = false; + if (this.connectTimer) clearTimeout(this.connectTimer); + // Wait a bit for optional challenge before sending connect + this.connectTimer = setTimeout(() => { + this.sendConnect(); + }, 500); + } + + private sendConnect(): void { + if (this.connectSent) return; + this.connectSent = true; + if (this.connectTimer) { + clearTimeout(this.connectTimer); + this.connectTimer = null; + } + + const params = { + minProtocol: PROTOCOL_VERSION, + maxProtocol: PROTOCOL_VERSION, + client: { + id: "mcp", + displayName: "Cursor MCP", + version: "2026.1.29", + platform: process.platform, + mode: "backend", + }, + caps: [], + auth: { + token: this.config.gatewayToken, + password: this.config.gatewayPassword, + }, + role: "operator", + scopes: ["operator.admin"], + }; + + this.request("connect", params) + .then(() => { + this.connected = true; + if (this.connectResolve) { + this.connectResolve(); + this.connectResolve = null; + this.connectReject = null; + } + }) + .catch((err) => { + if (this.connectReject) { + this.connectReject(err instanceof Error ? err : new Error(String(err))); + this.connectResolve = null; + this.connectReject = null; + } + this.ws?.close(1008, "connect failed"); + }); + } + + private handleMessage(raw: string): void { + try { + const parsed = JSON.parse(raw); + + // Handle events + if (parsed.type === "evt" || parsed.event) { + const evt = parsed as EventFrame; + + // Handle connect challenge + if (evt.event === "connect.challenge") { + const payload = evt.payload as { nonce?: unknown } | undefined; + const nonce = payload && typeof payload.nonce === "string" ? payload.nonce : null; + if (nonce) { + this.connectNonce = nonce; + this.sendConnect(); + } + return; + } + + // Dispatch to event handlers + const handlers = this.eventHandlers.get(evt.event); + if (handlers) { + for (const handler of handlers) { + try { + handler(evt.payload); + } catch (err) { + console.error(`Event handler error for ${evt.event}:`, err); + } + } + } + return; + } + + // Handle responses + if (parsed.type === "res" || (parsed.id && (parsed.ok !== undefined || parsed.error))) { + const res = parsed as ResponseFrame; + const pending = this.pending.get(res.id); + if (!pending) return; + + this.pending.delete(res.id); + if (res.ok) { + pending.resolve(res.payload); + } else { + pending.reject(new Error(res.error?.message ?? "Unknown error")); + } + } + } catch (err) { + console.error(`Gateway message parse error: ${String(err)}`); + } + } + + private flushPendingErrors(err: Error): void { + for (const [, p] of this.pending) { + p.reject(err); + } + this.pending.clear(); + } + + onEvent(eventName: string, handler: (payload: unknown) => void): () => void { + const handlers = this.eventHandlers.get(eventName) ?? []; + handlers.push(handler); + this.eventHandlers.set(eventName, handlers); + + return () => { + const idx = handlers.indexOf(handler); + if (idx >= 0) handlers.splice(idx, 1); + }; + } + + async request(method: string, params?: unknown): Promise { + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + throw new Error("Gateway not connected"); + } + + const id = randomUUID(); + const frame: RequestFrame = { type: "req", id, method, params }; + + return new Promise((resolve, reject) => { + this.pending.set(id, { + resolve: (value) => resolve(value as T), + reject, + }); + this.ws!.send(JSON.stringify(frame)); + }); + } + + // Convenience methods for common operations + + async chat(params: { + message: string; + sessionKey?: string; + model?: string; + deliver?: boolean; + }): Promise { + return this.request("chat.run", { + message: params.message, + sessionKey: params.sessionKey ?? this.config.defaultSessionKey ?? "agent:main:cursor", + model: params.model, + deliver: params.deliver ?? false, + }); + } + + async listSessions(): Promise { + return this.request("sessions.list", {}); + } + + async getSessionInfo(sessionKey: string): Promise { + return this.request("sessions.get", { sessionKey }); + } + + async clearSession(sessionKey: string): Promise { + return this.request("sessions.clear", { sessionKey }); + } + + async getChannelStatus(): Promise { + return this.request("channels.status", {}); + } + + async getHealth(): Promise { + return this.request("health", {}); + } + + async getModels(): Promise { + return this.request("models.list", {}); + } + + async executeCommand(command: string): Promise { + return this.request("command", { command }); + } +} diff --git a/extensions/cursor-mcp/src/server.ts b/extensions/cursor-mcp/src/server.ts new file mode 100644 index 000000000..aa3091448 --- /dev/null +++ b/extensions/cursor-mcp/src/server.ts @@ -0,0 +1,818 @@ +#!/usr/bin/env node +/** + * OpenClaw MCP Server for Cursor IDE Integration + * + * This server exposes OpenClaw functionality as an MCP server that can be used + * within Cursor IDE. It provides tools for chatting with the OpenClaw agent, + * managing sessions, and executing commands. + * + * Transport modes: + * - stdio (default): For local process-based integration + * - sse: For HTTP-based SSE integration (set OPENCLAW_MCP_PORT) + */ + +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { + CallToolRequestSchema, + ListToolsRequestSchema, + ListResourcesRequestSchema, + ReadResourceRequestSchema, + ListPromptsRequestSchema, + GetPromptRequestSchema, +} from "@modelcontextprotocol/sdk/types.js"; +import { z } from "zod"; + +import { McpGatewayClient } from "./gateway-client.js"; +import type { CursorMcpConfig, McpToolResult } from "./types.js"; + +// Tool input schemas +const ChatSchema = z.object({ + message: z.string().describe("The message to send to the OpenClaw agent"), + sessionKey: z + .string() + .optional() + .describe("Session key for conversation continuity (e.g., 'agent:main:cursor')"), + model: z.string().optional().describe("Model to use (e.g., 'anthropic/claude-sonnet-4')"), +}); + +const SessionKeySchema = z.object({ + sessionKey: z.string().describe("The session key to operate on"), +}); + +const CommandSchema = z.object({ + command: z.string().describe("The command to execute (e.g., '/status', '/help')"), +}); + +const SendMessageSchema = z.object({ + target: z.string().describe("Target identifier (phone number, group ID, or channel:target)"), + message: z.string().describe("Message content to send"), + channel: z + .string() + .optional() + .describe("Channel to use (whatsapp, telegram, discord, slack, etc.)"), +}); + +// MCP Server implementation +export class OpenClawMcpServer { + private server: Server; + private gatewayClient: McpGatewayClient; + private config: CursorMcpConfig; + + constructor(config: CursorMcpConfig = {}) { + this.config = { + gatewayUrl: process.env.OPENCLAW_GATEWAY_URL ?? config.gatewayUrl ?? "ws://127.0.0.1:18789", + gatewayToken: process.env.OPENCLAW_GATEWAY_TOKEN ?? config.gatewayToken, + gatewayPassword: process.env.OPENCLAW_GATEWAY_PASSWORD ?? config.gatewayPassword, + defaultSessionKey: config.defaultSessionKey ?? "agent:main:cursor", + autoApproveTools: config.autoApproveTools ?? [], + ...config, + }; + + this.gatewayClient = new McpGatewayClient(this.config); + + this.server = new Server( + { + name: "openclaw-mcp", + version: "2026.1.29", + }, + { + capabilities: { + tools: {}, + resources: {}, + prompts: {}, + }, + }, + ); + + this.setupHandlers(); + } + + private setupHandlers(): void { + this.setupToolHandlers(); + this.setupResourceHandlers(); + this.setupPromptHandlers(); + } + + private setupToolHandlers(): void { + // List available tools + this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: [ + { + name: "openclaw_chat", + description: + "Chat with the OpenClaw AI agent. Use this to ask questions, request help with coding, or have a conversation. The agent can help with various tasks including code generation, explanations, and problem-solving.", + inputSchema: { + type: "object", + properties: { + message: { + type: "string", + description: "The message to send to the OpenClaw agent", + }, + sessionKey: { + type: "string", + description: + "Session key for conversation continuity (e.g., 'agent:main:cursor'). Different session keys maintain separate conversation contexts.", + }, + model: { + type: "string", + description: + "Model to use (e.g., 'anthropic/claude-sonnet-4', 'openai/gpt-4o'). Leave empty to use the default model.", + }, + }, + required: ["message"], + }, + }, + { + name: "openclaw_list_sessions", + description: + "List all active OpenClaw chat sessions. Returns information about ongoing conversations including session keys, message counts, and last activity times.", + inputSchema: { + type: "object", + properties: {}, + }, + }, + { + name: "openclaw_get_session", + description: + "Get detailed information about a specific OpenClaw session, including conversation history summary and metadata.", + inputSchema: { + type: "object", + properties: { + sessionKey: { + type: "string", + description: "The session key to get information about", + }, + }, + required: ["sessionKey"], + }, + }, + { + name: "openclaw_clear_session", + description: + "Clear a specific OpenClaw session, removing all conversation history. Use this to start fresh with a new context.", + inputSchema: { + type: "object", + properties: { + sessionKey: { + type: "string", + description: "The session key to clear", + }, + }, + required: ["sessionKey"], + }, + }, + { + name: "openclaw_execute_command", + description: + "Execute an OpenClaw control command. Available commands include /status, /help, /models, /config, and more.", + inputSchema: { + type: "object", + properties: { + command: { + type: "string", + description: + "The command to execute (e.g., '/status', '/help', '/models list')", + }, + }, + required: ["command"], + }, + }, + { + name: "openclaw_send_message", + description: + "Send a message through OpenClaw to a specific target on a messaging channel (WhatsApp, Telegram, Discord, Slack, etc.).", + inputSchema: { + type: "object", + properties: { + target: { + type: "string", + description: + "Target identifier (phone number for WhatsApp, username/chat ID for Telegram, channel ID for Discord/Slack)", + }, + message: { + type: "string", + description: "Message content to send", + }, + channel: { + type: "string", + description: + "Channel to use: whatsapp, telegram, discord, slack, imessage, signal, line, etc.", + }, + }, + required: ["target", "message"], + }, + }, + { + name: "openclaw_get_status", + description: + "Get the current status of OpenClaw gateway, including connected channels, active sessions, and health information.", + inputSchema: { + type: "object", + properties: {}, + }, + }, + { + name: "openclaw_list_models", + description: + "List all available AI models configured in OpenClaw. Shows provider, model ID, and capabilities.", + inputSchema: { + type: "object", + properties: {}, + }, + }, + ], + })); + + // Handle tool calls + this.server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + try { + // Ensure gateway is connected + if (!this.gatewayClient.isConnected()) { + try { + await this.gatewayClient.connect(); + } catch (err) { + return this.errorResult(`Failed to connect to OpenClaw gateway: ${String(err)}`); + } + } + + switch (name) { + case "openclaw_chat": { + const parsed = ChatSchema.safeParse(args); + if (!parsed.success) { + return this.errorResult(`Invalid arguments: ${parsed.error.message}`); + } + const result = await this.gatewayClient.chat({ + message: parsed.data.message, + sessionKey: parsed.data.sessionKey ?? this.config.defaultSessionKey, + model: parsed.data.model, + deliver: false, + }); + return this.formatChatResult(result); + } + + case "openclaw_list_sessions": { + const result = await this.gatewayClient.listSessions(); + return this.formatJsonResult(result); + } + + case "openclaw_get_session": { + const parsed = SessionKeySchema.safeParse(args); + if (!parsed.success) { + return this.errorResult(`Invalid arguments: ${parsed.error.message}`); + } + const result = await this.gatewayClient.getSessionInfo(parsed.data.sessionKey); + return this.formatJsonResult(result); + } + + case "openclaw_clear_session": { + const parsed = SessionKeySchema.safeParse(args); + if (!parsed.success) { + return this.errorResult(`Invalid arguments: ${parsed.error.message}`); + } + const result = await this.gatewayClient.clearSession(parsed.data.sessionKey); + return this.formatJsonResult(result); + } + + case "openclaw_execute_command": { + const parsed = CommandSchema.safeParse(args); + if (!parsed.success) { + return this.errorResult(`Invalid arguments: ${parsed.error.message}`); + } + const result = await this.gatewayClient.executeCommand(parsed.data.command); + return this.formatJsonResult(result); + } + + case "openclaw_send_message": { + const parsed = SendMessageSchema.safeParse(args); + if (!parsed.success) { + return this.errorResult(`Invalid arguments: ${parsed.error.message}`); + } + const target = parsed.data.channel + ? `${parsed.data.channel}:${parsed.data.target}` + : parsed.data.target; + const result = await this.gatewayClient.request("message.send", { + target, + text: parsed.data.message, + }); + return this.formatJsonResult(result); + } + + case "openclaw_get_status": { + const [health, channels] = await Promise.all([ + this.gatewayClient.getHealth(), + this.gatewayClient.getChannelStatus(), + ]); + return this.formatJsonResult({ health, channels }); + } + + case "openclaw_list_models": { + const result = await this.gatewayClient.getModels(); + return this.formatJsonResult(result); + } + + default: + return this.errorResult(`Unknown tool: ${name}`); + } + } catch (err) { + return this.errorResult(`Tool execution failed: ${String(err)}`); + } + }); + } + + private setupResourceHandlers(): void { + // List available resources + this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({ + resources: [ + { + uri: "openclaw://status", + name: "OpenClaw Gateway Status", + description: "Current status of the OpenClaw gateway and connected channels", + mimeType: "application/json", + }, + { + uri: "openclaw://models", + name: "Available Models", + description: "List of AI models configured in OpenClaw", + mimeType: "application/json", + }, + { + uri: "openclaw://sessions", + name: "Active Sessions", + description: "List of active chat sessions", + mimeType: "application/json", + }, + { + uri: "openclaw://config", + name: "OpenClaw Configuration", + description: "Current OpenClaw configuration (sanitized, no secrets)", + mimeType: "application/json", + }, + ], + })); + + // Read resource content + this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { + const { uri } = request.params; + + try { + if (!this.gatewayClient.isConnected()) { + await this.gatewayClient.connect(); + } + + switch (uri) { + case "openclaw://status": { + const [health, channels] = await Promise.all([ + this.gatewayClient.getHealth(), + this.gatewayClient.getChannelStatus(), + ]); + return { + contents: [ + { + uri, + mimeType: "application/json", + text: JSON.stringify({ health, channels }, null, 2), + }, + ], + }; + } + + case "openclaw://models": { + const models = await this.gatewayClient.getModels(); + return { + contents: [ + { + uri, + mimeType: "application/json", + text: JSON.stringify(models, null, 2), + }, + ], + }; + } + + case "openclaw://sessions": { + const sessions = await this.gatewayClient.listSessions(); + return { + contents: [ + { + uri, + mimeType: "application/json", + text: JSON.stringify(sessions, null, 2), + }, + ], + }; + } + + case "openclaw://config": { + const config = await this.gatewayClient.request("config.get", {}); + return { + contents: [ + { + uri, + mimeType: "application/json", + text: JSON.stringify(config, null, 2), + }, + ], + }; + } + + default: + throw new Error(`Unknown resource: ${uri}`); + } + } catch (err) { + return { + contents: [ + { + uri, + mimeType: "text/plain", + text: `Error reading resource: ${String(err)}`, + }, + ], + }; + } + }); + } + + private setupPromptHandlers(): void { + // List available prompts + this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({ + prompts: [ + { + name: "code_review", + description: "Ask OpenClaw to review code for issues, improvements, and best practices", + arguments: [ + { + name: "code", + description: "The code to review", + required: true, + }, + { + name: "language", + description: "Programming language (optional, will be auto-detected)", + required: false, + }, + ], + }, + { + name: "explain_code", + description: "Ask OpenClaw to explain how code works", + arguments: [ + { + name: "code", + description: "The code to explain", + required: true, + }, + ], + }, + { + name: "generate_tests", + description: "Ask OpenClaw to generate tests for code", + arguments: [ + { + name: "code", + description: "The code to generate tests for", + required: true, + }, + { + name: "framework", + description: "Testing framework to use (e.g., jest, pytest, vitest)", + required: false, + }, + ], + }, + { + name: "refactor_code", + description: "Ask OpenClaw to suggest refactoring improvements", + arguments: [ + { + name: "code", + description: "The code to refactor", + required: true, + }, + { + name: "goal", + description: "Refactoring goal (e.g., 'improve readability', 'optimize performance')", + required: false, + }, + ], + }, + { + name: "debug_help", + description: "Ask OpenClaw to help debug an issue", + arguments: [ + { + name: "code", + description: "The code with the issue", + required: true, + }, + { + name: "error", + description: "Error message or description of the problem", + required: true, + }, + ], + }, + { + name: "send_notification", + description: "Send a notification message through OpenClaw channels", + arguments: [ + { + name: "message", + description: "The notification message to send", + required: true, + }, + { + name: "channel", + description: "Channel to use (whatsapp, telegram, discord, slack)", + required: false, + }, + ], + }, + ], + })); + + // Get prompt content + this.server.setRequestHandler(GetPromptRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + switch (name) { + case "code_review": { + const code = args?.code ?? ""; + const language = args?.language ?? ""; + return { + messages: [ + { + role: "user", + content: { + type: "text", + text: `Please review the following${language ? ` ${language}` : ""} code for: +- Bugs and potential issues +- Code quality and readability +- Performance considerations +- Security concerns +- Best practices and improvements + +Code to review: +\`\`\`${language} +${code} +\`\`\``, + }, + }, + ], + }; + } + + case "explain_code": { + const code = args?.code ?? ""; + return { + messages: [ + { + role: "user", + content: { + type: "text", + text: `Please explain how the following code works, step by step: + +\`\`\` +${code} +\`\`\` + +Include: +- What the code does overall +- Key components and their purposes +- Control flow and logic +- Any important patterns or techniques used`, + }, + }, + ], + }; + } + + case "generate_tests": { + const code = args?.code ?? ""; + const framework = args?.framework ?? ""; + return { + messages: [ + { + role: "user", + content: { + type: "text", + text: `Please generate comprehensive tests for the following code${framework ? ` using ${framework}` : ""}: + +\`\`\` +${code} +\`\`\` + +Include: +- Unit tests for individual functions/methods +- Edge cases and boundary conditions +- Error handling scenarios +- Mock/stub setup where needed`, + }, + }, + ], + }; + } + + case "refactor_code": { + const code = args?.code ?? ""; + const goal = args?.goal ?? "improve code quality"; + return { + messages: [ + { + role: "user", + content: { + type: "text", + text: `Please refactor the following code to ${goal}: + +\`\`\` +${code} +\`\`\` + +Provide: +- The refactored code +- Explanation of changes made +- Benefits of the refactoring`, + }, + }, + ], + }; + } + + case "debug_help": { + const code = args?.code ?? ""; + const error = args?.error ?? ""; + return { + messages: [ + { + role: "user", + content: { + type: "text", + text: `I'm encountering an issue with this code: + +\`\`\` +${code} +\`\`\` + +Error/Problem: +${error} + +Please help me: +1. Identify the root cause +2. Explain why this is happening +3. Provide a fix with explanation`, + }, + }, + ], + }; + } + + case "send_notification": { + const message = args?.message ?? ""; + const channel = args?.channel ?? "default"; + return { + messages: [ + { + role: "user", + content: { + type: "text", + text: `Please send this notification through OpenClaw${channel !== "default" ? ` via ${channel}` : ""}: + +"${message}" + +Use the openclaw_send_message tool if a specific target is configured, or inform me that I need to specify a target.`, + }, + }, + ], + }; + } + + default: + return { + messages: [ + { + role: "user", + content: { + type: "text", + text: `Unknown prompt: ${name}`, + }, + }, + ], + }; + } + }); + } + + private formatChatResult(result: unknown): McpToolResult { + if (!result || typeof result !== "object") { + return { + content: [{ type: "text", text: "No response from OpenClaw agent" }], + }; + } + + const response = result as { payloads?: Array<{ text?: string }>; error?: string }; + + if (response.error) { + return this.errorResult(response.error); + } + + const payloads = response.payloads; + if (!Array.isArray(payloads) || payloads.length === 0) { + return { + content: [{ type: "text", text: "No response from OpenClaw agent" }], + }; + } + + const text = payloads + .map((p) => (typeof p.text === "string" ? p.text : "")) + .filter(Boolean) + .join("\n\n"); + + return { + content: [{ type: "text", text: text || "No response from OpenClaw agent" }], + }; + } + + private formatJsonResult(result: unknown): McpToolResult { + return { + content: [ + { + type: "text", + text: typeof result === "string" ? result : JSON.stringify(result, null, 2), + }, + ], + }; + } + + private errorResult(message: string): McpToolResult { + return { + content: [{ type: "text", text: `Error: ${message}` }], + isError: true, + }; + } + + async start(): Promise { + // Connect to gateway + try { + await this.gatewayClient.connect(); + console.error("[openclaw-mcp] Connected to OpenClaw gateway"); + } catch (err) { + console.error(`[openclaw-mcp] Warning: Could not connect to gateway: ${String(err)}`); + console.error("[openclaw-mcp] Will attempt to connect when tools are called"); + } + + // Start stdio transport + const transport = new StdioServerTransport(); + await this.server.connect(transport); + console.error("[openclaw-mcp] MCP server started on stdio transport"); + } + + async stop(): Promise { + this.gatewayClient.disconnect(); + await this.server.close(); + } +} + +// Main entry point for standalone execution +async function main() { + const config: CursorMcpConfig = { + gatewayUrl: process.env.OPENCLAW_GATEWAY_URL, + gatewayToken: process.env.OPENCLAW_GATEWAY_TOKEN, + gatewayPassword: process.env.OPENCLAW_GATEWAY_PASSWORD, + defaultSessionKey: process.env.OPENCLAW_SESSION_KEY ?? "agent:main:cursor", + }; + + const server = new OpenClawMcpServer(config); + + process.on("SIGINT", async () => { + console.error("[openclaw-mcp] Shutting down..."); + await server.stop(); + process.exit(0); + }); + + process.on("SIGTERM", async () => { + console.error("[openclaw-mcp] Shutting down..."); + await server.stop(); + process.exit(0); + }); + + await server.start(); +} + +// Run if this is the main module +const isMain = + typeof process !== "undefined" && + process.argv[1] && + (process.argv[1].endsWith("server.ts") || + process.argv[1].endsWith("server.js") || + process.argv[1].includes("cursor-mcp")); + +if (isMain) { + main().catch((err) => { + console.error(`[openclaw-mcp] Fatal error: ${String(err)}`); + process.exit(1); + }); +} + +export { main }; diff --git a/extensions/cursor-mcp/src/types.ts b/extensions/cursor-mcp/src/types.ts new file mode 100644 index 000000000..a22fc40ba --- /dev/null +++ b/extensions/cursor-mcp/src/types.ts @@ -0,0 +1,55 @@ +/** + * Type definitions for the Cursor MCP server + */ + +export type CursorMcpConfig = { + enabled?: boolean; + port?: number; + autoApproveTools?: string[]; + gatewayUrl?: string; + gatewayToken?: string; + gatewayPassword?: string; + defaultSessionKey?: string; +}; + +export type McpToolResult = { + content: Array<{ + type: "text" | "image" | "resource"; + text?: string; + data?: string; + mimeType?: string; + uri?: string; + }>; + isError?: boolean; +}; + +export type ChatRequest = { + message: string; + sessionKey?: string; + model?: string; + stream?: boolean; +}; + +export type SessionInfo = { + sessionKey: string; + agentId: string; + messageCount: number; + lastActivity?: string; + model?: string; +}; + +export type ChannelStatus = { + channelId: string; + accountId: string; + status: "connected" | "disconnected" | "error"; + lastHeartbeat?: string; + error?: string; +}; + +export type GatewayHealth = { + status: "healthy" | "degraded" | "unhealthy"; + uptime: number; + version: string; + channels: ChannelStatus[]; + activeSessionCount: number; +}; diff --git a/extensions/cursor-mcp/tsconfig.json b/extensions/cursor-mcp/tsconfig.json new file mode 100644 index 000000000..c2ec4c815 --- /dev/null +++ b/extensions/cursor-mcp/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist", + "rootDir": ".", + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["*.ts", "src/**/*.ts", "bin/**/*.ts"], + "exclude": ["node_modules", "dist"] +} From c01df21ea497d8efd1fdbdb601ea6cc354ef66c0 Mon Sep 17 00:00:00 2001 From: Ananta Tamboli Date: Fri, 30 Jan 2026 13:47:58 +0530 Subject: [PATCH 2/4] feat: enhance OpenClaw integration with Cursor's AI models This commit adds comprehensive support for using Cursor's AI models (e.g., Claude, GPT-4) within OpenClaw. Key updates include new setup instructions, model availability details, and usage examples in the documentation. The `setup-models` command is introduced to facilitate configuration and health checks for the Copilot Proxy. Additionally, a new module for managing Cursor models is created to streamline integration. New files added: - `src/cursor-models.ts`: Module for Cursor model management. - Updates to `cursor-mcp.md`, `README.md`, and `index.ts` for enhanced documentation and command functionality. --- docs/plugins/cursor-mcp.md | 39 ++++ extensions/cursor-mcp/README.md | 65 ++++++ extensions/cursor-mcp/index.ts | 58 +++++ extensions/cursor-mcp/src/cursor-models.ts | 233 +++++++++++++++++++++ 4 files changed, 395 insertions(+) create mode 100644 extensions/cursor-mcp/src/cursor-models.ts diff --git a/docs/plugins/cursor-mcp.md b/docs/plugins/cursor-mcp.md index 7cfefdd3f..3ae420a99 100644 --- a/docs/plugins/cursor-mcp.md +++ b/docs/plugins/cursor-mcp.md @@ -207,6 +207,45 @@ Clear and restart a session using the `openclaw_clear_session` tool or: openclaw sessions clear agent:main:cursor ``` +## Using Cursor's Models in OpenClaw + +The integration is bidirectional - you can also use Cursor's AI models (Claude, GPT-4, etc.) as providers for OpenClaw. + +### Setup + +1. **Install Copilot Proxy extension** in Cursor (search for "Copilot Proxy" by AdrianGonz97) + +2. **Check the proxy**: + ```bash + openclaw mcp setup-models --check + ``` + +3. **Configure OpenClaw**: + ```bash + openclaw config set agents.defaults.model cursor/claude-sonnet-4 + ``` + +### Available Models + +| Model | ID | +|-------|-----| +| Claude Sonnet 4 | `cursor/claude-sonnet-4` | +| Claude Sonnet 4 (Thinking) | `cursor/claude-sonnet-4-thinking` | +| GPT-4o | `cursor/gpt-4o` | +| GPT-4o Mini | `cursor/gpt-4o-mini` | +| o1 | `cursor/o1` | +| Gemini 2.5 Pro | `cursor/gemini-2.5-pro` | + +### Usage + +```bash +# Use Cursor's Claude in OpenClaw +openclaw agent --model cursor/claude-sonnet-4 "Help me debug this" + +# In the TUI +openclaw tui --model cursor/gpt-4o +``` + ## See Also - [Gateway Configuration](/gateway/configuration) diff --git a/extensions/cursor-mcp/README.md b/extensions/cursor-mcp/README.md index 52e205f98..de297e011 100644 --- a/extensions/cursor-mcp/README.md +++ b/extensions/cursor-mcp/README.md @@ -216,6 +216,71 @@ node bin/server.js └───────────────┘ └───────────────┘ └───────────────┘ ``` +## Using Cursor's Models with OpenClaw + +You can also use Cursor's AI models (Claude, GPT-4, etc.) as providers for OpenClaw. This enables bidirectional integration: + +- **OpenClaw → Cursor**: Use OpenClaw tools in Cursor (MCP server) +- **Cursor → OpenClaw**: Use Cursor's models in OpenClaw + +### Setup Cursor Models + +1. **Install Copilot Proxy extension** in Cursor: + - Search for "Copilot Proxy" by AdrianGonz97 in Extensions + - Install and restart Cursor + +2. **Check the proxy is running**: + ```bash + openclaw mcp setup-models --check + ``` + +3. **Configure OpenClaw** to use Cursor models: + ```bash + # Set a Cursor model as default + openclaw config set agents.defaults.model cursor/claude-sonnet-4 + ``` + + Or manually add to `~/.clawdbot/config.yaml`: + ```yaml + models: + providers: + cursor: + baseUrl: "http://localhost:3000/v1" + apiKey: "cursor-proxy" + api: openai-completions + models: + - id: claude-sonnet-4 + name: Claude Sonnet 4 + contextWindow: 200000 + ``` + +### Available Cursor Models + +| Model ID | Description | +|----------|-------------| +| `cursor/claude-sonnet-4` | Claude Sonnet 4 | +| `cursor/claude-sonnet-4-thinking` | Claude Sonnet 4 with extended thinking | +| `cursor/gpt-4o` | GPT-4o | +| `cursor/gpt-4o-mini` | GPT-4o Mini | +| `cursor/o1` | OpenAI o1 (reasoning) | +| `cursor/o1-mini` | OpenAI o1-mini | +| `cursor/gemini-2.5-pro` | Gemini 2.5 Pro | + +> **Note**: Available models depend on your Cursor subscription tier. + +### Usage Examples + +```bash +# Chat using Cursor's Claude +openclaw agent --model cursor/claude-sonnet-4 "Explain this code" + +# Send message via channels using GPT-4 +openclaw message send --model cursor/gpt-4o "Hello from OpenClaw!" + +# Use in TUI +openclaw tui --model cursor/claude-sonnet-4 +``` + ## License MIT - Part of the OpenClaw project. diff --git a/extensions/cursor-mcp/index.ts b/extensions/cursor-mcp/index.ts index fbfd89a4a..0a12de2bf 100644 --- a/extensions/cursor-mcp/index.ts +++ b/extensions/cursor-mcp/index.ts @@ -78,6 +78,64 @@ const cursorMcpPlugin = { await server.start(); }); + // Command to set up Cursor models as a provider + mcpCommand + .command("setup-models") + .description("Configure OpenClaw to use Cursor's AI models via Copilot Proxy") + .option("--url ", "Copilot Proxy base URL", "http://localhost:3000/v1") + .option("--check", "Only check if proxy is running, don't configure") + .action(async (opts) => { + const { + checkCursorProxyHealth, + generateCursorProviderConfig, + CURSOR_AVAILABLE_MODELS, + CURSOR_SETUP_INSTRUCTIONS, + } = await import("./src/cursor-models.js"); + + console.log("Checking Cursor Copilot Proxy..."); + const health = await checkCursorProxyHealth(opts.url); + + if (!health.ok) { + console.error(`\n❌ Copilot Proxy not accessible: ${health.error}`); + console.log(CURSOR_SETUP_INSTRUCTIONS); + process.exit(1); + } + + console.log("✓ Copilot Proxy is running"); + + if (opts.check) { + console.log("\nAvailable models:"); + for (const model of CURSOR_AVAILABLE_MODELS) { + console.log(` - cursor/${model.id} (${model.name})`); + } + return; + } + + // Generate and apply config + const configPatch = generateCursorProviderConfig({ baseUrl: opts.url }); + console.log("\nTo use Cursor models, add this to your OpenClaw config:\n"); + console.log("```yaml"); + console.log("models:"); + console.log(" providers:"); + console.log(" cursor:"); + console.log(` baseUrl: "${opts.url}"`); + console.log(' apiKey: "cursor-proxy"'); + console.log(" api: openai-completions"); + console.log(" authHeader: false"); + console.log(" models:"); + for (const model of CURSOR_AVAILABLE_MODELS.slice(0, 4)) { + console.log(` - id: ${model.id}`); + console.log(` name: ${model.name}`); + console.log(` contextWindow: ${model.contextWindow}`); + } + console.log("```"); + console.log("\nOr run: openclaw config set agents.defaults.model cursor/claude-sonnet-4"); + console.log("\nAvailable models:"); + for (const model of CURSOR_AVAILABLE_MODELS) { + console.log(` - cursor/${model.id}`); + } + }); + mcpCommand .command("info") .description("Show MCP server configuration information for Cursor") diff --git a/extensions/cursor-mcp/src/cursor-models.ts b/extensions/cursor-mcp/src/cursor-models.ts new file mode 100644 index 000000000..2ac8ef3a2 --- /dev/null +++ b/extensions/cursor-mcp/src/cursor-models.ts @@ -0,0 +1,233 @@ +/** + * Cursor Model Provider for OpenClaw + * + * This module provides integration with Cursor's Language Model API, + * allowing OpenClaw to use models available through Cursor's subscription + * (Claude, GPT-4, etc.). + * + * How it works: + * 1. Cursor exposes models via a local HTTP API when the Copilot Proxy extension is active + * 2. OpenClaw connects to this API as an OpenAI-compatible endpoint + * 3. You can then use Cursor's models in OpenClaw prompts + * + * Setup: + * 1. Install "Copilot Proxy" extension in Cursor + * 2. Run `openclaw setup cursor` to configure + * 3. Use models like `cursor/claude-sonnet-4` in OpenClaw + */ + +import type { CursorMcpConfig } from "./types.js"; + +// Default Cursor proxy configuration +const DEFAULT_CURSOR_PROXY_URL = "http://localhost:3000/v1"; +const DEFAULT_CURSOR_PROXY_PORT = 3000; + +// Models typically available through Cursor +export const CURSOR_AVAILABLE_MODELS = [ + { + id: "claude-sonnet-4", + name: "Claude Sonnet 4", + provider: "anthropic", + contextWindow: 200000, + reasoning: false, + }, + { + id: "claude-sonnet-4-thinking", + name: "Claude Sonnet 4 (Thinking)", + provider: "anthropic", + contextWindow: 200000, + reasoning: true, + }, + { + id: "gpt-4o", + name: "GPT-4o", + provider: "openai", + contextWindow: 128000, + reasoning: false, + }, + { + id: "gpt-4o-mini", + name: "GPT-4o Mini", + provider: "openai", + contextWindow: 128000, + reasoning: false, + }, + { + id: "o1", + name: "o1", + provider: "openai", + contextWindow: 200000, + reasoning: true, + }, + { + id: "o1-mini", + name: "o1-mini", + provider: "openai", + contextWindow: 128000, + reasoning: true, + }, + { + id: "gemini-2.5-pro", + name: "Gemini 2.5 Pro", + provider: "google", + contextWindow: 1000000, + reasoning: false, + }, +] as const; + +export type CursorModelId = (typeof CURSOR_AVAILABLE_MODELS)[number]["id"]; + +export type CursorModelConfig = { + baseUrl: string; + models: string[]; +}; + +/** + * Check if the Cursor proxy is running and accessible + */ +export async function checkCursorProxyHealth( + baseUrl: string = DEFAULT_CURSOR_PROXY_URL, +): Promise<{ ok: boolean; error?: string }> { + try { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 5000); + + const response = await fetch(`${baseUrl}/models`, { + method: "GET", + signal: controller.signal, + }); + + clearTimeout(timeout); + + if (response.ok) { + return { ok: true }; + } + return { ok: false, error: `HTTP ${response.status}: ${response.statusText}` }; + } catch (err) { + if (err instanceof Error && err.name === "AbortError") { + return { ok: false, error: "Connection timeout - is Copilot Proxy running in Cursor?" }; + } + return { ok: false, error: String(err) }; + } +} + +/** + * Generate OpenClaw configuration patch for Cursor models + */ +export function generateCursorProviderConfig(opts: { + baseUrl?: string; + models?: string[]; +}): Record { + const baseUrl = opts.baseUrl ?? DEFAULT_CURSOR_PROXY_URL; + const modelIds = opts.models ?? CURSOR_AVAILABLE_MODELS.map((m) => m.id); + + return { + models: { + providers: { + cursor: { + baseUrl, + apiKey: "cursor-proxy", // Placeholder - Copilot Proxy handles auth + api: "openai-completions", + authHeader: false, + models: modelIds.map((id) => { + const model = CURSOR_AVAILABLE_MODELS.find((m) => m.id === id); + return { + id, + name: model?.name ?? id, + api: "openai-completions", + reasoning: model?.reasoning ?? false, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: model?.contextWindow ?? 128000, + maxTokens: 8192, + }; + }), + }, + }, + }, + agents: { + defaults: { + models: Object.fromEntries(modelIds.map((id) => [`cursor/${id}`, {}])), + }, + }, + }; +} + +/** + * Instructions for setting up Cursor model integration + */ +export const CURSOR_SETUP_INSTRUCTIONS = ` +# Using Cursor's Models with OpenClaw + +## Prerequisites +1. Cursor IDE with an active subscription (Pro/Business) +2. The "Copilot Proxy" VS Code extension installed in Cursor + +## Setup Steps + +### Step 1: Install Copilot Proxy Extension +In Cursor, go to Extensions and search for "Copilot Proxy" by AdrianGonz97. +Install it and restart Cursor. + +### Step 2: Start the Proxy Server +The extension should start automatically. Verify it's running: +- Look for "Copilot Proxy" in the status bar +- Or check http://localhost:3000/v1/models in your browser + +### Step 3: Configure OpenClaw +Run the setup command: + openclaw setup cursor + +Or manually add to your config (~/.clawdbot/config.yaml): + +models: + providers: + cursor: + baseUrl: "http://localhost:3000/v1" + apiKey: "cursor-proxy" + api: openai-completions + authHeader: false + models: + - id: claude-sonnet-4 + name: Claude Sonnet 4 + contextWindow: 200000 + - id: gpt-4o + name: GPT-4o + contextWindow: 128000 + +### Step 4: Use Cursor Models +Now you can use Cursor's models in OpenClaw: + +# Set as default model +openclaw config set agents.defaults.model cursor/claude-sonnet-4 + +# Use in a specific message +openclaw message send --model cursor/gpt-4o "Hello!" + +# Use in the TUI +openclaw tui --model cursor/claude-sonnet-4 + +## Available Models (depends on your Cursor subscription) +- cursor/claude-sonnet-4 +- cursor/claude-sonnet-4-thinking +- cursor/gpt-4o +- cursor/gpt-4o-mini +- cursor/o1 +- cursor/o1-mini +- cursor/gemini-2.5-pro + +## Troubleshooting + +### Proxy not responding +- Ensure Cursor is running with the Copilot Proxy extension active +- Check if http://localhost:3000/v1/models returns a response +- Try restarting Cursor + +### Model not available +- Your Cursor subscription may not include all models +- Check Cursor's model selector to see which models you have access to + +### Authentication errors +- Ensure you're logged into Cursor with your subscription account +- The proxy uses your Cursor session for authentication +`; From b9306684c2ce3944e4341aea66665a4b9ce73f35 Mon Sep 17 00:00:00 2001 From: Ananta Tamboli Date: Fri, 30 Jan 2026 14:20:02 +0530 Subject: [PATCH 3/4] fix: update lockfile for cursor-mcp extension --- extensions/cursor-mcp/package.json | 16 +--- extensions/cursor-mcp/tsconfig.json | 18 ----- pnpm-lock.yaml | 116 +++++++++++++++++++++++++--- 3 files changed, 106 insertions(+), 44 deletions(-) delete mode 100644 extensions/cursor-mcp/tsconfig.json diff --git a/extensions/cursor-mcp/package.json b/extensions/cursor-mcp/package.json index bd6bf4bbd..4abf9366f 100644 --- a/extensions/cursor-mcp/package.json +++ b/extensions/cursor-mcp/package.json @@ -3,27 +3,15 @@ "version": "2026.1.29", "type": "module", "description": "OpenClaw MCP server for Cursor IDE integration", - "main": "index.ts", "openclaw": { "extensions": [ "./index.ts" ] }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.25.3", - "ws": "^8.19.0", - "zod": "^3.25.0" - }, - "peerDependencies": { - "openclaw": "*" + "@modelcontextprotocol/sdk": "^1.25.3" }, "devDependencies": { - "@types/node": "^22.0.0", - "@types/ws": "^8.18.1", - "typescript": "^5.9.0" - }, - "scripts": { - "build": "tsc", - "start": "node --import tsx src/server.ts" + "openclaw": "workspace:*" } } diff --git a/extensions/cursor-mcp/tsconfig.json b/extensions/cursor-mcp/tsconfig.json deleted file mode 100644 index c2ec4c815..000000000 --- a/extensions/cursor-mcp/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "outDir": "./dist", - "rootDir": ".", - "declaration": true, - "declarationMap": true, - "sourceMap": true - }, - "include": ["*.ts", "src/**/*.ts", "bin/**/*.ts"], - "exclude": ["node_modules", "dist"] -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95b940c97..5b32d63dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,13 +42,13 @@ importers: version: 1.2.0-beta.3 '@mariozechner/pi-agent-core': specifier: 0.49.3 - version: 0.49.3(ws@8.19.0)(zod@4.3.6) + version: 0.49.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) '@mariozechner/pi-ai': specifier: 0.49.3 - version: 0.49.3(ws@8.19.0)(zod@4.3.6) + version: 0.49.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) '@mariozechner/pi-coding-agent': specifier: 0.49.3 - version: 0.49.3(ws@8.19.0)(zod@4.3.6) + version: 0.49.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) '@mariozechner/pi-tui': specifier: 0.49.3 version: 0.49.3 @@ -266,6 +266,16 @@ importers: extensions/copilot-proxy: {} + extensions/cursor-mcp: + dependencies: + '@modelcontextprotocol/sdk': + specifier: ^1.25.3 + version: 1.25.3(hono@4.11.4)(zod@4.3.6) + devDependencies: + openclaw: + specifier: workspace:* + version: link:../.. + extensions/diagnostics-otel: dependencies: '@opentelemetry/api': @@ -1489,6 +1499,16 @@ packages: '@mistralai/mistralai@1.10.0': resolution: {integrity: sha512-tdIgWs4Le8vpvPiUEWne6tK0qbVc+jMenujnvTqOjogrJUsCSQhus0tHTU1avDDh5//Rq2dFgP9mWRAdIEoBqg==} + '@modelcontextprotocol/sdk@1.25.3': + resolution: {integrity: sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + '@mozilla/readability@0.6.0': resolution: {integrity: sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==} engines: {node: '>=14.0.0'} @@ -3325,6 +3345,10 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + croner@9.1.0: resolution: {integrity: sha512-p9nwwR4qyT5W996vBZhdvBCnMhicY5ytZkR4D1Xj0wuTDEiMnjwR57Q3RXYY/s0EpX6Ay3vgIcfaR+ewGHsi+g==} engines: {node: '>=18.0'} @@ -3531,10 +3555,24 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + eventsource-parser@3.0.6: + resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + express@4.22.1: resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} engines: {node: '>= 0.10.0'} @@ -3976,6 +4014,9 @@ packages: jose@4.15.9: resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + js-base64@3.7.8: resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} @@ -4002,6 +4043,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -4697,6 +4741,10 @@ packages: resolution: {integrity: sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==} hasBin: true + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + playwright-core@1.58.0: resolution: {integrity: sha512-aaoB1RWrdNi3//rOeKuMiS65UCcgOVljU46At6eFcOFPFHWtd2weHRRow6z/n+Lec0Lvu0k9ZPKJSjPugikirw==} engines: {node: '>=18'} @@ -6649,10 +6697,12 @@ snapshots: '@glideapps/ts-necessities@2.2.3': {} - '@google/genai@1.34.0': + '@google/genai@1.34.0(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6))': dependencies: google-auth-library: 10.5.0 ws: 8.19.0 + optionalDependencies: + '@modelcontextprotocol/sdk': 1.25.3(hono@4.11.4)(zod@4.3.6) transitivePeerDependencies: - bufferutil - supports-color @@ -6700,7 +6750,6 @@ snapshots: '@hono/node-server@1.19.9(hono@4.11.4)': dependencies: hono: 4.11.4 - optional: true '@huggingface/jinja@0.5.3': optional: true @@ -6994,9 +7043,9 @@ snapshots: transitivePeerDependencies: - tailwindcss - '@mariozechner/pi-agent-core@0.49.3(ws@8.19.0)(zod@4.3.6)': + '@mariozechner/pi-agent-core@0.49.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': dependencies: - '@mariozechner/pi-ai': 0.49.3(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-ai': 0.49.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) '@mariozechner/pi-tui': 0.49.3 transitivePeerDependencies: - '@modelcontextprotocol/sdk' @@ -7007,11 +7056,11 @@ snapshots: - ws - zod - '@mariozechner/pi-ai@0.49.3(ws@8.19.0)(zod@4.3.6)': + '@mariozechner/pi-ai@0.49.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': dependencies: '@anthropic-ai/sdk': 0.71.2(zod@4.3.6) '@aws-sdk/client-bedrock-runtime': 3.972.0 - '@google/genai': 1.34.0 + '@google/genai': 1.34.0(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6)) '@mistralai/mistralai': 1.10.0 '@sinclair/typebox': 0.34.47 ajv: 8.17.1 @@ -7029,12 +7078,12 @@ snapshots: - ws - zod - '@mariozechner/pi-coding-agent@0.49.3(ws@8.19.0)(zod@4.3.6)': + '@mariozechner/pi-coding-agent@0.49.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6))(ws@8.19.0)(zod@4.3.6)': dependencies: '@mariozechner/clipboard': 0.3.0 '@mariozechner/jiti': 2.6.5 - '@mariozechner/pi-agent-core': 0.49.3(ws@8.19.0)(zod@4.3.6) - '@mariozechner/pi-ai': 0.49.3(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-agent-core': 0.49.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) + '@mariozechner/pi-ai': 0.49.3(@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6))(ws@8.19.0)(zod@4.3.6) '@mariozechner/pi-tui': 0.49.3 '@silvia-odwyer/photon-node': 0.3.4 chalk: 5.6.2 @@ -7111,6 +7160,28 @@ snapshots: zod: 3.25.76 zod-to-json-schema: 3.25.1(zod@3.25.76) + '@modelcontextprotocol/sdk@1.25.3(hono@4.11.4)(zod@4.3.6)': + dependencies: + '@hono/node-server': 1.19.9(hono@4.11.4) + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 7.5.1(express@5.2.1) + jose: 6.1.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.3.6 + zod-to-json-schema: 3.25.1(zod@4.3.6) + transitivePeerDependencies: + - hono + - supports-color + '@mozilla/readability@0.6.0': {} '@napi-rs/canvas-android-arm64@0.1.88': @@ -9210,6 +9281,11 @@ snapshots: core-util-is@1.0.3: {} + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + croner@9.1.0: {} cross-fetch@4.1.0: @@ -9403,8 +9479,18 @@ snapshots: events@3.3.0: {} + eventsource-parser@3.0.6: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.6 + expect-type@1.3.0: {} + express-rate-limit@7.5.1(express@5.2.1): + dependencies: + express: 5.2.1 + express@4.22.1: dependencies: accepts: 1.3.8 @@ -9981,6 +10067,8 @@ snapshots: jose@4.15.9: {} + jose@6.1.3: {} + js-base64@3.7.8: {} js-tokens@9.0.1: {} @@ -10002,6 +10090,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.2: {} + json-schema@0.4.0: {} json-stringify-safe@5.0.1: {} @@ -10729,6 +10819,8 @@ snapshots: dependencies: pngjs: 7.0.0 + pkce-challenge@5.0.1: {} + playwright-core@1.58.0: {} playwright@1.58.0: From 850c68cc92676990081d5e76a6e2f8048fac29f3 Mon Sep 17 00:00:00 2001 From: Ananta Tamboli Date: Fri, 30 Jan 2026 14:21:35 +0530 Subject: [PATCH 4/4] fix: update lockfile and fix lint issues in cursor-mcp --- extensions/cursor-mcp/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/extensions/cursor-mcp/index.ts b/extensions/cursor-mcp/index.ts index 0a12de2bf..13c327130 100644 --- a/extensions/cursor-mcp/index.ts +++ b/extensions/cursor-mcp/index.ts @@ -87,7 +87,6 @@ const cursorMcpPlugin = { .action(async (opts) => { const { checkCursorProxyHealth, - generateCursorProviderConfig, CURSOR_AVAILABLE_MODELS, CURSOR_SETUP_INSTRUCTIONS, } = await import("./src/cursor-models.js"); @@ -111,8 +110,7 @@ const cursorMcpPlugin = { return; } - // Generate and apply config - const configPatch = generateCursorProviderConfig({ baseUrl: opts.url }); + // Show configuration instructions console.log("\nTo use Cursor models, add this to your OpenClaw config:\n"); console.log("```yaml"); console.log("models:");