@anchrd/intel-api 0.2.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.
Files changed (114) hide show
  1. package/README.md +96 -0
  2. package/bin/intel.mjs +134 -0
  3. package/dist/adapters/cloudflare/cloudflare-flow-workflow.d.ts +21 -0
  4. package/dist/adapters/cloudflare/cloudflare-flow-workflow.js +39 -0
  5. package/dist/adapters/cloudflare/cloudflare.d.ts +8 -0
  6. package/dist/adapters/cloudflare/cloudflare.js +200 -0
  7. package/dist/adapters/cloudflare/cloudflare.types.d.ts +29 -0
  8. package/dist/adapters/cloudflare/cloudflare.types.js +1 -0
  9. package/dist/adapters/content/content.d.ts +3 -0
  10. package/dist/adapters/content/content.js +22 -0
  11. package/dist/adapters/content/content.types.d.ts +14 -0
  12. package/dist/adapters/content/content.types.js +1 -0
  13. package/dist/adapters/db/db-flows.d.ts +6 -0
  14. package/dist/adapters/db/db-flows.js +466 -0
  15. package/dist/adapters/db/db-indexing.d.ts +3 -0
  16. package/dist/adapters/db/db-indexing.js +70 -0
  17. package/dist/adapters/db/db-oauth.d.ts +3 -0
  18. package/dist/adapters/db/db-oauth.js +26 -0
  19. package/dist/adapters/db/db-tools.d.ts +6 -0
  20. package/dist/adapters/db/db-tools.js +200 -0
  21. package/dist/adapters/db/db.d.ts +6 -0
  22. package/dist/adapters/db/db.js +755 -0
  23. package/dist/adapters/db/db.types.d.ts +16 -0
  24. package/dist/adapters/db/db.types.js +1 -0
  25. package/dist/adapters/document-converter/document-converter.d.ts +2 -0
  26. package/dist/adapters/document-converter/document-converter.js +25 -0
  27. package/dist/adapters/document-converter/document-converter.types.d.ts +9 -0
  28. package/dist/adapters/document-converter/document-converter.types.js +1 -0
  29. package/dist/adapters/flow-runtime/flow-runtime.d.ts +3 -0
  30. package/dist/adapters/flow-runtime/flow-runtime.js +22 -0
  31. package/dist/adapters/flow-runtime/flow-runtime.types.d.ts +19 -0
  32. package/dist/adapters/flow-runtime/flow-runtime.types.js +1 -0
  33. package/dist/adapters/index-queue/index-queue.d.ts +10 -0
  34. package/dist/adapters/index-queue/index-queue.js +12 -0
  35. package/dist/adapters/index-queue/index-queue.types.d.ts +11 -0
  36. package/dist/adapters/index-queue/index-queue.types.js +1 -0
  37. package/dist/adapters/json-schema/json-schema.d.ts +2 -0
  38. package/dist/adapters/json-schema/json-schema.js +31 -0
  39. package/dist/adapters/openid/openid.d.ts +5 -0
  40. package/dist/adapters/openid/openid.js +159 -0
  41. package/dist/adapters/portal-tokens/portal-tokens.d.ts +8 -0
  42. package/dist/adapters/portal-tokens/portal-tokens.js +71 -0
  43. package/dist/adapters/remote-tools/remote-tools.d.ts +5 -0
  44. package/dist/adapters/remote-tools/remote-tools.js +109 -0
  45. package/dist/adapters/semantic-index/semantic-index.d.ts +2 -0
  46. package/dist/adapters/semantic-index/semantic-index.js +57 -0
  47. package/dist/adapters/semantic-index/semantic-index.types.d.ts +32 -0
  48. package/dist/adapters/semantic-index/semantic-index.types.js +1 -0
  49. package/dist/adapters/session-cookie/session-cookie.d.ts +5 -0
  50. package/dist/adapters/session-cookie/session-cookie.js +81 -0
  51. package/dist/adapters/tool-source-policy/tool-source-policy.d.ts +1 -0
  52. package/dist/adapters/tool-source-policy/tool-source-policy.js +22 -0
  53. package/dist/auth/auth.d.ts +2 -0
  54. package/dist/auth/auth.js +176 -0
  55. package/dist/auth/auth.types.d.ts +104 -0
  56. package/dist/auth/auth.types.js +1 -0
  57. package/dist/build/build.d.ts +2 -0
  58. package/dist/build/build.js +125 -0
  59. package/dist/build/build.types.d.ts +17 -0
  60. package/dist/build/build.types.js +1 -0
  61. package/dist/cli/cli.d.ts +4 -0
  62. package/dist/cli/cli.js +141 -0
  63. package/dist/cli/cli.types.d.ts +14 -0
  64. package/dist/cli/cli.types.js +1 -0
  65. package/dist/flows/flows.d.ts +4 -0
  66. package/dist/flows/flows.js +409 -0
  67. package/dist/flows/flows.types.d.ts +108 -0
  68. package/dist/flows/flows.types.js +1 -0
  69. package/dist/http/http.d.ts +8 -0
  70. package/dist/http/http.js +286 -0
  71. package/dist/http/http.types.d.ts +14 -0
  72. package/dist/http/http.types.js +1 -0
  73. package/dist/indexing/indexing.d.ts +6 -0
  74. package/dist/indexing/indexing.js +48 -0
  75. package/dist/indexing/indexing.types.d.ts +10 -0
  76. package/dist/indexing/indexing.types.js +1 -0
  77. package/dist/intel/intel.d.ts +3 -0
  78. package/dist/intel/intel.js +77 -0
  79. package/dist/intel/intel.types.d.ts +14 -0
  80. package/dist/intel/intel.types.js +1 -0
  81. package/dist/knowledge/knowledge.d.ts +2 -0
  82. package/dist/knowledge/knowledge.js +479 -0
  83. package/dist/knowledge/knowledge.types.d.ts +162 -0
  84. package/dist/knowledge/knowledge.types.js +1 -0
  85. package/dist/mcp/mcp.d.ts +2 -0
  86. package/dist/mcp/mcp.js +484 -0
  87. package/dist/mcp/mcp.types.d.ts +10 -0
  88. package/dist/mcp/mcp.types.js +1 -0
  89. package/dist/prepare/prepare.d.ts +4 -0
  90. package/dist/prepare/prepare.js +16 -0
  91. package/dist/prepare/prepare.types.d.ts +9 -0
  92. package/dist/prepare/prepare.types.js +1 -0
  93. package/dist/shared/gate-authorization/gate-authorization.d.ts +4 -0
  94. package/dist/shared/gate-authorization/gate-authorization.js +19 -0
  95. package/dist/shared/intel-error/intel-error.d.ts +5 -0
  96. package/dist/shared/intel-error/intel-error.js +10 -0
  97. package/dist/shared/problem-details/problem-details.d.ts +8 -0
  98. package/dist/shared/problem-details/problem-details.js +4 -0
  99. package/dist/shared/safe-return-path/safe-return-path.d.ts +2 -0
  100. package/dist/shared/safe-return-path/safe-return-path.js +11 -0
  101. package/dist/shared/sha256/sha256.d.ts +1 -0
  102. package/dist/shared/sha256/sha256.js +9 -0
  103. package/dist/tools/tools.d.ts +2 -0
  104. package/dist/tools/tools.js +107 -0
  105. package/dist/tools/tools.types.d.ts +56 -0
  106. package/dist/tools/tools.types.js +1 -0
  107. package/examples/branding/favicon.svg +5 -0
  108. package/examples/branding/logo.svg +5 -0
  109. package/examples/branding/theme.css +16 -0
  110. package/examples/dev.vars.example +7 -0
  111. package/examples/intel.json +9 -0
  112. package/migrations/0000_intel_foundation.sql +203 -0
  113. package/migrations/0001_portal_native_tools.sql +21 -0
  114. package/package.json +62 -0
@@ -0,0 +1,484 @@
1
+ import { ArchiveKnowledgeNodeInput, CompleteFlowRunStepInput, CreateFlowInput, CreateKnowledgeLinkInput, CreateKnowledgeNodeInput, DeleteKnowledgeLinkInput, ExecuteToolInput, GetFlowInput, GetFlowRunInput, GetKnowledgeNodeInput, KnowledgeGraphInput, ListFlowGrantsInput, ListKnowledgeGrantsInput, ListKnowledgeNodesInput, PublishFlowInput, RevokeFlowGrantInput, RevokeKnowledgeGrantInput, SaveFlowVersionInput, SaveKnowledgeAttachmentInput, SaveKnowledgeVersionInput, SearchKnowledgeInput, ShareFlowInput, ShareKnowledgeInput, StartFlowRunInput, TestToolInput, UpdateKnowledgeNodeInput, } from "@anchrd/intel-contract";
2
+ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
4
+ import { z } from "zod";
5
+ import { IntelError } from "../shared/intel-error/intel-error.js";
6
+ function text(value) {
7
+ return { content: [{ type: "text", text: JSON.stringify(value) }], isError: false };
8
+ }
9
+ // MCP returns resource payloads inline as base64, so an attachment cannot be streamed to the client
10
+ // and must fit in Worker memory twice over. Refuse oversized attachments instead of losing the
11
+ // isolate; HTTP still serves them as a stream.
12
+ const AttachmentInlineLimit = 10 * 1024 * 1024;
13
+ async function base64(stream) {
14
+ const bytes = new Uint8Array(await new Response(stream).arrayBuffer());
15
+ const chunkSize = 24_576;
16
+ let encoded = "";
17
+ for (let offset = 0; offset < bytes.length; offset += chunkSize) {
18
+ let binary = "";
19
+ for (const byte of bytes.subarray(offset, offset + chunkSize)) {
20
+ binary += String.fromCharCode(byte);
21
+ }
22
+ encoded += btoa(binary);
23
+ }
24
+ return encoded;
25
+ }
26
+ export async function handleMcp(request, deps) {
27
+ const actor = {
28
+ id: deps.authorization.identity.id,
29
+ email: deps.authorization.identity.email,
30
+ canReindex: deps.authorization.can("intel", "admin"),
31
+ };
32
+ const toolActor = {
33
+ id: deps.authorization.identity.id,
34
+ email: deps.authorization.identity.email,
35
+ canExecute: deps.authorization.can("tools", "execute"),
36
+ };
37
+ const flowActor = {
38
+ id: deps.authorization.identity.id,
39
+ email: deps.authorization.identity.email,
40
+ canRun: deps.authorization.can("flows", "run"),
41
+ canApprove: deps.authorization.can("flows", "approve"),
42
+ };
43
+ const server = new McpServer({ name: "intel", version: "0.1.0" });
44
+ const EmptyInput = z.strictObject({});
45
+ if (deps.authorization.can("knowledge", "read")) {
46
+ server.registerTool("knowledge_list", {
47
+ title: "List knowledge",
48
+ description: "List authorized folders, documents, and attachments under one parent.",
49
+ inputSchema: ListKnowledgeNodesInput,
50
+ annotations: {
51
+ title: "List knowledge",
52
+ readOnlyHint: true,
53
+ destructiveHint: false,
54
+ idempotentHint: true,
55
+ openWorldHint: false,
56
+ },
57
+ }, async (input) => text(await deps.knowledge.list(actor, input)));
58
+ server.registerTool("knowledge_get", {
59
+ title: "Get knowledge",
60
+ description: "Get one authorized Knowledge node and its current immutable content version.",
61
+ inputSchema: GetKnowledgeNodeInput,
62
+ annotations: {
63
+ title: "Get knowledge",
64
+ readOnlyHint: true,
65
+ destructiveHint: false,
66
+ idempotentHint: true,
67
+ openWorldHint: false,
68
+ },
69
+ }, async (input) => text(await deps.knowledge.get(actor, input.nodeId)));
70
+ server.registerTool("knowledge_versions_list", {
71
+ title: "List knowledge versions",
72
+ description: "List immutable versions of one authorized Knowledge node.",
73
+ inputSchema: GetKnowledgeNodeInput,
74
+ annotations: {
75
+ title: "List knowledge versions",
76
+ readOnlyHint: true,
77
+ destructiveHint: false,
78
+ idempotentHint: true,
79
+ openWorldHint: false,
80
+ },
81
+ }, async (input) => text(await deps.knowledge.listVersions(actor, input.nodeId)));
82
+ server.registerTool("knowledge_attachment_get", {
83
+ title: "Get knowledge attachment",
84
+ description: "Get authorized immutable attachment metadata and its explicit MCP resource URI.",
85
+ inputSchema: GetKnowledgeNodeInput,
86
+ annotations: {
87
+ title: "Get knowledge attachment",
88
+ readOnlyHint: true,
89
+ destructiveHint: false,
90
+ idempotentHint: true,
91
+ openWorldHint: false,
92
+ },
93
+ }, async (input) => text(await deps.knowledge.getAttachment(actor, input.nodeId)));
94
+ server.registerResource("knowledge-attachment", new ResourceTemplate("intel://knowledge/{nodeId}/attachment", { list: undefined }), { title: "Intel knowledge attachment" }, async (uri, variables) => {
95
+ const { attachment, body } = await deps.knowledge.readAttachment(actor, String(variables.nodeId));
96
+ if (attachment.version.size > AttachmentInlineLimit) {
97
+ await body.cancel();
98
+ throw new IntelError(413, "attachment_too_large", "Attachment is too large to inline; read it over HTTP instead");
99
+ }
100
+ return {
101
+ contents: [
102
+ {
103
+ uri: uri.href,
104
+ mimeType: attachment.version.mediaType,
105
+ blob: await base64(body),
106
+ },
107
+ ],
108
+ };
109
+ });
110
+ server.registerResource("knowledge-document", new ResourceTemplate("intel://knowledge/{nodeId}", { list: undefined }), { title: "Intel knowledge document", mimeType: "application/json" }, async (uri, variables) => {
111
+ const document = await deps.knowledge.get(actor, String(variables.nodeId));
112
+ return {
113
+ contents: [
114
+ { uri: uri.href, mimeType: "application/json", text: JSON.stringify(document) },
115
+ ],
116
+ };
117
+ });
118
+ server.registerTool("knowledge_search", {
119
+ title: "Search knowledge",
120
+ description: "Search only authorized indexed Knowledge and return version-pinned citations.",
121
+ inputSchema: SearchKnowledgeInput,
122
+ annotations: {
123
+ title: "Search knowledge",
124
+ readOnlyHint: true,
125
+ destructiveHint: false,
126
+ idempotentHint: true,
127
+ openWorldHint: false,
128
+ },
129
+ }, async (input) => text(await deps.knowledge.search(actor, input)));
130
+ server.registerTool("knowledge_links_list", {
131
+ title: "List knowledge links",
132
+ description: "List authorized outgoing links and backlinks for one Knowledge node.",
133
+ inputSchema: GetKnowledgeNodeInput,
134
+ annotations: {
135
+ title: "List knowledge links",
136
+ readOnlyHint: true,
137
+ destructiveHint: false,
138
+ idempotentHint: true,
139
+ openWorldHint: false,
140
+ },
141
+ }, async (input) => text(await deps.knowledge.listLinks(actor, input.nodeId)));
142
+ server.registerTool("knowledge_graph", {
143
+ title: "Get knowledge graph",
144
+ description: "Get the authorized Knowledge nodes and explicit relationships for discovery.",
145
+ inputSchema: KnowledgeGraphInput,
146
+ annotations: {
147
+ title: "Get knowledge graph",
148
+ readOnlyHint: true,
149
+ destructiveHint: false,
150
+ idempotentHint: true,
151
+ openWorldHint: false,
152
+ },
153
+ }, async (input) => text(await deps.knowledge.graph(actor, input)));
154
+ }
155
+ if (deps.authorization.can("intel", "admin")) {
156
+ server.registerTool("knowledge_reindex", {
157
+ title: "Reindex knowledge",
158
+ description: "Queue every current canonical Knowledge version for derived index rebuild.",
159
+ inputSchema: EmptyInput,
160
+ annotations: {
161
+ title: "Reindex knowledge",
162
+ readOnlyHint: false,
163
+ destructiveHint: false,
164
+ idempotentHint: true,
165
+ openWorldHint: false,
166
+ },
167
+ }, async () => text(await deps.knowledge.reindex(actor)));
168
+ }
169
+ if (deps.authorization.can("knowledge", "create")) {
170
+ server.registerTool("knowledge_create", {
171
+ title: "Create knowledge",
172
+ description: "Create a governed folder, document, or attachment metadata node.",
173
+ inputSchema: CreateKnowledgeNodeInput,
174
+ annotations: {
175
+ title: "Create knowledge",
176
+ readOnlyHint: false,
177
+ destructiveHint: false,
178
+ idempotentHint: true,
179
+ openWorldHint: false,
180
+ },
181
+ }, async (input) => text(await deps.knowledge.create(actor, input)));
182
+ }
183
+ if (deps.authorization.can("knowledge", "write")) {
184
+ server.registerTool("knowledge_save", {
185
+ title: "Save knowledge version",
186
+ description: "Append an immutable content version using an optimistic base version.",
187
+ inputSchema: SaveKnowledgeVersionInput,
188
+ annotations: {
189
+ title: "Save knowledge version",
190
+ readOnlyHint: false,
191
+ destructiveHint: false,
192
+ idempotentHint: true,
193
+ openWorldHint: false,
194
+ },
195
+ }, async (input) => text(await deps.knowledge.save(actor, input)));
196
+ server.registerTool("knowledge_attachment_save", {
197
+ title: "Save knowledge attachment",
198
+ description: "Append immutable base64 file bytes to an attachment node.",
199
+ inputSchema: SaveKnowledgeAttachmentInput,
200
+ annotations: {
201
+ title: "Save knowledge attachment",
202
+ readOnlyHint: false,
203
+ destructiveHint: false,
204
+ idempotentHint: true,
205
+ openWorldHint: false,
206
+ },
207
+ }, async (input) => text(await deps.knowledge.saveAttachment(actor, input)));
208
+ server.registerTool("knowledge_update", {
209
+ title: "Update knowledge",
210
+ description: "Rename, move, describe, or change the context policy of Knowledge.",
211
+ inputSchema: UpdateKnowledgeNodeInput,
212
+ annotations: {
213
+ title: "Update knowledge",
214
+ readOnlyHint: false,
215
+ destructiveHint: false,
216
+ idempotentHint: true,
217
+ openWorldHint: false,
218
+ },
219
+ }, async (input) => text(await deps.knowledge.update(actor, input)));
220
+ server.registerTool("knowledge_archive", {
221
+ title: "Archive knowledge",
222
+ description: "Archive or restore one Knowledge node using optimistic concurrency.",
223
+ inputSchema: ArchiveKnowledgeNodeInput,
224
+ annotations: {
225
+ title: "Archive knowledge",
226
+ readOnlyHint: false,
227
+ destructiveHint: true,
228
+ idempotentHint: true,
229
+ openWorldHint: false,
230
+ },
231
+ }, async (input) => text(await deps.knowledge.archive(actor, input)));
232
+ server.registerTool("knowledge_link_create", {
233
+ title: "Link knowledge",
234
+ description: "Create a governed relationship between two authorized Knowledge nodes.",
235
+ inputSchema: CreateKnowledgeLinkInput,
236
+ annotations: {
237
+ title: "Link knowledge",
238
+ readOnlyHint: false,
239
+ destructiveHint: false,
240
+ idempotentHint: true,
241
+ openWorldHint: false,
242
+ },
243
+ }, async (input) => text(await deps.knowledge.createLink(actor, input)));
244
+ server.registerTool("knowledge_link_delete", {
245
+ title: "Delete knowledge link",
246
+ description: "Delete one governed Knowledge relationship by ID.",
247
+ inputSchema: DeleteKnowledgeLinkInput,
248
+ annotations: {
249
+ title: "Delete knowledge link",
250
+ readOnlyHint: false,
251
+ destructiveHint: true,
252
+ idempotentHint: true,
253
+ openWorldHint: false,
254
+ },
255
+ }, async (input) => text(await deps.knowledge.deleteLink(actor, input)));
256
+ }
257
+ if (deps.authorization.can("knowledge", "share")) {
258
+ server.registerTool("knowledge_shares_list", {
259
+ title: "List knowledge shares",
260
+ description: "List direct grants for Knowledge the caller is allowed to manage.",
261
+ inputSchema: ListKnowledgeGrantsInput,
262
+ annotations: {
263
+ title: "List knowledge shares",
264
+ readOnlyHint: true,
265
+ destructiveHint: false,
266
+ idempotentHint: true,
267
+ openWorldHint: false,
268
+ },
269
+ }, async (input) => text(await deps.knowledge.listGrants(actor, input.resourceId)));
270
+ server.registerTool("knowledge_share", {
271
+ title: "Share knowledge",
272
+ description: "Grant inherited Knowledge access to a Gate user, verified email, or the organization.",
273
+ inputSchema: ShareKnowledgeInput,
274
+ annotations: {
275
+ title: "Share knowledge",
276
+ readOnlyHint: false,
277
+ destructiveHint: false,
278
+ idempotentHint: true,
279
+ openWorldHint: false,
280
+ },
281
+ }, async (input) => text(await deps.knowledge.share(actor, input)));
282
+ server.registerTool("knowledge_revoke_share", {
283
+ title: "Revoke knowledge share",
284
+ description: "Revoke one direct Knowledge grant by ID.",
285
+ inputSchema: RevokeKnowledgeGrantInput,
286
+ annotations: {
287
+ title: "Revoke knowledge share",
288
+ readOnlyHint: false,
289
+ destructiveHint: true,
290
+ idempotentHint: true,
291
+ openWorldHint: false,
292
+ },
293
+ }, async (input) => text(await deps.knowledge.revokeGrant(actor, input)));
294
+ }
295
+ if (deps.authorization.can("flows", "read")) {
296
+ server.registerTool("flows_list", {
297
+ title: "List flows",
298
+ description: "List authorized company flows by title and description.",
299
+ inputSchema: EmptyInput,
300
+ annotations: {
301
+ title: "List flows",
302
+ readOnlyHint: true,
303
+ destructiveHint: false,
304
+ idempotentHint: true,
305
+ openWorldHint: false,
306
+ },
307
+ }, async () => text(await deps.flows.list(flowActor)));
308
+ server.registerTool("flow_get", {
309
+ title: "Get flow",
310
+ description: "Load one authorized flow and its current immutable graph on demand.",
311
+ inputSchema: GetFlowInput,
312
+ annotations: {
313
+ title: "Get flow",
314
+ readOnlyHint: true,
315
+ destructiveHint: false,
316
+ idempotentHint: true,
317
+ openWorldHint: false,
318
+ },
319
+ }, async (input) => text(await deps.flows.get(flowActor, input.flowId)));
320
+ server.registerTool("flow_run_get", {
321
+ title: "Get flow run",
322
+ description: "Get durable run state and the next node an AI or person must handle.",
323
+ inputSchema: GetFlowRunInput,
324
+ annotations: {
325
+ title: "Get flow run",
326
+ readOnlyHint: true,
327
+ destructiveHint: false,
328
+ idempotentHint: true,
329
+ openWorldHint: false,
330
+ },
331
+ }, async (input) => text(await deps.flows.getRun(flowActor, input.runId)));
332
+ }
333
+ if (deps.authorization.can("flows", "share")) {
334
+ server.registerTool("flow_shares_list", {
335
+ title: "List flow shares",
336
+ description: "List direct grants for a Flow the caller is allowed to manage.",
337
+ inputSchema: ListFlowGrantsInput,
338
+ annotations: {
339
+ title: "List flow shares",
340
+ readOnlyHint: true,
341
+ destructiveHint: false,
342
+ idempotentHint: true,
343
+ openWorldHint: false,
344
+ },
345
+ }, async (input) => text(await deps.flows.listGrants(flowActor, input.resourceId)));
346
+ server.registerTool("flow_share", {
347
+ title: "Share flow",
348
+ description: "Grant Flow access to a Gate user, verified email, or the organization.",
349
+ inputSchema: ShareFlowInput,
350
+ annotations: {
351
+ title: "Share flow",
352
+ readOnlyHint: false,
353
+ destructiveHint: false,
354
+ idempotentHint: true,
355
+ openWorldHint: false,
356
+ },
357
+ }, async (input) => text(await deps.flows.share(flowActor, input)));
358
+ server.registerTool("flow_revoke_share", {
359
+ title: "Revoke flow share",
360
+ description: "Revoke one direct Flow grant by ID.",
361
+ inputSchema: RevokeFlowGrantInput,
362
+ annotations: {
363
+ title: "Revoke flow share",
364
+ readOnlyHint: false,
365
+ destructiveHint: true,
366
+ idempotentHint: true,
367
+ openWorldHint: false,
368
+ },
369
+ }, async (input) => text(await deps.flows.revokeGrant(flowActor, input)));
370
+ }
371
+ if (deps.authorization.can("flows", "create")) {
372
+ server.registerTool("flow_create", {
373
+ title: "Create flow",
374
+ description: "Create governed flow metadata before adding an immutable graph version.",
375
+ inputSchema: CreateFlowInput,
376
+ annotations: {
377
+ title: "Create flow",
378
+ readOnlyHint: false,
379
+ destructiveHint: false,
380
+ idempotentHint: true,
381
+ openWorldHint: false,
382
+ },
383
+ }, async (input) => text(await deps.flows.create(flowActor, input)));
384
+ }
385
+ if (deps.authorization.can("flows", "write")) {
386
+ server.registerTool("flow_save", {
387
+ title: "Save flow",
388
+ description: "Append a validated immutable flow graph version with optimistic concurrency.",
389
+ inputSchema: SaveFlowVersionInput,
390
+ annotations: {
391
+ title: "Save flow",
392
+ readOnlyHint: false,
393
+ destructiveHint: false,
394
+ idempotentHint: true,
395
+ openWorldHint: false,
396
+ },
397
+ }, async (input) => text(await deps.flows.save(flowActor, input)));
398
+ }
399
+ if (deps.authorization.can("flows", "publish")) {
400
+ server.registerTool("flow_publish", {
401
+ title: "Publish flow",
402
+ description: "Publish a version after resolving its authorized Knowledge and tool references.",
403
+ inputSchema: PublishFlowInput,
404
+ annotations: {
405
+ title: "Publish flow",
406
+ readOnlyHint: false,
407
+ destructiveHint: false,
408
+ idempotentHint: true,
409
+ openWorldHint: false,
410
+ },
411
+ }, async (input) => text(await deps.flows.publish(flowActor, input)));
412
+ }
413
+ if (deps.authorization.can("flows", "run")) {
414
+ server.registerTool("flow_run_start", {
415
+ title: "Start flow run",
416
+ description: "Start a durable run of the published graph and return its first actionable node.",
417
+ inputSchema: StartFlowRunInput,
418
+ annotations: {
419
+ title: "Start flow run",
420
+ readOnlyHint: false,
421
+ destructiveHint: false,
422
+ idempotentHint: true,
423
+ openWorldHint: false,
424
+ },
425
+ }, async (input) => text(await deps.flows.start(flowActor, input)));
426
+ server.registerTool("flow_run_complete_step", {
427
+ title: "Complete flow step",
428
+ description: "Submit a node result or semantic branch to advance a durable run without persisting credentials.",
429
+ inputSchema: CompleteFlowRunStepInput,
430
+ annotations: {
431
+ title: "Complete flow step",
432
+ readOnlyHint: false,
433
+ destructiveHint: true,
434
+ idempotentHint: true,
435
+ openWorldHint: true,
436
+ },
437
+ }, async (input) => text(await deps.flows.completeStep(flowActor, input)));
438
+ }
439
+ if (deps.authorization.can("tools", "read")) {
440
+ server.registerTool("tools_catalog_list", {
441
+ title: "List tools",
442
+ description: "List the MCP tools the calling user can reach through the portal, live rather than cached.",
443
+ inputSchema: EmptyInput,
444
+ annotations: {
445
+ title: "List tools",
446
+ readOnlyHint: true,
447
+ destructiveHint: false,
448
+ idempotentHint: true,
449
+ openWorldHint: false,
450
+ },
451
+ }, async () => text(await deps.tools.catalog(toolActor)));
452
+ }
453
+ if (deps.authorization.can("tools", "test")) {
454
+ server.registerTool("tools_test", {
455
+ title: "Test tool",
456
+ description: "Validate input and execute one explicitly read-only cached MCP tool with the caller's Gate connection.",
457
+ inputSchema: TestToolInput,
458
+ annotations: {
459
+ title: "Test tool",
460
+ readOnlyHint: true,
461
+ destructiveHint: false,
462
+ idempotentHint: false,
463
+ openWorldHint: true,
464
+ },
465
+ }, async (input) => text(await deps.tools.test(toolActor, input)));
466
+ }
467
+ if (deps.authorization.can("tools", "execute")) {
468
+ server.registerTool("tools_execute", {
469
+ title: "Execute tool",
470
+ description: "Validate and execute a discovered MCP tool with the caller's just-in-time Gate connection.",
471
+ inputSchema: ExecuteToolInput,
472
+ annotations: {
473
+ title: "Execute tool",
474
+ readOnlyHint: false,
475
+ destructiveHint: true,
476
+ idempotentHint: false,
477
+ openWorldHint: true,
478
+ },
479
+ }, async (input) => text(await deps.tools.execute(toolActor, input)));
480
+ }
481
+ const transport = new WebStandardStreamableHTTPServerTransport({ enableJsonResponse: true });
482
+ await server.connect(transport);
483
+ return await transport.handleRequest(request);
484
+ }
@@ -0,0 +1,10 @@
1
+ import type { Authorized } from "@anchrd/gate-sdk";
2
+ import type { FlowService } from "../flows/flows.types.js";
3
+ import type { KnowledgeService } from "../knowledge/knowledge.types.js";
4
+ import type { ToolService } from "../tools/tools.types.js";
5
+ export interface McpDeps {
6
+ authorization: Authorized;
7
+ knowledge: KnowledgeService;
8
+ flows: FlowService;
9
+ tools: ToolService;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { PrepareDeps } from "./prepare.types.js";
2
+ export declare function createPrepare(deps: PrepareDeps): {
3
+ prepare(): Promise<number>;
4
+ };
@@ -0,0 +1,16 @@
1
+ export function createPrepare(deps) {
2
+ return {
3
+ async prepare() {
4
+ const location = await deps.resolveMigrations();
5
+ if (!location) {
6
+ deps.log("Error: @anchrd/intel-api migrations were not found.");
7
+ return 1;
8
+ }
9
+ const temporaryOutDir = `${location.outDir}.tmp`;
10
+ await deps.copyDir(location.dir, temporaryOutDir);
11
+ await deps.replaceDir(temporaryOutDir, location.outDir);
12
+ deps.log(`Intel migrations copied to ${location.outDir}.`);
13
+ return 0;
14
+ },
15
+ };
16
+ }
@@ -0,0 +1,9 @@
1
+ export interface PrepareDeps {
2
+ resolveMigrations(): Promise<{
3
+ dir: string;
4
+ outDir: string;
5
+ } | null>;
6
+ copyDir(from: string, to: string): Promise<void>;
7
+ replaceDir(from: string, to: string): Promise<void>;
8
+ log(message: string): void;
9
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { Authorized, GateClient } from "@anchrd/gate-sdk";
2
+ export declare function bearer(headers: Headers): string | null;
3
+ export declare function authorize(gate: Pick<GateClient, "authorize">, headers: Headers, resource: string): Promise<Authorized | null>;
4
+ export declare function authorizeBearer(gate: Pick<GateClient, "authorize">, token: string, resource: string): Promise<Authorized | null>;
@@ -0,0 +1,19 @@
1
+ export function bearer(headers) {
2
+ const value = headers.get("authorization");
3
+ // RFC 9110 defines the auth scheme as case-insensitive, so a client sending "bearer" must not be
4
+ // turned away as unauthenticated.
5
+ if (!value || !/^bearer /i.test(value))
6
+ return null;
7
+ const token = value.slice(7);
8
+ return token.length > 0 ? token : null;
9
+ }
10
+ export async function authorize(gate, headers, resource) {
11
+ const token = bearer(headers);
12
+ if (!token)
13
+ return null;
14
+ return await authorizeBearer(gate, token, resource);
15
+ }
16
+ export async function authorizeBearer(gate, token, resource) {
17
+ const result = await gate.authorize(token);
18
+ return result.ok && result.resource === resource ? result : null;
19
+ }
@@ -0,0 +1,5 @@
1
+ export declare class IntelError extends Error {
2
+ readonly status: number;
3
+ readonly code: string;
4
+ constructor(status: number, code: string, message: string);
5
+ }
@@ -0,0 +1,10 @@
1
+ export class IntelError extends Error {
2
+ status;
3
+ code;
4
+ constructor(status, code, message) {
5
+ super(message);
6
+ this.status = status;
7
+ this.code = code;
8
+ this.name = "IntelError";
9
+ }
10
+ }
@@ -0,0 +1,8 @@
1
+ export declare function problemDetails(status: number, code: string, title: string, detail?: string): {
2
+ type: string;
3
+ status: number;
4
+ title: string;
5
+ detail?: string | undefined;
6
+ instance?: string | undefined;
7
+ code?: string | undefined;
8
+ };
@@ -0,0 +1,4 @@
1
+ import { ProblemDetails } from "@anchrd/intel-contract";
2
+ export function problemDetails(status, code, title, detail) {
3
+ return ProblemDetails.parse({ type: "about:blank", status, code, title, detail });
4
+ }
@@ -0,0 +1,2 @@
1
+ import { z } from "zod";
2
+ export declare const SafeReturnPath: z.ZodString;
@@ -0,0 +1,11 @@
1
+ import { z } from "zod";
2
+ export const SafeReturnPath = z
3
+ .string()
4
+ .startsWith("/")
5
+ .refine((value) => !value.startsWith("//") &&
6
+ !value.startsWith("/\\") &&
7
+ !value.includes("\\") &&
8
+ ![...value].some((character) => {
9
+ const code = character.charCodeAt(0);
10
+ return code < 32 || code === 127;
11
+ }), "Return path must be a local absolute path");
@@ -0,0 +1 @@
1
+ export declare function sha256Hex(crypto: Crypto, content: string | ArrayBuffer | Uint8Array): Promise<string>;
@@ -0,0 +1,9 @@
1
+ export async function sha256Hex(crypto, content) {
2
+ const source = typeof content === "string"
3
+ ? new TextEncoder().encode(content).buffer
4
+ : content instanceof Uint8Array
5
+ ? Uint8Array.from(content).buffer
6
+ : content;
7
+ const digest = await crypto.subtle.digest("SHA-256", source);
8
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
9
+ }
@@ -0,0 +1,2 @@
1
+ import type { ToolDeps, ToolService } from "./tools.types.js";
2
+ export declare function createTools(deps: ToolDeps): ToolService;