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,
           });
         }
       });
     });
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden but offers minimal behavioral insight. It mentions 'Prompt-based wrapper' which hints at LLM interaction but doesn't disclose critical traits like whether this is read-only, what permissions are needed, rate limits, or how plans are generated (e.g., step-by-step vs. high-level). For a tool with 9 parameters, this leaves significant gaps in understanding its behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is brief (two phrases) but not effectively structured. The first phrase is clear but basic, while 'Prompt-based wrapper' is ambiguous and doesn't add useful context. It's concise but under-informative rather than efficiently packed with value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given high complexity (9 parameters, 0% schema coverage, no annotations, no output schema), the description is incomplete. It doesn't explain what the tool returns, how plans are structured, or the role of most parameters. For a planning tool in a set of agent-related tools, this leaves too many unknowns for effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate but fails to do so. It mentions 'goal' and 'optional constraints' which map to two parameters, but ignores the other 7 parameters (output_format, extra_args, cwd, executable, model, force, echo_prompt). This leaves most parameters undocumented, severely limiting understanding of their purpose and usage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states 'Generate a plan for a goal with optional constraints' which provides a basic verb+resource combination, but it's vague about what kind of plan (e.g., code execution plan, project plan) and the 'Prompt-based wrapper' adds confusion rather than clarity. It doesn't distinguish this from sibling tools like cursor_agent_chat or cursor_agent_raw that might also involve planning.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There's no guidance on when to use this tool versus alternatives like cursor_agent_chat for discussion or cursor_agent_run for execution. The description mentions 'optional constraints' but doesn't explain what scenarios warrant using this tool over other planning or analysis tools in the sibling set.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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