Files
open-multi-agent/docs/tool-configuration.md
T
Jack Chen ed5c125aa0 chore: tidy root and polish README docs (#179)
- Tighten positioning, lift demo, sharpen comparison table.
- Refresh temodar star count, drop dead link.
- Fold tool/memory/context sections into docs/.
- Swap npm downloads badge for GitHub forks.
- Correct custom context strategy signature and Copilot env var (README, README_zh, docs/context-management.md).
- Inline agent definitions so quick-start snippet is self-contained.
- Move CONTRIBUTING/CODE_OF_CONDUCT/SECURITY/codecov to .github/, DECISIONS.md to docs/.
- Fix CONTRIBUTING README link after the .github/ move.
- Ignore local .claude/ config dir.
2026-04-26 13:11:24 +08:00

4.8 KiB

Tool Configuration

Agents can be configured with fine-grained tool access control using presets, allowlists, and denylists.

Tool Presets

Predefined tool sets for common use cases:

const readonlyAgent: AgentConfig = {
  name: 'reader',
  model: 'claude-sonnet-4-6',
  toolPreset: 'readonly',  // file_read, grep, glob
}

const readwriteAgent: AgentConfig = {
  name: 'editor',
  model: 'claude-sonnet-4-6',
  toolPreset: 'readwrite',  // file_read, file_write, file_edit, grep, glob
}

const fullAgent: AgentConfig = {
  name: 'executor',
  model: 'claude-sonnet-4-6',
  toolPreset: 'full',  // file_read, file_write, file_edit, grep, glob, bash
}

Advanced Filtering

Combine presets with allowlists and denylists for precise control:

const customAgent: AgentConfig = {
  name: 'custom',
  model: 'claude-sonnet-4-6',
  toolPreset: 'readwrite',        // Start with: file_read, file_write, file_edit, grep, glob
  tools: ['file_read', 'grep'],   // Allowlist: intersect with preset = file_read, grep
  disallowedTools: ['grep'],      // Denylist: subtract = file_read only
}

Resolution order: preset → allowlist → denylist → framework safety rails.

Custom Tools

Two ways to give an agent a tool that is not in the built-in set.

Inject at config time via customTools on AgentConfig. Good when the orchestrator wires up tools centrally. Tools defined here bypass preset/allowlist filtering but still respect disallowedTools.

import { defineTool } from '@jackchen_me/open-multi-agent'
import { z } from 'zod'

const weatherTool = defineTool({
  name: 'get_weather',
  description: 'Look up current weather for a city.',
  inputSchema: z.object({ city: z.string() }),
  execute: async ({ city }) => ({ data: await fetchWeather(city) }),
})

const agent: AgentConfig = {
  name: 'assistant',
  model: 'claude-sonnet-4-6',
  customTools: [weatherTool],
}

Register at runtime via agent.addTool(tool). Tools added this way are always available, regardless of filtering.

Tool Output Control

Long tool outputs can blow up conversation size and cost. Two controls work together.

Validation (optional). Add outputSchema to catch malformed tool results before they are forwarded:

Note — two different outputSchema fields. The one on defineTool() / ToolDefinition (shown below) validates a single tool's ToolResult.data — it is always a ZodSchema<string> because tool output is serialised as text. The outputSchema on AgentConfig is different: it validates the agent's final answer as parsed JSON against an arbitrary Zod schema (see Structured output in examples/). Different types, different scopes — TypeScript won't warn you if you mix them up, so pick the one that matches the layer you're working at.

const jsonTool = defineTool({
  name: 'json_tool',
  description: 'Return JSON payload as string.',
  inputSchema: z.object({}),
  outputSchema: z.string().refine((value) => {
    try {
      JSON.parse(value)
      return true
    } catch {
      return false
    }
  }, 'Output must be valid JSON'),
  execute: async () => ({ data: '{"ok": true}' }),
})

Truncation. Cap an individual tool result to a head + tail excerpt with a marker in between:

const agent: AgentConfig = {
  // ...
  maxToolOutputChars: 10_000, // applies to every tool this agent runs
}

// Per-tool override (takes priority over AgentConfig.maxToolOutputChars):
const bigQueryTool = defineTool({
  // ...
  maxOutputChars: 50_000,
})

Post-consumption compression. Once the agent has acted on a tool result, compress older copies in the transcript so they stop costing input tokens on every subsequent turn. Error results are never compressed.

const agent: AgentConfig = {
  // ...
  compressToolResults: true,                 // default threshold: 500 chars
  // or: compressToolResults: { minChars: 2_000 }
}

MCP Tools (Model Context Protocol)

open-multi-agent can connect to any MCP server and expose its tools directly to agents.

import { connectMCPTools } from '@jackchen_me/open-multi-agent/mcp'

const { tools, disconnect } = await connectMCPTools({
  command: 'npx',
  args: ['-y', '@modelcontextprotocol/server-github'],
  env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN },
  namePrefix: 'github',
})

// Register each MCP tool in your ToolRegistry, then include their names in AgentConfig.tools
// Don't forget cleanup when done
await disconnect()

Notes:

  • @modelcontextprotocol/sdk is an optional peer dependency, only needed when using MCP.
  • Current transport support is stdio.
  • MCP input validation is delegated to the MCP server (inputSchema is z.any()).

See integrations/mcp-github for a full runnable setup.