> To find and run AgentRouter tools, connect to https://mcp.agentrouter.to/. To search this documentation, use https://docs.agentrouter.to/_mcp/server. Append .md to a page URL for Markdown. For the documentation index, see https://docs.agentrouter.to/llms.txt.

# Find, describe, invoke

| Step     | HTTP                | MCP tool        | CLI                                      | Charged              |
| -------- | ------------------- | --------------- | ---------------------------------------- | -------------------- |
| Find     | `POST /v1/find`     | `find_tools`    | `agentrouter find "<task>"`              | No                   |
| Describe | `POST /v1/describe` | `describe_tool` | `agentrouter describe <tool_id>`         | No                   |
| Invoke   | `POST /v1/invoke`   | `invoke_tool`   | `agentrouter invoke <tool_id> --input …` | Per the tool's rules |

Find and describe never execute a provider call and work without a key. Invoke needs a credential with the `invoke` scope.

## Tools are addressed by `tool_id`

A tool id names one provider endpoint, for example `anyapi.twitter.search` or `hunter.email_verifier`. There are no domains, capabilities or routes to resolve first, and AgentRouter does not silently switch providers for you: the agent chooses the tool, and the receipt names the provider that ran.

## Find

```json
POST /v1/find
{ "query": "verify an email address", "limit": 5 }
```

* `query` is a short task description (up to 500 characters). Matching is by keywords over tool ids, titles, tags, descriptions, use cases, and category/group names and aliases.
* Optional filters: `provider`, `category_ids` (any), `group_ids` (any), `tags` (all must match), `include_unavailable` (tools in an outage are hidden by default).
* `match` is `exact`, `partial` or `none`. With `none`, record the gap with [feedback](/concepts/feedback-and-disputes) (`kind: tool_request`) instead of forcing an unrelated tool.
* Each candidate includes `required_inputs`, `price` (summary, `price_multiplier`, `free`) and `health` (`unknown` means too few recent runs, not unavailable).

## Browse the two-level catalog

`GET /v1/catalog` returns enabled tools plus `groups` and `categories`. Groups are top-level themes such as Social and GTM. Categories are second-level entries such as LinkedIn, Google Search, People Profiles or Web Search. There is no separate platform field. A category's `parent_ids` refer only to groups.

Tools include `category_ids` and derived `group_ids` in catalog, find and describe responses. A tool may appear in more than one category; `tool_count` counts each tool once per category/group. Empty entries are omitted. GET filters use comma-separated IDs, for example `/v1/catalog?category_ids=linkedin,people-data&provider=anyapi`. Filters from different dimensions intersect.

```json
POST /v1/find
{ "query": "person profile", "category_ids": ["linkedin"], "group_ids": ["gtm"] }
```

CLI: `agentrouter find "person profile" --category linkedin --group gtm`. MCP `find_tools` uses the same `category_ids` and `group_ids` arrays. Tags remain search keywords; they do not create categories or another navigation level.

## Describe

```json
POST /v1/describe
{ "tool_id": "hunter.email_verifier" }
```

The response is the tool's contract:

| Field                    | Use it for                                                                                                                                                                                         |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `input_schema`           | The JSON Schema `input` must match. Invalid input is rejected with `INPUT_INVALID` before anything runs.                                                                                           |
| `examples`               | Known-good inputs.                                                                                                                                                                                 |
| `limits`, `pagination`   | Maximum results per call and how to request the next page.                                                                                                                                         |
| `execution`              | `mode` (`sync` or `async`), `stoppable`, `max_duration_ms`.                                                                                                                                        |
| `pricing`                | `pricing_version`, `price_multiplier`, `max_per_call_usd` or the rule that sets the maximum, the provider's published price, and the charge `rules` for errors, empty results, timeouts and stops. |
| `health`, `alternatives` | Recent success and latency, and related tools.                                                                                                                                                     |

Health covers the full seven-day window in `health.window`, using each run's finish time. `samples` is the number of completed, failed and timed-out logical calls; blocked requests and cancellations are excluded. `success_rate` is completed calls divided by `samples`, including legitimate empty results as completed calls.

`health.latency` reports exact nearest-rank p50 and p95 execution times for successful calls only. Its own `samples` excludes missing or invalid durations. Execution time includes asynchronous processing and polling waits, but excludes final settlement and delivery to the client. The flat `p50_ms` and `p95_ms` fields remain as aliases of these successful-call percentiles.

Health is calculated from stored runs on demand and cached for 60 seconds. `computed_at` and the window bounds identify the snapshot; reading health never calls the provider. Empty-result detection and the legacy `empty_rate` remain endpoint-specific.

You can reuse a describe result for many calls. Describe again when invoke reports `PRICING_CHANGED` or `TOOL_VERSION_CHANGED`.

## Invoke

```json
POST /v1/invoke
Idempotency-Key: research-7-step-3
{
  "tool_id": "hunter.email_verifier",
  "pricing_version": "<from describe>",
  "input": { "email": "someone@example.com" },
  "max_cost_usd": "0.05",
  "session_id": "research-7"
}
```

| Field                    | Required    | Meaning                                                                                                                                                                                              |
| ------------------------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tool_id`                | yes         | The tool to run.                                                                                                                                                                                     |
| `pricing_version`        | yes         | The price terms you accepted in describe. If the price changed since then, nothing runs and you get `PRICING_CHANGED`.                                                                               |
| `input`                  | per schema  | Matches `input_schema`; an object or an array.                                                                                                                                                       |
| `max_cost_usd`           | no          | The most this call may cost (default `"0.50"`, lowered by budgets). If the tool's maximum for this input is higher, the run is `BLOCKED` with `MAX_COST_EXCEEDED` and a hint with the amount needed. |
| `session_id`             | no          | Groups the runs of one agent task; session budgets apply.                                                                                                                                            |
| `tool_version`           | no          | Pins a version; a change returns `TOOL_VERSION_CHANGED`.                                                                                                                                             |
| `Idempotency-Key` header | recommended | Makes retries safe. Same key and request → the original run (`replayed: true`, no new charge). Same key, different request → `IDEMPOTENCY_KEY_CONFLICT`.                                             |

Clients cannot send prices or multipliers; a request that includes them is rejected with `PRICE_NOT_CLIENT_SETTABLE`.

Invoke returns a [Run](/concepts/runs). HTTP `200` means it finished (completed, failed, stopped or timed out), `202` means it is still running on the server, `402`/`409` mean it was blocked before execution and not charged.

## Choosing between tools

When several tools fit, the agent decides within the task and the user's spending rules: compare `fit`, the inputs you already have, `max_per_call_usd` and health. Do not stop just to ask the user to choose a provider. Respect a tool or entry the user named, and tools the user already has for the same job.