cursor_agent_chat
Interact with cursor-agent using a prompt to generate responses in text, JSON, or markdown formats. Supports cost-effective repository analysis, code search, and task planning with configurable output options.
Instructions
Chat with cursor-agent using a prompt and optional model/force/output_format.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| cwd | No | ||
| echo_prompt | No | ||
| executable | No | ||
| extra_args | No | ||
| force | No | ||
| model | No | ||
| output_format | No | text | |
| prompt | Yes |
Implementation Reference
- server.js:277-297 (registration)Registers the 'cursor_agent_chat' MCP tool with its description, input schema (CHAT_SCHEMA.shape), and inline async handler function.'cursor_agent_chat', 'Chat with cursor-agent using a prompt and optional model/force/output_format.', CHAT_SCHEMA.shape, async (args) => { try { // Normalize prompt in case the host nests under "arguments" const prompt = (args && typeof args === 'object' && 'prompt' in args ? args.prompt : undefined) ?? (args && typeof args === 'object' && args.arguments && typeof args.arguments === 'object' ? args.arguments.prompt : undefined); const flat = { ...(args && typeof args === 'object' && args.arguments && typeof args.arguments === 'object' ? args.arguments : args), prompt, }; return await runCursorAgent(flat); } catch (e) { return { content: [{ type: 'text', text: `Invalid params: ${e?.message || e}` }], isError: true }; } }, );
- server.js:280-296 (handler)The tool handler: normalizes input arguments (handling nested 'arguments' structure), calls runCursorAgent helper with flattened params, and returns its result or error.async (args) => { try { // Normalize prompt in case the host nests under "arguments" const prompt = (args && typeof args === 'object' && 'prompt' in args ? args.prompt : undefined) ?? (args && typeof args === 'object' && args.arguments && typeof args.arguments === 'object' ? args.arguments.prompt : undefined); const flat = { ...(args && typeof args === 'object' && args.arguments && typeof args.arguments === 'object' ? args.arguments : args), prompt, }; return await runCursorAgent(flat); } catch (e) { return { content: [{ type: 'text', text: `Invalid params: ${e?.message || e}` }], isError: true }; } },
- server.js:234-237 (schema)CHAT_SCHEMA: Zod input schema requiring 'prompt' string and extending COMMON fields (output_format, extra_args, cwd, executable, model, force, echo_prompt). Referenced in tool registration.const CHAT_SCHEMA = z.object({ prompt: z.string().min(1, 'prompt is required'), ...COMMON, });
- server.js:152-192 (helper)runCursorAgent helper: back-compat wrapper for single-shot runs; builds argv from prompt/extra_args, invokes core executor, handles prompt echo.// Accepts either a flat args object or an object with an "arguments" field (some hosts). async function runCursorAgent(input) { const source = (input && typeof input === 'object' && input.arguments && typeof input.prompt === 'undefined') ? input.arguments : input; const { prompt, output_format = 'text', extra_args, cwd, executable, model, force, } = source || {}; const argv = [...(extra_args ?? []), String(prompt)]; const usedPrompt = argv.length ? String(argv[argv.length - 1]) : ''; // Optional prompt echo and debug diagnostics if (process.env.DEBUG_CURSOR_MCP === '1') { try { const preview = usedPrompt.slice(0, 400).replace(/\n/g, '\\n'); console.error('[cursor-mcp] prompt:', preview); if (extra_args?.length) console.error('[cursor-mcp] extra_args:', JSON.stringify(extra_args)); if (model) console.error('[cursor-mcp] model:', model); if (typeof force === 'boolean') console.error('[cursor-mcp] force:', String(force)); } catch {} } const result = await invokeCursorAgent({ argv, output_format, cwd, executable, model, force }); // Echo prompt either when env is set or when caller provided echo_prompt: true (if host forwards unknown args it's fine) const echoEnabled = process.env.CURSOR_AGENT_ECHO_PROMPT === '1' || source?.echo_prompt === true; if (echoEnabled) { const text = `Prompt used:\n${usedPrompt}`; const content = Array.isArray(result?.content) ? result.content : []; return { ...result, content: [{ type: 'text', text }, ...content] }; } return result;
- server.js:38-149 (helper)invokeCursorAgent core helper: spawns 'cursor-agent' CLI process with computed args/flags/env, captures/manages output, timeouts, idle kills, and errors.async function invokeCursorAgent({ argv, output_format = 'text', cwd, executable, model, force, print = true }) { const cmd = resolveExecutable(executable); // Compute model/force from args/env const userArgs = [...(argv ?? [])]; const hasModelFlag = userArgs.some((a) => a === '-m' || a === '--model' || /^(?:-m=|--model=)/.test(String(a))); const envModel = process.env.CURSOR_AGENT_MODEL && process.env.CURSOR_AGENT_MODEL.trim(); const effectiveModel = model?.trim?.() || envModel; const hasForceFlag = userArgs.some((a) => a === '-f' || a === '--force'); const envForce = (() => { const v = (process.env.CURSOR_AGENT_FORCE || '').toLowerCase(); return v === '1' || v === 'true' || v === 'yes' || v === 'on'; })(); const effectiveForce = typeof force === 'boolean' ? force : envForce; const finalArgv = [ ...(print ? ['--print', '--output-format', output_format] : []), ...userArgs, ...(hasForceFlag || !effectiveForce ? [] : ['-f']), ...(hasModelFlag || !effectiveModel ? [] : ['-m', effectiveModel]), ]; return new Promise((resolve) => { let settled = false; let out = ''; let err = ''; let idleTimer = null; let killedByIdle = false; const cleanup = () => { if (mainTimer) clearTimeout(mainTimer); if (idleTimer) clearTimeout(idleTimer); }; if (process.env.DEBUG_CURSOR_MCP === '1') { try { console.error('[cursor-mcp] spawn:', cmd, ...finalArgv); } catch {} } const child = spawn(cmd, finalArgv, { shell: false, // safer across platforms; rely on PATH/PATHEXT cwd: cwd || process.cwd(), env: process.env, }); try { child.stdin?.end(); } catch {} const idleMs = Number.parseInt(process.env.CURSOR_AGENT_IDLE_EXIT_MS || '0', 10); const scheduleIdleKill = () => { if (!Number.isFinite(idleMs) || idleMs <= 0) return; if (idleTimer) clearTimeout(idleTimer); idleTimer = setTimeout(() => { killedByIdle = true; try { child.kill('SIGKILL'); } catch {} }, idleMs); }; child.stdout.on('data', (d) => { out += d.toString(); scheduleIdleKill(); }); child.stderr.on('data', (d) => { err += d.toString(); }); child.on('error', (e) => { if (settled) return; settled = true; cleanup(); if (process.env.DEBUG_CURSOR_MCP === '1') { try { console.error('[cursor-mcp] error:', e); } catch {} } const msg = `Failed to start "${cmd}": ${e?.message || e}\n` + `Args: ${JSON.stringify(finalArgv)}\n` + (process.env.CURSOR_AGENT_PATH ? `CURSOR_AGENT_PATH=${process.env.CURSOR_AGENT_PATH}\n` : ''); resolve({ content: [{ type: 'text', text: msg }], isError: true }); }); const defaultTimeout = 30000; const timeoutMs = Number.parseInt(process.env.CURSOR_AGENT_TIMEOUT_MS || String(defaultTimeout), 10); const mainTimer = setTimeout(() => { try { child.kill('SIGKILL'); } catch {} if (settled) return; settled = true; cleanup(); resolve({ content: [{ type: 'text', text: `cursor-agent timed out after ${Number.isFinite(timeoutMs) ? timeoutMs : defaultTimeout}ms` }], isError: true, }); }, Number.isFinite(timeoutMs) ? timeoutMs : defaultTimeout); child.on('close', (code) => { if (settled) return; settled = true; cleanup(); if (process.env.DEBUG_CURSOR_MCP === '1') { try { console.error('[cursor-mcp] exit:', code, 'stdout bytes=', out.length, 'stderr bytes=', err.length); } catch {} } if (code === 0 || (killedByIdle && out)) { resolve({ content: [{ type: 'text', text: out || '(no output)' }] }); } else { resolve({ content: [{ type: 'text', text: `cursor-agent exited with code ${code}\n${err || out || '(no output)'}` }], isError: true, }); } }); }); }