# Smithers OpenAPI Tools

> Smithers OpenAPI tools: turn an OpenAPI spec into AI SDK tools, with auth, filters, and observability.

---

## OpenAPI Tools

> Generate AI SDK tools from an OpenAPI spec. Auth, filtering, observability built in.

`createOpenApiTools` parses an OpenAPI 3.x spec and returns AI SDK tools, one per operation, with Zod schemas converted from the spec's JSON schemas.

<Note>API reference: [OpenAPI](/reference/openapi) lists every OpenAPI export and option, and links to source and tests.</Note>

```ts
import { ToolLoopAgent } from "ai";
import { openai } from "@ai-sdk/openai";
import { createOpenApiTools } from "smithers-orchestrator/openapi";

const tools = await createOpenApiTools("./petstore.json", {
  baseUrl: "https://api.petstore.example.com",
  auth: { type: "bearer", token: process.env.PETSTORE_TOKEN! },
});

const agent = new ToolLoopAgent({
  model: openai("gpt-5.6-terra"),
  tools,
});
```

## Options

```ts
type OpenApiToolsOptions = {
  baseUrl?: string;                  // overrides spec.servers[0].url
  headers?: Record<string, string>;  // merged into every request
  auth?:
    | { type: "apiKey"; name: string; in: "header" | "query"; value: string }
    | { type: "bearer"; token: string }
    | { type: "basic"; username: string; password: string };
  include?: string[];                // operationId allowlist
  exclude?: string[];                // operationId blocklist
  namePrefix?: string;               // prefix generated tool names
  operations?: Record<
    string,
    | false
    | {
        include?: boolean;           // false skips this operation
        name?: string;               // agent-facing tool name
        description?: string;        // agent-facing tool description
        responseExamples?: Array<{
          status?: string | number;
          description?: string;
          value: unknown;
        }>;
      }
  >;
};
```

Pass the spec itself as the first argument to `createOpenApiTools(input, options)`. `input` may be a parsed spec object, file path, URL, or raw OpenAPI text.

## Public API

| Export | Purpose |
|---|---|
| `createOpenApiTools(input, options?)` / `createOpenApiToolsSync(input, options?)` | Build a record of AI SDK tools keyed by operationId. |
| `createOpenApiTool(input, operationId, options?)` / `createOpenApiToolSync(...)` | Build one operation as a tool. |
| `listOperations(input)` | Return parsed operation metadata without creating tools. |
| `extractOperations(spec)` | Extract operations from an already parsed spec. |
| `loadSpecEffect(input)` | Load and parse a spec from object, path, URL, or raw text. |
| `loadSpecSync(input)` | Load and parse a spec from object, local file path, or raw text. It does not fetch URLs. |
| `jsonSchemaToZod(schema, spec, visited?)` / `buildOperationSchema(parameters, requestBody, spec)` | Lower-level schema conversion helpers. |

## CLI

Generate a reusable AI SDK tools module:

```bash
bunx smithers-orchestrator openapi generate ./api/openapi.yaml ./src/openapi-tools.js
```

The generated module imports `createOpenApiToolsSync`, creates the tools from the spec, and exports them as both `tools` and the default export.

```bash
bunx smithers-orchestrator openapi list ./api/openapi.yaml
```

`openapi list` previews every operationId, method, path, and summary. Useful for auditing what an agent will be able to call before wiring it up.

## What gets generated

For each operation:

- A Zod schema for the request body + path/query/header parameters.
- An `execute(args)` function that performs the HTTP call.
- The operation's `summary` / `description` becomes the tool description.

## Response handling

- JSON responses are parsed and returned as objects.
- Non-JSON responses are returned as strings.
- HTTP status codes are not special-cased by the tool factory; if the server returns a JSON or text error body, that body is returned to the agent.
- Request failures, fetch failures, and schema/tool execution exceptions are returned as `{ error: true, message, status: "failed" }` so the agent can react in its loop.

## Filtering

`include` / `exclude` accept arrays of `operationId`. If both are set, exclude wins. Useful for limiting an agent's surface area to a specific feature ("just the inventory endpoints"). `namePrefix` prefixes generated tool names without changing the operationIds used for filtering.

Use `operations` for per-operation curation keyed by the original `operationId`. Set an entry to `false` or `{ include: false }` to skip that endpoint, set `name` to rename the generated tool, set `description` to replace the spec summary/description with agent-facing instructions, and add `responseExamples` to append concrete response shapes to the tool description. This lets a connector expose a small curated set instead of dumping every endpoint as an agent-callable tool. `include` / `exclude` still use the original `operationId`, not the curated name.

## Observability

OpenAPI tool calls update the exported Effect metrics (`openApiToolCallsTotal`, `openApiToolCallErrorsTotal`, `openApiToolDuration`) and carry Effect log annotations/spans with the operation id, method, and path. The current tool factory does not emit `OpenApiToolCalled` onto the Smithers run event bus; that event variant is typed/categorized for future event-bus integration.

## Notes / Limitations

- Schema composition (`allOf`, `anyOf`, `oneOf`) is supported; converts to Zod unions/intersections.
- Nullable fields and defaults from the spec are preserved.
- Cookie parameters are ignored when generating tool input schemas and requests.
- JSON request bodies are the only request body media type generated today; non-JSON bodies such as form data, multipart uploads, and binary payloads are not encoded.
- Parameter serialization styles are not implemented. Path parameters are URL-encoded and substituted directly, and query parameters are sent with default `URLSearchParams` serialization.
- Swagger 2.0 objects are not supported as a first-class input format. Use OpenAPI 3.x specs with `openapi`, `paths`, and `info` fields.

---

## OpenAPI Tools Quickstart

> Folded into the opt-in OpenAPI fragment.

This material is now in the opt-in [`/llms-openapi.txt`](/llms-openapi.txt) fragment.
