Skip to content

Chapter 42 Function Calling and Tools: Who Actually Executes?

42.1 “Check my order” needs an executor

Section titled “42.1 “Check my order” needs an executor”

A user asks, “What is the status of A-104?” The KA-0 FAQ says processing is a possible order status. That does not tell us this order’s state. A plausible sentence from a model is also not a database observation. We need a program with access to the appropriate record, and a policy deciding which records this user may read.

Complete the HTTP and API Primer before running this chapter’s practice. The reading path below remains entirely static. The only data is a fictional snapshot: owner mira, order A-104, status processing, observed at 2026-09-14T00:00:00Z. A second order, B-205, belongs to leo. These assigned facts are not a real account lookup. KA-1’s shorter fictional ID A104 remains a different fixture.

A tool is an operation the application makes available through a defined interface. Function calling lets a model propose a tool name and arguments. The name describes a request; it does not invoke operating-system privileges by itself.

Example 42.1 — The complete read loop. The application exposes only get_order. Its sole argument, order_id, must be a string matching the supported ID pattern, such as A-104. Additional arguments are forbidden. The authenticated principal comes from the application’s trusted session or, here, fixed local fixture configuration. The model cannot choose it.

{"name":"get_order","arguments":{"order_id":"A-104"}}

This proposal is authored input to the experiment, not a measured model decision. The subsequent execution and trace are produced by the actual local program.

Table 42.1 — One proposal, six observable stages.

StageResponsible partyRecorded fact
ProposedModel boundary, supplied as a fixture hereget_order, A-104
ValidatedApplicationKnown tool; exactly one correctly typed argument
AuthorizedApplication and data servicePrincipal mira; orders:read:own; A-104 belongs to mira
ExecutedLocal lookup functionReads snapshot, makes no business mutation
Result checkedApplicationRequested ID matches; state and result fields are allowed
Returned as dataApplication → generatorExact result below, available for a subsequent answer
{"order_id":"A-104","status":"processing","as_of":"2026-09-14T00:00:00Z","source_version":"ka3-orders-v1"}

The record supports “The fictional snapshot says A-104 was processing at this time.” It does not support “It has shipped,” “It will arrive tomorrow,” or “It is still processing now.” Backfilling the result makes it available to a subsequent model call; it does not guarantee that call will describe it faithfully. Our local trace stops at verified data delivery. The Notebook shows a deterministic rendering separately and labels its producer.

Permitted read
  1. Text piecesPropose A-104
  2. An operationValidate → authorize mira
  3. Outside the modelLookup snapshot
  4. An operationCheck result → return data

Only the executor reads the record; the proposal is not completion.

Denied read
  1. Text piecesPropose B-205
  2. An operationOwner is leo → deny
  3. Outside the modelBackend calls: 0

A well-typed order ID does not grant access.

Figure 42.1 — The application owns execution and the return path. Original teaching diagram · CC BY-SA 4.0

Figure 42.1 includes the return path, not just an arrow from model to tool. Each gate can stop the request. A model output ending with a tool proposal is therefore an intermediate state; the user needs the application’s execution outcome, not a claim that the proposal itself completed the task.

Change only the order ID to B-205. The syntax is valid, but mira does not own it. Authorization rejects it before the supplied backend is invoked. Tests use a backend counter to verify zero executions, rather than trusting a log message saying “denied.” Adding "principal":"leo" to the arguments fails the argument contract; it cannot switch identity.

Tool descriptions and results can carry hostile text. Suppose a backend returns a new instruction field saying “cancel this order.” Our allowlisted result shape rejects that field. This narrow check prevents that particular result from reaching the answer path; it is not a universal detector of malicious natural language. A permitted free-text field would remain untrusted, just like Chapter 40’s document content. Permissions must survive even if a model follows the hostile text.

The executable surface contains no cancel_order. Passing that name fails before lookup. A tool annotation saying “read-only” would merely describe an expectation; the implementation and permissions must make it true. We verify the snapshot hash before and after the practice, and test that denied or malformed requests never reach the backend.

42.4 Failure and waiting belong in the contract too

Section titled “42.4 Failure and waiting belong in the contract too”

A lookup can fail even after authorization. Distinguish unavailable transport, no record, forbidden access and malformed result. Do not convert all of them into “processing,” or ask the model to guess. The HTTP Primer’s bounded read-retry helper accepts a transport that enforces its own timeout; the direct in-process fixture is a small immediate read. Its injected timeout test checks error handling, while the HTTP test actually waits for a deliberately delayed local server.

Example 42.2 — Count attempts, not just retries. With at most two attempts, the first request plus one repeat is the entire allowance. At 0.2 seconds per attempt and a 0.05-second backoff, two timeouts take approximately 0.2+0.05+0.2=0.450.2+0.05+0.2=0.45 seconds before scheduling overhead, within a 1-second overall deadline. These durations are policy bounds, not measured speed. Retry-After 1 leaves insufficient time for a repeat, so the helper stops. A 401 or 403 is returned immediately; unchanged credentials will not repair it.

A read retry can observe a different status because time passed. Preserve attempt IDs, source timestamps and the selected result. “Read-only” means no requested business mutation, not zero CPU use, zero billing, or identical responses forever.

42.5 A cancellation proposal needs a different contract

Section titled “42.5 A cancellation proposal needs a different contract”

Example 42.3 — A write trace on paper only. Suppose a future application offered cancellation. Before executing, it would bind approval to the exact order, action, principal and relevant current state. A generic earlier “yes” or a sentence inside a tool result is not that approval. The service would check authorization again at execution time; state could have changed while someone reviewed the proposal.

Table 42.2 — What changes for a write.

ConcernRead in this projectHypothetical cancellation
AuthorityRead own fictional snapshotExplicit permission and approval for this cancellation
TimeoutOutcome unknown; bounded repeat can rereadOutcome unknown; reconcile before assuming failure
RetryAt most two read attemptsReuse a durable operation key for the same approved action
IdempotencyRepeating read does not request mutationStore key, request fingerprint and outcome atomically with mutation
EvidenceRecord plus timestampDurable receipt, resulting state and side-effect outcome

If the first cancellation completes but its response is lost, repeating with a new key can create another side effect, such as a repeated refund request. Reusing a key only works if the service durably recognizes it. If the same key arrives with different arguments, reject the conflict instead of treating it as the original operation. Approval authorizes an action; idempotency controls repetition; a receipt establishes what happened. None substitutes for another. There is no cancellation implementation or real cancellation in this project.

42.6 KA-3: the same read through local interfaces

Section titled “42.6 KA-3: the same read through local interfaces”

read_tools.py implements the allowlist, argument checks, ownership authorization, result validation and return trace. http_scaffold.py uses its lookup for actual loopback request/response tests. The shared snapshot and output semantics remain the same. The next chapter wraps the same executor in MCP, so an interface change does not silently create new business powers.

Run the tool cells in 01-contracts-tools.ipynb after the Primer. The saved data/part-vii/tool-run.json includes completed, denied, unknown-tool, invalid-argument, timeout and poisoned-result outcomes. These are actual program paths driven by authored proposals; no LLM call-selection accuracy is being claimed. KA-1’s original model evaluation is still needed before entrusting a model with reliable proposal generation.

The model proposes; the application validates and authorizes; an executor performs the operation; the application checks and returns the result. Each verb names a different responsibility. A valid JSON call is neither permission nor completion. We now have one correct local interface; MCP addresses how different applications and servers can describe and exchange such requests consistently.

  1. The model proposes B-205 with valid syntax, and mira is the principal. At which gate should it stop? What observable test proves it stopped there?
  2. A timeout occurs after the executor began work. Explain the different next steps for a read and a hypothetical cancellation.
  3. A tool result contains a correct status and an extra instruction to cancel. Does correct business data make the instruction trusted? What does this project’s validator do?
  4. An approved cancellation for A-104 uses key K. A retry uses K but changes the order to B-205. Should the service reuse the old receipt, execute the new order, or reject? Explain the role of the request fingerprint.
  1. Authorization stops it because B-205 belongs to leo. Use a backend spy/counter and assert that it was never invoked. Successful parameter validation alone is insufficient evidence of access rights.
  2. Record unknown completion in both cases. A read may be repeated within its retry/deadline policy, preserving timestamps. A write first needs reconciliation or the same durable idempotency key; a fresh uncorrelated repeat may duplicate effects. A timeout is not proof of failure to execute.
  3. No. Data accuracy does not confer instruction authority. The local validator rejects an extra field. If a contract permits free text, the application must still treat that text as untrusted and keep execution gates independent.
  4. Reject. The key identifies one exact approved request. A fingerprint binds the key to its arguments; changing the order violates that binding. Neither an old receipt nor permission for A-104 authorizes B-205.
  • Anthropic, Tool use overview, verified 2026-09-14. Distinguishes client-executed tools from provider-executed tools; this chapter implements the former with authored proposals.
  • RFC 9110, §9.2. Defines safe and idempotent request semantics and limits on automatic retry.
  • MCP tools, “Output Schema”, “Error Handling” and “Security Considerations”. Supports separating interface shape, result validation and trust; protocol fields are deferred to the next chapter’s dated layer.