Skip to main content

ARCH-21 public tool taxonomy and exposure model

This reference decision record is the architecture contract behind issue #1962 and epic #1961.

Quick orientation

Decision snapshot

  • Every shipped public tool gets exactly one canonical public ID. Compatibility aliases may exist during migration, but public docs and emitted canonical inventory must converge on one ID per tool.
  • The mcp. prefix stays public only for raw MCP-backed tools that are exposed as MCP inventory. Built-in MCP facades such as websearch, webfetch, and codesearch keep facade IDs, and built-in memory helpers move to the memory family as their canonical public IDs.
  • The tool. namespace stays public for the already-shipped node-backed, node-discovery, saved-place, and automation families. ARCH-19 dedicated node-backed tool and routing decision remains settled and is not reopened here.
  • Static taxonomy metadata is separate from computed effective_exposure. Canonical naming, aliases, deprecation state, grouping, tier, and visibility are descriptor facts. Exposure is the post-gating answer for a specific declared agent and, later, explicit profile selectors.
  • Tool inventory surfaces are agent-scoped today. If callers later need profile-aware or conversation-kind-aware answers, they must pass explicit selectors rather than relying on implicit runtime state.

Decision

  • Adopt one contributor-facing taxonomy contract for all shipped public families, including bare built-ins, built-in MCP facades, raw MCP-backed tools, memory helpers, workboard/work orchestration, plugins, saved places, automation schedules, and the settled tool.* families.
  • Treat public naming as a contract layered above current runtime provenance fields such as source, backing_server, and plugin.
  • Define the canonical public built-in memory helper IDs as the memory family plus the existing verbs seed, search, and write. The currently shipped mcp.memory.* IDs become deprecated public aliases during the migration window tracked by later epic items.
  • Keep mcp.<server>.<tool> canonical for raw MCP-backed tools that remain directly exposed from shared-state MCP server inventory.
  • Keep built-in Exa-backed web/code tools as public facades without an mcp. prefix. Their backing server metadata stays public as provenance, not as part of the canonical ID.
  • Classify legacy parser shims such as tool.fs.read, tool.exec, tool.http.fetch, tool.*, and tool.fs.* as compatibility-only inputs. They are not canonical public IDs and must not be emitted or documented as canonical taxonomy.

Naming rules

Allowed public shapes

  • Standalone built-ins may stay single-segment when that is already the shipped public contract: read, write, edit, apply_patch, glob, grep, bash, websearch, webfetch, codesearch.
  • Dotted public IDs use lowercase ASCII segments matching [a-z][a-z0-9-]*, separated by ..
  • The action or verb belongs in the final segment: artifact.describe, the memory family write helper, tool.location.place.create, tool.automation.schedule.pause, workboard.item.list, mcp.github.search.
  • Hyphenated final action segments are allowed when already shipped and stable: tool.desktop.wait-for, tool.secret.copy-to-node-clipboard.
  • Raw MCP canonical IDs keep mcp.<server>. as the leading public shape and preserve the backing tool identifier in the trailing segment. Existing MCP-backed public tools may therefore include underscores after the server segment, for example mcp.exa.web_search_exa.

Reserved public prefixes

  • mcp. is reserved for raw MCP-backed public tools only.
  • tool. is reserved for platform-owned public families that route through the settled node/location/automation taxonomy.
  • memory., sandbox., subagent., and workboard. are reserved for platform-owned public families.
  • Platform-owned standalone IDs such as read, bash, websearch, and artifact.describe are reserved exact IDs.
  • Plugin tools must not claim reserved platform prefixes or exact platform-owned IDs. Plugin-owned IDs stay plugin-defined and must live in their own namespace.

Family depth

  • One-segment IDs are exception families kept only where Tyrum already ships them as public built-ins.
  • New structured families should prefer a stable family prefix plus a trailing action segment.
  • Two-level family prefixes are canonical where already shipped and meaningful: tool.location.place.*, tool.automation.schedule.*, workboard.item.*, workboard.task.*, tool.node.capability.get.
  • Raw MCP IDs always encode the backing server as the second segment: mcp.<server>.<tool>.

Vocabulary

TermMeaning
canonicalThe one public ID that docs, read APIs, runtime emissions, and operator surfaces must converge on.
aliasA supported compatibility ID that resolves to a canonical ID but must not be emitted as the canonical answer.
deprecatedAn alias or legacy public ID still accepted during migration and scheduled for removal under an explicit migration policy.
publicEligible to appear in public descriptor-bearing inventory, config, policy, status, and execution-facing contracts.
defaultThe baseline public tier for common operator and agent-facing tools.
advancedA public tool or family that is intentionally exposed behind the advanced/operator-facing tier rather than the default public tier.
internalNot part of the model-facing or user-configurable public contract. Internal IDs may exist for implementation or backing integration reasons.
runtime-onlyInternal identifiers or helper names used only inside runtime execution, normalization, or transport bridging. They must not appear as public taxonomy entries.

Canonical public families

GroupCanonical family or patternCurrent shipped surfaceCanonical public statusTierNotes
corestandalone built-insread, write, edit, apply_patch, glob, grep, bashunchangeddefaultCurrent bare IDs remain canonical.
coreartifact.<verb>artifact.describeunchangeddefaultDotted built-in family stays canonical.
retrievalstandalone web facadeswebsearch, webfetch, codesearchunchangeddefaultBacked by Exa today, but mcp.exa.* is not the public ID for these facades.
memorymemory.<verb>mcp.memory.seed, mcp.memory.search, mcp.memory.writerenameddefaultCanonical public IDs use the memory family with verbs seed, search, and write; mcp.memory.* remains only as deprecated aliases during migration.
extensionmcp.<server>.<tool>raw MCP tools from shared-state MCP server inventoryunchangedadvancedmcp. remains public only for raw MCP-backed tools.
extensionplugin-owned namespaceplugin descriptor IDs surfaced through the plugin registryunchangedadvancedCanonical IDs are plugin-defined, but reserved platform prefixes are off-limits.
environmenttool.location.place.<verb>tool.location.place.list, .create, .update, .deleteunchangedadvancedSaved-place family stays under tool.location.place.*.
environmenttool.automation.schedule.<verb>tool.automation.schedule.list, .get, .create, .update, .pause, .resume, .deleteunchangedadvancedAutomation schedule family stays under tool.automation.schedule.*.
nodetool.node.<...> discovery helperstool.node.list, tool.node.capability.getunchangedadvancedPublic node discovery stays in tool.node.*; removed generic dispatch helpers stay removed.
nodetool.desktop.<action>tool.desktop.screenshot, .snapshot, .query, .act, .mouse, .keyboard, .wait-forunchangedadvancedSettled by ARCH-19.
nodetool.browser.<action>browser capability-backed tools such as tool.browser.navigate and tool.browser.snapshotunchangedadvancedSettled by ARCH-19.
nodetool.location.gettool.location.getunchangedadvancedSettled by ARCH-19 and distinct from saved places.
nodetool.camera.<action>tool.camera.capture-photo, tool.camera.capture-videounchangedadvancedSettled by ARCH-19.
nodetool.audio.<action>tool.audio.recordunchangedadvancedSettled by ARCH-19.
nodetool.secret.copy-to-node-clipboardtool.secret.copy-to-node-clipboardunchangedadvancedSettled by ARCH-19.
orchestrationsandbox.<verb>sandbox.current, .request, .release, .handoffunchangedadvancedManaged desktop control-plane family.
orchestrationsubagent.<verb>subagent.spawn, .list, .get, .send, .closeunchangedadvancedOperator/delegation control family.
orchestrationworkboard.<resource>.<verb>workboard.capture, workboard.item.*, workboard.task.*, workboard.artifact.*, workboard.decision.*, workboard.signal.*, workboard.state.*, workboard.clarification.*unchangedadvancedWork orchestration family remains public but advanced.

Current-to-canonical mapping

Current shipped ID or patternCanonical public ID or patternStatus after this decisionEvidence
readreadcanonicalpackages/gateway/src/modules/agent/tool-catalog.ts
bashbashcanonicalpackages/gateway/src/modules/agent/tool-catalog.ts
artifact.describeartifact.describecanonicalpackages/gateway/src/modules/agent/tool-catalog.ts
websearch / webfetch / codesearchunchangedcanonicalpackages/gateway/src/modules/agent/tool-catalog.ts
mcp.<server>.<tool>unchangedcanonicalpackages/gateway/src/modules/agent/mcp-manager.ts
mcp.memory.seedmemory + .seeddeprecated aliaspackages/gateway/src/modules/agent/mcp-manager.ts, packages/gateway/src/modules/agent/runtime/prompts.ts
mcp.memory.searchmemory + .searchdeprecated aliaspackages/gateway/src/modules/agent/mcp-manager.ts, packages/gateway/src/modules/agent/runtime/prompts.ts
mcp.memory.writememory + .writedeprecated aliaspackages/gateway/src/modules/agent/mcp-manager.ts, packages/gateway/src/modules/agent/runtime/prompts.ts
tool.location.place.*unchangedcanonicalpackages/gateway/src/modules/agent/tool-catalog-location.ts
tool.automation.schedule.*unchangedcanonicalpackages/gateway/src/modules/agent/tool-catalog-automation.ts
tool.node.list and tool.node.capability.getunchangedcanonicalpackages/gateway/src/modules/agent/tool-catalog.ts
tool.desktop.*unchangedcanonicalpackages/gateway/src/modules/agent/tool-desktop-definitions.ts
tool.browser.*unchangedcanonicalpackages/gateway/src/modules/agent/dedicated-capability-tools.ts
tool.location.getunchangedcanonicalpackages/gateway/src/modules/agent/dedicated-capability-tools.ts
tool.camera.* / tool.audio.recordunchangedcanonicalpackages/gateway/src/modules/agent/dedicated-capability-tools.ts
tool.secret.copy-to-node-clipboardunchangedcanonicalpackages/gateway/src/modules/agent/tool-secret-definitions.ts
sandbox.*unchangedcanonicalpackages/gateway/src/modules/agent/tool-catalog-sandbox.ts
subagent.*unchangedcanonicalpackages/gateway/src/modules/agent/tool-catalog-subagent.ts
workboard.*unchangedcanonicalpackages/gateway/src/modules/agent/tool-catalog-workboard.ts

Legacy compatibility envelope

The following IDs or patterns are compatibility shims only. They are not canonical taxonomy entries and must not be emitted as canonical public IDs:

Legacy inputCanonical targetCurrent source
tool.fs.readreadpackages/contracts/src/tool-id.ts
tool.fs.writewritepackages/contracts/src/tool-id.ts
tool.execbashpackages/contracts/src/tool-id.ts
tool.http.fetchwebfetchpackages/contracts/src/tool-id.ts
tool.** allowlist wildcardpackages/contracts/src/tool-id.ts
tool.fs.*filesystem built-in allowlist expansionpackages/contracts/src/tool-id.ts

tool.* and tool.fs.* are parser-time compatibility helpers for current config and policy inputs. They do not define a public family boundary and must not be treated as canonical family-level aliases.

Contributor rules for adding or changing tools

When a PR adds, renames, or newly exposes a model-facing tool, it must land the taxonomy work in the same change instead of relying on follow-up cleanup.

Required contributor rules:

  • Pick the target package or layer first using Target-state package graph. Do not hide new business logic inside gateway route handlers just to get a tool to show up.
  • Decide whether the tool is public, internal, or runtime_only before writing prompts, docs, or operator UI copy. Public inventory rows must not be inferred later from transport details.
  • Choose exactly one canonical public ID that follows the naming rules above. New public tool families must not introduce a new grammar when an existing family or reserved prefix already applies.
  • Add or update the taxonomy classification in code so the descriptor resolves stable canonical_id, family, group, tier, visibility, and lifecycle metadata in the same PR.
  • If a shipped public ID is being renamed, add the compatibility alias mapping, mark the legacy public ID as deprecated when it remains operator-visible during rollout, and update docs/examples to the canonical ID immediately.
  • Plugin-owned tool IDs must stay in a plugin-owned namespace. They must not claim reserved platform prefixes such as tool., memory., sandbox., subagent., workboard., or reserved exact public IDs such as read, bash, websearch, or artifact.describe.
  • Built-in MCP facades must keep facade IDs such as websearch, webfetch, and codesearch. Do not reintroduce mcp.-prefixed public aliases for those facades.
  • Add or update conformance coverage in the same PR. At minimum that means the shared taxonomy tests plus the owning registry/runtime tests for the tool family being changed.

Migration, deprecation, and removal policy

Supported deprecation window

A legacy public ID remains supported only as a migration alias and must not be reintroduced as canonical output.

The supported deprecation window is:

  • never shorter than one minor release after the canonical replacement, migration docs, and durable stored-data rewrite coverage are shipped
  • never shorter than one release where the legacy public ID is emitted only as alias/deprecation metadata rather than as the canonical answer
  • limited to the aliases explicitly documented in this decision record and in the shared normalization tables

Do not remove a legacy public ID in the same release that introduces its canonical replacement.

Operator migration checklist

Operators should migrate persisted and hand-authored config to canonical public IDs as soon as the canonical replacement is available.

  • Treat canonical_id from descriptor-bearing inventory as the source of truth. The aliases list is compatibility metadata, not a second canonical namespace.
  • Rewrite agent config, policy bundles, policy overrides, approval suggestions, standing rules, prompts, and exported fixtures to canonical public IDs instead of waiting for alias removal.
  • Use the normal gateway database migration flow before planning alias removal for persisted SQLite/Postgres records that may still contain supported legacy public IDs. The shipped rewrite coverage from #1992 lives in the canonical tool-ID migrations (packages/gateway/migrations/sqlite/121_canonical_tool_ids.sql, packages/gateway/migrations/postgres/121_canonical_tool_ids.sql) plus the public-memory follow-up migrations (packages/gateway/migrations/sqlite/164_canonical_public_memory_tool_ids.sql, packages/gateway/migrations/postgres/164_canonical_public_memory_tool_ids.sql).
  • For memory specifically, migrate all public references from mcp.memory.seed, mcp.memory.search, and mcp.memory.write to memory.seed, memory.search, and memory.write. The user-facing defaults/prompts/docs rollout landed in #1973, and runtime-policy plus execution-bookkeeping exact-match dependencies were removed in #1991.
  • After migration, keep legacy aliases only for backward-compatible reads during the supported deprecation window. Do not emit them back out in new config, runtime reports, or operator copy.

Supported removal path for legacy public IDs

Follow this path when the supported deprecation window closes:

  1. Confirm the canonical replacement is already the only documented and emitted public ID.
  2. Confirm the durable stored-data migration path has shipped for any persisted surface that previously stored the legacy public ID.
  3. Remove the alias from parser-time normalization and shared alias tables in one cleanup PR.
  4. Remove descriptor alias/deprecation metadata for the retired ID and update tests so the retired ID is rejected or ignored instead of silently round-tripping.
  5. Remove any remaining legacy examples, prompts, fixtures, and operator guidance in the same cleanup PR.

Static taxonomy metadata versus computed effective exposure

Required static taxonomy metadata

These fields describe the tool itself and do not depend on one agent's current allowlist or runtime state:

FieldMeaning
canonical_idCanonical public ID after alias normalization.
familyStable public family label, for example memory, tool.automation.schedule, tool.location.place, workboard, or mcp.
groupBroader grouping used for inventory and operator navigation: core, retrieval, environment, node, orchestration, or extension.
tierExposure tier. This decision uses default and advanced as the supported public tiers.
visibilitypublic, internal, or runtime_only.
lifecyclecanonical, alias, or deprecated.
aliasesSupported compatibility IDs that normalize to the canonical public ID.
sourceProvenance of the implementation: builtin, builtin_mcp, mcp, or plugin.
backing_server / pluginProvenance metadata for backed or plugin-owned tools. These fields explain source, not public naming.

Current /config/tools now emits the shared descriptor metadata from this decision record, including canonical_id, lifecycle, visibility, aliases, family, group, tier, source, backing_server, and plugin. Treat that route as the shared operator-facing source of truth for canonical taxonomy facts rather than reconstructing them in clients.

Effective exposure

effective_exposure is a computed answer, not taxonomy metadata. Today the gateway computes it from the declared agent scope, runtime state mode, schema validity, and current allowlist:

  • enabled
  • disabled_by_agent_allowlist
  • disabled_by_state_mode
  • disabled_invalid_schema

The current /config/tools route and transport SDK already expose this computed answer. The current /context/tools route exposes only enabled_by_agent, which is an agent-scoped partial answer and not a replacement for full effective-exposure semantics.

Canonical naming, aliases, deprecation state, group, tier, and visibility must not be inferred from effective_exposure. They remain static taxonomy facts even when a tool is currently disabled.

Inspection contract for inventory surfaces

Agent-scoped today

The current shipped inventory inputs are agent-scoped only:

  • /config/tools and packages/transport-sdk/src/http/tool-registry.ts accept only optional agent_key.
  • /context/tools and packages/transport-sdk/src/http/context.ts accept only optional agent_key.

Those surfaces must answer the declared agent's tool universe, not an inferred profile-specific or conversation-kind-specific subset.

Reserved explicit selectors for later work

If a later contract needs profile-aware or conversation-kind-aware exposure answers, the caller must pass explicit selectors on top of agent_key. The only reserved selector names from this decision are:

  • execution_profile
  • conversation_kind

Future implementations may add those selectors, but they must not infer exposure answers from implicit current conversation state, attached node state, prior turns, or transient operator request context.

conversation_id and turn_id remain context-report selectors, not tool-inventory exposure selectors.

Consequences

  • Follow-on migration issues must normalize public memory references from mcp.memory.* to the canonical memory family IDs across defaults, prompts, execution profiles, policy, bookkeeping, and docs.
  • Follow-on metadata issues must emit alias, deprecation, visibility, grouping, and tier metadata consistently across gateway and transport consumers.
  • Follow-on resolver issues must keep effective_exposure as a post-gating answer that composes with, but does not replace, static taxonomy metadata.
  • ARCH-19 routing, schema, approval, and audit rules stay authoritative for dedicated node-backed families.