Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions content/docs/automation/connectors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,25 @@ The document must be **OpenAPI 3.x**. A Swagger 2.0 document — or one with no
document, URL or file it came from: convert it to OpenAPI 3.x offline and point
`spec` at the converted document. The runtime never converts.

The ObjectStack repository carries that offline step as a script (it is not
shipped in the npm package): `normalize-openapi` reads a file or URL, JSON or
YAML, converts Swagger 2.0 to OpenAPI 3.0, bundles and inlines every `$ref`,
makes `servers[0].url` absolute, writes one JSON document and prints its
sha256 — and, given allow-patterns, the `include` list for the operations they
match.

```bash
pnpm --filter @objectstack/connector-openapi normalize-openapi \
--in https://example.com/openapi.yaml --out ./specs/crm-openapi.json \
--include-pattern '^GET /v1/customers'
```

Inlining is bounded: a recursive schema, or one more than `--max-depth`
(default 2) named schemas deep, becomes a typed stub whose description names
the reference it replaced. A Swagger 2.0 operation whose empty `consumes` list
would drop its request body is refused until `--empty-consumes <media type>`
says which media type it accepts.

### `provider: 'mcp'` — one action per tool

Config: **`transport`** (required) and **`include`** (optional tool-name
Expand Down
5 changes: 4 additions & 1 deletion packages/connectors/connector-openapi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@
"build": "tsup --config ../../../tsup.config.ts && node ../../../scripts/check-dts-emitted.mjs",
"check:test-typecheck": "tsx ../../../scripts/check-test-typecheck.mts --self-test && tsx ../../../scripts/check-test-typecheck.mts --package packages/connectors/connector-openapi --project tsconfig.test.json",
"gen:test-typecheck-debt": "tsx ../../../scripts/check-test-typecheck.mts --update --package packages/connectors/connector-openapi --project tsconfig.test.json",
"normalize-openapi": "tsx scripts/normalize-openapi.mts",
"test": "vitest run --passWithNoTests",
"typecheck": "tsc --noEmit && pnpm check:test-typecheck"
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json && pnpm check:test-typecheck"
},
"dependencies": {
"@objectstack/core": "workspace:*",
"@objectstack/spec": "workspace:*"
},
"devDependencies": {
"@objectstack/service-automation": "workspace:*",
"@scalar/json-magic": "^0.17.0",
"@scalar/openapi-upgrader": "^0.4.6",
"@types/node": "^26.6.5",
"typescript": "^6.0.3",
"vitest": "^4.1.11"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# The second file of the openapi3-billing-shape fixture: reached only by an
# external $ref, so the bundler has to load it.
components:
schemas:
balance:
type: object
properties:
available:
type: array
items:
$ref: '#/components/schemas/money'
money:
type: object
properties:
amount:
type: integer
currency:
type: string
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Shaped like Stripe's spec3 document, trimmed: OpenAPI 3.0 in YAML, component
# schemas that refer to each other (customer <-> subscription is recursive), a
# vendor extension carrying $refs, a shared default error response, a shared
# parameter, and one schema in a second file reached by an external $ref.
openapi: 3.0.0
info:
title: Billing API (trimmed fixture)
version: '2026-10-01'
servers:
- url: https://api.example.com/
paths:
/v1/customers:
get:
operationId: GetCustomers
summary: List customers
parameters:
- $ref: '#/components/parameters/limit'
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/customer'
default:
$ref: '#/components/responses/error'
post:
operationId: PostCustomers
summary: Create a customer
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/customer_create'
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/customer'
default:
$ref: '#/components/responses/error'
/v1/customers/{customer}:
get:
operationId: GetCustomersCustomer
parameters:
- name: customer
in: path
required: true
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/customer'
/v1/balance:
get:
summary: Retrieve the balance (no operationId -- keyed get_v1_balance)
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: './billing-shared.yaml#/components/schemas/balance'
components:
parameters:
limit:
name: limit
in: query
required: false
schema:
type: integer
responses:
error:
description: Error response.
content:
application/json:
schema:
$ref: '#/components/schemas/error'
schemas:
customer:
type: object
description: A customer.
properties:
id:
type: string
default:
description: A property NAMED default is a schema, not a literal.
$ref: '#/components/schemas/address'
subscriptions:
type: array
items:
$ref: '#/components/schemas/subscription'
subscription:
type: object
description: A subscription.
properties:
id:
type: string
customer:
anyOf:
- type: string
- $ref: '#/components/schemas/customer'
x-expansionResources:
oneOf:
- $ref: '#/components/schemas/customer'
customer_create:
type: object
properties:
email:
type: string
default: someone@example.com
address:
$ref: '#/components/schemas/address'
address:
type: object
properties:
city:
type: string
error:
type: object
properties:
message:
type: string
securitySchemes:
bearerAuth:
type: http
scheme: bearer
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
{
"swagger": "2.0",
"info": {
"title": "Envelope API (trimmed fixture)",
"version": "2.1",
"description": "Shaped like DocuSign's eSignature Swagger 2.0 document: host + basePath + schemes, every operation's parameters and an operation-level empty consumes list, definitions reached through $ref (one of them recursive)."
},
"host": "api.example.com",
"basePath": "/restapi",
"schemes": ["https"],
"consumes": ["application/json"],
"produces": ["application/json"],
"paths": {
"/v2.1/accounts/{accountId}/envelopes": {
"parameters": [],
"post": {
"operationId": "Envelopes_PostEnvelopes",
"summary": "Create an envelope",
"consumes": [],
"produces": [],
"parameters": [
{ "name": "accountId", "in": "path", "required": true, "type": "string", "description": "The account ID." },
{ "name": "merge_roles_on_draft", "in": "query", "required": false, "type": "string" },
{ "name": "envelopeDefinition", "in": "body", "required": true, "schema": { "$ref": "#/definitions/envelopeDefinition" } }
],
"responses": {
"201": { "description": "Created", "schema": { "$ref": "#/definitions/envelopeSummary" } },
"400": { "description": "Error", "schema": { "$ref": "#/definitions/errorDetails" } }
}
},
"get": {
"operationId": "Envelopes_GetEnvelopes",
"summary": "List envelopes",
"consumes": [],
"produces": [],
"parameters": [
{ "name": "accountId", "in": "path", "required": true, "type": "string" },
{ "name": "from_date", "in": "query", "required": false, "type": "string" }
],
"responses": {
"200": { "description": "OK", "schema": { "$ref": "#/definitions/envelopesInformation" } }
}
}
},
"/v2.1/accounts/{accountId}/envelopes/{envelopeId}": {
"get": {
"operationId": "Envelopes_GetEnvelope",
"summary": "Get an envelope",
"parameters": [
{ "name": "accountId", "in": "path", "required": true, "type": "string" },
{ "name": "envelopeId", "in": "path", "required": true, "type": "string" }
],
"responses": {
"200": { "description": "OK", "schema": { "$ref": "#/definitions/envelope" } }
}
}
}
},
"definitions": {
"envelopeDefinition": {
"type": "object",
"description": "The envelope to create.",
"properties": {
"emailSubject": { "type": "string" },
"status": { "type": "string", "enum": ["created", "sent"] },
"recipients": { "$ref": "#/definitions/recipients" }
}
},
"recipients": {
"type": "object",
"properties": {
"signers": { "type": "array", "items": { "$ref": "#/definitions/signer" } }
}
},
"signer": {
"type": "object",
"properties": {
"email": { "type": "string" },
"name": { "type": "string" }
}
},
"envelopeSummary": {
"type": "object",
"properties": { "envelopeId": { "type": "string" }, "status": { "type": "string" } }
},
"envelopesInformation": {
"type": "object",
"properties": { "envelopes": { "type": "array", "items": { "$ref": "#/definitions/envelope" } } }
},
"envelope": {
"type": "object",
"description": "An envelope.",
"properties": {
"envelopeId": { "type": "string" },
"parentEnvelope": { "$ref": "#/definitions/envelope" }
}
},
"errorDetails": {
"type": "object",
"properties": { "errorCode": { "type": "string" }, "message": { "type": "string" } }
}
}
}
Loading
Loading