# Import OpenAPI providers

> `ati provider import-openapi` and `inspect-openapi`: spec download, operation caps, tag filters, `x-ati-param-location` routing, and keyring key hints.

- Repository: Parcha-ai/ati
- GitHub: https://github.com/Parcha-ai/ati
- Human docs: https://grok-wiki.com/public/docs/parcha-ai-ati-a9d4398f11fa
- Complete Markdown: https://grok-wiki.com/public/docs/parcha-ai-ati-a9d4398f11fa/llms-full.txt

## Source Files

- `src/cli/provider.rs`
- `src/core/openapi.rs`
- `manifests/finnhub.toml`
- `specs/finnhub.json`
- `specs/crossref.json`
- `src/core/http.rs`

---

---
title: "Import OpenAPI providers"
description: "`ati provider import-openapi` and `inspect-openapi`: spec download, operation caps, tag filters, `x-ati-param-location` routing, and keyring key hints."
---

`ati provider import-openapi` downloads an OpenAPI 3.x spec (JSON or YAML) into `~/.ati/specs/`, writes an `handler = "openapi"` manifest under `~/.ati/manifests/`, and relies on `ManifestRegistry` to auto-register tools at load time. `ati provider inspect-openapi` previews the same spec without writing files. At runtime, `core/openapi.rs` injects `x-ati-param-location` into each tool’s input schema and `core/http.rs` routes arguments to path, query, header, or body.

## Commands

| Command | Purpose |
|---------|---------|
| `ati provider inspect-openapi <spec>` | Print title, base URL, detected auth, and operations grouped by tag |
| `ati provider import-openapi <spec>` | Download spec, generate manifest, save both under `~/.ati/` |

<ParamField body="spec" type="string" required>
Path or URL to an OpenAPI 3.x document (`.json`, `.yaml`, or `.yml`).
</ParamField>

<ParamField body="--name" type="string">
Provider name. When omitted, derived from the URL host or file stem (for example `https://api.finnhub.io/...` → `finnhub`, `finnhub.json` → `finnhub`).
</ParamField>

<ParamField body="--auth-key" type="string">
Keyring entry name for API credentials. Default: `{name}_api_key` (for example `finnhub_api_key`).
</ParamField>

<ParamField body="--include-tags" type="string[]">
Repeatable. Restrict which operations are counted during import and which tag filter is written into the manifest. During inspect, filters the printed operation list only.
</ParamField>

<ParamField body="--dry-run" type="boolean">
Print the generated manifest and paths that would be written; do not create files.
</ParamField>

<Note>
`--auth-key` is only available on `import-openapi`, not `inspect-openapi`.
</Note>

### Spec input: URL vs local file

`read_spec_content` in `cli/provider.rs` handles both:

- **URL**: SSRF validation via `validate_url_not_private`, 30s timeout, redirects disabled (a 3xx returns an error naming the `Location` target). HTTP URLs log an insecure-transport warning.
- **Local path**: read directly from disk.

At **registry load** time, `openapi::load_spec` does not fetch URLs. The spec must already live under `~/.ati/specs/` (typically from `import-openapi`). A manifest pointing at a bare URL without a local copy fails with guidance to import first.

## Workflow

<Steps>
<Step title="Inspect the spec">
Preview operations and auth before committing files.

```bash
ati provider inspect-openapi https://petstore3.swagger.io/api/v3/openapi.json
ati provider inspect-openapi ./specs/crossref.json --include-tags Members
```

Output includes API title/version, first `servers[0].url`, detected `auth_type` plus `auth_header_name` or `auth_query_name` when applicable, and operations grouped by OpenAPI tag.
</Step>

<Step title="Import (or dry-run)">
Generate the manifest and copy the normalized spec.

```bash
ati provider import-openapi https://finnhub.io/api/v1/openapi.json --name finnhub
ati provider import-openapi https://api.crossref.org/swagger.json --dry-run
```

Writes:

- `~/.ati/specs/{name}.json` — pretty-printed parsed spec
- `~/.ati/manifests/{name}.toml` — provider block only (no hand-written `[[tools]]`)
</Step>

<Step title="Store credentials">
When `auth_type` is not `none`, import logs a hint:

```bash
ati key set finnhub_api_key <your-token>
```

The manifest sets `auth_key_name` to your `--auth-key` value or `{name}_api_key` by default.
</Step>

<Step title="Verify tools">
```bash
ati provider list
ati tool list | grep finnhub
ati tool info finnhub:quote
```

OpenAPI tools are named `{provider}:{operationId}` (colon separator). If `operationId` is missing, ATI generates one from method and path (for example `get_pet_petId`).
</Step>
</Steps>

### Ephemeral alternative: `ati provider load`

`ati provider load <spec> --name <name>` caches the provider under `~/.ati/cache/providers/` for a TTL without writing permanent manifests. Use `--save` to delegate to `import-openapi`. See [Execution modes](/execution-modes) for when cached vs permanent providers apply.

## Generated manifest

`import-openapi` builds a minimal `[provider]` block:

| Field | Source |
|-------|--------|
| `name` | `--name` or derived |
| `description` | `info.title` from the spec |
| `handler` | always `openapi` |
| `base_url` | first entry in `servers` |
| `openapi_spec` | `{name}.json` (filename under `~/.ati/specs/`) |
| `auth_type` | first `components.securitySchemes` entry |
| `auth_key_name` | set when auth ≠ `none` |
| `auth_header_name` / `auth_query_name` | from API key scheme location |
| `openapi_include_tags` | only if `--include-tags` was passed |

<Warning>
Import does **not** set `openapi_max_operations`, `openapi_exclude_tags`, `openapi_include_operations`, or `openapi_exclude_operations`. Add those manually after import when you need tighter control over large specs.
</Warning>

### Example: Finnhub (auth + cap)

Bundled `manifests/finnhub.toml` shows post-import tuning:

```toml
[provider]
name = "finnhub"
handler = "openapi"
base_url = "https://finnhub.io/api/v1"
openapi_spec = "finnhub.json"
auth_type = "query"
auth_query_name = "token"
auth_key_name = "finnhub_api_key"
openapi_max_operations = 50
```

### Example: Crossref (no auth)

`manifests/crossref.toml` is a no-credential OpenAPI provider:

```toml
[provider]
name = "crossref"
handler = "openapi"
base_url = "https://api.crossref.org"
openapi_spec = "crossref.json"
auth_type = "none"
```

## Auth detection

`openapi::detect_auth` reads `components.securitySchemes` and maps the **first** scheme to ATI auth:

| OpenAPI scheme | `auth_type` | Extra manifest fields |
|----------------|-------------|------------------------|
| HTTP `bearer` | `bearer` | — |
| HTTP `basic` | `basic` | — |
| API key in header | `header` | `auth_header_name` |
| API key in query | `query` | `auth_query_name` |
| OAuth2 client credentials | `oauth2` | `oauth2_token_url` |
| API key in cookie | `none` | — |
| OpenID Connect | `none` | manual configuration required |

Cookie and OpenID schemes fall through to `none`; adjust the manifest by hand when the spec’s security does not match a supported mapping.

## Operation filters and caps

Filters apply in `openapi::extract_tools` when `ManifestRegistry` loads the provider (and when import counts operations for dry-run output). Order of evaluation:

1. `openapi_include_operations` — if non-empty, only listed `operationId`s pass
2. `openapi_exclude_operations`
3. `openapi_include_tags` — if non-empty, operation must have at least one matching tag
4. `openapi_exclude_tags` — drop if any tag matches
5. Skip `multipart/form-data` request bodies
6. `openapi_max_operations` — truncate to the first N tools after the above filters

<Info>
`openapi_max_operations` keeps the first N operations in spec iteration order. Because paths are stored in a hash map, that order can vary between runs—prefer tag or operation ID filters when you need deterministic subsets.
</Info>

`--include-tags` on import only persists `openapi_include_tags` in the manifest; exclude lists and caps are manifest-only fields documented in [Manifest reference](/manifest-reference).

### HTTP method mapping

OpenAPI `patch` maps to ATI `PUT` because `HttpMethod` has no PATCH variant. Unresolved `$ref` path items are skipped.

## Tool registration at load time

```mermaid
flowchart LR
  subgraph ati_home ["~/.ati"]
    M["manifests/{name}.toml"]
    S["specs/{name}.json"]
  end
  MR["ManifestRegistry::load"]
  OAP["openapi::load_and_register"]
  IDX["tool_index provider:operationId"]
  M --> MR
  S --> OAP
  MR --> OAP
  OAP --> IDX
```

On load, `openapi_spec` resolves relative to `~/.ati/specs/`. Each operation becomes a `Tool` with `endpoint` = path template, `method` from the spec, and `input_schema` built by `build_input_schema_with_locations`. Failures to read or parse the spec log a warning and leave the provider with zero tools (graceful degradation).

## `x-ati-param-location` routing

OpenAPI import does not store location metadata in the raw JSON on disk. Metadata is injected when tools are built:

| OpenAPI parameter `in` | Schema property metadata |
|------------------------|---------------------------|
| `path` | `"x-ati-param-location": "path"` |
| `query` | `"x-ati-param-location": "query"` (+ optional `x-ati-collection-format` for arrays) |
| `header` | `"x-ati-param-location": "header"` |
| `cookie` | treated as `query` |
| Request body properties | `"x-ati-param-location": "body"` |

Additional schema keys:

- `x-ati-body-encoding: "form"` when the body uses `application/x-www-form-urlencoded`
- `x-ati-collection-format`: `multi`, `csv`, `ssv`, or `pipes` for array query parameters (from OpenAPI `style` / `explode`)

At call time, `http::classify_params` reads these properties. If **no** property has `x-ati-param-location`, ATI falls back to **legacy** routing: GET/DELETE → all args as query string; POST/PUT → JSON body. Hand-written HTTP manifests use legacy mode unless you add the extension yourself.

```text
ati run finnhub:quote --symbol AAPL
        │
        ▼
classify_params (path / query / header / body)
        │
        ├─ path  → substitute {symbol} in /quote/...
        ├─ query → ?symbol=AAPL  (+ auth query param from keyring)
        ├─ header→ request headers
        └─ body  → JSON or form body
```

Path values are percent-encoded; values containing `..`, `?`, `#`, or NUL are rejected.

## Per-operation overrides

After import, tune individual tools under `[provider.openapi_overrides.<operationId>]`:

- `hint`, `tags`, `description`, `scope`
- `response_extract`, `response_format` (`markdown_table`, `json`, `raw`)

Overrides merge at `to_ati_tool` time; they do not change HTTP routing.

## Keyring key hints

| Situation | Key name | CLI |
|-----------|----------|-----|
| Default import with auth | `{name}_api_key` | `ati key set {name}_api_key <secret>` |
| Custom name | `--auth-key my_key` | `ati key set my_key <secret>` |
| Query auth (Finnhub) | value injected as `auth_query_name` (e.g. `token`) | same keyring entry as `auth_key_name` |
| Header auth | value sent as `auth_header_name` | same keyring entry |
| No auth (`crossref`) | no `auth_key_name` in manifest | none |

`ati provider load` checks the keyring and returns `needs_auth` with `setup_commands` when the key is missing.

## Troubleshooting

| Symptom | Likely cause | Action |
|---------|--------------|--------|
| `URL specs must be downloaded first with ati provider import-openapi` | Manifest references a URL, not a local file | Run import, or set `openapi_spec` to `{name}.json` under `~/.ati/specs/` |
| Provider listed, zero tools | Spec missing or parse error at load | Confirm `~/.ati/specs/{name}.json` exists; check logs with `--verbose` |
| `SSRF protection` on import | URL targets private/reserved space | Use a public HTTPS spec URL or a local file |
| Redirect error on download | Spec URL returns 3xx | Fetch the final URL yourself and import the file |
| Too many tools | Large spec | Add `openapi_max_operations`, tag filters, or operation include/exclude lists to the manifest |
| PATCH behaves like PUT | By design | Expect PUT semantics for OpenAPI PATCH operations |
| File-upload operations missing | `multipart/form-data` skipped | Use a hand-written HTTP tool or a different endpoint |

## Related pages

<CardGroup>
<Card title="Quickstart" href="/quickstart">
Import a no-auth provider, store a key, and run your first `ati run`.
</Card>
<Card title="Providers and handlers" href="/providers-and-handlers">
How `openapi` fits alongside `http`, `mcp`, and other handlers.
</Card>
<Card title="Manifest reference" href="/manifest-reference">
Full OpenAPI provider TOML fields, overrides, and validation.
</Card>
<Card title="OpenAPI stock research workflow" href="/openapi-stock-research">
End-to-end Finnhub recipe after import.
</Card>
<Card title="Configure JWT and keys" href="/configure-jwt-and-keys">
`ati key set` and scoped tokens for proxy mode.
</Card>
</CardGroup>
