Skip to main content
Glama
sailay1996

Cursor Agent MCP Server

by sailay1996

cursor_agent_plan_task

Create structured plans for coding goals with optional constraints to organize development tasks efficiently.

Instructions

Generate a plan for a goal with optional constraints. Prompt-based wrapper.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
goalYes
constraintsNo
output_formatNotext
extra_argsNo
cwdNo
executableNo
modelNo
forceNo
echo_promptNo

Implementation Reference

  • server.js:361-379 (registration)
    Registers the 'cursor_agent_plan_task' MCP tool with name, description, PLAN_TASK_SCHEMA, and inline async handler function.
    server.tool(
      'cursor_agent_plan_task',
      'Generate a plan for a goal with optional constraints. Prompt-based wrapper.',
      PLAN_TASK_SCHEMA.shape,
      async (args) => {
        try {
          const { goal, constraints, output_format, cwd, executable, model, force, extra_args } = args;
          const cons = constraints ?? [];
          const composedPrompt =
            `Create a step-by-step plan to accomplish the following goal:\n` +
            `- Goal: ${String(goal)}\n` +
            (cons.length ? `- Constraints:\n${cons.map((c)=>`  - ${String(c)}`).join('\n')}\n` : '') +
            `Provide a numbered list of actions.`;
          return await runCursorAgent({ prompt: composedPrompt, output_format, extra_args, cwd, executable, model, force });
        } catch (e) {
          return { content: [{ type: 'text', text: `Invalid params: ${e?.message || e}` }], isError: true };
        }
      },
    );
  • Zod schema for cursor_agent_plan_task inputs: required 'goal' string, optional 'constraints' array, plus shared COMMON fields (output_format, etc.).
    const PLAN_TASK_SCHEMA = z.object({
     goal: z.string().min(1, 'goal is required'),
     constraints: z.array(z.string()).optional(),
     ...COMMON,
    });
  • Handler composes a specific planning prompt from 'goal' and 'constraints', then delegates to runCursorAgent helper to execute via cursor-agent CLI.
    async (args) => {
      try {
        const { goal, constraints, output_format, cwd, executable, model, force, extra_args } = args;
        const cons = constraints ?? [];
        const composedPrompt =
          `Create a step-by-step plan to accomplish the following goal:\n` +
          `- Goal: ${String(goal)}\n` +
          (cons.length ? `- Constraints:\n${cons.map((c)=>`  - ${String(c)}`).join('\n')}\n` : '') +
          `Provide a numbered list of actions.`;
        return await runCursorAgent({ prompt: composedPrompt, output_format, extra_args, cwd, executable, model, force });
      } catch (e) {
        return { content: [{ type: 'text', text: `Invalid params: ${e?.message || e}` }], isError: true };
      }
    },
  • Shared helper for most cursor-agent tools: normalizes arguments (handles 'arguments' nesting), applies env/model/force overrides, invokes CLI via invokeCursorAgent.
    // 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;
    }
  • Core executor spawning 'cursor-agent' CLI process: handles --print/output-format, model/force flags, timeouts, idle timeouts, and output capture.
    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,
           });
         }
       });
     });
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sailay1996/cursor-agent-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server