Introduction

Language models are persuasive. Production systems cannot let persuasion masquerade as action. Any route that changes state—issuing refunds, updating records, scheduling meetings—must separate what the model says from what the system does. This article lays out a practical tool-mediation pattern that keeps control in your backend: the model proposes an action, your services validate scope and preconditions, and only then do you execute with full audit. The result is simple to implement, resistant to “implied writes,” and compatible with every major LLM API.


The Problem Tool Mediation Must Solve

Without mediation, you’ll see recurring failures:

All of these disappear when actions require a structured proposal, server-side validation, and recorded outcomes.


Design Goals

  1. No side effects from prose. Text cannot change the world.

  2. Typed, minimal interfaces. Tools expose small, well-typed argument schemas.

  3. Least privilege & time-bound identity. Each route/agent gets only what it needs, for a limited time.

  4. Idempotent by default. Every effect is safe to retry.

  5. Tamper-evident audit. You can replay plan → decision → outcome.


The Mediation Pattern

1) Propose (model)

The contract instructs the model to emit structured proposals instead of narrative claims of success.

{
  "proposed_tool": {
    "name": "create_support_ticket",
    "args": {
      "customer_id": "C-10429",
      "subject": "Billing discrepancy",
      "priority": "high"
    },
    "preconditions": ["customer_verified", "open_invoice_present"],
    "idempotency_key": "2a1d-8f4c-…"
  }
}

Contract tips

2) Validate (server)

Your middleware verifies before any tool executes:

Return a structured decision:

{
  "decision": "approved",
  "reason": null,
  "execution_plan": { "tool": "create_support_ticket", "args": {...} }
}

or

{
  "decision": "rejected",
  "reason": "Precondition 'open_invoice_present' failed",
  "remediation": "ASK_FOR_MORE: attach latest invoice ID"
}

3) Execute (service)

Only on approved decisions do you invoke the tool adapter. Capture the raw provider response, normalize it, and return a result object:

{
  "result": {
    "ticket_id": "T-77391",
    "url": "https://support.example.com/T-77391",
    "status": "created"
  }
}

Send this back to the model/UI to compose the final user-visible message (“I created ticket T-77391; here’s the link.”). The text mirrors the actual tool outcome, not speculation.


Tool Interface Shape

Adapter signature

type Tool<Args, Result> = (ctx: Context, args: Args, opts: { idempotencyKey: string }) 
  => Promise<Result>

Good practices


Identity, Permissions, and Limits

If you can’t explain an adapter’s permission in one sentence, it’s too broad.


Idempotency & Retries

Every execution path must be safe to repeat:


Observability & Audit

Store a complete, tamper-evident record:

{
  "trace_id": "x-9af3",
  "route": "billing_help",
  "contract_hash": "sha256:…",
  "proposal": { "name":"create_support_ticket","args":{…},"preconditions":[…],"idempotency_key":"…" },
  "decision": { "approved": true, "reason": null },
  "execution": { "tool":"create_support_ticket","args":{…},"started_at":"…","ended_at":"…","result":{"ticket_id":"T-77391"} },
  "actor": { "service_account":"route-billing-help@bots", "tenant":"acme" }
}

Hash-chain logs or write to an append-only store if you need stronger guarantees.


UX & Language Patterns


Failure Taxonomy (make fixes actionable)

Your metrics and alerts should break down along this taxonomy.


Metrics That Matter

Tie these to business KPIs (e.g., resolution time, CSAT, refund accuracy).


Implementation Checklist


Worked Example (Composite)

A “refund assistant” route supports partial credits.

  1. Proposal (model):

{"proposed_tool":{
  "name":"issue_credit",
  "args":{"order_id":"O-55291","amount":25,"currency":"USD"},
  "preconditions":["order_paid","within_return_window"],
  "idempotency_key":"O-55291-USD-25-2025-10-16"
}}
  1. Validation (server):

  1. Execution (adapter):

  1. User message (post-execution):

  1. Audit: full trace saved; if user asks later, you can prove exactly what happened.


Common Pitfalls—and Fixes


Conclusion

Tool mediation converts persuasive language into safe action. By forcing the model to propose, giving your backend the right to decide, and executing with least privilege and idempotency, you eliminate implied writes, improve auditability, and keep incidents cheap. In Part 6, we’ll formalize validators and safety policy—how to fail closed, repair small, and keep brand/legal rules enforceable at scale.