@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 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 declare function openApiPathsForRegistry<TContext>(registry: ApiOperationRegistry<TContext>): Record<string, Record<string, Record<string, unknown>>>;
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;AAEpE,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,OAAO;IACrC,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,CAAC,CAAC;CAC1B;AAED,MAAM,MAAM,gBAAgB,CAAC,MAAM,GAAG,OAAO,IAAI;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,gBAAgB,CAAC,MAAM,CAAC,GAAG;IACnG,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC3E,CAAC;AAEF,MAAM,MAAM,oBAAoB,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAEtG,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAC9E,SAAS,EAAE,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,GACjD,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAEzC;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,SAAS,oBAAoB,CAAC,QAAQ,CAAC,EAC1F,QAAQ,EAAE,SAAS,GAClB,SAAS,CAQX;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EACjD,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,CAAC,EACxC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAI9D;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAQ7F;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAC9C,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,CAAC,GACvC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CA0BzD"}
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 openApiPathsForRegistry(registry) {
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
- security: operation.scope ? [{ bearerAuth: [operation.scope] }] : undefined,
51
- parameters: operationPathParameters(operation.path).map((name) => ({
52
- name,
53
- in: "path",
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":"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"}
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;AAErD,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;CACvC,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,CAQrB;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;CACjC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQ1B"}
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
- name: operation.name,
4
- title: operation.title,
5
- description: operation.description,
6
- inputSchema: jsonSchemaPlaceholder(operation.path),
7
- annotations: options.includeScopes && operation.scope ? { scope: operation.scope } : undefined,
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":"AAUA,MAAM,UAAU,uBAAuB,CACrC,QAAwC,EACxC,UAAuC,EAAE;IAEzC,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACjD,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,WAAW,EAAE,SAAS,CAAC,WAAW;QAClC,WAAW,EAAE,qBAAqB,CAAC,SAAS,CAAC,IAAI,CAAC;QAClD,WAAW,EAAE,OAAO,CAAC,aAAa,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;KAC/F,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,OAK1C;IACC,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,SAAS,EAAE,MAAM;QACjB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,qBAAqB,EAAE,OAAO,CAAC,oBAAoB;KACpD,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAY;IACzC,MAAM,UAAU,GAA4B,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,+BAA+B,CAAC,EAAE,CAAC;QACnE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,UAAU;QACV,QAAQ;QACR,oBAAoB,EAAE,IAAI;KAC3B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
@@ -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 used for OpenAPI, admin APIs, and MCP exposure. |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@q32/core",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "Shared TypeScript primitives for Q32 Cloudflare Worker projects.",
5
5
  "license": "MIT",
6
6
  "type": "module",
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
- security: operation.scope ? [{ bearerAuth: [operation.scope] }] : undefined,
86
- parameters: operationPathParameters(operation.path).map((name) => ({
87
- name,
88
- in: "path",
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
- name: operation.name,
17
- title: operation.title,
18
- description: operation.description,
19
- inputSchema: jsonSchemaPlaceholder(operation.path),
20
- annotations: options.includeScopes && operation.scope ? { scope: operation.scope } : undefined,
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