zuplo 7.5.7 → 7.6.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/docs/handlers/mcp-server.mdx +7 -0
- 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/package.json +19 -6
|
@@ -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
|
|
|
@@ -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
|
|
@@ -38,14 +38,25 @@
|
|
|
38
38
|
"required": [],
|
|
39
39
|
"examples": [
|
|
40
40
|
{
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
"budgetRules": [
|
|
42
|
+
{
|
|
43
|
+
"budgetBy": "app",
|
|
44
|
+
"meters": [
|
|
45
|
+
{
|
|
46
|
+
"meter": "cost",
|
|
47
|
+
"period": "monthly",
|
|
48
|
+
"value": 100,
|
|
49
|
+
"action": "block"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"meter": "cost",
|
|
53
|
+
"period": "monthly",
|
|
54
|
+
"value": 80,
|
|
55
|
+
"action": "warn"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
47
58
|
}
|
|
48
|
-
|
|
59
|
+
],
|
|
49
60
|
"throwOnFailure": false
|
|
50
61
|
}
|
|
51
62
|
],
|
|
@@ -55,8 +66,61 @@
|
|
|
55
66
|
"default": false,
|
|
56
67
|
"description": "Throw when the metering service is unavailable instead of failing open."
|
|
57
68
|
},
|
|
69
|
+
"budgetRules": {
|
|
70
|
+
"type": "array",
|
|
71
|
+
"maxItems": 5,
|
|
72
|
+
"description": "Budget rules for this application. Each rule budgets the whole app (\"app\") or each distinct value of an expression (\"expression\"), with warn and block thresholds per meter and period.",
|
|
73
|
+
"items": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"additionalProperties": false,
|
|
76
|
+
"required": ["budgetBy", "meters"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"budgetBy": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"enum": ["app", "expression"]
|
|
81
|
+
},
|
|
82
|
+
"expression": {
|
|
83
|
+
"type": "string",
|
|
84
|
+
"minLength": 1,
|
|
85
|
+
"description": "Required when budgetBy is \"expression\"; forbidden for \"app\". Each distinct value gets its own budget."
|
|
86
|
+
},
|
|
87
|
+
"meters": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"minItems": 1,
|
|
90
|
+
"maxItems": 24,
|
|
91
|
+
"items": {
|
|
92
|
+
"type": "object",
|
|
93
|
+
"additionalProperties": false,
|
|
94
|
+
"required": ["meter", "period", "value", "action"],
|
|
95
|
+
"properties": {
|
|
96
|
+
"meter": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"enum": ["cost", "requests", "tokens"]
|
|
99
|
+
},
|
|
100
|
+
"period": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"enum": ["hourly", "daily", "weekly", "monthly"]
|
|
103
|
+
},
|
|
104
|
+
"value": {
|
|
105
|
+
"type": "number",
|
|
106
|
+
"minimum": 0
|
|
107
|
+
},
|
|
108
|
+
"action": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"enum": ["warn", "block"]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
58
118
|
"limits": {
|
|
59
119
|
"type": "object",
|
|
120
|
+
"deprecated": true,
|
|
121
|
+
"doNotSuggest": true,
|
|
122
|
+
"x-show-example": false,
|
|
123
|
+
"x-advanced": true,
|
|
60
124
|
"description": "Usage limits grouped by meter.",
|
|
61
125
|
"additionalProperties": false,
|
|
62
126
|
"properties": {
|
|
@@ -271,14 +335,25 @@
|
|
|
271
335
|
"export": "AIGatewayMeteringV2InboundPolicy",
|
|
272
336
|
"module": "$import(@zuplo/runtime)",
|
|
273
337
|
"options": {
|
|
274
|
-
"
|
|
275
|
-
|
|
276
|
-
"
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
338
|
+
"budgetRules": [
|
|
339
|
+
{
|
|
340
|
+
"budgetBy": "app",
|
|
341
|
+
"meters": [
|
|
342
|
+
{
|
|
343
|
+
"meter": "cost",
|
|
344
|
+
"period": "monthly",
|
|
345
|
+
"value": 100,
|
|
346
|
+
"action": "block"
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
"meter": "cost",
|
|
350
|
+
"period": "monthly",
|
|
351
|
+
"value": 80,
|
|
352
|
+
"action": "warn"
|
|
353
|
+
}
|
|
354
|
+
]
|
|
280
355
|
}
|
|
281
|
-
|
|
356
|
+
],
|
|
282
357
|
"throwOnFailure": false
|
|
283
358
|
}
|
|
284
359
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zuplo",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.6.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "The
|
|
5
|
+
"description": "The official Zuplo CLI for local development and platform management",
|
|
6
|
+
"homepage": "https://zuplo.com/docs/cli/overview",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/zuplo/zuplo.git"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"zuplo",
|
|
13
|
+
"cli",
|
|
14
|
+
"api-gateway",
|
|
15
|
+
"api-management",
|
|
16
|
+
"ai-gateway",
|
|
17
|
+
"mcp-gateway"
|
|
18
|
+
],
|
|
6
19
|
"author": "Zuplo, Inc.",
|
|
7
20
|
"license": "See LICENSE in LICENSE.txt",
|
|
8
21
|
"files": [
|
|
@@ -19,9 +32,9 @@
|
|
|
19
32
|
"zuplo": "zuplo.js"
|
|
20
33
|
},
|
|
21
34
|
"dependencies": {
|
|
22
|
-
"@zuplo/cli": "7.
|
|
23
|
-
"@zuplo/core": "7.
|
|
24
|
-
"@zuplo/runtime": "7.
|
|
25
|
-
"@zuplo/test": "7.
|
|
35
|
+
"@zuplo/cli": "7.6.1",
|
|
36
|
+
"@zuplo/core": "7.6.1",
|
|
37
|
+
"@zuplo/runtime": "7.6.1",
|
|
38
|
+
"@zuplo/test": "7.6.1"
|
|
26
39
|
}
|
|
27
40
|
}
|