@q32/core 0.1.23 → 0.1.24
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 +3 -0
- package/dist/api.d.ts +67 -2
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +173 -8
- package/dist/api.js.map +1 -1
- package/dist/mcp.d.ts +32 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +88 -7
- package/dist/mcp.js.map +1 -1
- package/docs/evaluation.md +2 -1
- package/package.json +1 -1
- package/src/api.ts +248 -8
- package/src/mcp.ts +124 -8
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ The first release focuses on common infrastructure repeated across Q32 apps:
|
|
|
6
6
|
|
|
7
7
|
- AI provider contracts and JSON extraction helpers
|
|
8
8
|
- API operation registries
|
|
9
|
+
- API/MCP discovery surfaces: OpenAPI docs, API catalogs, Agent Skills indexes, MCP manifests, and bearer challenges
|
|
9
10
|
- framework-neutral auth/session/MCP auth services with thin Hono and React Router adapters
|
|
10
11
|
- billing plan/status primitives
|
|
11
12
|
- Cloudflare binding guards
|
|
@@ -53,8 +54,10 @@ import {
|
|
|
53
54
|
appUrl,
|
|
54
55
|
createAuthSystem,
|
|
55
56
|
createId,
|
|
57
|
+
apiCatalogLinkset,
|
|
56
58
|
honoAuthMiddleware,
|
|
57
59
|
jsonResponse,
|
|
60
|
+
mcpManifest,
|
|
58
61
|
oauthAuthorizationServerMetadata,
|
|
59
62
|
reactRouterAuthContext,
|
|
60
63
|
renderSitemapXml,
|
package/dist/api.d.ts
CHANGED
|
@@ -1,16 +1,42 @@
|
|
|
1
1
|
export type ApiMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
2
|
+
export type ApiMethodLower = Lowercase<ApiMethod>;
|
|
3
|
+
export type ApiInputLocation = "path" | "query" | "body";
|
|
2
4
|
export interface SchemaLike<T = unknown> {
|
|
3
5
|
parse(value: unknown): T;
|
|
4
6
|
}
|
|
7
|
+
export type JsonSchemaLike = Record<string, unknown>;
|
|
8
|
+
export type ApiOperationInputSpec = {
|
|
9
|
+
properties?: Record<string, JsonSchemaLike>;
|
|
10
|
+
required?: string[];
|
|
11
|
+
locations?: Record<string, ApiInputLocation>;
|
|
12
|
+
pathDescriptions?: Record<string, string>;
|
|
13
|
+
schema?: JsonSchemaLike;
|
|
14
|
+
};
|
|
15
|
+
export type ApiTagSpec = {
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
};
|
|
5
19
|
export type ApiOperationSpec<TInput = unknown> = {
|
|
6
20
|
name: string;
|
|
7
21
|
title: string;
|
|
8
22
|
description: string;
|
|
9
|
-
method: ApiMethod;
|
|
23
|
+
method: ApiMethod | ApiMethodLower;
|
|
10
24
|
path: string;
|
|
11
25
|
scope?: string;
|
|
26
|
+
tags?: string[];
|
|
12
27
|
inputSchema?: SchemaLike<TInput>;
|
|
28
|
+
input?: ApiOperationInputSpec;
|
|
29
|
+
responseKey?: string;
|
|
30
|
+
responseSchema?: JsonSchemaLike;
|
|
13
31
|
openapi?: boolean;
|
|
32
|
+
mcp?: {
|
|
33
|
+
toolName?: string;
|
|
34
|
+
title?: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
inputSchema?: JsonSchemaLike;
|
|
37
|
+
requiresAuth?: boolean;
|
|
38
|
+
annotations?: Record<string, unknown>;
|
|
39
|
+
};
|
|
14
40
|
};
|
|
15
41
|
export type ApiOperation<TContext, TInput = unknown, TOutput = unknown> = ApiOperationSpec<TInput> & {
|
|
16
42
|
handler: (context: TContext, input: TInput) => Promise<TOutput> | TOutput;
|
|
@@ -21,5 +47,44 @@ export declare function defineApiRegistry<TContext, TRegistry extends ApiOperati
|
|
|
21
47
|
export declare function dispatchApiOperation<TContext>(registry: ApiOperationRegistry<TContext>, name: string, context: TContext, input: unknown): Promise<unknown>;
|
|
22
48
|
export declare function operationPathParameters(path: string): string[];
|
|
23
49
|
export declare function interpolateOperationPath(path: string, input: Record<string, unknown>): string;
|
|
24
|
-
export
|
|
50
|
+
export type OpenApiDocumentOptions = {
|
|
51
|
+
title: string;
|
|
52
|
+
version: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
origin?: string;
|
|
55
|
+
tags?: ApiTagSpec[];
|
|
56
|
+
securitySchemeName?: string;
|
|
57
|
+
includeDefaultErrorResponses?: boolean;
|
|
58
|
+
};
|
|
59
|
+
export declare function openApiDocumentForRegistry<TContext>(registry: ApiOperationRegistry<TContext>, options: OpenApiDocumentOptions): Record<string, unknown>;
|
|
60
|
+
export declare function openApiPathsForRegistry<TContext>(registry: ApiOperationRegistry<TContext>, options?: {
|
|
61
|
+
securitySchemeName?: string;
|
|
62
|
+
includeDefaultErrorResponses?: boolean;
|
|
63
|
+
}): Record<string, Record<string, Record<string, unknown>>>;
|
|
64
|
+
export type ApiCatalogService = {
|
|
65
|
+
anchor: string;
|
|
66
|
+
serviceDesc?: string;
|
|
67
|
+
serviceDescType?: string;
|
|
68
|
+
serviceDoc?: string;
|
|
69
|
+
serviceDocType?: string;
|
|
70
|
+
status?: string;
|
|
71
|
+
statusType?: string;
|
|
72
|
+
};
|
|
73
|
+
export declare function apiCatalogLinkset(services: ApiCatalogService[]): {
|
|
74
|
+
linkset: Array<Record<string, unknown>>;
|
|
75
|
+
};
|
|
76
|
+
export type AgentDiscoveryLink = {
|
|
77
|
+
href: string;
|
|
78
|
+
rel: string;
|
|
79
|
+
type?: string;
|
|
80
|
+
};
|
|
81
|
+
export declare function agentDiscoveryLinkHeader(links: AgentDiscoveryLink[]): string;
|
|
82
|
+
export type AgentSkill = {
|
|
83
|
+
name: string;
|
|
84
|
+
type: string;
|
|
85
|
+
description?: string;
|
|
86
|
+
url: string;
|
|
87
|
+
sha256?: string;
|
|
88
|
+
};
|
|
89
|
+
export declare function agentSkillsIndex(skills: AgentSkill[]): Record<string, unknown>;
|
|
25
90
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
CHANGED
|
@@ -35,32 +35,197 @@ export function interpolateOperationPath(path, input) {
|
|
|
35
35
|
return encodeURIComponent(String(value));
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
-
export function
|
|
38
|
+
export function openApiDocumentForRegistry(registry, options) {
|
|
39
|
+
const securitySchemeName = options.securitySchemeName ?? "bearerAuth";
|
|
40
|
+
return {
|
|
41
|
+
openapi: "3.0.3",
|
|
42
|
+
info: {
|
|
43
|
+
title: options.title,
|
|
44
|
+
version: options.version,
|
|
45
|
+
...(options.description ? { description: options.description } : {}),
|
|
46
|
+
},
|
|
47
|
+
...(options.origin ? { servers: [{ url: options.origin.replace(/\/$/, "") }] } : {}),
|
|
48
|
+
...(options.tags ? { tags: options.tags } : {}),
|
|
49
|
+
components: {
|
|
50
|
+
securitySchemes: {
|
|
51
|
+
[securitySchemeName]: { type: "http", scheme: "bearer" },
|
|
52
|
+
},
|
|
53
|
+
schemas: {
|
|
54
|
+
Error: {
|
|
55
|
+
type: "object",
|
|
56
|
+
properties: {
|
|
57
|
+
error: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: {
|
|
60
|
+
code: { type: "string" },
|
|
61
|
+
message: { type: "string" },
|
|
62
|
+
details: {},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
paths: openApiPathsForRegistry(registry, {
|
|
70
|
+
securitySchemeName,
|
|
71
|
+
includeDefaultErrorResponses: options.includeDefaultErrorResponses ?? true,
|
|
72
|
+
}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function openApiPathsForRegistry(registry, options = {}) {
|
|
39
76
|
const paths = {};
|
|
77
|
+
const securitySchemeName = options.securitySchemeName ?? "bearerAuth";
|
|
40
78
|
for (const operation of Object.values(registry)) {
|
|
41
79
|
if (operation.openapi === false)
|
|
42
80
|
continue;
|
|
43
81
|
const path = operation.path.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g, "{$1}");
|
|
44
82
|
const method = operation.method.toLowerCase();
|
|
83
|
+
const input = operation.input;
|
|
45
84
|
paths[path] ??= {};
|
|
46
85
|
paths[path][method] = {
|
|
47
86
|
operationId: operation.name,
|
|
48
87
|
summary: operation.title,
|
|
49
88
|
description: operation.description,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
required: true,
|
|
55
|
-
schema: { type: "string" },
|
|
56
|
-
})),
|
|
89
|
+
tags: operation.tags,
|
|
90
|
+
security: operation.scope ? [{ [securitySchemeName]: [operation.scope] }] : undefined,
|
|
91
|
+
parameters: openApiParametersForOperation(operation),
|
|
92
|
+
requestBody: openApiRequestBody(input),
|
|
57
93
|
responses: {
|
|
58
94
|
"200": {
|
|
59
95
|
description: "OK",
|
|
96
|
+
...(operation.responseSchema
|
|
97
|
+
? {
|
|
98
|
+
content: {
|
|
99
|
+
"application/json": { schema: operation.responseSchema },
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
: operation.responseKey
|
|
103
|
+
? {
|
|
104
|
+
content: {
|
|
105
|
+
"application/json": {
|
|
106
|
+
schema: {
|
|
107
|
+
type: "object",
|
|
108
|
+
additionalProperties: true,
|
|
109
|
+
properties: { [operation.responseKey]: {} },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
: {}),
|
|
60
115
|
},
|
|
116
|
+
...(options.includeDefaultErrorResponses === false
|
|
117
|
+
? {}
|
|
118
|
+
: {
|
|
119
|
+
"400": errorResponse(),
|
|
120
|
+
"401": errorResponse(),
|
|
121
|
+
"403": errorResponse(),
|
|
122
|
+
"404": errorResponse(),
|
|
123
|
+
"429": errorResponse(),
|
|
124
|
+
}),
|
|
61
125
|
},
|
|
62
126
|
};
|
|
63
127
|
}
|
|
64
128
|
return paths;
|
|
65
129
|
}
|
|
130
|
+
function openApiParametersForOperation(operation) {
|
|
131
|
+
const input = operation.input;
|
|
132
|
+
if (!input?.locations) {
|
|
133
|
+
return operationPathParameters(operation.path).map((name) => ({
|
|
134
|
+
name,
|
|
135
|
+
in: "path",
|
|
136
|
+
required: true,
|
|
137
|
+
schema: { type: "string" },
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
return Object.entries(input.locations)
|
|
141
|
+
.filter(([, location]) => location === "path" || location === "query")
|
|
142
|
+
.map(([name, location]) => ({
|
|
143
|
+
name,
|
|
144
|
+
in: location,
|
|
145
|
+
required: location === "path" || (input.required?.includes(name) ?? false),
|
|
146
|
+
...(location === "path"
|
|
147
|
+
? {
|
|
148
|
+
description: input.pathDescriptions?.[name] ?? name,
|
|
149
|
+
}
|
|
150
|
+
: {}),
|
|
151
|
+
schema: input.properties?.[name] ?? { type: "string" },
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
function openApiRequestBody(input) {
|
|
155
|
+
if (!input?.locations)
|
|
156
|
+
return undefined;
|
|
157
|
+
const bodyEntries = Object.entries(input.locations).filter(([, location]) => location === "body");
|
|
158
|
+
if (bodyEntries.length === 0)
|
|
159
|
+
return undefined;
|
|
160
|
+
const properties = Object.fromEntries(bodyEntries.map(([name]) => [name, input.properties?.[name] ?? {}]));
|
|
161
|
+
const required = (input.required ?? []).filter((name) => input.locations?.[name] === "body");
|
|
162
|
+
return {
|
|
163
|
+
required: required.length > 0,
|
|
164
|
+
content: {
|
|
165
|
+
"application/json": {
|
|
166
|
+
schema: input.schema ?? {
|
|
167
|
+
type: "object",
|
|
168
|
+
properties,
|
|
169
|
+
...(required.length ? { required } : {}),
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function errorResponse() {
|
|
176
|
+
return {
|
|
177
|
+
description: "Error response",
|
|
178
|
+
content: {
|
|
179
|
+
"application/json": { schema: { $ref: "#/components/schemas/Error" } },
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
export function apiCatalogLinkset(services) {
|
|
184
|
+
return {
|
|
185
|
+
linkset: services.map((service) => ({
|
|
186
|
+
anchor: service.anchor,
|
|
187
|
+
...(service.serviceDesc
|
|
188
|
+
? {
|
|
189
|
+
"service-desc": [
|
|
190
|
+
{
|
|
191
|
+
href: service.serviceDesc,
|
|
192
|
+
type: service.serviceDescType ?? "application/json",
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
}
|
|
196
|
+
: {}),
|
|
197
|
+
...(service.serviceDoc
|
|
198
|
+
? {
|
|
199
|
+
"service-doc": [
|
|
200
|
+
{
|
|
201
|
+
href: service.serviceDoc,
|
|
202
|
+
type: service.serviceDocType ?? "text/html",
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
}
|
|
206
|
+
: {}),
|
|
207
|
+
...(service.status
|
|
208
|
+
? {
|
|
209
|
+
status: [
|
|
210
|
+
{
|
|
211
|
+
href: service.status,
|
|
212
|
+
type: service.statusType ?? "application/json",
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
}
|
|
216
|
+
: {}),
|
|
217
|
+
})),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
export function agentDiscoveryLinkHeader(links) {
|
|
221
|
+
return links
|
|
222
|
+
.map((link) => `<${link.href}>; rel="${link.rel}"${link.type ? `; type="${link.type}"` : ""}`)
|
|
223
|
+
.join(", ");
|
|
224
|
+
}
|
|
225
|
+
export function agentSkillsIndex(skills) {
|
|
226
|
+
return {
|
|
227
|
+
$schema: "https://raw.githubusercontent.com/cloudflare/agent-skills-discovery-rfc/main/schema/v0.2.0/index.schema.json",
|
|
228
|
+
skills,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
66
231
|
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import type { ApiOperationRegistry } from "./api.js";
|
|
1
|
+
import type { ApiMethod, ApiMethodLower, ApiOperationRegistry } from "./api.js";
|
|
2
2
|
export type McpToolDescriptor = {
|
|
3
3
|
name: string;
|
|
4
4
|
title?: string;
|
|
5
5
|
description?: string;
|
|
6
6
|
inputSchema: Record<string, unknown>;
|
|
7
7
|
annotations?: Record<string, unknown>;
|
|
8
|
+
requiresAuth?: boolean;
|
|
9
|
+
};
|
|
10
|
+
export type McpToolAnnotations = {
|
|
11
|
+
readOnlyHint: boolean;
|
|
12
|
+
destructiveHint: boolean;
|
|
13
|
+
idempotentHint: boolean;
|
|
14
|
+
openWorldHint: boolean;
|
|
8
15
|
};
|
|
9
16
|
export declare function mcpToolsFromApiRegistry<TContext>(registry: ApiOperationRegistry<TContext>, options?: {
|
|
10
17
|
includeScopes?: boolean;
|
|
@@ -14,5 +21,29 @@ export declare function mcpWellKnownServerMetadata(options: {
|
|
|
14
21
|
url: string;
|
|
15
22
|
description?: string;
|
|
16
23
|
authorizationServers?: string[];
|
|
24
|
+
protectedResource?: string;
|
|
25
|
+
tools?: McpToolDescriptor[];
|
|
26
|
+
resources?: Array<Record<string, unknown>>;
|
|
27
|
+
prompts?: Array<Record<string, unknown>>;
|
|
28
|
+
oauth?: Record<string, unknown>;
|
|
29
|
+
metadata?: Record<string, unknown>;
|
|
30
|
+
}): Record<string, unknown>;
|
|
31
|
+
export declare function mcpManifest(options: {
|
|
32
|
+
name: string;
|
|
33
|
+
endpoint: string;
|
|
34
|
+
transport?: "streamable_http" | "http";
|
|
35
|
+
anonymousDescription?: string;
|
|
36
|
+
authenticatedDescription?: string;
|
|
37
|
+
tools?: McpToolDescriptor[];
|
|
38
|
+
resources?: Array<Record<string, unknown>>;
|
|
39
|
+
prompts?: Array<Record<string, unknown>>;
|
|
40
|
+
metadata?: Record<string, unknown>;
|
|
17
41
|
}): Record<string, unknown>;
|
|
42
|
+
export declare function mcpBearerChallenge(options: {
|
|
43
|
+
origin: string;
|
|
44
|
+
resourcePath?: string;
|
|
45
|
+
error?: string;
|
|
46
|
+
errorDescription?: string;
|
|
47
|
+
}): string;
|
|
48
|
+
export declare function annotationsForApiMethod(method: ApiMethod | ApiMethodLower): McpToolAnnotations;
|
|
18
49
|
//# sourceMappingURL=mcp.d.ts.map
|
package/dist/mcp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAEhF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,QAAQ,EAC9C,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,CAAC,EACxC,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,iBAAiB,EAAE,CAgBrB;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3C,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAa1B;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;IACvC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3C,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA8B1B;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,MAAM,CAIT;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,cAAc,GAAG,kBAAkB,CAK9F"}
|
package/dist/mcp.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
export function mcpToolsFromApiRegistry(registry, options = {}) {
|
|
2
|
-
return Object.values(registry).map((operation) =>
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
return Object.values(registry).map((operation) => {
|
|
3
|
+
const annotations = {
|
|
4
|
+
...annotationsForApiMethod(operation.method),
|
|
5
|
+
...(options.includeScopes && operation.scope ? { scope: operation.scope } : {}),
|
|
6
|
+
...(operation.mcp?.annotations ?? {}),
|
|
7
|
+
};
|
|
8
|
+
return {
|
|
9
|
+
name: operation.mcp?.toolName ?? operation.name,
|
|
10
|
+
title: operation.mcp?.title ?? operation.title,
|
|
11
|
+
description: operation.mcp?.description ?? operation.description,
|
|
12
|
+
inputSchema: operation.mcp?.inputSchema ?? operation.input?.schema ?? jsonSchemaPlaceholder(operation.path),
|
|
13
|
+
annotations,
|
|
14
|
+
requiresAuth: operation.mcp?.requiresAuth ?? Boolean(operation.scope),
|
|
15
|
+
};
|
|
16
|
+
});
|
|
9
17
|
}
|
|
10
18
|
export function mcpWellKnownServerMetadata(options) {
|
|
11
19
|
return {
|
|
@@ -14,6 +22,79 @@ export function mcpWellKnownServerMetadata(options) {
|
|
|
14
22
|
transport: "http",
|
|
15
23
|
url: options.url,
|
|
16
24
|
authorization_servers: options.authorizationServers,
|
|
25
|
+
oauth: options.oauth ?? oauthLinks(options.authorizationServers?.[0], options.protectedResource),
|
|
26
|
+
tools: options.tools,
|
|
27
|
+
resources: options.resources,
|
|
28
|
+
prompts: options.prompts,
|
|
29
|
+
metadata: options.metadata,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function mcpManifest(options) {
|
|
33
|
+
return {
|
|
34
|
+
server: {
|
|
35
|
+
name: options.name,
|
|
36
|
+
endpoint: options.endpoint,
|
|
37
|
+
transport: options.transport ?? "streamable_http",
|
|
38
|
+
},
|
|
39
|
+
authentication: {
|
|
40
|
+
anonymous: options.anonymousDescription,
|
|
41
|
+
authenticated: options.authenticatedDescription,
|
|
42
|
+
},
|
|
43
|
+
capabilities: {
|
|
44
|
+
tools: options.tools
|
|
45
|
+
? Object.fromEntries(options.tools.map((tool) => [
|
|
46
|
+
tool.name,
|
|
47
|
+
{
|
|
48
|
+
name: tool.name,
|
|
49
|
+
title: tool.title,
|
|
50
|
+
description: tool.description,
|
|
51
|
+
requiresAuth: tool.requiresAuth,
|
|
52
|
+
},
|
|
53
|
+
]))
|
|
54
|
+
: undefined,
|
|
55
|
+
resources: options.resources,
|
|
56
|
+
prompts: options.prompts,
|
|
57
|
+
},
|
|
58
|
+
metadata: options.metadata,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function mcpBearerChallenge(options) {
|
|
62
|
+
const origin = options.origin.replace(/\/$/, "");
|
|
63
|
+
const resourcePath = options.resourcePath ?? "/.well-known/oauth-protected-resource/mcp";
|
|
64
|
+
return `Bearer error="${options.error ?? "Unauthorized"}", error_description="${options.errorDescription ?? "Unauthorized"}", resource_metadata="${origin}${resourcePath}"`;
|
|
65
|
+
}
|
|
66
|
+
export function annotationsForApiMethod(method) {
|
|
67
|
+
const normalized = method.toUpperCase();
|
|
68
|
+
if (normalized === "GET")
|
|
69
|
+
return READ_ONLY_ANNOTATIONS;
|
|
70
|
+
if (normalized === "DELETE")
|
|
71
|
+
return DESTRUCTIVE_ANNOTATIONS;
|
|
72
|
+
return MUTATING_ANNOTATIONS;
|
|
73
|
+
}
|
|
74
|
+
const READ_ONLY_ANNOTATIONS = {
|
|
75
|
+
readOnlyHint: true,
|
|
76
|
+
destructiveHint: false,
|
|
77
|
+
idempotentHint: true,
|
|
78
|
+
openWorldHint: false,
|
|
79
|
+
};
|
|
80
|
+
const MUTATING_ANNOTATIONS = {
|
|
81
|
+
readOnlyHint: false,
|
|
82
|
+
destructiveHint: false,
|
|
83
|
+
idempotentHint: false,
|
|
84
|
+
openWorldHint: false,
|
|
85
|
+
};
|
|
86
|
+
const DESTRUCTIVE_ANNOTATIONS = {
|
|
87
|
+
readOnlyHint: false,
|
|
88
|
+
destructiveHint: true,
|
|
89
|
+
idempotentHint: false,
|
|
90
|
+
openWorldHint: false,
|
|
91
|
+
};
|
|
92
|
+
function oauthLinks(authorizationServer, protectedResource) {
|
|
93
|
+
if (!authorizationServer && !protectedResource)
|
|
94
|
+
return undefined;
|
|
95
|
+
return {
|
|
96
|
+
authorization_server: authorizationServer,
|
|
97
|
+
protected_resource: protectedResource,
|
|
17
98
|
};
|
|
18
99
|
}
|
|
19
100
|
function jsonSchemaPlaceholder(path) {
|
package/dist/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
|
package/docs/evaluation.md
CHANGED
|
@@ -25,7 +25,8 @@ This package was seeded from repeated infrastructure found across Erik/Q32 TypeS
|
|
|
25
25
|
| Billing plan/status checks | `billing` | Replace local plan rank and active subscription status helpers around Stripe-backed apps. |
|
|
26
26
|
| Worker test helpers | `testing` | Replace small fake Queue/R2 helpers and JSON response assertions in unit tests; complements workerd/Miniflare integration tests. |
|
|
27
27
|
| OAuth/MCP metadata and storage | `oauth`, `mcp` | Replace repeated discovery metadata, API-to-tool descriptors, and configurable D1 OAuth client/code/token repositories. |
|
|
28
|
-
| API operation registries | `api` | Replace local operation registries
|
|
28
|
+
| API operation registries and discovery surfaces | `api` | Replace local operation registries, OpenAPI path generation, RFC 9727 API catalog linksets, Agent Skills indexes, and agent-discovery Link headers. |
|
|
29
|
+
| MCP manifests and bearer challenges | `mcp` | Replace repeated server-card metadata, plain-GET MCP manifests, API-operation tool descriptors, tool annotations, and OAuth protected-resource challenge headers. |
|
|
29
30
|
| Framework request adapters | `hono`, `react-router` | Translate Hono middleware and React Router loader/action requests into the same framework-neutral auth/API services. |
|
|
30
31
|
|
|
31
32
|
## Current Fit
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -1,20 +1,49 @@
|
|
|
1
1
|
import { HttpError } from "./http.js";
|
|
2
2
|
|
|
3
3
|
export type ApiMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
4
|
+
export type ApiMethodLower = Lowercase<ApiMethod>;
|
|
5
|
+
export type ApiInputLocation = "path" | "query" | "body";
|
|
4
6
|
|
|
5
7
|
export interface SchemaLike<T = unknown> {
|
|
6
8
|
parse(value: unknown): T;
|
|
7
9
|
}
|
|
8
10
|
|
|
11
|
+
export type JsonSchemaLike = Record<string, unknown>;
|
|
12
|
+
|
|
13
|
+
export type ApiOperationInputSpec = {
|
|
14
|
+
properties?: Record<string, JsonSchemaLike>;
|
|
15
|
+
required?: string[];
|
|
16
|
+
locations?: Record<string, ApiInputLocation>;
|
|
17
|
+
pathDescriptions?: Record<string, string>;
|
|
18
|
+
schema?: JsonSchemaLike;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type ApiTagSpec = {
|
|
22
|
+
name: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
9
26
|
export type ApiOperationSpec<TInput = unknown> = {
|
|
10
27
|
name: string;
|
|
11
28
|
title: string;
|
|
12
29
|
description: string;
|
|
13
|
-
method: ApiMethod;
|
|
30
|
+
method: ApiMethod | ApiMethodLower;
|
|
14
31
|
path: string;
|
|
15
32
|
scope?: string;
|
|
33
|
+
tags?: string[];
|
|
16
34
|
inputSchema?: SchemaLike<TInput>;
|
|
35
|
+
input?: ApiOperationInputSpec;
|
|
36
|
+
responseKey?: string;
|
|
37
|
+
responseSchema?: JsonSchemaLike;
|
|
17
38
|
openapi?: boolean;
|
|
39
|
+
mcp?: {
|
|
40
|
+
toolName?: string;
|
|
41
|
+
title?: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
inputSchema?: JsonSchemaLike;
|
|
44
|
+
requiresAuth?: boolean;
|
|
45
|
+
annotations?: Record<string, unknown>;
|
|
46
|
+
};
|
|
18
47
|
};
|
|
19
48
|
|
|
20
49
|
export type ApiOperation<TContext, TInput = unknown, TOutput = unknown> = ApiOperationSpec<TInput> & {
|
|
@@ -69,32 +98,243 @@ export function interpolateOperationPath(path: string, input: Record<string, unk
|
|
|
69
98
|
});
|
|
70
99
|
}
|
|
71
100
|
|
|
101
|
+
export type OpenApiDocumentOptions = {
|
|
102
|
+
title: string;
|
|
103
|
+
version: string;
|
|
104
|
+
description?: string;
|
|
105
|
+
origin?: string;
|
|
106
|
+
tags?: ApiTagSpec[];
|
|
107
|
+
securitySchemeName?: string;
|
|
108
|
+
includeDefaultErrorResponses?: boolean;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export function openApiDocumentForRegistry<TContext>(
|
|
112
|
+
registry: ApiOperationRegistry<TContext>,
|
|
113
|
+
options: OpenApiDocumentOptions,
|
|
114
|
+
): Record<string, unknown> {
|
|
115
|
+
const securitySchemeName = options.securitySchemeName ?? "bearerAuth";
|
|
116
|
+
return {
|
|
117
|
+
openapi: "3.0.3",
|
|
118
|
+
info: {
|
|
119
|
+
title: options.title,
|
|
120
|
+
version: options.version,
|
|
121
|
+
...(options.description ? { description: options.description } : {}),
|
|
122
|
+
},
|
|
123
|
+
...(options.origin ? { servers: [{ url: options.origin.replace(/\/$/, "") }] } : {}),
|
|
124
|
+
...(options.tags ? { tags: options.tags } : {}),
|
|
125
|
+
components: {
|
|
126
|
+
securitySchemes: {
|
|
127
|
+
[securitySchemeName]: { type: "http", scheme: "bearer" },
|
|
128
|
+
},
|
|
129
|
+
schemas: {
|
|
130
|
+
Error: {
|
|
131
|
+
type: "object",
|
|
132
|
+
properties: {
|
|
133
|
+
error: {
|
|
134
|
+
type: "object",
|
|
135
|
+
properties: {
|
|
136
|
+
code: { type: "string" },
|
|
137
|
+
message: { type: "string" },
|
|
138
|
+
details: {},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
paths: openApiPathsForRegistry(registry, {
|
|
146
|
+
securitySchemeName,
|
|
147
|
+
includeDefaultErrorResponses: options.includeDefaultErrorResponses ?? true,
|
|
148
|
+
}),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
72
152
|
export function openApiPathsForRegistry<TContext>(
|
|
73
153
|
registry: ApiOperationRegistry<TContext>,
|
|
154
|
+
options: {
|
|
155
|
+
securitySchemeName?: string;
|
|
156
|
+
includeDefaultErrorResponses?: boolean;
|
|
157
|
+
} = {},
|
|
74
158
|
): Record<string, Record<string, Record<string, unknown>>> {
|
|
75
159
|
const paths: Record<string, Record<string, Record<string, unknown>>> = {};
|
|
160
|
+
const securitySchemeName = options.securitySchemeName ?? "bearerAuth";
|
|
76
161
|
for (const operation of Object.values(registry)) {
|
|
77
162
|
if (operation.openapi === false) continue;
|
|
78
163
|
const path = operation.path.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g, "{$1}");
|
|
79
164
|
const method = operation.method.toLowerCase();
|
|
165
|
+
const input = operation.input;
|
|
80
166
|
paths[path] ??= {};
|
|
81
167
|
paths[path][method] = {
|
|
82
168
|
operationId: operation.name,
|
|
83
169
|
summary: operation.title,
|
|
84
170
|
description: operation.description,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
required: true,
|
|
90
|
-
schema: { type: "string" },
|
|
91
|
-
})),
|
|
171
|
+
tags: operation.tags,
|
|
172
|
+
security: operation.scope ? [{ [securitySchemeName]: [operation.scope] }] : undefined,
|
|
173
|
+
parameters: openApiParametersForOperation(operation),
|
|
174
|
+
requestBody: openApiRequestBody(input),
|
|
92
175
|
responses: {
|
|
93
176
|
"200": {
|
|
94
177
|
description: "OK",
|
|
178
|
+
...(operation.responseSchema
|
|
179
|
+
? {
|
|
180
|
+
content: {
|
|
181
|
+
"application/json": { schema: operation.responseSchema },
|
|
182
|
+
},
|
|
183
|
+
}
|
|
184
|
+
: operation.responseKey
|
|
185
|
+
? {
|
|
186
|
+
content: {
|
|
187
|
+
"application/json": {
|
|
188
|
+
schema: {
|
|
189
|
+
type: "object",
|
|
190
|
+
additionalProperties: true,
|
|
191
|
+
properties: { [operation.responseKey]: {} },
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
: {}),
|
|
95
197
|
},
|
|
198
|
+
...(options.includeDefaultErrorResponses === false
|
|
199
|
+
? {}
|
|
200
|
+
: {
|
|
201
|
+
"400": errorResponse(),
|
|
202
|
+
"401": errorResponse(),
|
|
203
|
+
"403": errorResponse(),
|
|
204
|
+
"404": errorResponse(),
|
|
205
|
+
"429": errorResponse(),
|
|
206
|
+
}),
|
|
96
207
|
},
|
|
97
208
|
};
|
|
98
209
|
}
|
|
99
210
|
return paths;
|
|
100
211
|
}
|
|
212
|
+
|
|
213
|
+
function openApiParametersForOperation(operation: ApiOperationSpec): Array<Record<string, unknown>> {
|
|
214
|
+
const input = operation.input;
|
|
215
|
+
if (!input?.locations) {
|
|
216
|
+
return operationPathParameters(operation.path).map((name) => ({
|
|
217
|
+
name,
|
|
218
|
+
in: "path",
|
|
219
|
+
required: true,
|
|
220
|
+
schema: { type: "string" },
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
return Object.entries(input.locations)
|
|
224
|
+
.filter(([, location]) => location === "path" || location === "query")
|
|
225
|
+
.map(([name, location]) => ({
|
|
226
|
+
name,
|
|
227
|
+
in: location,
|
|
228
|
+
required: location === "path" || (input.required?.includes(name) ?? false),
|
|
229
|
+
...(location === "path"
|
|
230
|
+
? {
|
|
231
|
+
description: input.pathDescriptions?.[name] ?? name,
|
|
232
|
+
}
|
|
233
|
+
: {}),
|
|
234
|
+
schema: input.properties?.[name] ?? { type: "string" },
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function openApiRequestBody(input: ApiOperationInputSpec | undefined): Record<string, unknown> | undefined {
|
|
239
|
+
if (!input?.locations) return undefined;
|
|
240
|
+
const bodyEntries = Object.entries(input.locations).filter(([, location]) => location === "body");
|
|
241
|
+
if (bodyEntries.length === 0) return undefined;
|
|
242
|
+
const properties = Object.fromEntries(bodyEntries.map(([name]) => [name, input.properties?.[name] ?? {}]));
|
|
243
|
+
const required = (input.required ?? []).filter((name) => input.locations?.[name] === "body");
|
|
244
|
+
return {
|
|
245
|
+
required: required.length > 0,
|
|
246
|
+
content: {
|
|
247
|
+
"application/json": {
|
|
248
|
+
schema: input.schema ?? {
|
|
249
|
+
type: "object",
|
|
250
|
+
properties,
|
|
251
|
+
...(required.length ? { required } : {}),
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function errorResponse(): Record<string, unknown> {
|
|
259
|
+
return {
|
|
260
|
+
description: "Error response",
|
|
261
|
+
content: {
|
|
262
|
+
"application/json": { schema: { $ref: "#/components/schemas/Error" } },
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export type ApiCatalogService = {
|
|
268
|
+
anchor: string;
|
|
269
|
+
serviceDesc?: string;
|
|
270
|
+
serviceDescType?: string;
|
|
271
|
+
serviceDoc?: string;
|
|
272
|
+
serviceDocType?: string;
|
|
273
|
+
status?: string;
|
|
274
|
+
statusType?: string;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
export function apiCatalogLinkset(services: ApiCatalogService[]): { linkset: Array<Record<string, unknown>> } {
|
|
278
|
+
return {
|
|
279
|
+
linkset: services.map((service) => ({
|
|
280
|
+
anchor: service.anchor,
|
|
281
|
+
...(service.serviceDesc
|
|
282
|
+
? {
|
|
283
|
+
"service-desc": [
|
|
284
|
+
{
|
|
285
|
+
href: service.serviceDesc,
|
|
286
|
+
type: service.serviceDescType ?? "application/json",
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
}
|
|
290
|
+
: {}),
|
|
291
|
+
...(service.serviceDoc
|
|
292
|
+
? {
|
|
293
|
+
"service-doc": [
|
|
294
|
+
{
|
|
295
|
+
href: service.serviceDoc,
|
|
296
|
+
type: service.serviceDocType ?? "text/html",
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
}
|
|
300
|
+
: {}),
|
|
301
|
+
...(service.status
|
|
302
|
+
? {
|
|
303
|
+
status: [
|
|
304
|
+
{
|
|
305
|
+
href: service.status,
|
|
306
|
+
type: service.statusType ?? "application/json",
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
}
|
|
310
|
+
: {}),
|
|
311
|
+
})),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export type AgentDiscoveryLink = {
|
|
316
|
+
href: string;
|
|
317
|
+
rel: string;
|
|
318
|
+
type?: string;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
export function agentDiscoveryLinkHeader(links: AgentDiscoveryLink[]): string {
|
|
322
|
+
return links
|
|
323
|
+
.map((link) => `<${link.href}>; rel="${link.rel}"${link.type ? `; type="${link.type}"` : ""}`)
|
|
324
|
+
.join(", ");
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export type AgentSkill = {
|
|
328
|
+
name: string;
|
|
329
|
+
type: string;
|
|
330
|
+
description?: string;
|
|
331
|
+
url: string;
|
|
332
|
+
sha256?: string;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export function agentSkillsIndex(skills: AgentSkill[]): Record<string, unknown> {
|
|
336
|
+
return {
|
|
337
|
+
$schema: "https://raw.githubusercontent.com/cloudflare/agent-skills-discovery-rfc/main/schema/v0.2.0/index.schema.json",
|
|
338
|
+
skills,
|
|
339
|
+
};
|
|
340
|
+
}
|
package/src/mcp.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApiOperationRegistry } from "./api.js";
|
|
1
|
+
import type { ApiMethod, ApiMethodLower, ApiOperationRegistry } from "./api.js";
|
|
2
2
|
|
|
3
3
|
export type McpToolDescriptor = {
|
|
4
4
|
name: string;
|
|
@@ -6,19 +6,35 @@ export type McpToolDescriptor = {
|
|
|
6
6
|
description?: string;
|
|
7
7
|
inputSchema: Record<string, unknown>;
|
|
8
8
|
annotations?: Record<string, unknown>;
|
|
9
|
+
requiresAuth?: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type McpToolAnnotations = {
|
|
13
|
+
readOnlyHint: boolean;
|
|
14
|
+
destructiveHint: boolean;
|
|
15
|
+
idempotentHint: boolean;
|
|
16
|
+
openWorldHint: boolean;
|
|
9
17
|
};
|
|
10
18
|
|
|
11
19
|
export function mcpToolsFromApiRegistry<TContext>(
|
|
12
20
|
registry: ApiOperationRegistry<TContext>,
|
|
13
21
|
options: { includeScopes?: boolean } = {},
|
|
14
22
|
): McpToolDescriptor[] {
|
|
15
|
-
return Object.values(registry).map((operation) =>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
return Object.values(registry).map((operation) => {
|
|
24
|
+
const annotations = {
|
|
25
|
+
...annotationsForApiMethod(operation.method),
|
|
26
|
+
...(options.includeScopes && operation.scope ? { scope: operation.scope } : {}),
|
|
27
|
+
...(operation.mcp?.annotations ?? {}),
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
name: operation.mcp?.toolName ?? operation.name,
|
|
31
|
+
title: operation.mcp?.title ?? operation.title,
|
|
32
|
+
description: operation.mcp?.description ?? operation.description,
|
|
33
|
+
inputSchema: operation.mcp?.inputSchema ?? operation.input?.schema ?? jsonSchemaPlaceholder(operation.path),
|
|
34
|
+
annotations,
|
|
35
|
+
requiresAuth: operation.mcp?.requiresAuth ?? Boolean(operation.scope),
|
|
36
|
+
};
|
|
37
|
+
});
|
|
22
38
|
}
|
|
23
39
|
|
|
24
40
|
export function mcpWellKnownServerMetadata(options: {
|
|
@@ -26,6 +42,12 @@ export function mcpWellKnownServerMetadata(options: {
|
|
|
26
42
|
url: string;
|
|
27
43
|
description?: string;
|
|
28
44
|
authorizationServers?: string[];
|
|
45
|
+
protectedResource?: string;
|
|
46
|
+
tools?: McpToolDescriptor[];
|
|
47
|
+
resources?: Array<Record<string, unknown>>;
|
|
48
|
+
prompts?: Array<Record<string, unknown>>;
|
|
49
|
+
oauth?: Record<string, unknown>;
|
|
50
|
+
metadata?: Record<string, unknown>;
|
|
29
51
|
}): Record<string, unknown> {
|
|
30
52
|
return {
|
|
31
53
|
name: options.name,
|
|
@@ -33,6 +55,100 @@ export function mcpWellKnownServerMetadata(options: {
|
|
|
33
55
|
transport: "http",
|
|
34
56
|
url: options.url,
|
|
35
57
|
authorization_servers: options.authorizationServers,
|
|
58
|
+
oauth: options.oauth ?? oauthLinks(options.authorizationServers?.[0], options.protectedResource),
|
|
59
|
+
tools: options.tools,
|
|
60
|
+
resources: options.resources,
|
|
61
|
+
prompts: options.prompts,
|
|
62
|
+
metadata: options.metadata,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function mcpManifest(options: {
|
|
67
|
+
name: string;
|
|
68
|
+
endpoint: string;
|
|
69
|
+
transport?: "streamable_http" | "http";
|
|
70
|
+
anonymousDescription?: string;
|
|
71
|
+
authenticatedDescription?: string;
|
|
72
|
+
tools?: McpToolDescriptor[];
|
|
73
|
+
resources?: Array<Record<string, unknown>>;
|
|
74
|
+
prompts?: Array<Record<string, unknown>>;
|
|
75
|
+
metadata?: Record<string, unknown>;
|
|
76
|
+
}): Record<string, unknown> {
|
|
77
|
+
return {
|
|
78
|
+
server: {
|
|
79
|
+
name: options.name,
|
|
80
|
+
endpoint: options.endpoint,
|
|
81
|
+
transport: options.transport ?? "streamable_http",
|
|
82
|
+
},
|
|
83
|
+
authentication: {
|
|
84
|
+
anonymous: options.anonymousDescription,
|
|
85
|
+
authenticated: options.authenticatedDescription,
|
|
86
|
+
},
|
|
87
|
+
capabilities: {
|
|
88
|
+
tools: options.tools
|
|
89
|
+
? Object.fromEntries(
|
|
90
|
+
options.tools.map((tool) => [
|
|
91
|
+
tool.name,
|
|
92
|
+
{
|
|
93
|
+
name: tool.name,
|
|
94
|
+
title: tool.title,
|
|
95
|
+
description: tool.description,
|
|
96
|
+
requiresAuth: tool.requiresAuth,
|
|
97
|
+
},
|
|
98
|
+
]),
|
|
99
|
+
)
|
|
100
|
+
: undefined,
|
|
101
|
+
resources: options.resources,
|
|
102
|
+
prompts: options.prompts,
|
|
103
|
+
},
|
|
104
|
+
metadata: options.metadata,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function mcpBearerChallenge(options: {
|
|
109
|
+
origin: string;
|
|
110
|
+
resourcePath?: string;
|
|
111
|
+
error?: string;
|
|
112
|
+
errorDescription?: string;
|
|
113
|
+
}): string {
|
|
114
|
+
const origin = options.origin.replace(/\/$/, "");
|
|
115
|
+
const resourcePath = options.resourcePath ?? "/.well-known/oauth-protected-resource/mcp";
|
|
116
|
+
return `Bearer error="${options.error ?? "Unauthorized"}", error_description="${options.errorDescription ?? "Unauthorized"}", resource_metadata="${origin}${resourcePath}"`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function annotationsForApiMethod(method: ApiMethod | ApiMethodLower): McpToolAnnotations {
|
|
120
|
+
const normalized = method.toUpperCase();
|
|
121
|
+
if (normalized === "GET") return READ_ONLY_ANNOTATIONS;
|
|
122
|
+
if (normalized === "DELETE") return DESTRUCTIVE_ANNOTATIONS;
|
|
123
|
+
return MUTATING_ANNOTATIONS;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const READ_ONLY_ANNOTATIONS: McpToolAnnotations = {
|
|
127
|
+
readOnlyHint: true,
|
|
128
|
+
destructiveHint: false,
|
|
129
|
+
idempotentHint: true,
|
|
130
|
+
openWorldHint: false,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const MUTATING_ANNOTATIONS: McpToolAnnotations = {
|
|
134
|
+
readOnlyHint: false,
|
|
135
|
+
destructiveHint: false,
|
|
136
|
+
idempotentHint: false,
|
|
137
|
+
openWorldHint: false,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const DESTRUCTIVE_ANNOTATIONS: McpToolAnnotations = {
|
|
141
|
+
readOnlyHint: false,
|
|
142
|
+
destructiveHint: true,
|
|
143
|
+
idempotentHint: false,
|
|
144
|
+
openWorldHint: false,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
function oauthLinks(authorizationServer: string | undefined, protectedResource: string | undefined): Record<string, unknown> | undefined {
|
|
148
|
+
if (!authorizationServer && !protectedResource) return undefined;
|
|
149
|
+
return {
|
|
150
|
+
authorization_server: authorizationServer,
|
|
151
|
+
protected_resource: protectedResource,
|
|
36
152
|
};
|
|
37
153
|
}
|
|
38
154
|
|