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

# Chat.* Tools

> The unified messaging surface agents use to talk on Slack, Discord, Telegram, and Linear: every chat.* tool, its parameters, addressing and threading semantics.

Agents talk to humans through one provider-neutral tool family: `chat.*`. The same seven tools send messages, read history, download attachments, react, and manage issues across **Slack, Discord, Telegram, and Linear** — the provider is just a field in the target. This page documents every tool and the addressing model they share. Read it when you are writing prompts that tell an agent how to reply, or deciding which connections an agent needs.

## Enabling chat tools

`chat.*` tools register on a session when the agent declares a `kind: local, implementation: chat` tool. The tool's `auth` binds one connection or several providers under one alias:

```yaml .auto/agents/assistant.yaml theme={null}
tools:
  chat:
    kind: local
    implementation: chat
    auth:
      kind: connections
      connections:
        - provider: slack
          connection: slack
        - provider: telegram
          connection: telegram-acme_bot
```

Use `auth: { kind: connection, provider: slack, connection: slack }` for a single provider. The declared connections are the complete universe the tools can reach: target resolution matches the call's target against them, and a project must be authorized to use the underlying connection grant. See [connections and identities](/concepts/connections-and-identities) for how connections are created.

Tool names as the harness sees them are flattened — claude-code renders `chat.send` as `mcp__auto__chat_send` (see [Auto MCP](/runtime/auto-mcp)).

## Addressing: targets, destinations, recipients

Every tool that touches a conversation takes a `target` — a provider-discriminated address:

```json theme={null}
{
  "provider": "slack",
  "destination": { "channel": "#releases", "thread": "slack:C0891B2K3:1720613024.001200" }
}
```

* **`provider`** — `"slack"`, `"discord"`, `"telegram"`, or `"linear"`.
* **`destination`** — where in the provider: a channel plus optional thread. Per provider:
  * Slack: `workspace?`, `channel?` (id or name, `#` optional), `thread?`.
  * Discord: `workspace?` (connection or guild name), `channel?` (snowflake id or `#name`), `thread?` (the canonical `discord:<guildId>:<channelId>:<threadId>` id from a prior send/history result, or a bare thread id together with `channel`).
  * Telegram: `workspace?` (the connection name), `channel?` (chat id such as `"-1001234"` or a known group title), `thread?`.
  * Linear: `workspace?`, `team?`, `issue?` (issue comment threads), `thread?`.
* **`to`** — a direct-message recipient instead of a destination: `{ kind: "user", user: "<provider user id>" }` (Slack additionally supports `{ kind: "userGroup" }`). Telegram users must have started the bot first; Discord users must share a guild with the installed bot.

Thread ids are canonical strings prefixed with their provider (`slack:…`, `discord:…`) — always pass them back exactly as a prior tool result or trigger delivery rendered them. The special selector `"current"` is accepted where a thread is expected.

`target` is optional on `chat.send`: with it omitted, the call resolves against the session's declared connections and succeeds only when exactly one matches — a session with a single Slack connection can just send, while a multi-provider session must name the provider (an ambiguous target is rejected with an error, not guessed). `destination.workspace` narrows by connection name or account name when one provider has several connections.

## Message content

Message bodies are a plain string, or an explicit format wrapper:

| Form                  | Meaning                                                                                    |
| --------------------- | ------------------------------------------------------------------------------------------ |
| `"text"`              | Plain text; the platform renders it appropriately per provider.                            |
| `{ "markdown": "…" }` | Markdown source, converted to the provider's format.                                       |
| `{ "raw": "…" }`      | Provider-native syntax passed through untouched (e.g. Slack mrkdwn with `<@U…>` mentions). |

## The tools

### chat.send

Send a message. Returns the address actually used plus provider ids you should keep.

| Parameter | Type                              | Notes                                  |
| --------- | --------------------------------- | -------------------------------------- |
| `message` | string \| `{markdown}` \| `{raw}` | Required.                              |
| `target`  | address                           | Optional when unambiguous (see above). |

Output: `{ address, messageId, threadId, provider }`. **Save `threadId`** — a send may open a new thread, and the returned id is the canonical handle for replies, reactions, and history.

Sends are attributed: the platform resolves the agent's per-agent persona (display name, avatar, bot identity from the agent's [identity](/reference/identity)) and posts as it. After an attributed send, the platform automatically records a thread-continuation binding keyed on the returned `threadId` and subscribes provider ingest for it — so unmentioned replies in a thread the agent opened can route back to the same session through a `chat.message.subscribed` trigger, with no explicit `auto.bind` call. (Declaring the matching trigger is still the agent author's job; see [Slack events](/reference/events/slack).)

```json theme={null}
{
  "message": { "markdown": "Deploy finished — 14 services green." },
  "target": { "provider": "slack", "destination": { "channel": "#releases" } }
}
```

### chat.search

Search the session's available workspaces, channels, and users. Each result includes a ready-made `target` you can pass straight to `chat.send` or `chat.history`.

| Parameter  | Type                      | Notes                                                      |
| ---------- | ------------------------- | ---------------------------------------------------------- |
| `provider` | string                    | Optional filter; default searches every declared provider. |
| `query`    | string                    | Optional name query.                                       |
| `types`    | `("channel" \| "user")[]` | Default both.                                              |
| `limit`    | int 1–100                 | Default 25.                                                |

Search results are returned for Slack and Discord workspaces today; `warnings` reports providers that could not be searched.

<Tip>
  Do not search just to resolve a Slack channel id — `chat.send` and `chat.history` accept Slack channel *names* directly, with or without `#`.
</Tip>

### chat.history

Read recent messages from a channel or thread.

| Parameter | Type      | Notes                                                         |
| --------- | --------- | ------------------------------------------------------------- |
| `target`  | address   | Required. Slack `destination.channel` may be an id or a name. |
| `limit`   | int 1–100 | Default 20.                                                   |

Each message carries `messageId`, `threadId`, `text`, `author`, and — when files were attached — `attachments` metadata: `{ name?, mimeType?, size?, fileId?, url? }`. The `url` is a provider URL that generally requires provider credentials; treat it as a reference, and use `chat.attachment_download` to actually fetch bytes.

### chat.attachment\_download

Turn an attachment reference into a short-lived signed URL the agent can `curl` from its sandbox. The platform fetches the bytes server-side through the provider connection (Slack `url_private` with the bot token), stores them privately, and mints the URL — provider credentials never reach the sandbox.

| Parameter                            | Type        | Notes                                                                                         |
| ------------------------------------ | ----------- | --------------------------------------------------------------------------------------------- |
| `target`                             | address     | The conversation the file came from.                                                          |
| `attachment.fileId`                  | string      | Required — the provider file id, exactly as `chat.history` or a trigger delivery rendered it. |
| `attachment.mimeType`                | string      | Required; must be on the allowlist.                                                           |
| `attachment.size`, `attachment.name` | int, string | Optional.                                                                                     |

Output: `{ downloadUrl, expiresAt, mimeType, size, filename }`. The URL expires **5 minutes** after minting — fetch it in the same turn.

Allowed types and per-category size caps: images (`png`, `jpeg`, `gif`, `webp`) and documents (`pdf`, `markdown`, `plain text`) up to 20 MB; archives (`zip`, `gzip`, `tar`) up to 100 MB; video (`mp4`, `quicktime`, `webm`) up to 200 MB. Over-cap or unsupported types fail with an error naming the limit.

<Note>
  Attachment download is implemented for Slack targets today; other providers' attachments surface as metadata only.
</Note>

### chat.react

Add an emoji reaction — the cheapest acknowledgement an agent can give.

| Parameter | Type                      | Notes                                                         |
| --------- | ------------------------- | ------------------------------------------------------------- |
| `emoji`   | string                    | Required (e.g. `"eyes"`).                                     |
| `message` | `{ threadId, messageId }` | Required — the message to react to, ids exactly as delivered. |
| `target`  | address                   | Optional when unambiguous.                                    |

Output: `{ added, emoji, messageId, threadId }`. Reactions are attributed to the agent's persona bot.

### chat.issue.get

Read issue metadata through a chat provider connection. Pass a `linear` target naming the issue:

```json theme={null}
{ "target": { "provider": "linear", "destination": { "issue": "ENG-142" } } }
```

Output is the full issue: `id`, `identifier`, `title`, `description`, `url`, `priority` + `priorityLabel`, `team`, `state`, `assignee`, `parent`/`children`, `project`, `labels`, timestamps.

### chat.issue.update

Mutate issue metadata on the same target shape:

| Parameter  | Type                      | Notes                                |
| ---------- | ------------------------- | ------------------------------------ |
| `target`   | address                   | The issue to update.                 |
| `assignee` | string \| null            | User to assign, or null to unassign. |
| `labels`   | `{ add: [], remove: [] }` | Label names to add/remove.           |
| `priority` | int 0–4 \| null           | Provider priority scale.             |
| `project`  | string \| null            | Project to set, or null to clear.    |
| `state`    | string                    | Workflow state name.                 |

Output returns the updated issue plus an `updated` report of which fields actually changed — an update that names an already-set state reports `state: false`.

## Threading semantics

The rules that make multi-turn conversations work:

1. **Thread ids are canonical and provider-prefixed.** Whatever a trigger delivery, `chat.send`, or `chat.history` gives you (`slack:…`, `discord:…`) is the id to pass back. Tools validate that a thread id matches its provider and reject mismatches.
2. **A reply is a send with `destination.thread` set.** To start a thread from a channel message, use that message's id as the thread root.
3. **Continuation is a binding, not magic.** Events route to sessions through bindings. A platform-delivered mention typically binds its thread to the spawned session automatically; an attributed `chat.send` that opens a new thread binds it too. For any other thread the agent wants follow-ups from, it calls [`auto.bind`](/runtime/auto-tools) with a `slack.thread` target (the target type covers both Slack and Discord threads). The agent must also declare a trigger for `chat.message.subscribed` (or `chat.message.mentioned`) events for the delivery to actually reach it — see [triggers](/reference/triggers).
4. **Sends and reactions are persona-attributed**, and messages authored by the agent itself are flagged in event payloads (`$.auto.authored`) so trigger filters can keep an agent from replying to itself.

## A complete example

The messaging heart of a conversational agent, adapted from a production configuration:

```yaml .auto/agents/assistant.yaml theme={null}
name: assistant
tools:
  auto:
    kind: local
    implementation: auto
  chat:
    kind: local
    implementation: chat
    auth:
      kind: connection
      provider: slack
      connection: slack
triggers:
  # First mention spawns a session and binds the thread to it.
  - event: chat.message.mentioned
    connection: slack
    where:
      $.auto.authored: false
      $.auto.attributions:
        exists: false
    message: |
      {{message.author.userName}} mentioned you in {{chat.channelId}}:
      {{message.text}}
      Reply in thread {{chat.threadId}} with chat.send.
    routing:
      kind: spawn
      bind:
        target: slack.thread
  # Later replies in bound threads deliver into the existing session.
  - events:
      - chat.message.mentioned
      - chat.message.subscribed
    connection: slack
    where:
      $.auto.authored: false
      $.auto.attributions:
        exists: true
    message: |
      {{message.author.userName}} replied in your thread:
      {{message.text}}
    routing:
      kind: deliver
      routeBy:
        kind: attributedSessions
      onUnmatched: drop
```

In a session spawned by that first trigger, the agent replies with:

```json theme={null}
{
  "message": "On it — checking the failing job now.",
  "target": {
    "provider": "slack",
    "destination": { "channel": "C0891B2K3", "thread": "slack:C0891B2K3:1720613024.001200" }
  }
}
```

and every later reply in that thread arrives as a new delivery to the same session.
