# Define and grant a JavaScript Tool

Create one typed callable for application work and explicit, capability-scoped Agent grants.
Canonical: https://agent-markup-language.com/docs/cookbook/tools/
Documentation index: https://agent-markup-language.com/docs/
Complete documentation: https://agent-markup-language.com/docs/llms.txt

**Host-process capability**

## Goal

Create one application-owned callable with validated input and safe JSON output, then grant it to an [`<Agent />`](https://agent-markup-language.com/docs/reference/primitives/agent/) only when the model should choose whether to call it. The workflow injects an application-owned repository into a component, then grants a narrow [`<Tool />`](https://agent-markup-language.com/docs/reference/primitives/tool/) rather than exposing the repository itself.

## Prerequisites

- Node.js `>=26`;
- `@aml-jsx/sdk`, `@aml-jsx/sdk/testing`, and `zod`;
- no credentials or network for this deterministic workflow.

## Complete source

```tsx
import { Agent, AmlRuntime, defineTool, Tool } from "@aml-jsx/sdk"
import { DeterministicAgentProvider } from "@aml-jsx/sdk/testing"
import { z } from "zod"

class SessionRepository {
  async listOrders(): Promise<readonly string[]> {
    return ["order-17", "order-29"]
  }
}

const provider = new DeterministicAgentProvider({
  async respond(request, context) {
    const tool = request.tools.find(candidate => candidate.name === "list_session_orders")

    if (tool?.kind !== "javascript") {
      throw new Error("Session Tool was not granted")
    }

    const orders = await tool.execute({}, { signal: context.signal, trace: context.trace })

    return { text: `orders:` + JSON.stringify(orders) }
  },
})

function OrderAgent({ repository }: { repository: SessionRepository }) {
  const listOrders = defineTool({
    description: "List orders for the active application session.",
    execute: async () => await repository.listOrders(),
    input: z.object({}),
    name: "list_session_orders",
  })

  return (
    <Agent provider={provider}>
      <Tool use={listOrders} />
      Inspect the active session orders.
    </Agent>
  )
}

const result = await new AmlRuntime().evaluate(<OrderAgent repository={new SessionRepository()} />)

console.log(result)
```

## Run it

Save the complete source as `recipe.tsx` in a project configured as shown in [Getting started](https://agent-markup-language.com/docs/getting-started/), then run it directly:

```sh title="Terminal"
npx vite-node recipe.tsx
```

## Expected output

```text
orders:["order-17","order-29"]
```

## How it works

1. The application passes `SessionRepository` into `OrderAgent`; AML does not expose that object to the provider.
2. `defineTool()` returns a typed callable carrying the Tool declaration. Calling `listOrders({})` from an active component would run it as application-selected work without exposing it to a model.
3. `<Tool use={listOrders} />` grants the Tool only to its containing `<Agent />`. A sibling `<Agent />` does not receive it.
4. The deterministic provider receives AML's low-level execution port and calls `.execute(input, context)` to simulate a model-selected Tool call. Application components use the callable form instead.

## Failure and security notes

**Danger — Tool code is application code**

Tools execute in the host process. A Tool is not sandboxed just because its owning `<Agent />` is inside an AML tree.
Do not pass unrestricted paths, shell commands, network clients, secrets, or database handles to model-controlled
input.

- Input is validated before execution; output must be stable JSON. Cyclic values, `BigInt`, class instances, `Map`, `Set`, symbols, non-finite numbers, and `undefined` are not portable Tool results.
- Duplicate Tool names and runtime `allowedTools` policy violations fail closed.
- Forward `context.signal` into I/O so cancellation can stop work that supports it.
- Keep capability descriptions narrow and auditable. The model should not receive a Tool merely because the application happens to have the function available.
- Calling a Tool from application code requires an active AML function component. Defining it at module scope is valid; calling it there is not.

## API and source links

- [`defineTool()`](https://agent-markup-language.com/docs/reference/primitives/tool/)
- [`<Tool />`](https://agent-markup-language.com/docs/reference/primitives/tool/)
- [`AgentToolExecutionContext`](https://agent-markup-language.com/docs/reference/primitives/tool/)
