What to know

  • Tool names and arguments are checked before dispatch.
  • Only two pure local functions are available.
  • Allowlisting a name does not replace authorization inside a consequential tool.
limits
Local bounded fixtures passed; no external model or production benchmark tested. No model, shell, dynamic imports, filesystem tool or production authorization layer is exercised.
duration
20–30 minutes
difficulty
Intermediate
environment
Python 3.12.10; standard library; local synthetic fixtures
verified at
2026-10-16
prerequisites
Python 3.12; no installed packages or credentials; local text fixtures.

Make the capability list visible

An agent tool call should be a request to a defined function, not a string that becomes executable code. This workflow exposes two pure operations: word_count and make_slug. The dispatcher accepts a tool name and an arguments object, validates both, then looks up a function in a fixed dictionary. It does not interpret Python supplied by a model. The result is a small, inspectable capability boundary that can be tested without an external model or a service account.

Describe input and output precisely

A request must contain only tool and arguments. The arguments object must contain only text, and that value must be a string of at most 10,000 characters. word_count returns a words count based on whitespace splitting. make_slug lowercases the text and keeps an ASCII letter/digit representation separated by hyphens. These deliberately modest semantics matter: this word counter is not a linguistic tokenizer, and the slug helper does not promise unique or culturally appropriate URLs for every language.

Run a harmless request first

Save tool_allowlist.py and run it using Python 3.12. The main block turns Source Review Checklist into a slug-like value without reading or writing a file. The test suite also checks word counting, unknown tool names, unexpected arguments, wrong value types and excessive text. The shell example in the rejection test is merely a string used to prove that no shell tool exists. There is no eval, subprocess call or dynamic import behind the dispatch table.

Give each tool its own policy

A fixed name is only the first boundary. If a future tool can modify a record, it must check the caller’s authenticated role, resource scope and allowed operation internally. A model saying that an editor approved the change is not evidence of an authenticated approval. OWASP’s excessive-agency guidance is relevant because unnecessary functionality and permission enlarge the possible consequences. Keep the tool’s purpose small enough that reviewers can understand why each input and capability is required for the intended task.

Fail before doing work

Unknown tools and malformed arguments raise a controlled ValueError before a function runs. A caller should show a useful failure state and preserve the task for correction rather than automatically granting another capability. Avoid returning full request contents in logs; text may contain material that should remain private. A retry should change the invalid request, not weaken the validator. If several tools accept similar fields, keep their schemas explicit instead of forwarding every unused property to whichever function happens to be selected.

Extend through reviewable contracts

Add a tool only when a real task needs it, with documentation for its inputs, outputs, limits and failure behavior. Write a fixture that proves both legitimate access and denied behavior. Keep reading, drafting and external action separate where their permissions differ. For larger systems, use established schema validation and authenticated server enforcement rather than expanding this tiny dispatcher into an improvised platform. The educational result is a deterministic baseline: the model can ask for one of the exposed operations, while the surrounding application decides whether that request is valid.

Know what the test does not show

Passing these checks does not establish that a future network tool is safe, that a model will choose the right tool or that every output is suitable to publish. Even a pure slug function can create an undesirable label from inappropriate input. Application review still owns meaning, destination and user experience. Use the fixture to make authority visible, then validate any added capability according to the real consequences it introduces.

Runnable Python fixture

"""Dispatch two bounded pure functions, never code strings or shell commands."""
import re

def word_count(text):
    return {"words": len(text.split())}

def make_slug(text):
    return {"slug": re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")}

TOOLS = {"word_count": word_count, "make_slug": make_slug}

def dispatch(request):
    if type(request) is not dict or set(request) != {"tool", "arguments"}:
        raise ValueError("unexpected request shape")
    name, arguments = request["tool"], request["arguments"]
    if type(name) is not str or name not in TOOLS:
        raise ValueError("tool not permitted")
    if type(arguments) is not dict or set(arguments) != {"text"}:
        raise ValueError("unexpected arguments")
    text = arguments["text"]
    if type(text) is not str or len(text) > 10000:
        raise ValueError("text must be bounded")
    return TOOLS[name](text)

if __name__ == "__main__":
    print(dispatch({"tool": "make_slug", "arguments": {"text": "Source review checklist"}}))

Verification and references

The dispatcher passed four fixture tests on 16 October 2026 using Python 3.12.10, including rejected names and invalid arguments. No model or shell was invoked. Run tool_allowlist.py for the example and test_workflows.py for the complete suite.

Editorial note: AI-assisted original BijliCoin educational workflow with locally tested synthetic fixtures. No external model, publisher feed, production service or private account was tested. AI-generated conceptual illustration.

Sources & context

  1. Python 3.12 control-flow and function tutorial ↗
  2. OWASP excessive-agency guidance ↗

See our editorial policy or report a correction.