Validated Tool Adapter
Part of AI Engineering: Implement a validated, tenant-scoped tool adapter
Validated Tool Adapter
A support assistant lets a language model look up tickets through a tool named get_ticket. The model only proposes the call: it picks a tool name and supplies arguments. Your adapter decides whether the call runs. Treat the name and the arguments as untrusted input, and take identity only from the trusted context that the application builds from the authenticated session.
Implement executeTool(name, args, context, records). On success it returns { ok: true, ticket: { id, title, status } }. Otherwise it returns { ok: false, code }. Run the checks in this order and return at the first one that fails:
| Order | Code | When |
|---|---|---|
| 1 | "unknown_tool" | name is not exactly "get_ticket" |
| 2 | "invalid_arguments" | args is not a plain object: null, undefined, an array, a string, or a number |
| 3 | "invalid_arguments" | args has any key other than ticket_id, or no ticket_id at all |
| 4 | "invalid_arguments" | ticket_id is not a string made of TKT- followed by exactly three digits from 0 to 9 |
| 5 | "forbidden" | context.canReadTickets is false |
| 6 | "not_found" | records has no entry under the key context.tenant + ":" + ticket_id |
Inputs:
name: the tool name the model chose.args: the arguments the model supplied, already parsed from JSON. Their shape is not guaranteed.context:{ tenant, canReadTickets }, built by the application from the authenticated session.records: aMapfrom"tenant:ticket_id"(for example"alpha:TKT-104") to the stored ticket. A stored ticket can carry internal fields besidesid,title, andstatus.
Two rules for the result:
- The returned
tickethas exactlyid,title, andstatus, copied from the stored record. Never return the stored object itself, and never pass other stored fields to the model. - The tenant always comes from
context. Atenantargument is rejected with"invalid_arguments", even when its value matches the trusted tenant.
Fixtures
The store holds two tickets with the same ID in different tenants:
| Key | Stored ticket |
|---|---|
alpha:TKT-104 | { id: "TKT-104", title: "Settings crash", status: "open" } |
beta:TKT-104 | { id: "TKT-104", title: "Private billing issue", status: "closed" } |
| Tool and arguments | Trusted context | Expected |
|---|---|---|
get_ticket, { ticket_id: "TKT-104" } | alpha, can read | { ok: true, ticket: { id: "TKT-104", title: "Settings crash", status: "open" } } |
get_ticket, { ticket_id: "TKT-104" } | beta, can read | { ok: true, ticket: { id: "TKT-104", title: "Private billing issue", status: "closed" } } |
get_ticket, { ticket_id: "TKT-999" } | alpha, can read | { ok: false, code: "not_found" } |
get_ticket, { ticket_id: "TKT-104" } | alpha, cannot read | { ok: false, code: "forbidden" } |
delete_ticket, { ticket_id: "TKT-104" } | alpha, can read | { ok: false, code: "unknown_tool" } |
get_ticket, { ticket_id: 104 } | alpha, can read | { ok: false, code: "invalid_arguments" } |
get_ticket, { ticket_id: "TKT-104", tenant: "beta" } | alpha, can read | { ok: false, code: "invalid_arguments" } |
get_ticket, null | alpha, can read | { ok: false, code: "invalid_arguments" } |
Notes
"TKT-1040","TKT-10","tkt-104", and" TKT-104"are all invalid. The ID must match the whole string.- In JavaScript an array is also an object, so check for arrays explicitly.
- Do not modify
records.
In Python, implement execute_tool(name, args, context, records). args is a dict (or None, a list, a str, ...), context is {"tenant": ..., "can_read_tickets": ...}, and records is a dict. Return dicts such as {"ok": False, "code": "not_found"}: expected errors are returned, not raised.
0 views
Solution
Hints
