> 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.

# Connector authoring

Every AgentRouter tool is a **connector definition**: a reviewed description of what a provider endpoint accepts, how the call is built, how the raw response becomes an outcome, and where the usage and cost evidence is. The hosted platform, the HTTP API, the CLI, MCP and the website all read the same generated catalog, so adding a tool never requires changes to those clients.

> **Note**
>
> Connector definitions live in the AgentRouter connectors repository. It is private while the first providers are reviewed; a public repository will be created from reviewed sources before launch. Contributing a connector does not come with an AgentRouter wallet, production keys or free upstream usage.

## What is public and what is not

| In the connector (reviewable)                       | Kept private by AgentRouter               |
| --------------------------------------------------- | ----------------------------------------- |
| Input schema, examples, output description          | Purchase rates and invoices               |
| Request mapping and allowed hosts                   | Price multipliers, promotions and budgets |
| Result rules: success, business errors, legal empty | Provider account credentials              |
| Usage evidence and the provider's published price   | Enablement, spend caps and customer data  |
| Async polling, timeouts, idempotency                | Reconciliation reports                    |

## What you write

One directory per provider; Definitions are written in TypeScript, the shared category registry is JSON, and the catalog is generated.

```text
connectors/<provider>/
  provider.ts   identity, official docs, allowed hosts, auth (variable names only)
  tools.ts      tool definitions (or tools/<name>.ts for many)
  fixtures/     sanitized recordings of real calls
  README.md     how to get a key and what you verified
```

| Part         | Fill in                                                                                                                                                                                                    |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Identity     | `id` = `<provider>.<tool>`, `version` = `YYYY-MM-DD.N`, title, description, tags, use cases                                                                                                                |
| Contract     | `inputSchema` (JSON Schema), output description, at least one example input                                                                                                                                |
| Request      | A declarative template (`{ "$input": "field" }`, `{ "$const": value }`, `{ "$context": "idempotencyKey" }`); a small pure `build()` only when a template cannot express it                                 |
| Result rules | Success statuses, errors returned with HTTP 200, the legal-empty condition, where the output and upstream request id are                                                                                   |
| Metering     | `upstream_usd` when the provider reports dollars, `units` for credits or requests; where actual usage is read; what errors cost; a trusted upper bound before the call; whether an empty result is charged |
| Execution    | Sync or async, timeouts, the poll request, and whether resending is safe (provider-side idempotency)                                                                                                       |

## Rules

* No secret values anywhere. Definitions name variables (`EXA_API_KEY`); the transport injects values and only reaches `allowedHosts`.
* Definitions never set `Authorization`, cookies or API-key headers themselves.
* Functions are pure: no network, environment, filesystem or clock access, no customer pricing, no `eval`.
* Usage comes from the raw provider response. If a case is undocumented, leave it unknown — never write `0` for "probably free".
* A provider error is never billable to the customer. You may propose more waivers, such as empty results; AgentRouter reviews them.
* Any change to a published tool bumps its `version`; the catalog lock turns a silent change into a validation error.

## Steps

1. Copy `templates/provider/` to `connectors/<provider>/`, or generate a candidate:

   * from an OpenAPI operation: `agentrouter-connectors import openapi --spec <url-or-file> --operation "POST /search" --provider acme --tool search`
   * from an AnyAPI catalog SKU: `agentrouter-connectors import anyapi --sku twitter.search`

   Imported candidates are starting points: the importer cannot know billing, business errors or async behaviour. Fill those in from the provider's docs.
2. Register the provider in `connectors/index.ts`.
3. Make a real call with **your own** key; only the variables your provider declares are read, and the runner refuses inputs whose upper bound exceeds `--max-cost-usd`:

   ```bash
   npm install && npm run build
   node dist/tools/cli.js run acme.search --input '{"query":"test"}' --env-file .env --usd-per-unit 0.001 --max-cost-usd 0.01
   ```
4. Record sanitized fixtures for a normal result and each case that changes billing (empty, provider error) with `--record <name>`. Check them for keys, balances and private data before committing.
5. `node dist/tools/cli.js catalog lock`, then `npm run check` (types, build, validation, fixture replay, tests).
6. Open a pull request with the definitions, fixtures, and links to the official API and pricing pages you used.

## From pull request to live

| State     | Meaning                                                                               | Who         |
| --------- | ------------------------------------------------------------------------------------- | ----------- |
| submitted | Pull request open                                                                     | Contributor |
| reviewed  | Contract, host and auth boundary, and metering evidence checked; merged               | AgentRouter |
| verified  | Re-run with AgentRouter's own provider account; cost reconciled within a small budget | AgentRouter |
| enabled   | Purchase rate, price multiplier and budgets configured; listed in `find`              | AgentRouter |

A merge is not a listing and not a purchase commitment. CI for external pull requests has no provider secrets and makes no paid calls. The hosted platform pins a reviewed connector version by commit and catalog digest, and refuses to start if they do not match.

## Keeping connectors current

`agentrouter-connectors drift` compares imported contracts with the provider's current catalog or OpenAPI document and reports changed inputs, outputs or prices, so a provider change becomes a reviewed version bump instead of a silent break.

## Not ready to write a connector?

Request the tool instead: `POST /v1/feedback` with `kind: tool_request`, the `request_tool` MCP tool, `agentrouter tools request`, or [Add your API](https://www.agentrouter.to/add-your-api) on the website.

## Catalog categories

Choose second-level IDs from `contract/taxonomy.json`. Set `provider.defaultCategoryIds` for a common default; a tool can omit `categoryIds` to inherit or provide a complete replacement array. For example Hunter defaults to `["contact-data"]`, with company enrichment overriding to `["company-data"]`.

Website/product names and capabilities are both categories: an X tool can use `["x"]`, while a LinkedIn profile can use `["linkedin", "people-data"]`. There is no separate platform field. Category `parentIds` point only to top-level groups such as Social and GTM. Keep actions like comments or transcripts in tool names, tags and descriptions.

Add a missing category and its parent references in the same PR as the connector. Unknown IDs, empty final categories and third-level nesting prevent publication. Importers leave a draft classification for review. Default changes affect tool hashes: bump affected tool versions and regenerate the catalog lock.