@desktopaccountingapi/quickbooks-desktop-mcp 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +196 -2
- package/dist/catalog.d.ts +47 -0
- package/dist/catalog.js +140 -0
- package/dist/catalog.json +33216 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +71 -0
- package/dist/http.d.ts +14 -0
- package/dist/http.js +80 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +8 -0
- package/dist/key.d.ts +3 -0
- package/dist/key.js +41 -0
- package/dist/server.d.ts +24 -0
- package/dist/server.js +73 -0
- package/dist/stdio.d.ts +2 -0
- package/dist/stdio.js +40 -0
- package/dist/tools.d.ts +89 -0
- package/dist/tools.js +520 -0
- package/package.json +62 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
First release of `@desktopaccountingapi/quickbooks-desktop-mcp`, generated from API contract sha256 `1cc3058cecb5` (API version 1.0.0, 275 operations).
|
|
6
|
+
|
|
7
|
+
- Local MCP server over stdio: `npx -y @desktopaccountingapi/quickbooks-desktop-mcp`. Node.js 20 or later on Windows, macOS and Linux; no other runtime and no runtime dependencies.
|
|
8
|
+
- Tools: `list_end_users`, `list_api_endpoints`, `get_api_endpoint_schema`, `invoke_api_endpoint`, `search_docs`; optional one tool per operation with `--resources`.
|
|
9
|
+
- Writes carry an `Idempotency-Key`; an unknown outcome is reported with recovery steps, never resent.
|
|
10
|
+
- `--read-only` hides and refuses writes; read-only secret keys are enforced by the API.
|
|
11
|
+
- Library exports (`McpServer`, `handleMcpRequest`, `Tools`) to embed the server in your own process.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Desktop Accounting API
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,197 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Desktop Accounting API MCP server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@desktopaccountingapi/quickbooks-desktop-mcp` connects Claude, Cursor, VS Code, Codex and other AI tools to QuickBooks Desktop through [Desktop Accounting API](https://www.desktopaccountingapi.com/) and the [Model Context Protocol](https://modelcontextprotocol.io). Ask in plain English for overdue invoices, a vendor's payment history or last month's profit and loss, and the agent finds the operation, calls the API and answers from the company file.
|
|
4
|
+
|
|
5
|
+
- Covers all 275 operations of API 1.0.0 through five compact tools, so it does not fill the agent's context.
|
|
6
|
+
- Writes carry an idempotency key and are never retried blindly. Read-only keys are enforced by the API itself.
|
|
7
|
+
- Runs over stdio with Node.js 20 or later on Windows, macOS and Linux, with no runtime dependencies.
|
|
8
|
+
|
|
9
|
+
The current version is **0.1.1**. [MCP guide](https://www.desktopaccountingapi.com/docs/guides/mcp/) · [Documentation](https://www.desktopaccountingapi.com/docs/) · [Changelog](CHANGELOG.md) · [Status](https://status.desktopaccountingapi.com)
|
|
10
|
+
|
|
11
|
+
## Hosted server or local package
|
|
12
|
+
|
|
13
|
+
Both expose the same tools and call the same API with your secret key:
|
|
14
|
+
|
|
15
|
+
| | Hosted server | This package |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| Address | `https://mcp.desktopaccountingapi.com/` (Streamable HTTP) | `npx -y @desktopaccountingapi/quickbooks-desktop-mcp` (stdio) |
|
|
18
|
+
| Install | Nothing | Node.js 20 or later |
|
|
19
|
+
| Secret key | `Authorization: Bearer sk_...` header | `DAAPI_SECRET_KEY` environment variable |
|
|
20
|
+
| Use it when | Your client supports remote servers with a header | Your client runs only local servers, or you prefer a local process |
|
|
21
|
+
|
|
22
|
+
## Before you start
|
|
23
|
+
|
|
24
|
+
- Create a secret key in the [dashboard](https://www.desktopaccountingapi.com/dashboard) under **API keys**. Use a separate key per person or tool, so you can revoke one without affecting the rest. For tools that should only look things up, choose **Read-only** (see [Read-only access](#read-only-access)). Test projects issue `sk_test_...` keys, production projects `sk_live_...` keys.
|
|
25
|
+
- Connect at least one end user's QuickBooks Desktop company file. QuickBooks must be open on that computer for data requests to succeed.
|
|
26
|
+
|
|
27
|
+
Replace `sk_live_...` below with your key. Keep it in your client's configuration only, never in shared documents, chat or a repository.
|
|
28
|
+
|
|
29
|
+
## Setup
|
|
30
|
+
|
|
31
|
+
### Claude Desktop
|
|
32
|
+
|
|
33
|
+
Open **Settings > Developer > Edit Config** (`claude_desktop_config.json`) and add:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"mcpServers": {
|
|
38
|
+
"quickbooks-desktop": {
|
|
39
|
+
"command": "npx",
|
|
40
|
+
"args": ["-y", "@desktopaccountingapi/quickbooks-desktop-mcp@0.1.1"],
|
|
41
|
+
"env": { "DAAPI_SECRET_KEY": "sk_live_..." }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If the file already has an `mcpServers` section, add the `quickbooks-desktop` entry inside it, then restart Claude Desktop. Drop `@0.1.1` from the package name to always run the latest version.
|
|
48
|
+
|
|
49
|
+
### Claude Code
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
claude mcp add quickbooks-desktop --env DAAPI_SECRET_KEY=sk_live_... -- npx -y @desktopaccountingapi/quickbooks-desktop-mcp
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Or the hosted server:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
claude mcp add --transport http quickbooks-desktop https://mcp.desktopaccountingapi.com/ --header "Authorization: Bearer sk_live_..."
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Add `--scope user` to make it available in all your projects.
|
|
62
|
+
|
|
63
|
+
### Cursor
|
|
64
|
+
|
|
65
|
+
In `~/.cursor/mcp.json`, or `.cursor/mcp.json` inside a project:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"mcpServers": {
|
|
70
|
+
"quickbooks-desktop": {
|
|
71
|
+
"command": "npx",
|
|
72
|
+
"args": ["-y", "@desktopaccountingapi/quickbooks-desktop-mcp"],
|
|
73
|
+
"env": { "DAAPI_SECRET_KEY": "sk_live_..." }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For the hosted server, replace `command`, `args` and `env` with `"url": "https://mcp.desktopaccountingapi.com/"` and `"headers": { "Authorization": "Bearer sk_live_..." }`.
|
|
80
|
+
|
|
81
|
+
### VS Code
|
|
82
|
+
|
|
83
|
+
Run **MCP: Open User Configuration** from the Command Palette, or use `.vscode/mcp.json` in a workspace. The top-level key is `servers`:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"inputs": [{ "type": "promptString", "id": "daapi-key", "description": "Desktop Accounting API secret key", "password": true }],
|
|
88
|
+
"servers": {
|
|
89
|
+
"quickbooks-desktop": {
|
|
90
|
+
"type": "stdio",
|
|
91
|
+
"command": "npx",
|
|
92
|
+
"args": ["-y", "@desktopaccountingapi/quickbooks-desktop-mcp"],
|
|
93
|
+
"env": { "DAAPI_SECRET_KEY": "${input:daapi-key}" }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
VS Code asks for the key once and stores it securely, so it never sits in the file.
|
|
100
|
+
|
|
101
|
+
### Other clients
|
|
102
|
+
|
|
103
|
+
Any stdio client: run `npx -y @desktopaccountingapi/quickbooks-desktop-mcp` with `DAAPI_SECRET_KEY` set. Any client that supports remote servers with custom headers can use `https://mcp.desktopaccountingapi.com/` with `Authorization: Bearer sk_live_...`. Setup for Codex CLI and more clients is in the [MCP guide](https://www.desktopaccountingapi.com/docs/guides/mcp/).
|
|
104
|
+
|
|
105
|
+
### Verify it works
|
|
106
|
+
|
|
107
|
+
Ask the agent: "List my QuickBooks Desktop end users and show the 5 most recent invoices for the first one." If it returns real data from the company file, you are set up. The key is checked when the agent first requests data, so a "connected" indicator alone does not prove the key works.
|
|
108
|
+
|
|
109
|
+
## Options
|
|
110
|
+
|
|
111
|
+
| Flag | Environment | Meaning |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| | `DAAPI_SECRET_KEY` | Secret key (`sk_live_...` or `sk_test_...`). Required. |
|
|
114
|
+
| `--read-only` | `DAAPI_MCP_READ_ONLY=true` | Hide and refuse operations that change data. |
|
|
115
|
+
| `--resources invoices,customers` | `DAAPI_MCP_RESOURCES` | Also expose one tool per operation for these resources (`all` for every operation). |
|
|
116
|
+
| `--end-user-id eu_...` | `DAAPI_END_USER_ID` | Default end user (company file) for QuickBooks operations. A tool call's `end_user_id` overrides it. |
|
|
117
|
+
| `--base-url <url>` | `DAAPI_BASE_URL` | API origin. Default `https://api.desktopaccountingapi.com`. |
|
|
118
|
+
| `--version` | | Print the version. |
|
|
119
|
+
| `--help` | | Print the options. |
|
|
120
|
+
|
|
121
|
+
## Tools
|
|
122
|
+
|
|
123
|
+
| Tool | What it does |
|
|
124
|
+
| --- | --- |
|
|
125
|
+
| `list_end_users` | Your end users (one QuickBooks company file each), with connection status and QuickBooks company name. |
|
|
126
|
+
| `list_api_endpoints` | Searches the operations by resource, name or words, for example "open invoices" or "profit and loss". |
|
|
127
|
+
| `get_api_endpoint_schema` | One operation's description and the JSON Schema of its arguments. |
|
|
128
|
+
| `invoke_api_endpoint` | Calls an operation and returns its JSON result. `fields` (dot paths such as `["id", "refNumber"]`) trims each record. |
|
|
129
|
+
| `search_docs` | Searches the documentation, including the error codes. |
|
|
130
|
+
|
|
131
|
+
With `--resources`, each operation of those resources also becomes its own tool, such as `qbd_invoices_list` and `qbd_invoices_create`.
|
|
132
|
+
|
|
133
|
+
## Writes and safety
|
|
134
|
+
|
|
135
|
+
- By default the agent can read and write. The server instructs agents to describe each write and get your confirmation first.
|
|
136
|
+
- Every write carries an `Idempotency-Key`, and the result shows it. Repeating a call with the same key returns the original result instead of writing twice.
|
|
137
|
+
- Nothing is retried automatically. When a write's outcome is unknown, the result tells the agent not to resend it and how to check the request instead.
|
|
138
|
+
- Errors include the `userFacingMessage` and `fixes` from the [error catalog](https://www.desktopaccountingapi.com/docs/errors/), so the agent can tell you what to do.
|
|
139
|
+
- Every call appears in the dashboard's request log, like any other API call.
|
|
140
|
+
|
|
141
|
+
## Read-only access
|
|
142
|
+
|
|
143
|
+
Create a **read-only** secret key in the dashboard and use it in the setup above. The API enforces it for every client: a read-only key can call every `GET` operation and passthrough requests that contain only queries, and any other call returns `403 API_KEY_READ_ONLY` before anything reaches QuickBooks. Add `--read-only` (or `DAAPI_MCP_READ_ONLY=true`) to also hide write tools from the agent; the read-only key is what guarantees writes cannot happen.
|
|
144
|
+
|
|
145
|
+
## Use as a library
|
|
146
|
+
|
|
147
|
+
`handleMcpRequest(request, options)` serves Streamable HTTP from any runtime with web-standard `Request` and `Response`, such as Cloudflare Workers, Deno or Bun:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { buildCatalog, handleMcpRequest } from "@desktopaccountingapi/quickbooks-desktop-mcp";
|
|
151
|
+
|
|
152
|
+
// The tool catalog comes from the API's OpenAPI document, published with the documentation.
|
|
153
|
+
const spec = await (await fetch("https://www.desktopaccountingapi.com/docs/openapi.json")).json();
|
|
154
|
+
const catalog = buildCatalog(spec);
|
|
155
|
+
|
|
156
|
+
export default {
|
|
157
|
+
fetch: (request: Request): Promise<Response> =>
|
|
158
|
+
handleMcpRequest(request, { catalog, version: "1.0.0", apiBaseUrl: "https://api.desktopaccountingapi.com" }),
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Clients send their own secret key as `Authorization: Bearer sk_...`; the server passes it to the API unchanged. `McpServer` and `Tools` are exported for other transports.
|
|
163
|
+
|
|
164
|
+
## Troubleshooting
|
|
165
|
+
|
|
166
|
+
- **"No secret key reached the MCP server".** `DAAPI_SECRET_KEY` (or the `Authorization` header for the hosted server) is missing or not passed through.
|
|
167
|
+
- **"The secret key ... is malformed".** The key was cut off or mistyped. Copy it again from the dashboard.
|
|
168
|
+
- **`API_KEY_INVALID`.** The key is unknown or was revoked. The message shows only the key's last four characters.
|
|
169
|
+
- **Requests fail with a QuickBooks error.** QuickBooks must be open on the end user's computer and the Web Connector must be running. See [troubleshooting](https://www.desktopaccountingapi.com/docs/troubleshooting/).
|
|
170
|
+
|
|
171
|
+
## Versioning and changelog
|
|
172
|
+
|
|
173
|
+
- The package follows [semantic versioning](https://semver.org/) and is released together with the [Node.js](https://github.com/DesktopAccountingAPI/quickbooks-desktop-node), [Python](https://github.com/DesktopAccountingAPI/quickbooks-desktop-python), [.NET](https://github.com/DesktopAccountingAPI/quickbooks-desktop-dotnet) and [Java](https://github.com/DesktopAccountingAPI/quickbooks-desktop-java) SDKs, with the same version number.
|
|
174
|
+
- It is generated from the Desktop Accounting API contract (sha256 `1cc3058cecb5...` for this release) by the same pipeline as the SDKs.
|
|
175
|
+
- Every release is listed in [CHANGELOG.md](CHANGELOG.md) and tagged `v<version>` on GitHub.
|
|
176
|
+
|
|
177
|
+
## Support
|
|
178
|
+
|
|
179
|
+
- [MCP guide](https://www.desktopaccountingapi.com/docs/guides/mcp/), [documentation](https://www.desktopaccountingapi.com/docs/) and [status page](https://status.desktopaccountingapi.com).
|
|
180
|
+
- Bugs and feature requests for this package: [GitHub issues](https://github.com/DesktopAccountingAPI/quickbooks-desktop-mcp/issues).
|
|
181
|
+
- Questions about your account, keys, billing or a connection: [contact us](https://www.desktopaccountingapi.com/contact). Never send your secret key.
|
|
182
|
+
- Security reports: use **Report a vulnerability** on this repository's Security tab.
|
|
183
|
+
|
|
184
|
+
## Development
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
mise install # pinned Node.js
|
|
188
|
+
mise run check # install, typecheck, build, tests with the official MCP client, README samples, smoke test, package contents
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
This README is generated; `mise run check` parses its JSON configuration blocks and type-checks its TypeScript sample against the build.
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT. See [LICENSE](LICENSE).
|
|
196
|
+
|
|
197
|
+
QuickBooks is a registered trademark of Intuit Inc. Desktop Accounting API is an independent product and is not affiliated with, endorsed by, or approved by Intuit Inc.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type JsonSchema = Record<string, unknown>;
|
|
2
|
+
export interface CatalogParam {
|
|
3
|
+
name: string;
|
|
4
|
+
required: boolean;
|
|
5
|
+
description: string;
|
|
6
|
+
schema: JsonSchema;
|
|
7
|
+
}
|
|
8
|
+
export interface CatalogEndpoint {
|
|
9
|
+
/** The contract operationId, for example `qbd.invoices.create`. */
|
|
10
|
+
name: string;
|
|
11
|
+
method: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
|
|
12
|
+
path: string;
|
|
13
|
+
/** First contract tag, for example `Invoices`. */
|
|
14
|
+
tag: string;
|
|
15
|
+
/** x-tagGroups group of the tag, for example `Transactions`. */
|
|
16
|
+
group: string;
|
|
17
|
+
summary: string;
|
|
18
|
+
description: string;
|
|
19
|
+
/** Anything other than GET changes data (read-only keys are rejected by the API). */
|
|
20
|
+
write: boolean;
|
|
21
|
+
/** The operation requires the Daapi-End-User-Id header. */
|
|
22
|
+
endUser: boolean;
|
|
23
|
+
/** The operation accepts an Idempotency-Key header. */
|
|
24
|
+
idempotent: boolean;
|
|
25
|
+
pathParams: CatalogParam[];
|
|
26
|
+
queryParams: CatalogParam[];
|
|
27
|
+
/** JSON request body, when the operation takes one. `schema` may contain `$ref: #/$defs/<name>`. */
|
|
28
|
+
body: {
|
|
29
|
+
required: boolean;
|
|
30
|
+
schema: JsonSchema;
|
|
31
|
+
} | null;
|
|
32
|
+
}
|
|
33
|
+
export interface Catalog {
|
|
34
|
+
apiVersion: string;
|
|
35
|
+
servers: {
|
|
36
|
+
url: string;
|
|
37
|
+
description: string;
|
|
38
|
+
}[];
|
|
39
|
+
endpoints: CatalogEndpoint[];
|
|
40
|
+
/** Every component schema reachable from an endpoint input, keyed by component name. */
|
|
41
|
+
defs: Record<string, JsonSchema>;
|
|
42
|
+
}
|
|
43
|
+
type Obj = Record<string, unknown>;
|
|
44
|
+
export declare function buildCatalog(doc: Obj): Catalog;
|
|
45
|
+
/** The `$defs` an input schema needs, transitively. */
|
|
46
|
+
export declare function defsFor(catalog: Catalog, roots: unknown[]): Record<string, JsonSchema>;
|
|
47
|
+
export {};
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Code generated by packages/sdk-generator from packages/mcp/src. DO NOT EDIT.
|
|
2
|
+
// Contract: packages/api-contract/generated/openapi.json (OpenAPI 3.1.0) sha256:1cc3058cecb557ce1cc724d5236d36860df6bec39636e2d427c8a408bf5f2ca2
|
|
3
|
+
// MCP endpoint catalog: a compact, input-only view of the public OpenAPI contract that the MCP
|
|
4
|
+
// tools search, describe and invoke. Built from packages/api-contract/generated/openapi.json by
|
|
5
|
+
// scripts/api-contract.mjs (committed as packages/mcp/generated/catalog.json, drift-checked) and
|
|
6
|
+
// shipped inside the stdio package by packages/sdk-generator. Pure data; no runtime imports.
|
|
7
|
+
const METHODS = ['get', 'post', 'put', 'patch', 'delete'];
|
|
8
|
+
function deref(doc, value) {
|
|
9
|
+
let v = value;
|
|
10
|
+
for (let i = 0; i < 10 && v && typeof v.$ref === 'string'; i++) {
|
|
11
|
+
const parts = v.$ref.replace(/^#\//, '').split('/');
|
|
12
|
+
let cur = doc;
|
|
13
|
+
for (const p of parts)
|
|
14
|
+
cur = cur[p];
|
|
15
|
+
v = cur;
|
|
16
|
+
}
|
|
17
|
+
return v;
|
|
18
|
+
}
|
|
19
|
+
/** Rewrites component refs to `#/$defs/<name>` and records the referenced names. */
|
|
20
|
+
function rewrite(value, seen) {
|
|
21
|
+
if (Array.isArray(value))
|
|
22
|
+
return value.map((v) => rewrite(v, seen));
|
|
23
|
+
if (!value || typeof value !== 'object')
|
|
24
|
+
return value;
|
|
25
|
+
const out = {};
|
|
26
|
+
for (const [k, v] of Object.entries(value)) {
|
|
27
|
+
if (k === '$ref' && typeof v === 'string' && v.startsWith('#/components/schemas/')) {
|
|
28
|
+
const name = v.slice('#/components/schemas/'.length);
|
|
29
|
+
seen.add(name);
|
|
30
|
+
out.$ref = `#/$defs/${name}`;
|
|
31
|
+
}
|
|
32
|
+
else if (k === 'example' || k === 'examples') {
|
|
33
|
+
// Examples stay: they show agents the expected shape.
|
|
34
|
+
out[k] = v;
|
|
35
|
+
}
|
|
36
|
+
else
|
|
37
|
+
out[k] = rewrite(v, seen);
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
export function buildCatalog(doc) {
|
|
42
|
+
const groups = new Map();
|
|
43
|
+
for (const g of doc['x-tagGroups'] ?? [])
|
|
44
|
+
for (const t of g.tags)
|
|
45
|
+
groups.set(t, g.name);
|
|
46
|
+
const pending = new Set();
|
|
47
|
+
const endpoints = [];
|
|
48
|
+
for (const [path, item] of Object.entries(doc.paths)) {
|
|
49
|
+
for (const method of METHODS) {
|
|
50
|
+
const op = item[method];
|
|
51
|
+
if (!op)
|
|
52
|
+
continue;
|
|
53
|
+
const pathParams = [];
|
|
54
|
+
const queryParams = [];
|
|
55
|
+
let endUser = false;
|
|
56
|
+
let idempotent = false;
|
|
57
|
+
for (const raw of op.parameters ?? []) {
|
|
58
|
+
const p = deref(doc, raw);
|
|
59
|
+
if (p.in === 'header') {
|
|
60
|
+
if (p.name === 'Daapi-End-User-Id')
|
|
61
|
+
endUser = true;
|
|
62
|
+
if (p.name === 'Idempotency-Key')
|
|
63
|
+
idempotent = true;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const schema = { ...rewrite(p.schema, pending) };
|
|
67
|
+
if (schema.description === p.description)
|
|
68
|
+
delete schema.description;
|
|
69
|
+
const param = { name: String(p.name), required: p.required === true, description: String(p.description ?? ''), schema };
|
|
70
|
+
if (p.in === 'path')
|
|
71
|
+
pathParams.push(param);
|
|
72
|
+
else if (p.in === 'query')
|
|
73
|
+
queryParams.push(param);
|
|
74
|
+
}
|
|
75
|
+
const rb = op.requestBody ? deref(doc, op.requestBody) : undefined;
|
|
76
|
+
const json = rb?.content?.['application/json'];
|
|
77
|
+
const tag = String(op.tags[0]);
|
|
78
|
+
endpoints.push({
|
|
79
|
+
name: String(op.operationId),
|
|
80
|
+
method: method.toUpperCase(),
|
|
81
|
+
path,
|
|
82
|
+
tag,
|
|
83
|
+
group: groups.get(tag) ?? 'Other',
|
|
84
|
+
summary: String(op.summary ?? ''),
|
|
85
|
+
description: String(op.description ?? ''),
|
|
86
|
+
write: method !== 'get',
|
|
87
|
+
endUser,
|
|
88
|
+
idempotent,
|
|
89
|
+
pathParams,
|
|
90
|
+
queryParams,
|
|
91
|
+
body: json ? { required: rb?.required === true, schema: rewrite(json.schema, pending) } : null,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const schemas = (doc.components.schemas ?? {});
|
|
96
|
+
const defs = {};
|
|
97
|
+
while (pending.size) {
|
|
98
|
+
const [name] = pending;
|
|
99
|
+
pending.delete(name);
|
|
100
|
+
if (defs[name])
|
|
101
|
+
continue;
|
|
102
|
+
const found = new Set();
|
|
103
|
+
defs[name] = rewrite(schemas[name], found);
|
|
104
|
+
for (const n of found)
|
|
105
|
+
if (!defs[n])
|
|
106
|
+
pending.add(n);
|
|
107
|
+
}
|
|
108
|
+
const sortedDefs = {};
|
|
109
|
+
for (const k of Object.keys(defs).sort())
|
|
110
|
+
sortedDefs[k] = defs[k];
|
|
111
|
+
return {
|
|
112
|
+
apiVersion: String(doc.info.version),
|
|
113
|
+
servers: doc.servers ?? [],
|
|
114
|
+
endpoints,
|
|
115
|
+
defs: sortedDefs,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** The `$defs` an input schema needs, transitively. */
|
|
119
|
+
export function defsFor(catalog, roots) {
|
|
120
|
+
const out = {};
|
|
121
|
+
const visit = (v) => {
|
|
122
|
+
if (Array.isArray(v))
|
|
123
|
+
return v.forEach(visit);
|
|
124
|
+
if (!v || typeof v !== 'object')
|
|
125
|
+
return;
|
|
126
|
+
for (const [k, x] of Object.entries(v)) {
|
|
127
|
+
if (k === '$ref' && typeof x === 'string' && x.startsWith('#/$defs/')) {
|
|
128
|
+
const name = x.slice('#/$defs/'.length);
|
|
129
|
+
if (!out[name] && catalog.defs[name]) {
|
|
130
|
+
out[name] = catalog.defs[name];
|
|
131
|
+
visit(catalog.defs[name]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else
|
|
135
|
+
visit(x);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
roots.forEach(visit);
|
|
139
|
+
return out;
|
|
140
|
+
}
|