# OpenAPI Import

OpenAPI import onboards a REST service as a set of federated MCP tools without writing a native MCP
server. One OpenAPI operation becomes one MCP tool.

## Import a document

Import takes the spec from a public `specUrl` (a link) or from `specText` (the uploaded document
contents) — exactly one of the two. It does not accept an inline OpenAPI object.

```bash
curl "$PROVON_API_URL/projects/$PROJECT_ID/mcp/openapi/import" \
  -H "Authorization: Bearer $PROVON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "displayName": "Acme CRM",
    "specText": "{\"openapi\":\"3.0.0\",\"paths\":{\"/contacts/{id}\":{\"get\":{\"operationId\":\"getContact\",\"summary\":\"Get a contact\"}}}}"
  }'
```

Or point at a public document instead of uploading its text:

```bash
curl "$PROVON_API_URL/projects/$PROJECT_ID/mcp/openapi/import" \
  -H "Authorization: Bearer $PROVON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "displayName": "Acme CRM", "specUrl": "https://api.acme.example/openapi.json" }'
```

The body is `{ "displayName"?: string, "specUrl"?: string, "specText"?: string }` with exactly one
of `specUrl` or `specText`. The route creates an `openapi`-source server and one `draft` tool per
operation, and returns the server record plus a list of tool records. A `specUrl` document must be
reachable over HTTP(S), stays under the 10 MB import cap, and allows at most five redirect hops
during download.

Imported operations land as drafts on purpose: they are not discoverable or callable until you
explicitly publish them (see [Managing imported tools](#managing-imported-tools) below). A bulk
import cannot silently expose every REST operation at once.

Import is normalization only — it does not call the upstream service — so it works on both the
self-hosted Node runtime and the hosted edge.

## How operations become tools

For every `path` and HTTP method (`get`, `put`, `post`, `delete`, `options`, `head`, `patch`,
`trace`) that defines an operation:

- **Tool name** is the normalized `operationId`, or `METHOD /path` when there is no `operationId`.
  Names are lowercased and reduced to `[a-z0-9_-]`: other characters become `_`, repeated
  separators collapse, surrounding separators trim, and the result caps at 128 characters; an empty
  result falls back to `tool`.
- **Title** is the operation `summary`; **description** is the operation `description`.
- **Input schema** is built from the operation's path and query parameters plus the
  `application/json` request-body schema. Required parameters are flagged `required`.

The stored tool keeps its `httpMethod` and `httpPath` template.

## How arguments map to a REST call

When the tool gateway invokes an imported tool:

1. Path parameters in the template (for example `/contacts/{id}`) are substituted from the matching
   argument, URI-encoded.
2. For `GET` and `HEAD`, the remaining arguments become query parameters.
3. For `POST`, `PUT`, `PATCH`, and other write methods, the remaining arguments become the JSON
   request body (`Content-Type: application/json`).

The sealed upstream credential is injected as `Authorization: Bearer <secret>` server-side.
Inbound headers are never forwarded, tool-call redirects are not followed, and private or loopback
hosts are blocked — see [Security](./security.md#transport-hardening).

## Managing imported tools

- Re-importing or re-syncing upserts tools by normalized name. Newly discovered operations land as
  `draft`; existing tools keep their publish state.
- Tools no longer present upstream are archived on sync, so the tool gateway stops invoking them.
- Imported tools start as `draft`. Publish the operations you want discoverable:

```bash
curl -X PATCH "$PROVON_API_URL/projects/$PROJECT_ID/mcp/servers/$SERVER_ID/tools/$TOOL_ID" \
  -H "Authorization: Bearer $PROVON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "publishState": "published" }'
```

## Success checks

1. The import response lists one tool per expected operation, each in `draft`.
2. `tools/list` on the federation connection shows nothing until a tool is published and an allow
   policy covers the caller.
3. After publishing, `tools/list` returns the tool with its input schema, and `tools/call` returns
   the upstream response; a missing allow policy surfaces as `-32001`.
4. Re-running sync after an upstream spec change keeps published tools intact and archives removed
   operations.
