Tools — Cloud Function reference
Server-side dispatch of MCP or HTTP tools for a registered agent. See the Tools subsystem for the narrative.
registerTool
Caller: authenticated human (the owner of agentId)
onCall — Firebase Callable
registerTool({
agentId: string;
name: string;
kind: 'mcp' | 'http';
url: string;
authToken?: string; // KMS-encrypted at rest; omit for anonymous tools
manifest?: unknown; // skip to let the server fetch it from `url`
}) => {ok: true}
Errors
| Code | Reason |
|---|---|
invalid-argument | Missing agentId/name/kind/url or URL is unparseable |
permission-denied | Caller does not own agentId |
Side effects
- Writes
agents/{agentId}/tools/{name} - KMS-encrypts
authTokenvia keyprojects/axy-ujex/locations/us-central1/keyRings/ujex/cryptoKeys/secrets - If
kind === 'mcp'andmanifestis not supplied, POSTs{jsonrpc: '2.0', id: 1, method: 'tools/list'}tourland stores the response - Audit:
tool.register
listTools
Caller: authenticated agent onCall
listTools({}) => {
tools: Array<{name: string; kind: 'mcp' | 'http'; url: string; manifest: unknown}>
}
Returns only enabled tools. Credentials (authTokenEnc) are never
included.
invokeTool
Caller: authenticated agent onCall
invokeTool({
name: string;
args?: unknown; // tool-defined; for MCP becomes `params.arguments`
timeoutMs?: number; // default 15_000, clamped to 60_000
sessionId: string; // stable id for this agent run
approvalId?: string; // approved retry for the exact name+args digest
}) => {status: number; result: unknown} | {status: 'waiting_approval'; approvalId: string; reason: string}
Errors
| Code | Reason |
|---|---|
invalid-argument | name or sessionId missing |
not-found | Tool doesn't exist or is disabled |
failed-precondition | approvalId is missing, not approved, expired, or bound to a different action digest |
resource-exhausted | tools.invoke quota exhausted for this owner/month |
internal | Tool returned non-2xx or threw a network error. details.status = HTTP code (0 on network failure). Quota is credited back on network failure. |
Behaviour
- Server-side policy runs before fetch. Default-deny verbs or Rule-of-Two sessions return
waiting_approvaland do not call the remote URL. - Approved retries execute only when
approvalIdmatches the exactname,argsdigest. - Budget preflight runs before quota/fetch and fails closed on budget overage.
- Decrypts
authTokenEnctransiently; injectsAuthorization: Bearer <token>if present - MCP: body
{jsonrpc: '2.0', id: <rand>, method: 'tools/call', params: {name, arguments: args}} - HTTP: body
args ?? {} - Audit:
tool.invokewith{status, ok, error}
getToolCredential
Caller: authenticated agent onCall
getToolCredential({name: string}) => throws failed-precondition
Errors
| Code | Reason |
|---|---|
failed-precondition | Agent-side plaintext credential access is disabled |
This callable remains exported for short-term compatibility, but production
servers deny agent-side plaintext credential access. Use invokeTool; Ujex
injects credentials server-side so tokens never touch agent memory.
Firestore state written
| Path | Written by | Contents |
|---|---|---|
agents/{agentId}/tools/{name} | registerTool | Full tool record including authTokenEnc (Uint8Array) + kmsKey path |
audit/{seq} | all four | Hash-chained audit events (actor, action, target, meta) |
Quota bucket
| Bucket | Charged by | Credited by |
|---|---|---|
tools.invoke | invokeTool success or HTTP-level failure | invokeTool network error |
Retention
Tool registrations live until the owner deletes the agent or marks
enabled: false. Audit entries are append-only and retained per the
audit subsystem's own policy.