Version: 0.1 · July 2026 · Status: OS-level canonical Supersedes: (none — first version) Related documents: Agent Spec · Agent Runtime Policy Spec · Orchestration & Runtime Strategy · n8n Implementation Guide · LangGraph Runtime Implementation Guide · ADR-OS-001 · POC Workflow WF-OS-POC-001 · Event Taxonomy Standard


Purpose

This document answers: what is a tool set and how does tool authorization work in Synedrix OS?

The Agent Runtime Policy Spec is the authoritative schema reference — it defines the tool_authorization_policy fields, the three-layer model summary, MCP/A2A policies, and observability events. Read that document to configure tool authorization for a specific agent.

Read this document to understand why the model is structured the way it is, what the key concepts mean, how resolution works end-to-end, and where enforcement happens at runtime.


1. What is a tool

A tool is a named, typed capability that an agent may invoke during a reasoning step to read data, write state, or call an external system. From the agent’s perspective a tool is a function: it accepts structured input and returns structured output (or an error).

Tools are distinct from LLM inference. The LLM decides whether to call a tool and with what arguments; the tool executes the call and returns a result. The LLM never directly touches an external system — it can only do so through an authorized tool.

Tool ID convention

Every tool in Synedrix OS is identified by a canonical tool_id following the pattern:

tool:<provider>-<verb>
Illustrative example, not a production implementation. Review and test before use.

Where <provider> identifies the system or adapter (supabase, questdb, redis, searxng, external-broker) and <verb> is a lowercase action word (read, write, query, search, send, delete).

Examples: tool:questdb-read, tool:supabase-write, tool:searxng-search, tool:redis-read, tool:external-broker-api.

The tool_id is the canonical reference used in all agent specs, runtime policies, event envelopes, and invocation contracts. Tool function names inside a LangGraph or n8n node use underscores (searxng_search) and map to tool_id by replacing underscores with hyphens and prepending tool:.

Tool categories

CategoryExample tool IDsSide-effect classApproval default
Internal read-onlytool:questdb-read, tool:redis-read, tool:qdrant-query, tool:neo4j-readnoneauto-approved
Internal writetool:supabase-write, tool:redis-write, tool:neo4j-writewritepolicy-gated
External read-onlytool:searxng-searchnoneauto-approved
External write / irreversibletool:external-broker-api, tool:send-email, tool:webhook-postwrite / irreversiblepolicy-gated; HITL for irreversible
MCP-backedany tool exposed via an MCP servervariespolicy-gated by default
A2A-backedcross-agent delegation targetsvariespolicy-gated by default

Tools with side_effect_class: write or higher require Policy Guard (os-agt-002) approval before execution when mcp_write_approval_required: true. See Agent Runtime Policy Spec §Tool authorization schema.


2. The capability ceiling

Every agent declares the maximum superset of tools it may ever use in its Agent Spec tool_permissions list. See Agent Spec.

tool_permissions:
  - tool_id: tool:questdb-read
    side_effect_class: none
    approval_tier: auto
  - tool_id: tool:supabase-write
    side_effect_class: write
    approval_tier: policy
  - tool_id: tool:external-broker-api
    side_effect_class: irreversible
    approval_tier: hitl
Illustrative example, not a production implementation. Review and test before use.

This list is the capability ceiling — the hard upper bound on what the agent was designed to be able to do. It is:

  • Declared once, at agent definition time
  • Immutable at runtime — no runtime policy or business instance configuration can add tools beyond this ceiling
  • Registered in the OS agent registry and versioned with the agent spec

The ceiling rule: A tool not declared in tool_permissions can never appear in the active tool set, regardless of what any runtime policy or instance override requests.


3. The active tool set

The active tool set is the runtime-resolved subset of the capability ceiling that an agent executor actually receives for a specific invocation. It is a flat list of tool_id strings:

["tool:questdb-read", "tool:redis-read"]
Illustrative example, not a production implementation. Review and test before use.

Key properties:

  • Computed by the orchestration layer (n8n, Layer 3) before the agent is invoked — never by the agent itself
  • Passed explicitly in the invocation request — for LangGraph agents it travels in the HTTP request body; for n8n AI Agent nodes it is expressed as the set of connected tool sub-nodes
  • The LLM inside the agent never sees the ceiling — it only ever sees the active set; a tool absent from the active set is invisible to the model
  • Can only be equal to or smaller than the capability ceiling — never larger

This separation — ceiling declared statically, active set computed dynamically per invocation — is what allows the same agent definition to behave differently across environments, tenants, and risk contexts without modifying the agent.


4. Three-layer resolution

The active tool set is produced by applying three layers of restriction in order. Each layer can only restrict downward — it cannot grant capabilities beyond what the layer above permits.

flowchart TD
  C[CAPABILITY CEILING. Source: Agent Spec tool_permissions. Owner: Agent definition, synedrix-os or domain pack. Example: questdb-read, supabase-write, redis-read, external-broker-api, qdrant-query]:::agent
  R[RUNTIME RESTRICTION. Source: Agent Runtime Policy Spec tool_authorization_policy. Owner: OS / deployment configuration. Example: active_tool_set: restricted, tool_suspensions: supabase-write, external-broker. Result: questdb-read, redis-read, qdrant-query]:::check
  I[INSTANCE RESTRICTION. Source: Business instance environment_overrides. Owner: Tenant / business instance configuration. Example: suspend qdrant-query in staging environment. Result: questdb-read, redis-read]:::check
  A([ACTIVE TOOL SET → passed to agent executor: tool:questdb-read, tool:redis-read]):::term
  C -- runtime policy applies tool_authorization_policy --> R
  R -- business instance environment_overrides apply --> I
  I --> A

The three active_tool_set policy modes (full schema in Agent Runtime Policy Spec):

ModeMeaning
allThe full capability ceiling passes through; no runtime restriction applied
declaredOnly tools listed in tool_grants are active; all others are suppressed
restrictedAll ceiling tools are active except those listed in tool_suspensions

5. Resolution flow (step by step)

At agent invocation time, orchestration (n8n, Layer 3) performs the following steps before handing the task to the agent executor:

  1. Read the capability ceiling from the agent registry entry (tool_permissions list for the agent’s current version).
  2. Read the runtime policy for the agent (tool_authorization_policy in the active runtime policy for the current deployment mode).
  3. Apply the runtime restriction using the active_tool_set mode (all / declared / restricted) and the accompanying tool_grants or tool_suspensions list.
  4. Apply instance overrides from the business instance’s environment_overrides for the current environment.
  5. Produce the active tool set — the flat list of tool_id strings that survived all three layers.
  6. Pass the active tool set to the agent executor:
    • runtime_engine: langgraph → included in the HTTP invocation request body as active_tool_set
    • runtime_engine: n8n-aiagent → expressed as the sub-nodes connected to the AI Agent node
  7. The agent executor enforces the active tool set at tool-call time (see §6).

The full invocation contract between n8n and LangGraph is specified in Orchestration & Runtime Strategy §5.


6. Enforcement points

Tool authorization is enforced at two execution points depending on the runtime engine. Both apply the same rule: any tool call referencing a tool not in the active set is rejected before execution.

n8n AI Agent node (Layer 3)

For agents running as n8n AI Agent nodes, the active tool set is structural — it is the set of tool sub-nodes connected to the AI Agent node in the workflow graph. The n8n execution engine only exposes connected tools to the model; disconnected tools are architecturally invisible and cannot be called.

When implementing a workflow, tool sub-nodes must match the resolved active tool set for the current environment. Any ceiling tool absent from the active set must not be connected. See n8n Implementation Guide.

LangGraph executor (Layer 4)

For agents running in the LangGraph Agent Runtime (see ADR-OS-001), the active tool set is passed in the invocation request and enforced programmatically inside the graph’s conditional edge — before ToolNode executes any call.

The enforcement pattern (reference implementation from WF-OS-POC-001):

def _should_continue(state: AgentState) -> Literal["tools", "end"]:
    last = state["messages"][-1]
    if not isinstance(last, AIMessage) or not last.tool_calls:
        return "end"
    for tc in last.tool_calls:
        # Map tool function name (underscores) to canonical tool_id (hyphens)
        tool_key = f"tool:{tc['name'].replace('_', '-')}"
        if tool_key not in state["active_tool_set"]:
            raise PermissionError(
                f"Tool '{tc['name']}' ('{tool_key}') not in active_tool_set. "
                f"Allowed: {state['active_tool_set']}"
            )
    return "tools"
Illustrative example, not a production implementation. Review and test before use.

A PermissionError causes the graph to return a status: failure Outcome Envelope and emit agent.tool.unauthorized. The tool is never executed. See LangGraph Runtime Implementation Guide for the full compliance checklist.


7. Observability events

The following events are emitted during tool authorization and conform to the Event Taxonomy Standard envelope. Full event schemas are in Agent Runtime Policy Spec §Observability events.

Event typeEmitted when
agent.tool.authorizedA tool call passed all authorization checks and was forwarded to execution
agent.tool.unauthorizedA tool call was rejected — the tool is not in the active set
agent.tool.suspendedA tool call was rejected — the tool appears in tool_suspensions
agent.tool.mcp_approval_requestedAn MCP tool call was submitted to Policy Guard before execution
agent.tool.a2a_approval_requestedAn A2A delegation was submitted to Policy Guard before execution
agent.tool.mcp_server_blockedAn MCP server call was blocked by mcp_server_denylist

agent.tool.unauthorized is the most operationally significant event. It indicates either a misconfiguration (agent spec and runtime policy out of sync) or an LLM hallucination (the model attempted a tool it was not told about). Both require investigation.


8. Worked example — POC agent (poc-agt-001)

The POC Workflow WF-OS-POC-001 provides the simplest real example of the full resolution chain.

Agent: poc-agt-001 — research and summarize using SearXNG · Runtime engine: langgraph

Capability ceiling (agent definition):

tool_permissions:
  - tool_id: tool:searxng-search
    side_effect_class: none
    approval_tier: auto
Illustrative example, not a production implementation. Review and test before use.

Runtime restriction (runtime policy):

tool_authorization_policy:
  active_tool_set: all   # no restriction — pass the full ceiling through
Illustrative example, not a production implementation. Review and test before use.

Instance override: none.

Active tool set produced: ["tool:searxng-search"]

Invocation request (n8n → LangGraph HTTP POST /invoke):

{
  "correlation_id":   "corr-1751234567-abc123",
  "causation_id":     "wf-exec-987654",
  "agent_id":         "poc-agt-001",
  "active_tool_set":  ["tool:searxng-search"],
  "input": { "topic": "LangGraph multi-agent orchestration patterns" }
}
Illustrative example, not a production implementation. Review and test before use.

If the LLM attempts an unauthorized tool call (e.g., hallucinates supabase_write):

The conditional edge maps the name to tool:supabase-write, finds it absent from ["tool:searxng-search"], and raises PermissionError. The graph returns:

{
  "status": "failure",
  "errors": ["Tool 'supabase_write' ('tool:supabase-write') not in active_tool_set. Allowed: ['tool:searxng-search']"],
  "execution_context": { "correlation_id": "corr-1751234567-abc123", "agent_id": "poc-agt-001" }
}
Illustrative example, not a production implementation. Review and test before use.

And emits: agent.tool.unauthorized with tool_id: tool:supabase-write.


9. Invariants

These rules are unconditional. They apply in all deployment modes, all domains, and all execution patterns.

  1. Ceiling is the hard limit. A tool not declared in an agent’s tool_permissions ceiling can never appear in the active tool set for that agent, regardless of runtime policy or instance configuration.

  2. Orchestration resolves, executor enforces. The active tool set is always computed by the orchestration layer (n8n) and passed into the agent executor. An agent executor must never compute or expand its own active tool set.

  3. Every tool call is checked before execution. Authorization is checked at the moment of tool invocation, not at agent startup. Late binding is required because the LLM decides which tools to call at inference time.

  4. Rejections are observable. A tool call that fails authorization must emit agent.tool.unauthorized. Silent suppression is not permitted — it creates lineage gaps and hides misconfiguration.

  5. os-agt-008 (Audit & Lineage Agent) must never appear in any agent’s tool_permissions ceiling. This agent is strictly read-only and must never be callable as a tool from any other agent.