> ## Documentation Index
> Fetch the complete documentation index at: https://docs.conduit.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Tools Overview

> Author Python or JavaScript tools that your agents can call. This page is the contract: function signature, return shape, parameters, and available dependencies.

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](/api-reference/custom-tools/list), or through the
[MCP server](/mcp/overview) (the `*_custom_tool` tools).

Before you write code, call
[List Custom Tool Runtimes](/api-reference/custom-tools/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.

<CodeGroup>
  ```python Python theme={null}
  def main(**params):
      # params are passed as keyword arguments
      order_id = params["order_id"]
      ...
      return {"message": "..."}
  ```

  ```javascript JavaScript theme={null}
  async function main(params) {
      // params arrive as a single object
      const { order_id } = params
      ...
      return { message: "..." }
  }
  ```
</CodeGroup>

| Language   | Entrypoint                    | Parameters arrive as              |
| ---------- | ----------------------------- | --------------------------------- |
| Python     | `def main(**params)`          | keyword arguments (`params` dict) |
| JavaScript | `async 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.

<CodeGroup>
  ```python Python theme={null}
  return {
      "message": f"Order {order_id} is shipped",
      "rawData": {"status": "shipped", "carrier": "UPS"},
  }
  ```

  ```javascript JavaScript theme={null}
  return {
      message: `Order ${order_id} is shipped`,
      rawData: { status: "shipped", carrier: "UPS" },
  }
  ```
</CodeGroup>

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`.

```json theme={null}
{
  "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."
  }
}
```

| Field                           | Required | Meaning                                              |
| ------------------------------- | -------- | ---------------------------------------------------- |
| `type`                          | yes      | `string`, `number`, `boolean`, `object`, or `array`. |
| `required`                      | yes      | Whether the agent must supply the value.             |
| `description`                   | yes      | Shown to the model so it fills the value correctly.  |
| `enum_values`                   | no       | A closed set of allowed string values.               |
| `execution_message_description` | no       | Per-parameter note shown while the tool runs.        |

## Available dependencies

The full, authoritative list per language is returned by
[`GET /v1/custom-tools/runtimes`](/api-reference/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.

* [`GET /v1/agents/{id}/tools`](/api-reference/agents/list-tools) lists the
  built-in and custom tools an agent can resolve, each with its `enabled` state.
* [`PATCH /v1/agents/{id}/tools`](/api-reference/agents/set-tool) toggles one
  tool for that agent. Identify it by the `(tool_name, source)` pair, where
  `source` is `builtin` or `custom`.

Over MCP these are the `list_agent_tools` and `set_agent_tool_access` tools.
