@questpie/mcp 3.16.0 → 3.17.0
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/README.md +150 -6
- package/dist/create-server-CEWFf041.mjs +2394 -0
- package/dist/{index-Bt_uk798.d.mts → index-DlBFWTIg.d.mts} +28 -23
- package/dist/index.d.mts +91 -47
- package/dist/index.mjs +92 -5
- package/dist/mcp-CuJEhynp.mjs +350 -0
- package/dist/modules/mcp.d.mts +1 -1
- package/dist/modules/mcp.mjs +1 -1
- package/dist/{plugin-BJ6gELRj.mjs → plugin-BZQe1pji.mjs} +1 -1
- package/dist/plugin.mjs +1 -1
- package/dist/{stdio-qyQFVEyU.mjs → stdio-CgIPiPUb.mjs} +1 -1
- package/dist/stdio-YoM70-42.d.mts +323 -0
- package/dist/stdio.d.mts +1 -1
- package/dist/stdio.mjs +1 -1
- package/package.json +5 -4
- package/dist/create-server-DIvtVlma.mjs +0 -1014
- package/dist/mcp-snyVygDQ.mjs +0 -168
- package/dist/stdio-t_9rKayH.d.mts +0 -154
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Model Context Protocol integration for QUESTPIE apps. The package exposes select
|
|
|
10
10
|
- **Route tools** - opt annotated JSON routes into MCP with path-param support.
|
|
11
11
|
- **Schema resources** - let agents inspect allowed collections, globals, routes, and fields.
|
|
12
12
|
- **Request context preservation** - HTTP tools run under the connecting request/session.
|
|
13
|
+
- **Workload boundary** - remote workloads use a consumer-supplied, fail-closed authorizer instead of OAuth, cookies, requester identity, or stdio system authority.
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
@@ -34,12 +35,48 @@ import { mcpConfig } from "@questpie/mcp";
|
|
|
34
35
|
export default mcpConfig({
|
|
35
36
|
crud: {
|
|
36
37
|
collections: {
|
|
37
|
-
posts: {
|
|
38
|
+
posts: {
|
|
39
|
+
operations: {
|
|
40
|
+
list: true,
|
|
41
|
+
get: true,
|
|
42
|
+
create: true,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
38
45
|
},
|
|
39
46
|
},
|
|
47
|
+
routes: {
|
|
48
|
+
routes: {
|
|
49
|
+
"reports/generate": { operations: { execute: true } },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
resources: {
|
|
53
|
+
collections: { posts: true },
|
|
54
|
+
routes: { "reports/generate": true },
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Omission exposes nothing. Entity names, operations, annotated routes, and
|
|
60
|
+
schema resources must each be present in this catalog. Unknown names are
|
|
61
|
+
ignored. The resolved catalog also supplies the OAuth scope catalog, so OAuth
|
|
62
|
+
never advertises an app entity or operation that MCP does not release.
|
|
63
|
+
|
|
64
|
+
## Stdio Authority
|
|
65
|
+
|
|
66
|
+
Stdio has no ambient system authority. Bind it to an exact user-mode `ctx`, or
|
|
67
|
+
explicitly opt a local maintenance process into the system bypass:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
await startStdioServer(app, {
|
|
71
|
+
config: {
|
|
72
|
+
stdio: { trustedMaintenance: true },
|
|
73
|
+
},
|
|
40
74
|
});
|
|
41
75
|
```
|
|
42
76
|
|
|
77
|
+
Do not enable `trustedMaintenance` for remote or requester-controlled
|
|
78
|
+
processes. It intentionally bypasses ordinary user RBAC and OAuth scope checks.
|
|
79
|
+
|
|
43
80
|
## Custom Tools
|
|
44
81
|
|
|
45
82
|
```ts
|
|
@@ -48,6 +85,8 @@ import { mcpTool } from "@questpie/mcp";
|
|
|
48
85
|
import { z } from "zod";
|
|
49
86
|
|
|
50
87
|
export default mcpTool("publish-summary", {
|
|
88
|
+
access: ({ session }) => !!session,
|
|
89
|
+
scopes: false,
|
|
51
90
|
inputSchema: z.object({ postId: z.string() }),
|
|
52
91
|
}).handler(async ({ input, ctx }) => {
|
|
53
92
|
const post = await ctx.collections.posts.findById(input.postId);
|
|
@@ -55,14 +94,119 @@ export default mcpTool("publish-summary", {
|
|
|
55
94
|
});
|
|
56
95
|
```
|
|
57
96
|
|
|
97
|
+
## Remote Workloads
|
|
98
|
+
|
|
99
|
+
Remote workload execution uses a separate public factory.
|
|
100
|
+
`createWorkloadMcpServer` accepts only an opaque envelope, a consumer-supplied
|
|
101
|
+
authorizer, a required consumer-supplied context binder, an optional audit sink,
|
|
102
|
+
and an optional opaque execution handoff. The binder turns the opaque authorized
|
|
103
|
+
context into the exact QUESTPIE request context used for discovery access checks,
|
|
104
|
+
call-time access checks, and handler execution. The bound context must use
|
|
105
|
+
`accessMode: "user"`; remote workloads cannot acquire the system bypass. The
|
|
106
|
+
factory deliberately has no request, cookie, requester context, OAuth, or
|
|
107
|
+
access-mode option.
|
|
108
|
+
|
|
109
|
+
Set `concurrencyKey` to a stable, non-secret consumer or tenant identifier when
|
|
110
|
+
multiple workload servers or ports should share one per-principal concurrency
|
|
111
|
+
bucket. Different keys are isolated; omitting the key creates an instance-local
|
|
112
|
+
bucket.
|
|
113
|
+
|
|
114
|
+
Every workload-visible tool opts into named capability facts:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { mcpTool } from "@questpie/mcp";
|
|
118
|
+
import { z } from "zod";
|
|
119
|
+
|
|
120
|
+
export default mcpTool("messages.reply", {
|
|
121
|
+
access: true,
|
|
122
|
+
scopes: false,
|
|
123
|
+
inputSchema: z.object({ body: z.string() }),
|
|
124
|
+
workload: {
|
|
125
|
+
capabilities: ["messages.write"],
|
|
126
|
+
handoff: "messages.commit",
|
|
127
|
+
},
|
|
128
|
+
}).handler(async ({ input }) => ({
|
|
129
|
+
content: [{ type: "text", text: input.body }],
|
|
130
|
+
}));
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
MCP passes only the opaque envelope, phase, and bounded tool facts (`kind`,
|
|
134
|
+
`name`, `operation`, `intent`, `transport`, `capabilities`, and optional
|
|
135
|
+
`handoff`) to the authorizer. Discovery and every call are authorized and bound
|
|
136
|
+
independently. MCP does not interpret the returned opaque context or attribution.
|
|
137
|
+
Calls with a `handoff` capability execute through the consumer's handoff; MCP
|
|
138
|
+
does not add a durable effect or idempotency store. Missing or malformed
|
|
139
|
+
authorization or context binding fails closed, and tools without an explicit
|
|
140
|
+
workload requirement stay hidden.
|
|
141
|
+
|
|
142
|
+
When a trusted in-process subsystem needs only workload-enabled custom tools,
|
|
143
|
+
use the programmatic port instead of creating an MCP client/server loop:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { createWorkloadMcpToolPort, mcpPublicErrorCode } from "@questpie/mcp";
|
|
147
|
+
|
|
148
|
+
const tools = createWorkloadMcpToolPort(app, workloadOptions);
|
|
149
|
+
const released = await tools.listCustomTools({ signal });
|
|
150
|
+
const result = await tools.callCustomTool({
|
|
151
|
+
name: "messages.reply",
|
|
152
|
+
input: { body: "Hello" },
|
|
153
|
+
signal,
|
|
154
|
+
requestId: runId,
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The port excludes generated CRUD tools, routes, and resources by construction.
|
|
159
|
+
It uses the same immutable release catalog, per-call workload authorization,
|
|
160
|
+
context binding, MCP access rule, Zod input/output validation, execution
|
|
161
|
+
budgets, cancellation, and public error contract as transport calls. A failed
|
|
162
|
+
call returns `isError: true` with a stable code and correlation ID under
|
|
163
|
+
`_meta["questpie/error"]`. `mcpPublicErrorCode(error)` safely reads the code
|
|
164
|
+
from errors thrown by list/cancellation paths.
|
|
165
|
+
|
|
166
|
+
## Execution Budgets And Errors
|
|
167
|
+
|
|
168
|
+
All tool calls and schema resource reads share bounded execution state for an
|
|
169
|
+
app, including across HTTP requests and independently created workload ports.
|
|
170
|
+
Defaults can be narrowed with `execution`:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
export default mcpConfig({
|
|
174
|
+
execution: {
|
|
175
|
+
maxInputBytes: 64 * 1024,
|
|
176
|
+
maxInputDepth: 16,
|
|
177
|
+
maxOutputDepth: 64,
|
|
178
|
+
maxValueNodes: 10_000,
|
|
179
|
+
maxOutputBytes: 1024 * 1024,
|
|
180
|
+
timeoutMs: 30_000,
|
|
181
|
+
maxConcurrency: 64,
|
|
182
|
+
maxConcurrencyPerPrincipal: 8,
|
|
183
|
+
maxTools: 512,
|
|
184
|
+
maxResources: 512,
|
|
185
|
+
onDiagnostic(event) {
|
|
186
|
+
// Trusted, bounded server-side telemetry only.
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Saturation rejects immediately; it does not create an unbounded queue. Timed
|
|
193
|
+
out or cancelled work keeps its concurrency permit until the underlying
|
|
194
|
+
operation actually settles, so code that ignores `signal` cannot bypass the
|
|
195
|
+
limit. Custom tool handlers and workload handoffs receive `signal`,
|
|
196
|
+
`requestId`, and `correlationId`. Public errors use stable codes:
|
|
197
|
+
`access_denied`, `invalid_input`, `input_too_large`, `output_too_large`,
|
|
198
|
+
`timeout`, `cancelled`, `busy`, and `internal`. Protocol responses and
|
|
199
|
+
diagnostics never include raw handler/database error messages, request input,
|
|
200
|
+
output, credentials, authorization envelopes, or workload attribution.
|
|
201
|
+
|
|
58
202
|
## Exports
|
|
59
203
|
|
|
60
|
-
| Entry Point | Purpose
|
|
61
|
-
| --------------------------- |
|
|
204
|
+
| Entry Point | Purpose |
|
|
205
|
+
| --------------------------- | ------------------------------------ |
|
|
62
206
|
| `@questpie/mcp` | Config, plugin, server, tool factory |
|
|
63
|
-
| `@questpie/mcp/modules/mcp` | `mcpModule` for `modules.ts`
|
|
64
|
-
| `@questpie/mcp/plugin` | Codegen plugin export
|
|
65
|
-
| `@questpie/mcp/stdio` | Stdio server entry
|
|
207
|
+
| `@questpie/mcp/modules/mcp` | `mcpModule` for `modules.ts` |
|
|
208
|
+
| `@questpie/mcp/plugin` | Codegen plugin export |
|
|
209
|
+
| `@questpie/mcp/stdio` | Stdio server entry |
|
|
66
210
|
|
|
67
211
|
## License
|
|
68
212
|
|