zuplo 7.6.0 → 7.6.2
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/docs/ai-gateway/{introduction.mdx → overview.mdx} +2 -2
- package/docs/ai-gateway/policies/overview.mdx +1 -1
- package/docs/api-management/{introduction.md → overview.md} +7 -4
- package/docs/articles/api-key-administration.mdx +5 -6
- package/docs/articles/api-key-consumer-bucket-portal-ui.mdx +5 -5
- package/docs/articles/api-key-end-users.mdx +3 -3
- package/docs/articles/custom-domains.mdx +1 -1
- package/docs/articles/environment-variables.mdx +1 -1
- package/docs/articles/feature-flags.mdx +4 -4
- package/docs/articles/migrate-from-apigee.md +3 -3
- package/docs/articles/migrate-from-aws-api-gateway.md +1 -1
- package/docs/articles/migrate-from-azure-apim.md +2 -2
- package/docs/articles/migrate-from-kong.md +3 -3
- package/docs/articles/migration-overview.md +1 -1
- package/docs/articles/monetization-integrations.mdx +2 -2
- package/docs/articles/step-3-add-api-key-auth.mdx +1 -1
- package/docs/articles/step-5-dynamic-rate-limiting-local.mdx +1 -1
- package/docs/articles/step-5-dynamic-rate-limiting.mdx +1 -1
- package/docs/articles/version-support-policy.mdx +2 -2
- package/docs/cli/create-zuplo-api.mdx +2 -2
- package/docs/concepts/ai-gateway.mdx +69 -0
- package/docs/concepts/api-keys.md +3 -3
- package/docs/concepts/mcp.mdx +63 -0
- package/docs/concepts/project-structure.mdx +1 -1
- package/docs/dedicated/akamai/ai-powered-applications.mdx +3 -3
- package/docs/dev-portal/documenting-mcp-servers.mdx +3 -3
- package/docs/dev-portal/{introduction.mdx → overview.mdx} +5 -2
- package/docs/dev-portal/zudoku/configuration/api-reference.md +45 -0
- package/docs/dev-portal/zudoku/configuration/docs.md +56 -1
- package/docs/dev-portal/zudoku/configuration/llms.md +39 -4
- package/docs/dev-portal/zudoku/configuration/navigation.mdx +4 -4
- package/docs/handlers/mcp-server.mdx +7 -0
- package/docs/managed-edge/overview.md +1 -1
- package/docs/mcp-gateway/cross-app-access/quickstart.mdx +1 -1
- package/docs/mcp-gateway/{introduction.mdx → overview.mdx} +2 -2
- package/docs/mcp-gateway/policies/overview.mdx +13 -0
- package/docs/mcp-server/custom-tools.mdx +2 -2
- package/docs/mcp-server/graphql.mdx +1 -1
- package/docs/mcp-server/openai-apps-sdk.mdx +3 -4
- package/docs/mcp-server/{introduction.mdx → overview.mdx} +6 -3
- package/docs/mcp-server/tools.mdx +172 -1
- package/docs/mcp-server/troubleshooting.mdx +47 -5
- package/docs/policies/ai-gateway-metering-v2-inbound/doc.md +38 -102
- package/docs/policies/ai-gateway-metering-v2-inbound/intro.md +3 -3
- package/docs/policies/ai-gateway-metering-v2-inbound/schema.json +89 -14
- package/docs/programmable-api/mcp-sdk.mdx +3 -3
- package/package.json +5 -5
|
@@ -13,7 +13,7 @@ During build, you can optionally generate:
|
|
|
13
13
|
- **`llms.txt`** - Summary with links to all pages
|
|
14
14
|
- **`llms-full.txt`** - Complete documentation in one file
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Per-page `.md` export is enabled by default. The two aggregate `llms.txt` files are opt-in.
|
|
17
17
|
|
|
18
18
|
## Configuration
|
|
19
19
|
|
|
@@ -28,13 +28,15 @@ export default {
|
|
|
28
28
|
llmsTxt: true, // Generate llms.txt
|
|
29
29
|
llmsTxtFull: true, // Generate llms-full.txt
|
|
30
30
|
includeProtected: false, // Exclude protected routes
|
|
31
|
+
title: "Acme API",
|
|
32
|
+
description: "Build and operate integrations with the Acme API.",
|
|
33
|
+
instructions:
|
|
34
|
+
"Use these docs when creating or debugging an Acme integration. Start with the quickstart, then consult the API reference for request and response schemas.",
|
|
31
35
|
},
|
|
32
36
|
},
|
|
33
37
|
};
|
|
34
38
|
```
|
|
35
39
|
|
|
36
|
-
All options are disabled by default.
|
|
37
|
-
|
|
38
40
|
:::tip
|
|
39
41
|
|
|
40
42
|
When enabled, markdown files are generated during build and deleted after creating the `llms.txt`
|
|
@@ -50,11 +52,40 @@ files unless `publishMarkdown: true` is set (see
|
|
|
50
52
|
Generates an `llms.txt` file with links to all documentation pages:
|
|
51
53
|
|
|
52
54
|
```markdown title="llms.txt"
|
|
53
|
-
#
|
|
55
|
+
# Acme API
|
|
56
|
+
|
|
57
|
+
> Build and operate integrations with the Acme API.
|
|
58
|
+
|
|
59
|
+
Use these docs when creating or debugging an Acme integration. Start with the quickstart, then
|
|
60
|
+
consult the API reference for request and response schemas.
|
|
61
|
+
|
|
62
|
+
## Documentation
|
|
54
63
|
|
|
55
64
|
- [Quickstart](/dev-portal/zudoku/quickstart.md): Get started with Dev Portal - [Writing](/dev-portal/zudoku/writing.md): A guide to writing documentation
|
|
56
65
|
```
|
|
57
66
|
|
|
67
|
+
### `title`
|
|
68
|
+
|
|
69
|
+
**Type:** `string` **Default:** the configured site title, or `"Documentation"`
|
|
70
|
+
|
|
71
|
+
Sets the project or site name in the required H1 at the top of `llms.txt`. It also sets the H1 in
|
|
72
|
+
`llms-full.txt` when that file is enabled.
|
|
73
|
+
|
|
74
|
+
### `description`
|
|
75
|
+
|
|
76
|
+
**Type:** `string` **Default:** `"Documentation files for Large Language Models"`
|
|
77
|
+
|
|
78
|
+
Sets the short summary in the blockquote immediately after the H1.
|
|
79
|
+
|
|
80
|
+
### `instructions`
|
|
81
|
+
|
|
82
|
+
**Type:** `string` **Default:** `undefined`
|
|
83
|
+
|
|
84
|
+
Adds optional guidance between the blockquote and the documentation link section. Use it to tell
|
|
85
|
+
agents which concrete jobs the documentation is best suited for and how to navigate or call the
|
|
86
|
+
product. The value can contain Markdown paragraphs and lists, but should not contain headings;
|
|
87
|
+
headings in `llms.txt` are reserved for sections containing link lists.
|
|
88
|
+
|
|
58
89
|
### `llmsTxtFull`
|
|
59
90
|
|
|
60
91
|
**Type:** `boolean` **Default:** `false`
|
|
@@ -81,6 +112,10 @@ dist/
|
|
|
81
112
|
└── ...
|
|
82
113
|
```
|
|
83
114
|
|
|
115
|
+
On Vercel builds, this tree is written beneath `.vercel/output/static/` instead of `dist/`. The
|
|
116
|
+
deployed public URLs remain the same. See the [Vercel deployment guide](/dev-portal/zudoku/deploy/vercel) for the
|
|
117
|
+
generated routing and Markdown content-negotiation behavior.
|
|
118
|
+
|
|
84
119
|
**Important:** Individual `.md` files are only kept in the final build if `publishMarkdown: true`.
|
|
85
120
|
If only `llmsTxt` or `llmsTxtFull` is enabled, the `.md` files are generated temporarily during the
|
|
86
121
|
build but deleted after the `llms.txt` files are created.
|
|
@@ -328,10 +328,10 @@ details.
|
|
|
328
328
|
|
|
329
329
|
:::note
|
|
330
330
|
|
|
331
|
-
Avoid naming files `index.md` or `index.mdx` and relying on their default path.
|
|
332
|
-
|
|
333
|
-
routing issues. Instead, give files descriptive names and use the `path` property to
|
|
334
|
-
the desired URL.
|
|
331
|
+
Avoid naming files `index.md` or `index.mdx` and relying on their default path. Zudoku's generated
|
|
332
|
+
Vercel clean-URL routes strip `/index` with a redirect, and other hosting providers may do the same.
|
|
333
|
+
This can cause routing issues. Instead, give files descriptive names and use the `path` property to
|
|
334
|
+
serve them at the desired URL.
|
|
335
335
|
|
|
336
336
|
:::
|
|
337
337
|
|
|
@@ -98,6 +98,13 @@ The MCP Server handler supports the following configuration options:
|
|
|
98
98
|
- `operations` - An array of operation references to register with the MCP
|
|
99
99
|
server. Each operation can be a tool, prompt, or resource based on its
|
|
100
100
|
`x-zuplo-route.mcp` configuration.
|
|
101
|
+
- `toolInputStyle` (optional, default `flat`) - How generated tools advertise an
|
|
102
|
+
operation's inputs. With `flat`, a request body's properties and the
|
|
103
|
+
operation's path, query, and header parameters all become top-level tool
|
|
104
|
+
arguments. With `nested`, each input channel stays its own object (`body`,
|
|
105
|
+
`queryParams`, `pathParams`, `headers`). Flat tools accept the nested form
|
|
106
|
+
too, so switching styles doesn't break existing clients. See
|
|
107
|
+
[Tool input schemas](../mcp-server/tools.mdx#tool-input-schemas).
|
|
101
108
|
|
|
102
109
|
### MCP `2025-06-18` Global Options
|
|
103
110
|
|
|
@@ -74,5 +74,5 @@ deployment. You can start building your API immediately—no additional
|
|
|
74
74
|
configuration is required.
|
|
75
75
|
|
|
76
76
|
For more information about getting started with Zuplo, see the
|
|
77
|
-
[API Management
|
|
77
|
+
[API Management overview](/docs/api-management/overview) or
|
|
78
78
|
[sign up](https://portal.zuplo.com/signup?utm_source=docs) for free.
|
|
@@ -238,7 +238,7 @@ The XAA exchange happens entirely on the gateway's outbound side. See
|
|
|
238
238
|
|
|
239
239
|
// Registers the MCP Gateway, which adds the OAuth and upstream-connection
|
|
240
240
|
// routes used to expose and secure MCP servers through your gateway.
|
|
241
|
-
// Docs: https://zuplo.com/docs/mcp-
|
|
241
|
+
// Docs: https://zuplo.com/docs/mcp-gateway/overview
|
|
242
242
|
export function runtimeInit(runtime: RuntimeExtensions) {
|
|
243
243
|
runtime.addPlugin(new McpGatewayPlugin());
|
|
244
244
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "MCP Gateway"
|
|
3
|
-
sidebar_label: "
|
|
2
|
+
title: "MCP Gateway overview"
|
|
3
|
+
sidebar_label: "Overview"
|
|
4
4
|
description:
|
|
5
5
|
Overview of the Zuplo MCP Gateway — a single OAuth-protected MCP endpoint that
|
|
6
6
|
fronts multiple upstream MCP servers with curated tools and per-call
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: MCP Gateway policies overview
|
|
3
|
+
sidebar_label: Overview
|
|
4
|
+
description: Every policy available to an MCP Gateway and what each one does.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
import policies from "../../../policies.ui.json";
|
|
8
|
+
|
|
9
|
+
MCP Gateway policies secure client access, authorize requests, control exposed
|
|
10
|
+
capabilities, and protect upstream MCP servers. See
|
|
11
|
+
[How the MCP Gateway works](../how-it-works.mdx) for the request flow.
|
|
12
|
+
|
|
13
|
+
<PolicyOverview policies={policies} products={["mcp-gateway"]} />
|
|
@@ -513,7 +513,7 @@ independently. For the full API reference, see
|
|
|
513
513
|
tool results
|
|
514
514
|
- [MCP Server Handler](../handlers/mcp-server.mdx) - For simple route-to-tool
|
|
515
515
|
mapping
|
|
516
|
-
- [Model Context Protocol Overview](../mcp-server/
|
|
517
|
-
|
|
516
|
+
- [Model Context Protocol Overview](../mcp-server/overview.mdx) - Understanding
|
|
517
|
+
MCP concepts
|
|
518
518
|
- [MCP Specification](https://modelcontextprotocol.io/specification/) - Official
|
|
519
519
|
protocol documentation
|
|
@@ -228,7 +228,7 @@ workflows, see the [Custom Tools documentation](./custom-tools.mdx).
|
|
|
228
228
|
|
|
229
229
|
## See Also
|
|
230
230
|
|
|
231
|
-
- [MCP Server
|
|
231
|
+
- [MCP Server overview](./overview.mdx) - Main MCP Server documentation
|
|
232
232
|
- [Custom Tools](./custom-tools.mdx) - Build custom MCP tools with complex logic
|
|
233
233
|
- [GraphQL Best Practices](https://graphql.org/learn/best-practices/) -
|
|
234
234
|
GraphQL.org recommendations
|
|
@@ -14,10 +14,9 @@ metadata and set response metadata required for ChatGPT widget rendering.
|
|
|
14
14
|
This page covers the **MCP Server handler**, which turns your own OpenAPI routes
|
|
15
15
|
into an MCP server. If you're proxying to upstream MCP servers (Linear, Stripe,
|
|
16
16
|
Notion, internal services, etc.) through Zuplo's
|
|
17
|
-
[MCP Gateway](../mcp-gateway/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
emits the UI.
|
|
17
|
+
[MCP Gateway](../mcp-gateway/overview.mdx), Apps SDK UI surfaces pass through
|
|
18
|
+
the gateway as ordinary MCP resources — no extra configuration is required on
|
|
19
|
+
the gateway side. Build the Apps SDK app on whichever MCP server emits the UI.
|
|
21
20
|
|
|
22
21
|
:::
|
|
23
22
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
3
|
-
sidebar_label:
|
|
2
|
+
title: MCP Server overview
|
|
3
|
+
sidebar_label: Overview
|
|
4
|
+
description:
|
|
5
|
+
Expose OpenAPI operations to AI clients as MCP tools, prompts, and resources
|
|
6
|
+
with Zuplo's MCP Server handler.
|
|
4
7
|
---
|
|
5
8
|
|
|
6
9
|
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is
|
|
@@ -17,7 +20,7 @@ The **MCP Server handler** described on this page turns your own OpenAPI routes
|
|
|
17
20
|
into an MCP server. If you want to put a single OAuth-protected endpoint in
|
|
18
21
|
front of one or more existing upstream MCP servers (Linear, Stripe, Notion,
|
|
19
22
|
internal services, etc.), see the
|
|
20
|
-
[MCP Gateway
|
|
23
|
+
[MCP Gateway overview](../mcp-gateway/overview.mdx) instead.
|
|
21
24
|
|
|
22
25
|
:::
|
|
23
26
|
|
|
@@ -140,6 +140,176 @@ array:
|
|
|
140
140
|
See further details in the
|
|
141
141
|
[MCP Server Handler documentation](../handlers/mcp-server.mdx).
|
|
142
142
|
|
|
143
|
+
## Tool input schemas
|
|
144
|
+
|
|
145
|
+
Zuplo derives each tool's `inputSchema` from the operation's OpenAPI definition
|
|
146
|
+
and advertises it as a single flat object. The request body's properties and the
|
|
147
|
+
operation's path, query, and header parameters all become top-level tool
|
|
148
|
+
arguments, which is the shape MCP clients send on a first call.
|
|
149
|
+
|
|
150
|
+
When a client calls the tool, the gateway routes each argument back to the
|
|
151
|
+
channel it came from, so a body field lands in the request body and a query
|
|
152
|
+
parameter lands in the URL.
|
|
153
|
+
|
|
154
|
+
The weather route above declares one required query parameter, so its tool takes
|
|
155
|
+
one required argument:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"type": "object",
|
|
160
|
+
"properties": {
|
|
161
|
+
"location": {
|
|
162
|
+
"type": "string"
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"required": ["location"],
|
|
166
|
+
"additionalProperties": false
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
A request body works the same way — its fields are the tool's arguments. Given
|
|
171
|
+
this operation:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"/orders": {
|
|
176
|
+
"post": {
|
|
177
|
+
"operationId": "createOrder",
|
|
178
|
+
"requestBody": {
|
|
179
|
+
"required": true,
|
|
180
|
+
"content": {
|
|
181
|
+
"application/json": {
|
|
182
|
+
"schema": {
|
|
183
|
+
"type": "object",
|
|
184
|
+
"properties": {
|
|
185
|
+
"productId": { "type": "string" },
|
|
186
|
+
"quantity": { "type": "number" }
|
|
187
|
+
},
|
|
188
|
+
"required": ["productId"],
|
|
189
|
+
"additionalProperties": false
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The tool advertises the body's fields directly:
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"type": "object",
|
|
204
|
+
"properties": {
|
|
205
|
+
"productId": { "type": "string" },
|
|
206
|
+
"quantity": { "type": "number" }
|
|
207
|
+
},
|
|
208
|
+
"required": ["productId"],
|
|
209
|
+
"additionalProperties": false
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
And a call passes them at the top level:
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"name": "createOrder",
|
|
218
|
+
"arguments": {
|
|
219
|
+
"productId": "sku-123",
|
|
220
|
+
"quantity": 2
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
:::note
|
|
226
|
+
|
|
227
|
+
Parameters declared with `in: cookie` aren't exposed as tool arguments.
|
|
228
|
+
|
|
229
|
+
:::
|
|
230
|
+
|
|
231
|
+
### Undeclared arguments
|
|
232
|
+
|
|
233
|
+
Your own OpenAPI schema decides whether an argument the tool doesn't declare is
|
|
234
|
+
rejected:
|
|
235
|
+
|
|
236
|
+
- **The schema sets `additionalProperties: false`.** The tool's flat schema is
|
|
237
|
+
closed too, and an undeclared argument fails with JSON-RPC error `-32602`.
|
|
238
|
+
- **The schema omits `additionalProperties`.** JSON Schema permits extras, so
|
|
239
|
+
the tool accepts the argument and forwards it in the request body.
|
|
240
|
+
|
|
241
|
+
Set `additionalProperties: false` on a request body schema when you want the
|
|
242
|
+
gateway to reject fields a model invented rather than pass them upstream.
|
|
243
|
+
|
|
244
|
+
An argument routes to a request header only when a header parameter declares
|
|
245
|
+
that exact name. An undeclared argument never becomes an upstream header.
|
|
246
|
+
|
|
247
|
+
### When arguments stay nested
|
|
248
|
+
|
|
249
|
+
Some operations can't be flattened without changing what the tool accepts. Those
|
|
250
|
+
tools keep the nested shape, and the MCP server logs the reason when it
|
|
251
|
+
registers the tool:
|
|
252
|
+
|
|
253
|
+
- **Two channels declare the same name.** A `limit` query parameter alongside a
|
|
254
|
+
`limit` body field, for example. The whole schema stays nested and the server
|
|
255
|
+
logs a warning. Rename one of them to get flat arguments.
|
|
256
|
+
- **The request body isn't a plain object.** An array, a string, or a body built
|
|
257
|
+
from `oneOf`, `anyOf`, or `allOf` has no property names to hoist. Neither does
|
|
258
|
+
one that constrains the object as a whole, through `minProperties`,
|
|
259
|
+
`patternProperties`, or `dependentRequired`. The parameters still flatten and
|
|
260
|
+
the body keeps its own `body` argument. Enable
|
|
261
|
+
[`debugMode`](../handlers/mcp-server.mdx#configuration) to see which tools
|
|
262
|
+
this applies to.
|
|
263
|
+
|
|
264
|
+
A nested schema wraps each channel the operation declares in its own object:
|
|
265
|
+
|
|
266
|
+
```json
|
|
267
|
+
{
|
|
268
|
+
"type": "object",
|
|
269
|
+
"properties": {
|
|
270
|
+
"body": {
|
|
271
|
+
"type": "object",
|
|
272
|
+
"properties": { "productId": { "type": "string" } },
|
|
273
|
+
"required": ["productId"],
|
|
274
|
+
"additionalProperties": false
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
"required": ["body"],
|
|
278
|
+
"additionalProperties": false
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The channel names are `body`, `queryParams`, `pathParams`, and `headers`. Only
|
|
283
|
+
the channels the operation declares appear.
|
|
284
|
+
|
|
285
|
+
### Calling a tool with nested arguments
|
|
286
|
+
|
|
287
|
+
Flat tools also accept the nested form, so a client written against the nested
|
|
288
|
+
schema keeps working:
|
|
289
|
+
|
|
290
|
+
```json
|
|
291
|
+
{
|
|
292
|
+
"name": "createOrder",
|
|
293
|
+
"arguments": {
|
|
294
|
+
"body": { "productId": "sku-123", "quantity": 2 }
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Send flat arguments in new clients — that's what the tool advertises.
|
|
300
|
+
|
|
301
|
+
To make every generated tool advertise the nested shape instead, set
|
|
302
|
+
[`toolInputStyle: "nested"`](../handlers/mcp-server.mdx#configuration) in the
|
|
303
|
+
handler options.
|
|
304
|
+
|
|
305
|
+
:::note
|
|
306
|
+
|
|
307
|
+
[Prompts](./prompts.mdx) don't use input channels. A prompt's arguments come
|
|
308
|
+
from its request body schema directly, so `prompts/get` always takes them at the
|
|
309
|
+
top level.
|
|
310
|
+
|
|
311
|
+
:::
|
|
312
|
+
|
|
143
313
|
## Testing MCP Tools
|
|
144
314
|
|
|
145
315
|
### List Available Tools
|
|
@@ -175,7 +345,8 @@ Response:
|
|
|
175
345
|
"type": "string"
|
|
176
346
|
}
|
|
177
347
|
},
|
|
178
|
-
"required": ["location"]
|
|
348
|
+
"required": ["location"],
|
|
349
|
+
"additionalProperties": false
|
|
179
350
|
}
|
|
180
351
|
}
|
|
181
352
|
]
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
title: "Troubleshooting the MCP Server Handler"
|
|
3
3
|
sidebar_label: "Troubleshooting"
|
|
4
4
|
description:
|
|
5
|
-
Diagnose MCP Server handler tool calls that fail in an AI client
|
|
6
|
-
|
|
7
|
-
configuration
|
|
5
|
+
Diagnose MCP Server handler tool calls that fail in an AI client — a route
|
|
6
|
+
that returns 200 while the client errors, and invalid-argument rejections —
|
|
7
|
+
using debug logging, the structured-content configuration, and the tool's
|
|
8
|
+
advertised input schema.
|
|
8
9
|
---
|
|
9
10
|
|
|
10
11
|
When an AI client calls a tool exposed by the
|
|
@@ -14,8 +15,9 @@ underlying gateway route can return `200` while the MCP client still rejects the
|
|
|
14
15
|
response, which makes these failures hard to diagnose from the client alone.
|
|
15
16
|
|
|
16
17
|
This page shows how to turn on the handler's debug logging, read the log lines
|
|
17
|
-
that reveal a misconfiguration, and fix the most common
|
|
18
|
-
advertises an output schema but returns no structured content
|
|
18
|
+
that reveal a misconfiguration, and fix the most common causes: a tool that
|
|
19
|
+
advertises an output schema but returns no structured content, and arguments
|
|
20
|
+
that don't match the schema the tool advertises.
|
|
19
21
|
|
|
20
22
|
:::tip
|
|
21
23
|
|
|
@@ -147,6 +149,46 @@ For the full definitions of both options, see the
|
|
|
147
149
|
[MCP `2025-06-18` Global Options](../handlers/mcp-server.mdx#mcp-2025-06-18-global-options)
|
|
148
150
|
in the handler reference.
|
|
149
151
|
|
|
152
|
+
## Invalid arguments for tool (-32602)
|
|
153
|
+
|
|
154
|
+
**Symptom.** A tool call fails with JSON-RPC error `-32602` and a message naming
|
|
155
|
+
an argument:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
-32602 Invalid arguments for tool 'create_order':
|
|
159
|
+
✖ Unrecognized key: "sku"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Likely cause 1: the argument isn't in the schema.** The request body schema
|
|
163
|
+
backing the tool sets `additionalProperties: false`, so the tool's input schema
|
|
164
|
+
is closed and an argument it doesn't declare is invalid. Read the tool's
|
|
165
|
+
`inputSchema` from `tools/list` and compare the argument names. A model that
|
|
166
|
+
invents a field name lands here.
|
|
167
|
+
|
|
168
|
+
**Likely cause 2: the tool takes nested arguments.** Most tools take the request
|
|
169
|
+
body's fields and the operation's parameters as top-level arguments. A tool
|
|
170
|
+
whose OpenAPI can't be flattened keeps the nested shape instead, so its
|
|
171
|
+
arguments sit under `body`, `queryParams`, `pathParams`, or `headers`. When the
|
|
172
|
+
error reads `at body`, the caller sent flat arguments to a nested tool:
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
-32602 Invalid arguments for tool 'create_order':
|
|
176
|
+
✖ Invalid input: expected object, received undefined
|
|
177
|
+
→ at body
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Two things cause a tool to stay nested, and the MCP server logs which one when
|
|
181
|
+
it registers the tool: a name collision between two input channels (logged as a
|
|
182
|
+
warning), or a request body that isn't a plain object (logged at debug level —
|
|
183
|
+
enable `debugMode` to see it). See
|
|
184
|
+
[When arguments stay nested](./tools.mdx#when-arguments-stay-nested) for both
|
|
185
|
+
cases and how to resolve the collision.
|
|
186
|
+
|
|
187
|
+
**Fix.** Always craft `arguments` against the `inputSchema` the tool advertises
|
|
188
|
+
in `tools/list` rather than assuming a shape. The
|
|
189
|
+
[MCP Inspector](#client-side-debugging-tools) shows both the schema and the
|
|
190
|
+
error for a call.
|
|
191
|
+
|
|
150
192
|
## Client-side debugging tools
|
|
151
193
|
|
|
152
194
|
When the gateway looks healthy, reproduce the failure against the client to see
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
AI Gateway Metering records application usage and configures the app's own
|
|
4
4
|
budgets before the provider request runs. It meters spend, tokens, and requests.
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Budget rules can cover the whole application or each distinct value of an
|
|
6
|
+
expression.
|
|
7
7
|
|
|
8
8
|
When a limit is exceeded, the policy activates the model selection's
|
|
9
9
|
`quotaFallback` when AI Gateway Fallback Model supplied one. Otherwise it
|
|
@@ -20,31 +20,47 @@ Semantic Cache, after Metering so cache hits still count toward request limits.
|
|
|
20
20
|
"name": "ai-gateway-metering-v2-inbound",
|
|
21
21
|
"options": {
|
|
22
22
|
"throwOnFailure": false,
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
"
|
|
23
|
+
"budgetRules": [
|
|
24
|
+
{
|
|
25
|
+
"budgetBy": "app",
|
|
26
|
+
"meters": [
|
|
27
|
+
{
|
|
28
|
+
"meter": "cost",
|
|
29
|
+
"period": "monthly",
|
|
30
|
+
"value": 100,
|
|
31
|
+
"action": "block"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"meter": "cost",
|
|
35
|
+
"period": "monthly",
|
|
36
|
+
"value": 80,
|
|
37
|
+
"action": "warn"
|
|
31
38
|
}
|
|
32
|
-
|
|
39
|
+
]
|
|
33
40
|
},
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
{
|
|
42
|
+
"budgetBy": "expression",
|
|
43
|
+
"expression": "request.headers.get(\"x-user\")",
|
|
44
|
+
"meters": [
|
|
45
|
+
{
|
|
46
|
+
"meter": "cost",
|
|
47
|
+
"period": "daily",
|
|
48
|
+
"value": 5,
|
|
49
|
+
"action": "block"
|
|
50
|
+
}
|
|
51
|
+
]
|
|
39
52
|
}
|
|
40
|
-
|
|
53
|
+
]
|
|
41
54
|
}
|
|
42
55
|
}
|
|
43
56
|
```
|
|
44
57
|
|
|
45
|
-
The supported meters are `
|
|
46
|
-
`daily` and `monthly
|
|
47
|
-
|
|
58
|
+
The supported meters are `cost`, `requests`, and `tokens`. The supported periods
|
|
59
|
+
are `hourly`, `daily`, `weekly`, and `monthly`. An application can have at most
|
|
60
|
+
five rules. An expression rule creates a separate budget for every distinct
|
|
61
|
+
value of its expression. An action of `"warn"` notifies without blocking. An
|
|
62
|
+
action of `"block"` activates the configured quota fallback or returns
|
|
63
|
+
`429 Too Many Requests` when usage reaches the value.
|
|
48
64
|
|
|
49
65
|
> **Budgets fail open by default.** When `throwOnFailure` is `false`, a metering
|
|
50
66
|
> service failure lets the request proceed unmetered and no limit is checked.
|
|
@@ -52,8 +68,8 @@ The supported meters are `costs`, `tokens`, and `requests`. Each meter can have
|
|
|
52
68
|
|
|
53
69
|
## Team limits
|
|
54
70
|
|
|
55
|
-
|
|
56
|
-
|
|
71
|
+
Budgets configured on this application govern only this app. Limits configured
|
|
72
|
+
on a parent team or the gateway root are enforced centrally after the
|
|
57
73
|
application's policy chain, whether or not this policy appears in that chain. An
|
|
58
74
|
inherited limit activates the selected model's quota fallback when available and
|
|
59
75
|
otherwise returns `429 Too Many Requests`.
|
|
@@ -62,83 +78,3 @@ An application cannot disable inherited enforcement through its policy chain. If
|
|
|
62
78
|
the central hierarchical check is unavailable, the request proceeds. The
|
|
63
79
|
policy's `throwOnFailure` option controls failures while checking or recording
|
|
64
80
|
the app's own limits; it does not change inherited-limit behavior.
|
|
65
|
-
|
|
66
|
-
## Set limits from custom code
|
|
67
|
-
|
|
68
|
-
Use a custom inbound policy to vary limits for individual requests. For example,
|
|
69
|
-
the following policy gives callers with a `pro` plan a higher daily request
|
|
70
|
-
limit:
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
import {
|
|
74
|
-
AIGatewayMeteringV2InboundPolicy,
|
|
75
|
-
type ZuploContext,
|
|
76
|
-
type ZuploRequest,
|
|
77
|
-
} from "@zuplo/runtime";
|
|
78
|
-
|
|
79
|
-
export default function applyPlanLimit(
|
|
80
|
-
request: ZuploRequest,
|
|
81
|
-
context: ZuploContext
|
|
82
|
-
) {
|
|
83
|
-
const dailyRequestLimit = request.user?.data.plan === "pro" ? 10_000 : 1_000;
|
|
84
|
-
|
|
85
|
-
AIGatewayMeteringV2InboundPolicy.setLimits(context, {
|
|
86
|
-
requests: {
|
|
87
|
-
daily: {
|
|
88
|
-
enabled: true,
|
|
89
|
-
limit: dailyRequestLimit,
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
return request;
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Register the module as a custom policy:
|
|
99
|
-
|
|
100
|
-
```json
|
|
101
|
-
{
|
|
102
|
-
"name": "plan-based-ai-limits",
|
|
103
|
-
"policyType": "custom-code-inbound",
|
|
104
|
-
"handler": {
|
|
105
|
-
"export": "default",
|
|
106
|
-
"module": "$import(./modules/plan-based-ai-limits)"
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
Place it before metering in the application's inbound policy chain:
|
|
112
|
-
|
|
113
|
-
```json
|
|
114
|
-
{
|
|
115
|
-
"inboundPolicyChain": [
|
|
116
|
-
{
|
|
117
|
-
"name": "plan-based-ai-limits"
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
"name": "ai-gateway-metering-v2-inbound"
|
|
121
|
-
}
|
|
122
|
-
]
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
`setLimits` changes only the fields supplied for the current request. Other
|
|
127
|
-
settings from the metering policy remain unchanged. For example, setting only
|
|
128
|
-
`requests.daily.limit` preserves `requests.daily.enabled`, its warning settings,
|
|
129
|
-
and all monthly limits. It does not update the policy configuration or affect
|
|
130
|
-
later requests.
|
|
131
|
-
|
|
132
|
-
The exported `AIGatewayMeteringV2LimitOverrides` interface can be used when a
|
|
133
|
-
custom policy builds the override object separately. Its shape matches the
|
|
134
|
-
`limits` option. These values are called overrides because they temporarily
|
|
135
|
-
replace matching policy settings for the current request:
|
|
136
|
-
|
|
137
|
-
- Choose a meter: `costs`, `tokens`, or `requests`.
|
|
138
|
-
- Choose a period: `daily` or `monthly`.
|
|
139
|
-
- Set one or more values: `enabled`, `limit`, or `warning`.
|
|
140
|
-
|
|
141
|
-
If model filtering and fallback-model policies selected a `quotaFallback`, an
|
|
142
|
-
exceeded limit activates that fallback instead of returning 429. The fallback
|
|
143
|
-
request is still metered. Order the chain as model filtering, fallback model,
|
|
144
|
-
metering, then policies that may short-circuit such as semantic cache.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
The AI Gateway Metering policy records each application's spend, tokens, and
|
|
2
|
-
requests and enforces
|
|
3
|
-
uses the model selection's quota fallback when one is configured or
|
|
4
|
-
`429 Too Many Requests`.
|
|
2
|
+
requests and enforces spend, token, and request budgets. When a limit is
|
|
3
|
+
exceeded, it uses the model selection's quota fallback when one is configured or
|
|
4
|
+
returns `429 Too Many Requests`.
|
|
5
5
|
|
|
6
6
|
Place it after Model Filtering and Fallback Model, and before policies such as
|
|
7
7
|
Semantic Cache that may answer without calling a provider. Metering fails open
|