Mentu

Using MCP Servers

Using MCP Servers

Mentu scripts can call tools on external MCP servers through the servers.* proxy. This lets you compose mentu's SDK with any MCP-compatible tool — web crawlers, databases, code analysis, and more.

1. Configure servers

MCP servers are configured in .mcp.json at your workspace root. This is the standard MCP configuration format:

{
  "mcpServers": {
    "crawlio": {
      "command": "/path/to/CrawlioMCP",
      "args": [],
      "env": {}
    },
    "sqlite": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-sqlite", "/path/to/db.sqlite"]
    }
  }
}

Each key (e.g., crawlio, sqlite) becomes the server ID you use in scripts.

2. Call tools from scripts

Access any configured server through the servers proxy:

// Call a tool on the 'crawlio' server
const result = await servers.crawlio.call('search_api', {
  query: 'authentication endpoints',
  limit: 10,
});
 
console.log(JSON.stringify(result, null, 2));

The servers.* proxy returns a Promise — use await to get the result. This is one of only two async APIs in the SDK (the other is sleep()).

3. Compose with CIR

The real power is composing MCP tool results with mentu's SDK. Fetch data from external sources, process it, and capture observations into CIR:

const query = mentu.vars.QUERY ?? 'security vulnerabilities';
 
// Fetch from an external MCP server
const searchResults = await servers.crawlio.call('search_api', { query });
 
// Process and capture into CIR
const items = Array.isArray(searchResults) ? searchResults : [];
console.log(`Found ${items.length} results for "${query}"`);
 
for (const item of items.slice(0, 5)) {
  const body = typeof item === 'object' ? JSON.stringify(item) : String(item);
  mentu.cir.capture(`Search result for "${query}": ${body.slice(0, 200)}`, {
    type: 'observation',
    domain: 'research',
    source: 'mcp-search',
    tags: ['search', query],
  });
}
 
return { query, count: items.length };

4. Lazy spawning

Servers are lazy-spawned — the child process is not started when the script begins. The server starts only on the first servers.<id>.call() invocation.

This means:

  • Scripts that don't use MCP servers pay no startup cost
  • Different code paths can conditionally call different servers
  • Servers that aren't needed are never spawned
// The crawlio server process does NOT start here
console.log('Starting analysis...');
 
const stats = mentu.cir.stats();
 
// The crawlio server process starts HERE, on first call
if (stats.contradictions > 0) {
  const result = await servers.crawlio.call('analyze_page', { url: 'https://example.com' });
  console.log('Analysis complete');
}
// If no contradictions, crawlio was never spawned

After the script completes (or fails), all spawned server processes are automatically shut down.

5. Error handling

When a server call fails, the error is thrown as a standard JavaScript error:

try {
  const result = await servers.myserver.call('some_tool', { arg: 'value' });
  console.log('Success:', JSON.stringify(result));
} catch (err) {
  console.error(`MCP call failed: ${err}`);
  // Continue with fallback logic
  mentu.cir.capture(`MCP call to myserver.some_tool failed: ${err}`, {
    type: 'observation',
    domain: 'errors',
    source: 'mcp-script',
  });
}

Common failure cases:

Scenario What happens
Server not configured Error: server ID not found in .mcp.json
Server binary not found Error: spawn fails
Server crashes during call Error thrown with crash details
Tool name doesn't exist Error from the MCP server
No .mcp.json file All servers.* calls fail with empty catalog

6. Multiple servers in one script

You can call multiple servers in a single script:

// Fetch data from one server
const webData = await servers.crawlio.call('search_api', { query: 'mentu SDK' });
 
// Query another server
const dbResults = await servers.sqlite.call('read_query', {
  sql: 'SELECT count(*) as total FROM events',
});
 
// Combine results and capture to CIR
mentu.cir.capture(`Cross-source analysis: ${webData.length} web results, ${JSON.stringify(dbResults)} db records`, {
  type: 'observation',
  domain: 'analysis',
  source: 'multi-server-script',
});
 
return { web: webData.length, db: dbResults };

Each server is independently lazy-spawned. If your script calls two servers, two child processes are started (and cleaned up on exit).

Configuration tips

  • Place .mcp.json in your workspace root — the script runner reads it from the --workspace directory
  • Server commands must be absolute paths or available in $PATH
  • Environment variables in the env field are passed to the child process

Next steps

© 2026 Mentu.