<!-- Generated by `just docs` from proto/automaton/v1/router.proto. Edit the source, not this file. -->

# RouterService

Find the tool for a task by describing the task. You send what the person actually asked for; you get back a short ranked list of the actions that fit, each with a compact argument schema and a flag saying whether that person has connected the app it belongs to. Telling atmon afterwards whether the tool you picked was the right one is what improves the next answer.

Every call is a POST to `https://api.atmon.ai/automaton.v1.RouterService/<Call>` with a JSON body, and authenticates with `Authorization: Bearer <your project key>`. Field names in JSON are lowerCamelCase, so the field written `tool_slug` below is `toolSlug` on the wire. [How to call the API](./index.md) has the whole convention.

## Calls

| Call | Request | Response | Summary |
| --- | --- | --- | --- |
| `ResolveTools` | `ResolveToolsRequest` | `ResolveToolsResponse` | Resolves an intent to a small ranked slate of tools with schemas compacted for a model, and returns the resolution_id that identifies the decision. |
| `ReportOutcome` | `ReportOutcomeRequest` | `ReportOutcomeResponse` | Reports what happened under a resolution, which is what ranking learns from. |

### ResolveTools

Resolves an intent to a small ranked slate of tools with schemas compacted
for a model, and returns the resolution_id that identifies the decision.
This is what the MCP search_tools meta-tool answers with. Tools the
project's entity visibility hides never reach the slate, and by default
neither do tools of toolkits the entity has not connected
(include_unconnected).

Request `ResolveToolsRequest`, response `ResolveToolsResponse`.

```http
POST /automaton.v1.RouterService/ResolveTools HTTP/1.1
Host: api.atmon.ai
Authorization: Bearer amk_your_project_key
Content-Type: application/json

{
  "entityId": "...",
  "intent": "...",
  "contextMessages": ["..."],
  "toolkitFilter": ["..."],
  "maxTools": 0,
  "includeUnconnected": true
}
```

The response:

```json
{
  "matches": [{
    "toolSlug": "...",
    "score": 0.0,
    "compactInputSchemaJson": "{}",
    "connected": true
  }],
  "resolutionId": "...",
  "connectionScoped": true
}
```

### ReportOutcome

Reports what happened under a resolution, which is what ranking learns
from. Two limits bound it: the named call must have run one of that
resolution's own matches, and a resolution is reportable exactly once,
claimed atomically so the first report wins.

Request `ReportOutcomeRequest`, response `ReportOutcomeResponse`.

```http
POST /automaton.v1.RouterService/ReportOutcome HTTP/1.1
Host: api.atmon.ai
Authorization: Bearer amk_your_project_key
Content-Type: application/json

{
  "resolutionId": "...",
  "toolCallId": "...",
  "outcome": "OUTCOME_SUCCESS",
  "detail": "..."
}
```

The response:

```json
{}
```

## Messages

### ReportOutcomeRequest

| Field | Type | # | Notes |
| --- | --- | --- | --- |
| `resolution_id` | `string` | 1 |  |
| `tool_call_id` | `string` | 2 | empty when no call was made |
| `outcome` | `Outcome` | 3 |  |
| `detail` | `string` | 4 |  |

### ReportOutcomeResponse

No fields. The call takes its scope from the authenticated project.

### ResolveToolsRequest

| Field | Type | # | Notes |
| --- | --- | --- | --- |
| `entity_id` | `string` | 1 |  |
| `intent` | `string` | 2 | natural-language statement of what the agent needs |
| `context_messages` | repeated `string` | 3 | recent turns, most recent last |
| `toolkit_filter` | repeated `string` | 4 | empty means the request's whole search scope |
| `max_tools` | `int32` | 5 |  |
| `include_unconnected` | `bool` | 6 | Search the whole catalog instead of the entity's connected surface. By default a resolve searches the toolkits this entity has an active connection for, plus the project's own private toolkits, because a tool the entity cannot call is not an answer. Set this to browse what the project could connect next; the matches then carry connected = false. The default does nothing when the entity has connected nothing: an entity with no connections is looking for what to connect, so its resolve searches the whole catalog either way. |

### ResolveToolsResponse

| Field | Type | # | Notes |
| --- | --- | --- | --- |
| `matches` | repeated `ToolMatch` | 1 |  |
| `resolution_id` | `string` | 2 | echo in ReportOutcome to close the loop |
| `connection_scoped` | `bool` | 3 | True when the search was bounded to the entity's connected surface. False means the whole catalog was searched, either because include_unconnected was set or because the entity has connected nothing yet. |

### ToolMatch

| Field | Type | # | Notes |
| --- | --- | --- | --- |
| `tool_slug` | `string` | 1 |  |
| `score` | `double` | 2 |  |
| `compact_input_schema_json` | `string` | 3 | trimmed for this context |
| `connected` | `bool` | 4 | entity holds an active connection for its toolkit |

## Enums

### Outcome

| Value | # | Meaning |
| --- | --- | --- |
| `OUTCOME_UNSPECIFIED` | 0 |  |
| `OUTCOME_SUCCESS` | 1 |  |
| `OUTCOME_EXECUTION_ERROR` | 2 | right tool, call failed |
| `OUTCOME_WRONG_TOOL` | 3 | agent had to re-route after seeing the result |
| `OUTCOME_NO_TOOL_FOUND` | 4 | nothing returned matched the intent |
