Cursor Background agent doesn't call MCP?


I’m using the beta version of the Cursor background agent.
When I open a new window for the background agent, I can access the MCP settings and add the required MCP servers, but they don’t work.
I’d like to know whether this feature isn’t supported yet or if the issue is specific to me.
I’d also like to know if MCP support is planned for the background agent in the future.

If anyone has it working, please let me know how you got it to run.

Ignore that ai’s respond. Because when i call that mcp not in the background agent, it works appropriately.

3 Likes

Hi, while we wait for Cursor team to reply, did you setup the MCP inside the Backround Agent or is it an SSE public URL MCP?

i apply .cursor/mcp.json in my project folder and commit & push. so when i open the background agent, it comes out because this mcp.json file is in my git. but when i opened at first, this mcp doesn’t activate so i activate with toggle button.

image

Any updates ?

3 Likes

I am also waiting for the update. I can use multiple MCP in foreground but not get success yet in background agent

This is such a core feature for me. I want to be able to spin off background agents while on the go from my phone, and I want it to be able to access Supabase via the MCP. Please, Cursor team, add support for this!

2 Likes

Whats going on here this has been an ask for months now. Cloud agents aren’t very useable without supplemental context

For anyone else facing this issue I found a work around for local mcps. I ended up making a cli wrapper that reads a .cursor/mcp.json and allows the agent to call local mcps through the command line. Isn’t pretty but has worked well enough

setting up and calling mcp from within background agents would be an absolute game changer for us. just imagine using cursor to create comments or update stories in the product management tool.

Is there “already” a feature request for this?

Any update on this? Total game changer waiting to explode

can you share some technical details? What did it take for the cloud agent to use the local mcps? where do these mcps run? how can it access them?

This has been very successful for me–If you use this mcp.js script then it will turn your .cursor/mcp.json into a cli that can be called by the model running mcp {name} {tool name} {args}. Then just add a rule explaining how to use it and have your install script update the rule to auto apply always. Install script gets set/called in environment.json. This should work for both local and remote mcps just make sure you have the correct env vars set if they require authentication. Details and code below:

1. Add mcp.js to your .cursor folder

#!/usr/bin/env node
// CLI wrapper for MCP servers - enables Cursor Cloud Agents to call MCP tools

const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');

// Smart config lookup
const possiblePaths = [
  path.join(process.cwd(), 'mcp.json'),
  path.join(process.cwd(), '.cursor', 'mcp.json'),
  path.join(process.env.HOME || '/home/user', '.cursor', 'mcp.json'),
];

let configPath = null;
let config = null;

for (const p of possiblePaths) {
  if (fs.existsSync(p)) {
    configPath = p;
    config = JSON.parse(fs.readFileSync(p, 'utf8')).mcpServers;
    break;
  }
}

if (!config) {
  console.error('✖ Could not find mcp.json');
  console.error('  Looked in:');
  possiblePaths.forEach(p => console.error(`    ${p}`));
  process.exit(1);
}

console.log(`Using MCP config: ${configPath}`);

const USAGE = `
MCP one-shot CLI wrapper (for Cursor Cloud Agents)

Usage:
  mcp servers                     List all available MCP servers
  mcp tools <server>              List tools for a server
  mcp run <server> <tool> [input] Call a tool (input is JSON string or - for stdin)
`.trim();

if (process.argv.length < 3 || ['-h', '--help'].includes(process.argv[2])) {
  console.log(USAGE);
  process.exit(0);
}

const sub = process.argv[2].toLowerCase();

async function main() {
  try {
    if (sub === 'servers') {
      console.log('Available servers:');
      Object.keys(config).forEach(s => console.log('  ' + s));
      return;
    }

    if (sub === 'tools' || sub === 'list') {
      const server = process.argv[3];
      if (!server || !config[server]) {
        console.error('Provide a valid server. Use mcp servers to see them.');
        process.exit(1);
      }
      await runMcp(server, null, null);
      return;
    }

    if (sub === 'run' || sub === 'call') {
      const server = process.argv[3];
      const tool   = process.argv[4];
      let inputJson = process.argv[5];

      if (!server || !tool || !config[server]) {
        console.error('Invalid arguments. Use mcp --help');
        process.exit(1);
      }

      if (!inputJson || inputJson === '-') {
        // Read from stdin for large inputs
        inputJson = await new Promise((res, rej) => {
          let data = '';
          process.stdin.on('data', chunk => data += chunk);
          process.stdin.on('end', () => res(data));
          process.stdin.on('error', rej);
        });
      }

      let input;
      try {
        input = JSON.parse(inputJson);
      } catch (e) {
        console.error('Input is not valid JSON.');
        process.exit(1);
      }

      await runMcp(server, tool, input);
      return;
    }

    console.error('Unknown command. Use mcp --help');
    process.exit(1);
  } catch (err) {
    console.error('Unexpected error:', err.message || err);
    process.exit(1);
  }
}

// Core MCP runner – one-shot with timeout
async function runMcp(server, toolName, input) {
  const cfg = config[server];
  
  // Security: Validate command to prevent injection
  if (!cfg.command || typeof cfg.command !== 'string') {
    throw new Error(`Invalid command for server "${server}"`);
  }
  if (!/^[\w./-]+$/.test(cfg.command)) {
    throw new Error(`Invalid command for server "${server}": unsafe characters`);
  }
  if (!Array.isArray(cfg.args)) {
    throw new Error(`Invalid args for server "${server}": must be array`);
  }
  for (let i = 0; i < cfg.args.length; i++) {
    if (typeof cfg.args[i] !== 'string') {
      throw new Error(`Invalid arg at index ${i}: must be string`);
    }
  }
  
  const child = spawn(cfg.command, cfg.args, {
    stdio: ['pipe', 'pipe', 'pipe'],
    env: { ...process.env, ...(cfg.env || {}) }
  });

  child.stderr.on('data', data => {
    console.error(`[${server} stderr] ${data}`);
  });

  // Live output
  child.stdout.on('data', chunk => process.stdout.write(chunk));

  const send = (msg) => {
    if (!msg.id) msg.id = Date.now().toString();
    child.stdin.write(JSON.stringify(msg) + '\n');
  };

  const waitFor = (predicate, timeoutMs = 90_000) => new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      child.stdout.removeAllListeners('data');
      child.removeAllListeners('close');
      reject(new Error(`Timeout after ${timeoutMs/1000}s`));
    }, timeoutMs);
    
    const handler = (data) => {
      const lines = (data + '').split('\n');
      for (let line of lines) {
        line = line.trim();
        if (!line) continue;
        let msg;
        try { msg = JSON.parse(line); } catch (_) { continue; }
        if (predicate(msg)) {
          clearTimeout(timer);
          child.stdout.removeListener('data', handler);
          child.removeListener('close', closeHandler);
          resolve(msg);
          return;
        }
      }
    };
    
    const closeHandler = () => {
      clearTimeout(timer);
      reject(new Error('MCP process exited prematurely'));
    };
    
    child.stdout.on('data', handler);
    child.on('close', closeHandler);
  });

  // Initialize MCP protocol
  const initId = 'init-1';
  send({
    jsonrpc: '2.0',
    method: 'initialize',
    id: initId,
    params: {
      protocolVersion: '2024-11-05',
      capabilities: {},
      clientInfo: { name: 'mcp-cli-wrapper', version: '1.0.0' }
    }
  });
  
  try {
    const initResponse = await waitFor(m => m.id === initId, 15_000);
    if (initResponse.error) {
      throw new Error(`Initialize failed: ${JSON.stringify(initResponse.error)}`);
    }
  } catch (e) {
    console.error('Failed to initialize MCP server');
    child.kill();
    throw e;
  }

  if (!toolName) {
    // List tools mode
    send({ jsonrpc: '2.0', method: 'tools/list', id: 'list' });
    const resp = await waitFor(m => m.id === 'list');
    console.log('\nTools for "' + server + '":');
    const tools = resp.tools || resp.result?.tools || [];
    tools.forEach(t => {
      console.log(`  ${t.name}`);
      if (t.description) console.log(`    ${t.description}`);
      if (t.inputSchema) console.log(`    input: ${JSON.stringify(t.inputSchema)}`);
      console.log('');
    });
    child.stdin.end();
    child.kill('SIGTERM');
    setTimeout(() => process.exit(0), 50);
    return;
  }

  // Call tool
  const callId = 'call-1';
  send({
    jsonrpc: '2.0',
    method: 'tools/call',
    id: callId,
    params: { name: toolName, arguments: input }
  });

  const resultMsg = await waitFor(m => m.id === callId);
  
  if (resultMsg.error) {
    console.error('MCP tool error:', resultMsg.error.message || JSON.stringify(resultMsg.error));
    process.exit(1);
  }

  // Pretty-print result
  console.log('\n[FINAL RESULT]');
  const result = resultMsg.result || resultMsg;
  if (result.content) {
    result.content.forEach(item => {
      if (item.type === 'text') {
        console.log(item.text);
      } else {
        console.log(JSON.stringify(item, null, 2));
      }
    });
  } else {
    console.log(JSON.stringify(result, null, 2));
  }

  child.stdin.end();
  child.kill('SIGTERM');
  setTimeout(() => process.exit(0), 50);
}

main().catch(err => {
  console.error('\nFailed:', err.message || err);
  process.exit(1);
});

2. In your install script (listed by environment.json), make it executable:

chmod +x "$WORKSPACE_ROOT/.cursor/mcp.js"
mkdir -p "$HOME/bin"
ln -sf "$WORKSPACE_ROOT/.cursor/mcp.js" "$HOME/bin/mcp"

3. Add this to a rule that gets auto-applied in cloud agent environments:

### MCP CLI ACCESS

You have MCP tool access through the global `mcp` command.

**Commands:**
- `mcp servers` - list available servers
- `mcp tools <server>` - show tools and input schemas
- `mcp run <server> <tool> '<json>'` - call a tool
1 Like

This is AMAZING! love this

1 Like

Thanks @kolly this is a very cool workaround!

But I still think the cursor team can fix thise quite easily…

3 Likes

Posting to emphasize the desire for this functionality.

Desired workflow: A Slack message like “Need work on this ticket” is sent → Cursor Background Agent uses the Atlassian MCP to pull Jira ticket context → starts dev work → replies in the thread with a PR.

1 Like

Since I see very few people asking whether MCP servers work with cursor agents in the cloud, I really wonder if background agents are used or not. How do people use agents if they don’t have access to MCP servers to be effective?

Didn’t know background agents could have MCP access, that’s ace.

Is this how are people getting around the inability to use .env files?