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

# Write a custom code tool

> Run typed Python logic in an isolated tool environment.

Create a Custom Code tool for calculations and data processing that do not need
network access. The Python input and output models become the tool contract the
agent sees.

## Prerequisites

* Permission to manage organization tools.
* A deterministic Python task that can run without an internet connection.
* Vault secrets for any sensitive values the code needs.

<Steps>
  <Step title="Create the draft">
    <Tabs>
      <Tab title="Dashboard" icon="monitor">
        Go to **Global Tools**, choose **New tool**, then select **Custom Code**.
        Enter a precise tool name and **Instructions**. **Instructions** maps to
        the API `description` field.

        <Frame caption="Select Custom Code from the New tool page">
          <img src="https://mintcdn.com/slng-new-docs/_MURdOw87SJsfVag/heroshots/custom-code-select.png?fit=max&auto=format&n=_MURdOw87SJsfVag&q=85&s=333f5def32ab7980868ecdab2c64245e" alt="The New tool page with Custom Code highlighted" width="2560" height="1600" data-path="heroshots/custom-code-select.png" />
        </Frame>
      </Tab>

      <Tab title="API" icon="code">
        Define the source in a JSON request body.

        ```json tool.json theme={null}
        {
          "name": "calculate_discount",
          "description": "Calculate a percentage discount for an order subtotal.",
          "tool_type": "code",
          "code_src": "from pydantic import BaseModel, Field\n\nclass Input(BaseModel):\n    subtotal: float = Field(..., ge=0)\n    percent: float = Field(..., ge=0, le=100)\n\nclass Output(BaseModel):\n    discount: float\n    total: float\n\ndef handler(input: Input) -> Output:\n    discount = round(input.subtotal * input.percent / 100, 2)\n    return Output(discount=discount, total=round(input.subtotal - discount, 2))",
          "declared_secrets": [],
          "dependencies": [],
          "argument_defaults": {}
        }
        ```

        ```bash Request theme={null}
        curl -X POST https://api.agents.slng.ai/v1/agents/tools \
          -H "Authorization: Bearer $SLNG_API_KEY" \
          -H "Content-Type: application/json" \
          -d @tool.json
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Define typed input and output">
    Define a Pydantic `Input` model, an `Output` model, and a synchronous
    `handler(input: Input) -> Output` function. Add field descriptions so the
    model knows which values to supply.

    Return the declared output model. Keep logs useful for testing, but return
    the data the agent needs from the handler.
  </Step>

  <Step title="Add dependencies and secrets">
    List each dependency as an exact version such as `pydantic==2.11.7`.
    Packages with URLs, environment markers, or extras are rejected.

    Declare Vault secret names separately from the source. Do not place secret
    values in code or logs. The environment exposes only the declared secrets.

    <Tabs>
      <Tab title="Dashboard" icon="monitor">
        Expand **Metadata**, select the Vault secrets the tool can access, and
        enter the pinned packages in **Dependencies**.

        <Frame caption="Configure Vault secrets and Python dependencies">
          <img src="https://mintcdn.com/slng-new-docs/_MURdOw87SJsfVag/heroshots/custom-code-metadata.png?fit=max&auto=format&n=_MURdOw87SJsfVag&q=85&s=f2cf41f38c79eeef3f708170c1da8533" alt="The Custom Code metadata panel with the Dependencies field highlighted" width="2560" height="1600" data-path="heroshots/custom-code-metadata.png" />
        </Frame>
      </Tab>

      <Tab title="API" icon="code">
        Add the allowed Vault secret names to `declared_secrets` and the pinned
        packages to `dependencies` in the tool request.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Build the tool">
    <Tabs>
      <Tab title="Dashboard" icon="monitor">
        Save the draft, then choose **Build tool**.

        <Frame caption="Build the tool after defining its code and metadata">
          <img src="https://mintcdn.com/slng-new-docs/_MURdOw87SJsfVag/heroshots/custom-code-build.png?fit=max&auto=format&n=_MURdOw87SJsfVag&q=85&s=f2f29aa71136d74b45fb537b6aa42003" alt="The Custom Code editor with Build tool highlighted" width="2560" height="1600" data-path="heroshots/custom-code-build.png" />
        </Frame>
      </Tab>

      <Tab title="API" icon="code">
        Save the draft, then send
        `POST /v1/agents/tools/{tool_id}/introspect`.
      </Tab>
    </Tabs>

    The build installs the pinned dependencies, imports the declared modules,
    and derives the argument schema from the Pydantic models.
  </Step>

  <Step title="Test and publish">
    Run representative input, check the structured output and logs, then
    publish. You must have a valid schema, available secrets and dependencies,
    and a successful run for the current draft.
  </Step>

  <Step title="Attach the published version">
    Attach the version to an agent and verify it in a test call. See
    [Attach a tool to an agent](/guides/agents/tools-and-mcp/attach-to-an-agent).
  </Step>
</Steps>

## Environment limits

Custom Code runs in an isolated environment with no internet access. Use an
[API Request tool](/guides/agents/tools-and-mcp/api-request-tool) or
[MCP server](/guides/agents/tools-and-mcp/connect-with-mcp) for network calls.
Avoid unbounded loops, large payloads, and large output objects. A run that
exceeds its time or data limit returns a failure instead of partial output.

Code tools can run on call start, first user message, and tool success or
failure. They cannot run on call end. See
[Run a tool on call events](/guides/agents/tools-and-mcp/run-on-call-events).
