nvoken

Build

Tools

Choose where a tool runs, understand when an Invocation parks, and make external side effects safe to retry.

Choose a tool mode by asking one question: who should run the code?

ModeRunnerBest fit
builtinnvokenThe fixed nvoken_fetch public-page reader
hostYour application or workerLong work, private systems, browser actions, human input
callbackYour public HTTPS endpointUnattended work that finishes within ten seconds
MCPA remote MCP serverAn existing streamable-HTTP tool server
ProviderThe model providerAnthropic server-side web search

You can mix these modes in one execution spec. Tools are declared on each Invocation; there is no nvoken tool registry.

Host tools

A host tool has a name, description, and bounded JSON input schema:

{
  "name": "lookup_order",
  "description": "Look up the current state of one order.",
  "mode": "host",
  "input_schema": {
    "type": "object",
    "properties": { "order_id": { "type": "string" } },
    "required": ["order_id"],
    "additionalProperties": false
  }
}

When the model calls it, the Invocation moves to waiting. nvoken exposes the pending ToolCall with a stable ID and holds no execution lease. Your host runs the tool and submits the result:

POST /v1/invocations/{invocation_id}/tool-results
{
  "results": [{
    "tool_call_id": "tcal_…",
    "content": { "order_id": "1842", "state": "shipped" },
    "is_error": false
  }]
}

The first accepted result wins. Retrying the same result is safe; changing it returns tool_result_conflict. A partial batch leaves the turn waiting.

The SDK Agent facades can attach local handlers and run this park/answer/resume loop for you. Use the lower-level handle when another queue or process owns the tool.

Ask the user with a host tool

A structured question is an ordinary host tool named ask_user. The TypeScript SDK ships the same question shape for confirm, select, multiselect, and text input.

import { askUserTool, Client } from "@deepnoodle/nvoken";
 
const agent = new Client().agent({
  agentKey: "support",
  spec: { tools: [askUserTool((question) => ui.prompt(question))] },
});

If the person dismisses the prompt, return { canceled: true } as a normal tool result. An error tells the model the tool broke and often causes an unhelpful retry.

The wait is unbounded by default. A question can remain parked for days without holding a worker or consuming the Invocation's active time.

Finish host tools with nobody watching

Set notify on admission to receive signed invocation.waiting and invocation.settled webhooks. A waiting notification includes the pending ToolCall IDs. The receiver reads the authoritative Invocation, claims each side-effecting call in its own database, runs it, and submits the result.

Return 2xx from the notification receiver within ten seconds after verifying and enqueueing the work. Delivery is at least once, and the notification's Idempotency-Key is stable across retries.

An attached client and a webhook worker may race to answer the same host tool. Result acceptance is deduplicated, but the external effect is not. Take a claim on tool_call_id before charging a card, sending a message, or changing data.

Callback tools

Use mode: callback when nvoken should deliver the call directly to your public HTTPS endpoint. The turn parks, nvoken signs and retries the request, and the endpoint returns the result in its response.

The response must arrive within ten seconds and use this envelope:

{ "content": { "available": true }, "is_error": false }

The signature is HMAC-SHA256 over:

v1.<delivery_id>.<unix_timestamp>.<raw_request_body>

Verify the version, key ID, timestamp, and signature against the raw body before parsing JSON. Deduplicate on tool_call_id or the delivery Idempotency-Key. Callback delivery is at least once.

A slow or queue-backed operation belongs in host mode. Returning 202 and finishing later is not a callback completion protocol.

Remote MCP

spec.mcp_servers accepts public HTTPS streamable-HTTP servers. Each descriptor includes a name, URL, allowed tools, optional encrypted headers, and discovery and call timeouts.

Probe the projected names before admission:

nvoken mcp list-tools \
  --name support \
  --url https://mcp.example.com/rpc

The allowlist is enforced. Secret headers are encrypted for the Invocation and never returned in specs, streams, transcripts, errors, or logs. nvoken also checks public-address resolution, redirects, and response bounds.

Recovery repeats an unfinished MCP call only when it was admitted as safe to repeat. A possibly mutating call with an unknown outcome is reported as unknown instead of being issued twice.

Builtin and provider tools

The only general builtin is:

{ "name": "nvoken_fetch", "mode": "builtin" }

It fetches public HTTPS text, follows guarded redirects, and converts HTML to Markdown within fixed time and size limits. It does not use caller credentials or ambient HTTP proxies.

spec.provider_tools currently exposes web_search for qualified Anthropic models. The provider runs and bills that search inside generation. Search spend is not included in max_estimated_cost_usd; bound it with max_uses.

What nvoken does not decide

Host and callback tool declarations do not carry read-only, destructive, permission, preview, or approval policy. Keep that metadata and enforcement in your application.

A stable ToolCall ID makes result submission idempotent. It does not turn an arbitrary external side effect into exactly-once work.