Skip to main content
Custom tools are functions you write in Python or JavaScript that run in a sandbox whenever an agent decides to call them. You can manage them from the dashboard, over the REST API, or through the MCP server (the *_custom_tool tools). Before you write code, call List Custom Tool Runtimes (GET /v1/custom-tools/runtimes). It returns the exact signature, return contract, starter template, built-in modules, and installable packages for each language, straight from the runtime, so you never have to guess.

The function signature

Every tool defines a single entrypoint named main. The parameters you declare on the tool are passed in to it.
def main(**params):
    # params are passed as keyword arguments
    order_id = params["order_id"]
    ...
    return {"message": "..."}
LanguageEntrypointParameters arrive as
Pythondef main(**params)keyword arguments (params dict)
JavaScriptasync function main(params)a single params object

The return shape

main must return an object/dict containing a string message key. The message is what the agent reads back. Optionally include a rawData key with structured data for the agent to reason over.
return {
    "message": f"Order {order_id} is shipped",
    "rawData": {"status": "shipped", "carrier": "UPS"},
}
Writes perform a static check for the required main entrypoint and a return statement. When the tool runs, a return value without a string message key will fail runtime validation.

Parameters

Declare each parameter your tool accepts in the parameters map. The keys become the fields available inside main.
{
  "order_id": {
    "type": "string",
    "required": true,
    "description": "The order id to look up."
  },
  "include_history": {
    "type": "boolean",
    "required": false,
    "description": "Whether to include the full status history."
  }
}
FieldRequiredMeaning
typeyesstring, number, boolean, object, or array.
requiredyesWhether the agent must supply the value.
descriptionyesShown to the model so it fills the value correctly.
enum_valuesnoA closed set of allowed string values.
execution_message_descriptionnoPer-parameter note shown while the tool runs.

Available dependencies

The full, authoritative list per language is returned by GET /v1/custom-tools/runtimes. In short:
  • Standard library modules are always importable with no install step (Python: json, math, datetime, re, urllib, …; JavaScript: fs, path, crypto, https, …).
  • A curated set of third-party packages installs automatically the first time you import it. For Python this includes requests, httpx, numpy, pandas, and more. JavaScript tools run on Bun with its built-in modules.

Naming rules

Tool names are lowercase kebab-case: letters, numbers, and single hyphens only (for example lookup-order-status). No spaces, no uppercase, no leading, trailing, or consecutive hyphens. The name is how the agent references the tool, and it must be unique within the workspace.

Lifecycle

  1. POST /v1/custom-tools (or create_custom_tool) creates the tool.
  2. PATCH /v1/custom-tools/{id} updates it. Changing code bumps the tool’s version.
  3. Set enable_globally: true to make the tool available to every agent in the workspace without per-agent enablement.
  4. DELETE /v1/custom-tools/{id} removes the tool and its access-control rows.

Turning a tool on for an agent

Creating a custom tool does not automatically expose it to your agents. Chat agents are opt-in: a tool is disabled until you turn it on for that agent. Over MCP these are the list_agent_tools and set_agent_tool_access tools.