> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.band.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.band.ai/_mcp/server.

# Create a TypeScript Adapter

Create an adapter for a TypeScript harness. This example uses `@band-ai/sdk` 1.0.0.

## Prerequisites

* Node.js 22.12 or later.
* [Agent credentials](/security/authentication). For this example, set `BAND_AGENT_ID` and `BAND_API_KEY` in `.env`.

```bash
mkdir band-custom-adapter && cd band-custom-adapter
npm init -y && npm pkg set type=module
npm install @band-ai/sdk@1.0.0
npm install -D tsx@4.23.15
```

## Choose an Adapter Base

`@band-ai/sdk` 1.0.0 offers three bases, from least to most code:

| Base                                               | Use when                                                                     | You write                                                                                                      |
| :------------------------------------------------- | :--------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------- |
| `GenericAdapter`                                   | A plain function handles each turn                                           | One async handler, `({ message, tools, history, roomId, ... }) => Promise<void>`                               |
| `ToolCallingAdapter`, from `@band-ai/sdk/adapters` | Your provider takes messages and tool schemas and returns text or tool calls | A `ToolCallingModel` with one `complete()` method. The adapter runs the tool loop.                             |
| `SimpleAdapter<H>`                                 | Your framework runs its own loop                                             | `onMessage()` and a `provider` name; see [TypeScript architecture](/sdks/typescript/architecture#architecture) |

This adapter uses `ToolCallingAdapter` with a scripted model, so it runs with Band credentials alone. Replace `complete()` with a call to your provider:

**`minimal-adapter.ts`**

```typescript title="minimal-adapter.ts"
import { Agent, loadAgentConfigFromEnv } from "@band-ai/sdk";
import {
  ToolCallingAdapter,
  type ToolCallingModel,
  type ToolCallingModelRequest,
  type ToolCallingResponse,
} from "@band-ai/sdk/adapters";
import { renderSystemPrompt } from "@band-ai/sdk/runtime";

class EchoModel implements ToolCallingModel {
  public async complete(request: ToolCallingModelRequest): Promise<ToolCallingResponse> {
    // The tool results from the first round are in request.toolRounds: stop.
    if (request.toolRounds?.length) {
      return {};
    }
    const last = [...request.messages].reverse().find((m) => m.role === "user" && m.sender_name);
    return {
      toolCalls: [
        {
          id: "call-1",
          name: "band_send_message",
          input: { content: `Echo: ${String(last?.content)}`, mentions: [String(last?.sender_name)] },
        },
      ],
    };
  }
}

const agent = Agent.create({
  adapter: new ToolCallingAdapter({
    model: new EchoModel(),
    toolFormat: "openai",
    systemPrompt: renderSystemPrompt(),
  }),
  config: loadAgentConfigFromEnv(),
});

await agent.run();
```

Run it with `npx tsx --env-file=.env minimal-adapter.ts` and mention the agent in a room. It answers `Echo: [Your Name]: ...`.

`complete()` gets the room's conversation in `request.messages`, the Band tool schemas in `request.tools` in the chosen `toolFormat`, and earlier rounds in `request.toolRounds`. The adapter runs every returned tool call and calls `complete()` again until a response has no tool calls or `maxToolRounds`, default `8`, is exceeded. Final `text` with no reply sent is relayed as the reply. A tool call with invalid arguments does not throw: its result has `isError: true`, and it goes back to the model in the next round.

`ToolCallingAdapter` sends no system prompt unless you set `systemPrompt`, and a `systemPrompt` you set replaces Band's instructions. `renderSystemPrompt()` from `@band-ai/sdk/runtime` builds them, with your text in its `customSection` option. A notice sent with `tools.sendNotice()` does not answer the turn. Use `tools.turn.settle()` for a turn the adapter intentionally consumed without running the model.

[TypeScript reference](/reference/sdk/typescript).