Toolkit
Toolkits: your connections' operations, the operations that need no account, and capabilities as LLM tools.
gmail = noclick.Connection.from_name("gmail", integration="gmail", create_if_missing=True)
toolkit = noclick.Toolkit(["search", gmail])
response = anthropic.messages.create(..., tools=toolkit.definitions())
messages.append({"role": "user", "content": toolkit.handle(response)})A call never raises for something the model can act on: a missing connection,
a policy refusal or a hold for approval comes back as an is_error result.
A hold's approval_required (approval_id, request_id, approve_url,
operation, approver) is never shown to the model; pass on_approval=
to the handle helpers to receive it, and on_connect= to receive each
result that needs a connection. request_id (the request a person answers),
approve_url and operation (the approval's operation object) are None
unless the request is addressed to you; the approver is emailed either way.
Once the person approves, the identical call goes through.
class ToolkitA set of tools for your LLM. Entries are connections (a Connection or its
name: every operation, as {connection}__{operation} tools), single
operations ("gmail.send_email_message"), capabilities ("search",
"fetch", "code") or a Database ({database}__query, __execute and __schema;
invite-only beta), a Bucket and operations with arguments bound
(gmail.only("send_email_message").bind({ to }): the bound parameters leave the tool's schema and
are filled on every call, so a toolkit sends its entries with each call). A Connection object
from another scope, another user's or the
account's, stays that connection (invite-only beta). A name resolves for the user the calls
act for (user if given, else the user current at each call, asUser), so one module-level
toolkit serves every user: a connection there, else an integration's operations that need no
account (on NoClick's key), so "exa.search" needs no connection.
| refs | ToolRef[] | What was passed: names, |
| simulated | boolean | Whether a simulated world answers every call instead of the real accounts: the definitions are the real ones, nothing is read or sent, and each answer is billed as AI at NoClick's model price. |
| scenario | string | null | What that world is like; its answers keep to it. |
| tools | string[] | The refs as names (a |
mcpServer
mcpServer(name: string): McpServerThese tools as a hosted MCP server called name, in the toolkit's user (McpServer.fromName
with createIfMissing). Sends nothing yet.
The server, created on first use (or brought in step with this toolkit's tools); its url is
what you give an MCP client.
definitions
definitions(
options?: { format?: ToolFormat; user?: UserOption },
): Promise<JsonObject[]>Tool definitions to pass straight to your LLM API. Cached per format.
Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
"anthropic"The tool definitions in that API's shape, ready to pass as tools. The first call for a
format fetches them; later calls reuse them.
call
call(
name: string,
args?: JsonObject,
options?: { approvalId?: string; user?: UserOption },
): Promise<ToolResult>Run one tool by its wire name (gmail__send_email_message, search). A policy refusal or a hold is
an isError result, never a throw; a held call goes through once approved when it is repeated
with the same arguments (approvalId names the approval explicitly).
Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The tool's content (text as one string) and whether it is_error. A call that couldn't
run says why in content, with needs_connection or approval_required set when that's the reason.
handle
handle(
content: unknown,
options?: HandleOptions,
): Promise<AnthropicToolResultBlock[]>Run every Anthropic tool_use block (from a Message, its content list,
or plain objects) concurrently and return the tool_result blocks for the
next user turn. onApproval receives each call held for approval, and onConnect each one whose
needsConnection is set, so your app can show the user the connect link itself. Each result
carries the model's callId and the arguments.
Its older name handleAnthropic still works, deprecated.
Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
One tool_result block per tool_use, in order: the content of the next user turn.
handleOpenAI
handleOpenAI(
toolCalls: unknown,
options?: HandleOptions,
): Promise<OpenAIToolOutput[]>Run OpenAI tool calls (a ChatCompletion, its message, a tool_calls list,
or Responses function_call items) and return { role: "tool" } messages
(or function_call_output items). onApproval and onConnect work as in handle.
Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
One {"role": "tool"} message per call, in order, or a function_call_output item for each
Responses call. A call whose arguments aren't valid JSON isn't run; its message tells the model so.
ToolResult
interface ToolResultThe outcome of one tool call. An unconnected account is a normal result with needsConnection.
| name | string | |
| content | unknown | |
| isError | boolean | |
| needsConnection | NeedsConnectionInfo | null | |
| raw | JsonObject | The response JSON exactly as the API sent it. |
| approvalRequired | ApprovalRequiredInfo | null | |
| arguments | JsonObject | The arguments the call was made with, as the model gave them. |
| callId | string | null | The model's id for the call when it came through |
| connectUrl | string | null | |
| approval | HumanRequest<boolean> | null | The approval a held call waits on, as a request: |
| approveUrl | string | null | The approval link, when the call waits for your approval; once approved, repeat the identical call. |
| text | string | The content as one string (what an OpenAI tool message carries). |