Skip to content

Tools & MCP

Agents need tools to interact with the world. cycgraph uses structured tool sources and the Model Context Protocol (MCP) for secure, standardized tool integration. This decouples the agent definition from the underlying transport configuration and connection secrets.

Agents declare their tools using a ToolSource[] array with two possible types:

  1. Built-in tools: Provided by the orchestrator itself. No external connection needed.
  2. MCP tools: Provided by a registered MCP server. References the server by ID.
import { InMemoryAgentRegistry } from '@cycgraph/orchestrator';
const agentRegistry = new InMemoryAgentRegistry();
const RESEARCH_AGENT = agentRegistry.register({
name: 'Research Specialist',
model: 'claude-sonnet-4-20250514',
provider: 'anthropic',
system_prompt: 'You are a research specialist...',
tools: [
{ type: 'builtin', name: 'save_to_memory' },
{ type: 'mcp', server_id: 'web-search' }
],
});
// Single-key agent — no save_to_memory needed
const WRITER = agentRegistry.register({
name: 'Writer',
model: 'claude-sonnet-4-20250514',
provider: 'anthropic',
system_prompt: 'You write clear summaries.',
tools: [],
permissions: { read_keys: ['notes'], write_keys: ['draft'] },
});

Optionally, you can filter to specific tools from an MCP server by providing tool_names:

{
type: 'mcp',
server_id: 'web-search',
tool_names: ['search', 'fetch']
}

Graph nodes can override an agent’s configured tools for a specific execution step. This lets you reuse the same general-purpose agent with different contextual tool sets throughout a graph.

{
id: 'initial-research',
type: 'agent',
agent_id: RESEARCH_AGENT,
tools: [
{ type: 'builtin', name: 'save_to_memory' },
{ type: 'mcp', server_id: 'web-search' },
{ type: 'mcp', server_id: 'twitter-search' }
],
read_keys: ['goal'],
write_keys: ['initial_notes'],
}

In this example, save_to_memory is included because the node may need to write structured data to multiple keys. For nodes that write to a single key, you can omit it — the orchestrator captures text output automatically.

The trusted MCP Server Registry holds transport configurations and connection secrets. This is the security boundary — agents reference servers by ID, but never see connection details.

import { InMemoryMCPServerRegistry } from '@cycgraph/orchestrator';
const mcpRegistry = new InMemoryMCPServerRegistry();
// HTTP transport
mcpRegistry.register({
id: 'web-search',
name: 'Web Search',
description: 'Search the web via Brave Search API',
transport: {
type: 'http',
url: 'https://mcp.example.com/web-search',
headers: {
'Authorization': `Bearer <API_KEY>`,
},
},
});
// Stdio transport (local MCP server)
mcpRegistry.register({
id: 'code-executor',
name: 'Code Executor',
transport: {
type: 'stdio',
command: 'npx',
args: ['-y', '@mcp/code-sandbox'],
env: { SANDBOX_TIMEOUT: '30000' },
},
});
// SSE transport
mcpRegistry.register({
id: 'slack-tools',
name: 'Slack Integration',
transport: {
type: 'sse',
url: 'https://mcp.example.com/slack/sse',
},
timeout_ms: 60_000,
});

(Note: For production environments, use DrizzleMCPServerRegistry from @cycgraph/orchestrator-postgres to store server configurations durably in the database.)

TransportUse caseSecurity
stdioLocal MCP server processesCommand allowlist: npx, node, python3, python, uvx
httpRemote MCP servers (stateless)HTTPS URLs only in production
sseRemote MCP servers (streaming)HTTPS URLs only in production

Each MCPServerEntry also supports the following optional fields:

FieldTypeDefaultDescription
timeout_msnumberConnection-level timeout for the MCP transport
tool_timeout_msnumberPer-tool-call execution timeout (overrides manager default)
max_retriesnumber2Number of connection retry attempts with exponential backoff
allowed_agentsstring[]Restrict which agents can access this server

You can restrict which agents are allowed to use a specific server with the allowed_agents field:

mcpRegistry.register({
id: 'admin-tools',
name: 'Admin Tools',
transport: { type: 'http', url: 'https://internal.example.com/admin' },
allowed_agents: ['admin-agent-001', 'ops-agent-002'],
});

When allowed_agents is set, only the listed agents can resolve tools from that server. Omit the field for unrestricted access.

The MCPConnectionManager includes built-in resilience features for production reliability:

Connection retry with backoff: Failed connections are automatically retried with exponential backoff. Configure max_retries on each server entry (default: 2):

mcpRegistry.register({
id: 'web-search',
name: 'Web Search',
transport: { type: 'http', url: 'https://mcp.example.com/search' },
max_retries: 3, // Retry up to 3 times with backoff (1s, 2s, 4s)
});

Per-tool execution timeouts: Set tool_timeout_ms on the server entry to enforce a timeout on each individual tool call. This prevents hung tools from blocking the entire workflow:

mcpRegistry.register({
id: 'slow-api',
name: 'External API',
transport: { type: 'http', url: 'https://api.example.com/mcp' },
tool_timeout_ms: 10_000, // 10 second timeout per tool call
});

You can also set a default timeout for all servers via MCPConnectionManagerOptions:

const manager = new MCPConnectionManager(mcpRegistry, {
default_tool_timeout_ms: 30_000, // 30s default for all servers
});

Server-level tool_timeout_ms overrides the default.

Tool manifest caching: Tool manifests from MCP servers are cached for 5 minutes by default, avoiding redundant client.tools() calls. Configure via cache_ttl_ms:

const manager = new MCPConnectionManager(mcpRegistry, {
cache_ttl_ms: 600_000, // 10 minute cache TTL
});

Set to 0 to disable caching.

Manual reconnection: Use manager.reconnect(serverId) to invalidate a stale connection and force a fresh reconnection on the next resolveTools() call.

All results returned from MCP tools are automatically taint-tracked. The raw tool result is returned directly to the LLM (no wrapper), while taint metadata is accumulated internally by the MCPConnectionManager. After agent execution completes, taint entries are drained via drainTaintEntries() and applied to any memory keys that received MCP tool results.

This design ensures:

  • The LLM sees clean results — no taint metadata leaks into the model’s context
  • The taint registry is accurate — provenance is tracked in memory._taint_registry
  • The tool node executor also correctly handles taint for tool-type nodes

See Taint Tracking for the full taint propagation model and API reference.

cycgraph includes pre-configured entries for two common MCP reference servers. Use registerDefaultMCPServers() for one-line setup:

import {
InMemoryMCPServerRegistry,
registerDefaultMCPServers,
MCPConnectionManager,
} from '@cycgraph/orchestrator';
const mcpRegistry = new InMemoryMCPServerRegistry();
await registerDefaultMCPServers(mcpRegistry);
// Registers: web-search (Brave Search via npx) and fetch (URL content via uvx)
Server IDPackageCommandRequires
web-search@modelcontextprotocol/server-brave-searchnpxBRAVE_API_KEY env var
fetchmcp-server-fetch (Python)uvxuv package manager

Options for selective registration:

// Only fetch (no web search)
await registerDefaultMCPServers(mcpRegistry, { only: ['fetch'] });
// Override the Brave API key
await registerDefaultMCPServers(mcpRegistry, { brave_api_key: 'BSA-...' });
// Restrict to specific agents
await registerDefaultMCPServers(mcpRegistry, { allowed_agents: [RESEARCHER_ID] });
  • Adding MCP Tools — wiring tools into the execution pipeline and building custom MCP servers
  • Agents — how agents use tools
  • Nodes — node-level tool overrides
  • Security — access control and taint tracking