@anchrd/intel-api 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/cloudflare/cloudflare.js +1 -68
- package/dist/adapters/cloudflare/cloudflare.types.d.ts +0 -39
- package/dist/adapters/db/db-flows.js +1 -1
- package/dist/adapters/db/db-grants.js +1 -1
- package/dist/adapters/db/db-indexing.js +79 -0
- package/dist/adapters/db/db.js +81 -139
- package/dist/adapters/semantic-index/semantic-index.js +97 -17
- package/dist/adapters/semantic-index/semantic-index.types.d.ts +20 -1
- package/dist/bundle/bundle.js +42 -134
- package/dist/cli/cli.js +3 -9
- package/dist/http/http.js +5 -206
- package/dist/http/http.types.d.ts +0 -8
- package/dist/indexing/indexing.js +133 -55
- package/dist/indexing/indexing.types.d.ts +1 -0
- package/dist/intel/intel.js +4 -9
- package/dist/intel/intel.types.d.ts +0 -6
- package/dist/mcp/mcp.js +33 -308
- package/dist/mcp/mcp.types.d.ts +2 -7
- package/dist/nodes/document-links/document-links.d.ts +6 -8
- package/dist/nodes/document-links/document-links.js +8 -31
- package/dist/nodes/nodes.js +92 -826
- package/dist/nodes/nodes.types.d.ts +57 -158
- package/dist/tools/tools.js +37 -148
- package/dist/tools/tools.types.d.ts +0 -21
- package/migrations/0009_no_context_policy.sql +15 -0
- package/migrations/0017_a_vector_per_card.sql +38 -0
- package/migrations/0018_no_context_policy_at_last.sql +97 -0
- package/migrations/0019_one_name_for_the_grants.sql +52 -0
- package/package.json +2 -2
- package/dist/adapters/cloudflare-api/cloudflare-api.d.ts +0 -22
- package/dist/adapters/cloudflare-api/cloudflare-api.js +0 -214
- package/dist/adapters/cloudflare-api/cloudflare-api.types.d.ts +0 -64
- package/dist/adapters/cloudflare-api/cloudflare-api.types.js +0 -1
- package/dist/adapters/gate-applications/gate-applications.d.ts +0 -23
- package/dist/adapters/gate-applications/gate-applications.js +0 -88
- package/dist/adapters/tool-delegation/tool-delegation.d.ts +0 -22
- package/dist/adapters/tool-delegation/tool-delegation.js +0 -90
- package/dist/agent-costs/agent-costs.d.ts +0 -16
- package/dist/agent-costs/agent-costs.js +0 -105
- package/dist/agent-costs/agent-costs.types.d.ts +0 -30
- package/dist/agent-costs/agent-costs.types.js +0 -1
- package/dist/agent-runtime/agent-runtime.d.ts +0 -16
- package/dist/agent-runtime/agent-runtime.js +0 -150
- package/dist/agent-runtime/agent-runtime.types.d.ts +0 -122
- package/dist/agent-runtime/agent-runtime.types.js +0 -1
- package/dist/model-catalog/model-catalog.d.ts +0 -2
- package/dist/model-catalog/model-catalog.js +0 -99
- package/dist/model-catalog/model-catalog.types.d.ts +0 -15
- package/dist/model-catalog/model-catalog.types.js +0 -1
- package/dist/nodes/board/board.d.ts +0 -59
- package/dist/nodes/board/board.js +0 -528
- package/dist/nodes/board/board.types.d.ts +0 -31
- package/dist/nodes/board/board.types.js +0 -1
- package/migrations/0013_agents_in_the_tree.sql +0 -76
- package/migrations/0014_agent_applications.sql +0 -25
- package/migrations/0015_tools_delegated_from_a_connection.sql +0 -15
- package/migrations/0016_boards_in_the_tree.sql +0 -80
package/dist/mcp/mcp.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { McpServer, ResourceTemplate
|
|
1
|
+
import { AppendTableRowsInput, ArchiveFlowInput, ArchiveNodeInput, CancelFlowRunInput, CompleteFlowRunStepInput, CreateFlowInput, CreateNodeInput, DefineTableInput, DeleteTableRowsInput, ExecuteToolInput, GetFlowInput, GetFlowRunInput, GetFlowVersionInput, GetNodeInput, GetNodeVersionInput, GetTableInput, IntelId, ListFlowRunsInput, ListFlowsInput, ListGrantsInput, ListNodesInput, NodeGraphInput, PreviewFlowPublishInput, PublishFlowInput, RedefineTableInput, RelationGraphInput, ResolveNodeLinksInput, RevokeGrantInput, SaveAttachmentInput, SaveFlowVersionInput, SaveNodeVersionInput, SearchInput, ShareInput, StartFlowRunInput, TestToolInput, UnpublishFlowInput, UpdateFlowInput, UpdateNodeInput, UpdateTableRowsInput, } from "@anchrd/intel-contract";
|
|
2
|
+
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { permits } from "../shared/gate-authorization/gate-authorization.js";
|
|
@@ -8,18 +8,18 @@ function text(value) {
|
|
|
8
8
|
return { content: [{ type: "text", text: JSON.stringify(value) }], isError: false };
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* ⚠️ One name per tool, and the second one is gone for good (#149).
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* Every tool here carried a `knowledge_*` alias beside its name from #125 until today. The alias
|
|
14
|
+
* was never politeness: a tool name lives in somebody else's configuration — a portal endpoint, an
|
|
15
|
+
* agent definition, a saved prompt — so renaming it here alone breaks them at the moment we deploy
|
|
16
|
+
* rather than at a moment they chose. ADR-0005 §8 therefore kept both, **for a transition**.
|
|
17
|
+
*
|
|
18
|
+
* A transition without an end date is not a transition, it is a second permanent name — and two
|
|
19
|
+
* names for one tool is exactly the friction #125 removed. It ends here. What follows for anybody
|
|
20
|
+
* writing a tool: there is one name, and a rename is a breaking change that needs its own ticket
|
|
21
|
+
* and its own notice, not a quiet second entry.
|
|
18
22
|
*/
|
|
19
|
-
function registerWithAlias(server, names, config, handler) {
|
|
20
|
-
server.registerTool(names.name, config, handler);
|
|
21
|
-
server.registerTool(names.deprecated, { ...config, description: `Deprecated: use ${names.name}. ${config.description}` }, handler);
|
|
22
|
-
}
|
|
23
23
|
// MCP returns resource payloads inline as base64, so an attachment cannot be streamed to the client
|
|
24
24
|
// and must fit in Worker memory twice over. Refuse oversized attachments instead of losing the
|
|
25
25
|
// isolate; HTTP still serves them as a stream.
|
|
@@ -61,12 +61,6 @@ export async function handleMcp(request, deps) {
|
|
|
61
61
|
canReadFlows: permits(deps.authorization, "flows", "read"),
|
|
62
62
|
canCreateFlows: permits(deps.authorization, "flows", "create"),
|
|
63
63
|
};
|
|
64
|
-
const agentActor = {
|
|
65
|
-
id: deps.authorization.identity.id,
|
|
66
|
-
email: deps.authorization.identity.email,
|
|
67
|
-
isAdmin: deps.authorization.can("intel", "admin"),
|
|
68
|
-
canRun: permits(deps.authorization, "agents", "run"),
|
|
69
|
-
};
|
|
70
64
|
const server = new McpServer({ name: "intel", version: "0.1.0" });
|
|
71
65
|
const EmptyInput = z.strictObject({});
|
|
72
66
|
// `null` is the root: the whole tree as this caller may read it. The field is required rather
|
|
@@ -112,7 +106,7 @@ export async function handleMcp(request, deps) {
|
|
|
112
106
|
name: deps.authorization.identity.name ?? null,
|
|
113
107
|
}));
|
|
114
108
|
if (permits(deps.authorization, "knowledge", "read")) {
|
|
115
|
-
|
|
109
|
+
server.registerTool("node_list", {
|
|
116
110
|
title: "List nodes",
|
|
117
111
|
description: "List authorized folders, documents, attachments, and tables under one parent.",
|
|
118
112
|
inputSchema: ListNodesInput,
|
|
@@ -124,7 +118,7 @@ export async function handleMcp(request, deps) {
|
|
|
124
118
|
openWorldHint: false,
|
|
125
119
|
},
|
|
126
120
|
}, async (input) => text(await deps.nodes.list(actor, input)));
|
|
127
|
-
|
|
121
|
+
server.registerTool("node_get", {
|
|
128
122
|
title: "Get node",
|
|
129
123
|
description: "Get one authorized node and its current immutable content version.",
|
|
130
124
|
inputSchema: GetNodeInput,
|
|
@@ -136,7 +130,7 @@ export async function handleMcp(request, deps) {
|
|
|
136
130
|
openWorldHint: false,
|
|
137
131
|
},
|
|
138
132
|
}, async (input) => text(await deps.nodes.get(actor, input.nodeId)));
|
|
139
|
-
|
|
133
|
+
server.registerTool("node_versions_list", {
|
|
140
134
|
title: "List node versions",
|
|
141
135
|
description: "List immutable versions of one authorized node.",
|
|
142
136
|
inputSchema: GetNodeInput,
|
|
@@ -163,7 +157,7 @@ export async function handleMcp(request, deps) {
|
|
|
163
157
|
openWorldHint: false,
|
|
164
158
|
},
|
|
165
159
|
}, async (input) => text(await deps.nodes.getVersion(actor, input.nodeId, input.versionId)));
|
|
166
|
-
|
|
160
|
+
server.registerTool("node_attachment_get", {
|
|
167
161
|
title: "Get node attachment",
|
|
168
162
|
description: "Get authorized immutable attachment metadata and its explicit MCP resource URI.",
|
|
169
163
|
inputSchema: GetNodeInput,
|
|
@@ -199,7 +193,7 @@ export async function handleMcp(request, deps) {
|
|
|
199
193
|
],
|
|
200
194
|
};
|
|
201
195
|
});
|
|
202
|
-
|
|
196
|
+
server.registerTool("search", {
|
|
203
197
|
title: "Search nodes",
|
|
204
198
|
description: "Search only authorized indexed nodes and return version-pinned citations. Pass scopeId " +
|
|
205
199
|
"with a folder node id to search that folder and everything beneath it instead of " +
|
|
@@ -213,34 +207,7 @@ export async function handleMcp(request, deps) {
|
|
|
213
207
|
openWorldHint: false,
|
|
214
208
|
},
|
|
215
209
|
}, async (input) => text(await deps.nodes.search(actor, input)));
|
|
216
|
-
|
|
217
|
-
// `knowledge_` name, so inventing one would create a deprecated name that was never used —
|
|
218
|
-
// and #149, which removes the aliases, would then have one more thing to remove.
|
|
219
|
-
server.registerTool("agent_list", {
|
|
220
|
-
title: "List agents",
|
|
221
|
-
description: "List the authorized agents, over the whole tree or under one folder. Omit parentId for every agent, pass null for the root level.",
|
|
222
|
-
inputSchema: ListAgentsInput,
|
|
223
|
-
annotations: {
|
|
224
|
-
title: "List agents",
|
|
225
|
-
readOnlyHint: true,
|
|
226
|
-
destructiveHint: false,
|
|
227
|
-
idempotentHint: true,
|
|
228
|
-
openWorldHint: false,
|
|
229
|
-
},
|
|
230
|
-
}, async (input) => text(await deps.nodes.listAgents(actor, input)));
|
|
231
|
-
server.registerTool("agent_get", {
|
|
232
|
-
title: "Get agent",
|
|
233
|
-
description: "Read one authorized agent with its current definition: the nodes it references and their role, its schedules, and its model.",
|
|
234
|
-
inputSchema: GetAgentInput,
|
|
235
|
-
annotations: {
|
|
236
|
-
title: "Get agent",
|
|
237
|
-
readOnlyHint: true,
|
|
238
|
-
destructiveHint: false,
|
|
239
|
-
idempotentHint: true,
|
|
240
|
-
openWorldHint: false,
|
|
241
|
-
},
|
|
242
|
-
}, async (input) => text(await deps.nodes.getAgent(actor, input)));
|
|
243
|
-
registerWithAlias(server, { name: "node_table_get", deprecated: "knowledge_table_get" }, {
|
|
210
|
+
server.registerTool("node_table_get", {
|
|
244
211
|
title: "Get node table",
|
|
245
212
|
description: "Read one authorized table as column names and rows.",
|
|
246
213
|
inputSchema: GetTableInput,
|
|
@@ -252,22 +219,7 @@ export async function handleMcp(request, deps) {
|
|
|
252
219
|
openWorldHint: false,
|
|
253
220
|
},
|
|
254
221
|
}, async (input) => text(await deps.nodes.getTable(actor, input.nodeId)));
|
|
255
|
-
|
|
256
|
-
// beside it, because a card is small and the interesting question is almost always about
|
|
257
|
-
// several of them at once — what is in review, what waits for what (#285).
|
|
258
|
-
server.registerTool("board_get", {
|
|
259
|
-
title: "Get board",
|
|
260
|
-
description: "Read one authorized board: its status list and every task with its full field set. A board that has never been written answers with the default statuses and no tasks.",
|
|
261
|
-
inputSchema: GetBoardInput,
|
|
262
|
-
annotations: {
|
|
263
|
-
title: "Get board",
|
|
264
|
-
readOnlyHint: true,
|
|
265
|
-
destructiveHint: false,
|
|
266
|
-
idempotentHint: true,
|
|
267
|
-
openWorldHint: false,
|
|
268
|
-
},
|
|
269
|
-
}, async (input) => text(await deps.nodes.getBoard(actor, input)));
|
|
270
|
-
registerWithAlias(server, { name: "node_links_list", deprecated: "knowledge_links_list" }, {
|
|
222
|
+
server.registerTool("node_links_list", {
|
|
271
223
|
title: "List node links",
|
|
272
224
|
description: "List authorized outgoing links and backlinks for one node.",
|
|
273
225
|
inputSchema: GetNodeInput,
|
|
@@ -286,7 +238,7 @@ export async function handleMcp(request, deps) {
|
|
|
286
238
|
// ⚠️ A target this caller may not reach, or one that is gone, is simply absent from the answer.
|
|
287
239
|
// The two are indistinguishable on purpose: telling them apart would confirm that a document
|
|
288
240
|
// exists somewhere they cannot look.
|
|
289
|
-
|
|
241
|
+
server.registerTool("node_links_resolve", {
|
|
290
242
|
title: "Resolve node links",
|
|
291
243
|
description: "Resolve document link targets to the titles this caller is authorized to see. Targets that are unreachable or deleted are absent from the result.",
|
|
292
244
|
inputSchema: ResolveNodeLinksInput,
|
|
@@ -298,7 +250,7 @@ export async function handleMcp(request, deps) {
|
|
|
298
250
|
openWorldHint: false,
|
|
299
251
|
},
|
|
300
252
|
}, async (input) => text(await deps.nodes.resolveLinks(actor, input)));
|
|
301
|
-
|
|
253
|
+
server.registerTool("node_graph", {
|
|
302
254
|
title: "Get node graph",
|
|
303
255
|
description: "Get the authorized nodes and explicit relationships for discovery.",
|
|
304
256
|
inputSchema: NodeGraphInput,
|
|
@@ -330,7 +282,7 @@ export async function handleMcp(request, deps) {
|
|
|
330
282
|
}, async (input) => text(await deps.bundle.manifest(bundleActor, input.nodeId)));
|
|
331
283
|
}
|
|
332
284
|
if (deps.authorization.can("intel", "admin")) {
|
|
333
|
-
|
|
285
|
+
server.registerTool("node_reindex", {
|
|
334
286
|
title: "Reindex nodes",
|
|
335
287
|
description: "Queue every current canonical node version for derived index rebuild.",
|
|
336
288
|
inputSchema: EmptyInput,
|
|
@@ -344,7 +296,7 @@ export async function handleMcp(request, deps) {
|
|
|
344
296
|
}, async () => text(await deps.nodes.reindex(actor)));
|
|
345
297
|
}
|
|
346
298
|
if (permits(deps.authorization, "knowledge", "create")) {
|
|
347
|
-
|
|
299
|
+
server.registerTool("node_create", {
|
|
348
300
|
title: "Create node",
|
|
349
301
|
description: "Create a governed folder, document, attachment, or table node.",
|
|
350
302
|
inputSchema: CreateNodeInput,
|
|
@@ -356,66 +308,9 @@ export async function handleMcp(request, deps) {
|
|
|
356
308
|
openWorldHint: false,
|
|
357
309
|
},
|
|
358
310
|
}, async (input) => text(await deps.nodes.create(actor, input)));
|
|
359
|
-
/**
|
|
360
|
-
* ⚠️ There is no longer ANY tool result in Intel that contains a secret, and that is the whole
|
|
361
|
-
* of D29 seen from here. Until #207 this one tool answered with the Application key in plain
|
|
362
|
-
* text — a deliberate exception, because the result WAS the credential and a redacted answer
|
|
363
|
-
* would have meant "an agent you can create but never run". The exception is gone rather than
|
|
364
|
-
* weakened: the key now travels from Gate into the agent runtime over Intel's service binding
|
|
365
|
-
* inside this call, so there is nothing left to hand to a model. `agent_create`, `agent_get`,
|
|
366
|
-
* `agent_list` and every node read answer with `applicationId` alone, which names the principal
|
|
367
|
-
* without authenticating it.
|
|
368
|
-
*/
|
|
369
|
-
server.registerTool("agent_create", {
|
|
370
|
-
title: "Create agent",
|
|
371
|
-
description: "Create an agent node with its first definition, and the Gate application it runs as. The definition names nodes and their role, schedules and a model — never accounts, secrets or channels. The agent's application key is handed to the agent runtime internally and never appears in this result; the agent can run straight away. A repeated idempotency key returns the existing agent and mints nothing.",
|
|
372
|
-
inputSchema: CreateAgentInput,
|
|
373
|
-
annotations: {
|
|
374
|
-
title: "Create agent",
|
|
375
|
-
readOnlyHint: false,
|
|
376
|
-
destructiveHint: false,
|
|
377
|
-
idempotentHint: true,
|
|
378
|
-
// Creating an agent creates a principal in Gate, which is a system outside Intel.
|
|
379
|
-
openWorldHint: true,
|
|
380
|
-
},
|
|
381
|
-
}, async (input) => text(await deps.nodes.createAgent(actor, input, { token: deps.bearer })));
|
|
382
311
|
}
|
|
383
312
|
if (permits(deps.authorization, "knowledge", "write")) {
|
|
384
|
-
server.registerTool("
|
|
385
|
-
title: "Update agent definition",
|
|
386
|
-
description: "Append an immutable definition version to an agent using an optimistic base version.",
|
|
387
|
-
inputSchema: SaveAgentDefinitionInput,
|
|
388
|
-
annotations: {
|
|
389
|
-
title: "Update agent definition",
|
|
390
|
-
readOnlyHint: false,
|
|
391
|
-
destructiveHint: false,
|
|
392
|
-
idempotentHint: true,
|
|
393
|
-
openWorldHint: false,
|
|
394
|
-
},
|
|
395
|
-
}, async (input) => text(await deps.nodes.saveAgentDefinition(actor, input, { token: deps.bearer })));
|
|
396
|
-
/**
|
|
397
|
-
* The MCP half of the "Replace key" action in the agent profile (D29, #207).
|
|
398
|
-
*
|
|
399
|
-
* ⚠️ `knowledge/write` like `agent_update`, not `agents/run`: which principal an agent acts as
|
|
400
|
-
* is a property of the agent, changed by whoever may change the agent. And the result names the
|
|
401
|
-
* principal and the moment — never the key, which went to the runtime and nowhere else.
|
|
402
|
-
*/
|
|
403
|
-
server.registerTool("agent_rotate_key", {
|
|
404
|
-
title: "Replace an agent's application key",
|
|
405
|
-
description: "Issue a new Gate application key for an agent and give it to the agent runtime. The previous key stops working immediately. Use it when a run fails with agent_key_missing, or when a key may have been exposed. The key itself is never returned.",
|
|
406
|
-
inputSchema: RotateAgentKeyInput,
|
|
407
|
-
annotations: {
|
|
408
|
-
title: "Replace an agent's application key",
|
|
409
|
-
readOnlyHint: false,
|
|
410
|
-
// The old key stops working the moment this runs, and anything still using it breaks.
|
|
411
|
-
destructiveHint: true,
|
|
412
|
-
// Asking twice issues twice, and the second answer invalidates the first key.
|
|
413
|
-
idempotentHint: false,
|
|
414
|
-
// The principal lives in Gate, which is a system outside Intel.
|
|
415
|
-
openWorldHint: true,
|
|
416
|
-
},
|
|
417
|
-
}, async (input) => text(await deps.nodes.rotateAgentKey(actor, input, { token: deps.bearer })));
|
|
418
|
-
registerWithAlias(server, { name: "node_save", deprecated: "knowledge_save" }, {
|
|
313
|
+
server.registerTool("node_save", {
|
|
419
314
|
title: "Save node version",
|
|
420
315
|
description: "Append an immutable content version using an optimistic base version.",
|
|
421
316
|
inputSchema: SaveNodeVersionInput,
|
|
@@ -427,7 +322,7 @@ export async function handleMcp(request, deps) {
|
|
|
427
322
|
openWorldHint: false,
|
|
428
323
|
},
|
|
429
324
|
}, async (input) => text(await deps.nodes.save(actor, input)));
|
|
430
|
-
|
|
325
|
+
server.registerTool("node_attachment_save", {
|
|
431
326
|
title: "Save node attachment",
|
|
432
327
|
description: "Append immutable base64 file bytes to an attachment node.",
|
|
433
328
|
inputSchema: SaveAttachmentInput,
|
|
@@ -459,7 +354,7 @@ export async function handleMcp(request, deps) {
|
|
|
459
354
|
zip: decodeZipBase64(input.zipBase64),
|
|
460
355
|
idempotencyKey: input.idempotencyKey,
|
|
461
356
|
})));
|
|
462
|
-
|
|
357
|
+
server.registerTool("node_table_define", {
|
|
463
358
|
title: "Define node table columns",
|
|
464
359
|
description: "Write the column names of an empty table. The header is the contract every append is checked against and cannot be rewritten.",
|
|
465
360
|
inputSchema: DefineTableInput,
|
|
@@ -473,7 +368,7 @@ export async function handleMcp(request, deps) {
|
|
|
473
368
|
}, async (input) => text(await deps.nodes.defineTable(actor, input)));
|
|
474
369
|
// The tool #40 exists for: an agent collecting findings on a schedule appends them without
|
|
475
370
|
// reading or resending what is already there, and two agents appending at once lose nothing.
|
|
476
|
-
|
|
371
|
+
server.registerTool("node_table_append", {
|
|
477
372
|
title: "Append node table rows",
|
|
478
373
|
description: "Append rows to a table without reading or resending its existing content. Each row must have exactly as many cells as the table has columns; a row that does not is rejected and nothing is written.",
|
|
479
374
|
inputSchema: AppendTableRowsInput,
|
|
@@ -528,80 +423,7 @@ export async function handleMcp(request, deps) {
|
|
|
528
423
|
openWorldHint: false,
|
|
529
424
|
},
|
|
530
425
|
}, async (input) => text(await deps.nodes.redefineTable(actor, input)));
|
|
531
|
-
|
|
532
|
-
* The board's write surface (#285). No `knowledge_*` alias on any of them — these are new under
|
|
533
|
-
* the post-#125 naming.
|
|
534
|
-
*
|
|
535
|
-
* ⚠️ Not one of them takes a `baseVersionId`, and the descriptions say so. That is the property
|
|
536
|
-
* an agent has to be able to rely on: two agents working the same board at the same time touch
|
|
537
|
-
* different tasks, and neither is asked to read the whole board first or to retry a conflict
|
|
538
|
-
* that says nothing about what they were doing. The server applies each call by task id to the
|
|
539
|
-
* board as it stands at that moment.
|
|
540
|
-
*/
|
|
541
|
-
server.registerTool("board_configure", {
|
|
542
|
-
title: "Configure board statuses",
|
|
543
|
-
description: 'Write the board\'s whole status list, in the order it should be drawn. Adding, renaming and reordering columns are all this call. The "archived" status is part of every board and cannot be removed, and a status that tasks still sit in cannot be removed either. Set "terminal" on a column to say that standing in it means the work is finished — that is what decides whether a task waiting on another is still blocked. Several columns may be terminal; "archived" always is and cannot be set otherwise. Omitting it leaves an ordinary column not terminal.',
|
|
544
|
-
inputSchema: ConfigureBoardInput,
|
|
545
|
-
annotations: {
|
|
546
|
-
title: "Configure board statuses",
|
|
547
|
-
readOnlyHint: false,
|
|
548
|
-
// Removing a column is possible here, so this is not a purely additive write.
|
|
549
|
-
destructiveHint: true,
|
|
550
|
-
idempotentHint: true,
|
|
551
|
-
openWorldHint: false,
|
|
552
|
-
},
|
|
553
|
-
}, async (input) => text(await deps.nodes.configureBoard(actor, input)));
|
|
554
|
-
server.registerTool("board_task_add", {
|
|
555
|
-
title: "Add board task",
|
|
556
|
-
description: "Add one task to a board. The server assigns its id and its place in the order; name afterTaskId or beforeTaskId to put it between two cards, or neither to put it last in its column. parentId makes it a subtask of another task on the same board, dependsOn names tasks of the same board it waits for, and references names Intel nodes. dependsOn, labels and references are sets: naming the same task, word or node twice is refused rather than quietly folded together. Needs no baseVersionId: tasks are addressed by id, so parallel writers do not collide.",
|
|
557
|
-
inputSchema: AddBoardTaskInput,
|
|
558
|
-
annotations: {
|
|
559
|
-
title: "Add board task",
|
|
560
|
-
readOnlyHint: false,
|
|
561
|
-
destructiveHint: false,
|
|
562
|
-
idempotentHint: true,
|
|
563
|
-
openWorldHint: false,
|
|
564
|
-
},
|
|
565
|
-
}, async (input) => text(await deps.nodes.addBoardTask(actor, input)));
|
|
566
|
-
server.registerTool("board_task_update", {
|
|
567
|
-
title: "Update board task",
|
|
568
|
-
description: "Change what a task says: title, assignee, labels, dates, dependencies, description or references. Where a task SITS — its status, its parent, its order — is board_task_move instead. Every named field replaces its current value whole; fields that are not named stay as they are. dependsOn, labels and references are sets: appending to a list this task already holds is refused rather than quietly folded together, so read the current value before you write it back.",
|
|
569
|
-
inputSchema: UpdateBoardTaskInput,
|
|
570
|
-
annotations: {
|
|
571
|
-
title: "Update board task",
|
|
572
|
-
readOnlyHint: false,
|
|
573
|
-
// Every named field replaces what stood there; the version history keeps it, the task
|
|
574
|
-
// does not.
|
|
575
|
-
destructiveHint: true,
|
|
576
|
-
idempotentHint: true,
|
|
577
|
-
openWorldHint: false,
|
|
578
|
-
},
|
|
579
|
-
}, async (input) => text(await deps.nodes.updateBoardTask(actor, input)));
|
|
580
|
-
server.registerTool("board_task_move", {
|
|
581
|
-
title: "Move board task",
|
|
582
|
-
description: 'Move one task: into another status, under another parent, or between two neighbours. Archiving a task is this call with status "archived" — there is no separate verb, and nothing is deleted by it. Two moves on two different tasks both go through.',
|
|
583
|
-
inputSchema: MoveBoardTaskInput,
|
|
584
|
-
annotations: {
|
|
585
|
-
title: "Move board task",
|
|
586
|
-
readOnlyHint: false,
|
|
587
|
-
destructiveHint: false,
|
|
588
|
-
idempotentHint: true,
|
|
589
|
-
openWorldHint: false,
|
|
590
|
-
},
|
|
591
|
-
}, async (input) => text(await deps.nodes.moveBoardTask(actor, input)));
|
|
592
|
-
server.registerTool("board_task_delete", {
|
|
593
|
-
title: "Delete board task",
|
|
594
|
-
description: 'Remove a task AND every subtask under it, recursively. The answer counts everything that went, and dependsOn entries in the remaining tasks that pointed at a deleted one are removed with it. To take a task off the board without losing it, move it to the "archived" status instead.',
|
|
595
|
-
inputSchema: DeleteBoardTaskInput,
|
|
596
|
-
annotations: {
|
|
597
|
-
title: "Delete board task",
|
|
598
|
-
readOnlyHint: false,
|
|
599
|
-
destructiveHint: true,
|
|
600
|
-
idempotentHint: true,
|
|
601
|
-
openWorldHint: false,
|
|
602
|
-
},
|
|
603
|
-
}, async (input) => text(await deps.nodes.deleteBoardTask(actor, input)));
|
|
604
|
-
registerWithAlias(server, { name: "node_update", deprecated: "knowledge_update" }, {
|
|
426
|
+
server.registerTool("node_update", {
|
|
605
427
|
title: "Update node",
|
|
606
428
|
description: "Rename, move, or describe a node.",
|
|
607
429
|
inputSchema: UpdateNodeInput,
|
|
@@ -613,7 +435,7 @@ export async function handleMcp(request, deps) {
|
|
|
613
435
|
openWorldHint: false,
|
|
614
436
|
},
|
|
615
437
|
}, async (input) => text(await deps.nodes.update(actor, input)));
|
|
616
|
-
|
|
438
|
+
server.registerTool("node_archive", {
|
|
617
439
|
title: "Archive node",
|
|
618
440
|
description: "Archive or restore one node using optimistic concurrency. Archiving an agent also switches off its Gate application; restoring switches it back on.",
|
|
619
441
|
inputSchema: ArchiveNodeInput,
|
|
@@ -632,7 +454,7 @@ export async function handleMcp(request, deps) {
|
|
|
632
454
|
async (input) => text(await deps.nodes.archive(actor, input, { token: deps.bearer })));
|
|
633
455
|
}
|
|
634
456
|
if (permits(deps.authorization, "knowledge", "share")) {
|
|
635
|
-
|
|
457
|
+
server.registerTool("node_shares_list", {
|
|
636
458
|
title: "List node shares",
|
|
637
459
|
description: "List direct grants for nodes the caller is allowed to manage.",
|
|
638
460
|
inputSchema: ListGrantsInput,
|
|
@@ -644,7 +466,7 @@ export async function handleMcp(request, deps) {
|
|
|
644
466
|
openWorldHint: false,
|
|
645
467
|
},
|
|
646
468
|
}, async (input) => text(await deps.nodes.listGrants(actor, input.resourceId)));
|
|
647
|
-
|
|
469
|
+
server.registerTool("node_share", {
|
|
648
470
|
title: "Share node",
|
|
649
471
|
description: "Grant inherited node access to a Gate user, verified email, or the organization.",
|
|
650
472
|
inputSchema: ShareInput,
|
|
@@ -656,7 +478,7 @@ export async function handleMcp(request, deps) {
|
|
|
656
478
|
openWorldHint: false,
|
|
657
479
|
},
|
|
658
480
|
}, async (input) => text(await deps.nodes.share(actor, input)));
|
|
659
|
-
|
|
481
|
+
server.registerTool("node_revoke_share", {
|
|
660
482
|
title: "Revoke node share",
|
|
661
483
|
description: "Revoke one direct node grant by ID.",
|
|
662
484
|
inputSchema: RevokeGrantInput,
|
|
@@ -951,101 +773,6 @@ export async function handleMcp(request, deps) {
|
|
|
951
773
|
},
|
|
952
774
|
}, async (input) => text(await deps.flows.cancelRun(flowActor, input)));
|
|
953
775
|
}
|
|
954
|
-
/**
|
|
955
|
-
* Driving an agent, over the same door the screen uses.
|
|
956
|
-
*
|
|
957
|
-
* ⚠️ The three below are the MCP half of the two head actions on the agent page — the repository's
|
|
958
|
-
* "one business layer, three thin surfaces" rule, kept in the same delivery. They call the runtime
|
|
959
|
-
* through `deps.agents`, which resolves the resource ACL and forwards the CALLER's bearer; a tool
|
|
960
|
-
* that reached the runtime with anything else would be Intel acting as somebody it is not.
|
|
961
|
-
*/
|
|
962
|
-
if (permits(deps.authorization, "agents", "run")) {
|
|
963
|
-
server.registerTool("agent_pause", {
|
|
964
|
-
title: "Pause agent",
|
|
965
|
-
description: "Stop an agent from acting on its own: no schedule fires, no task and no mail is taken. Its chat keeps answering, and nothing is queued up for later.",
|
|
966
|
-
inputSchema: PauseAgentInput,
|
|
967
|
-
annotations: {
|
|
968
|
-
title: "Pause agent",
|
|
969
|
-
readOnlyHint: false,
|
|
970
|
-
// Nothing is deleted, and yet work that would have happened does not — a client deciding
|
|
971
|
-
// whether to ask first has to be told that.
|
|
972
|
-
destructiveHint: true,
|
|
973
|
-
idempotentHint: true,
|
|
974
|
-
openWorldHint: false,
|
|
975
|
-
},
|
|
976
|
-
}, async (input) => text(await deps.agents.request({
|
|
977
|
-
actor: agentActor,
|
|
978
|
-
token: deps.bearer,
|
|
979
|
-
agentId: input.nodeId,
|
|
980
|
-
method: "POST",
|
|
981
|
-
path: "/pause",
|
|
982
|
-
body: "{}",
|
|
983
|
-
})));
|
|
984
|
-
server.registerTool("agent_resume", {
|
|
985
|
-
title: "Resume agent",
|
|
986
|
-
description: "Let a paused agent act again. Its schedules are armed from the definition as it stands now; nothing that was due while it stood still is caught up.",
|
|
987
|
-
inputSchema: PauseAgentInput,
|
|
988
|
-
annotations: {
|
|
989
|
-
title: "Resume agent",
|
|
990
|
-
readOnlyHint: false,
|
|
991
|
-
destructiveHint: false,
|
|
992
|
-
idempotentHint: true,
|
|
993
|
-
openWorldHint: false,
|
|
994
|
-
},
|
|
995
|
-
}, async (input) => text(await deps.agents.request({
|
|
996
|
-
actor: agentActor,
|
|
997
|
-
token: deps.bearer,
|
|
998
|
-
agentId: input.nodeId,
|
|
999
|
-
method: "POST",
|
|
1000
|
-
path: "/resume",
|
|
1001
|
-
body: "{}",
|
|
1002
|
-
})));
|
|
1003
|
-
/**
|
|
1004
|
-
* ⚠️ The MCP half of the profile's cost figures, in the same delivery slice — the repository's
|
|
1005
|
-
* "every user-visible capability has an Intel MCP equivalent" rule.
|
|
1006
|
-
*
|
|
1007
|
-
* ⚠️ Under `agents/run` beside pause and resume, not under `knowledge/read`. What an agent
|
|
1008
|
-
* spends is as sensitive as what it did, and this answer stands beside the run list rather than
|
|
1009
|
-
* beside the node.
|
|
1010
|
-
*
|
|
1011
|
-
* ⚠️ `status` travels with the numbers and is not flattened away. A client that read an empty
|
|
1012
|
-
* `runs` list as "this agent is free" would be reporting a Cloudflare outage as a saving.
|
|
1013
|
-
*/
|
|
1014
|
-
server.registerTool("agent_costs", {
|
|
1015
|
-
title: "Agent costs",
|
|
1016
|
-
description: "What one agent has actually cost, read from Cloudflare's AI Gateway log: per run, and as a total over the last 7 and 30 days. `status` says whether the numbers were read at all — `not_configured` and `unreadable` both mean no figures, never zero cost. `partial` means the read hit its page limit, so the totals are a floor.",
|
|
1017
|
-
inputSchema: PauseAgentInput,
|
|
1018
|
-
annotations: {
|
|
1019
|
-
title: "Agent costs",
|
|
1020
|
-
readOnlyHint: true,
|
|
1021
|
-
destructiveHint: false,
|
|
1022
|
-
idempotentHint: true,
|
|
1023
|
-
openWorldHint: false,
|
|
1024
|
-
},
|
|
1025
|
-
}, async (input) => text(await deps.agentCosts.read(agentActor, input.nodeId)));
|
|
1026
|
-
server.registerTool("agent_run_now", {
|
|
1027
|
-
title: "Run agent now",
|
|
1028
|
-
description: "Fire one of the agent's own schedules straight away, against the document or flow it names. The run appears in the agent's run list; a target the agent does not schedule is refused.",
|
|
1029
|
-
inputSchema: RunAgentNowInput,
|
|
1030
|
-
annotations: {
|
|
1031
|
-
title: "Run agent now",
|
|
1032
|
-
readOnlyHint: false,
|
|
1033
|
-
destructiveHint: false,
|
|
1034
|
-
// Every run is a new run, so asking twice runs it twice.
|
|
1035
|
-
idempotentHint: false,
|
|
1036
|
-
// The agent may reach whatever its own definition grants it, so what a run touches is not
|
|
1037
|
-
// knowable from this schema.
|
|
1038
|
-
openWorldHint: true,
|
|
1039
|
-
},
|
|
1040
|
-
}, async (input) => text(await deps.agents.request({
|
|
1041
|
-
actor: agentActor,
|
|
1042
|
-
token: deps.bearer,
|
|
1043
|
-
agentId: input.nodeId,
|
|
1044
|
-
method: "POST",
|
|
1045
|
-
path: "/run",
|
|
1046
|
-
body: JSON.stringify({ target: input.target }),
|
|
1047
|
-
})));
|
|
1048
|
-
}
|
|
1049
776
|
if (permits(deps.authorization, "tools", "read")) {
|
|
1050
777
|
server.registerTool("tools_catalog_list", {
|
|
1051
778
|
title: "List tools",
|
|
@@ -1059,9 +786,7 @@ export async function handleMcp(request, deps) {
|
|
|
1059
786
|
openWorldHint: false,
|
|
1060
787
|
},
|
|
1061
788
|
}, async () => text(await deps.tools.catalog(toolActor)));
|
|
1062
|
-
// The
|
|
1063
|
-
// given tools from a client instead of from the screen. Assigning and removing is
|
|
1064
|
-
// `agent_update` — a delegation is a field of the definition, not a surface of its own.
|
|
789
|
+
// The servers behind the same live list (D30), as the portal itself names them.
|
|
1065
790
|
server.registerTool("tools_servers_list", {
|
|
1066
791
|
title: "List tool servers",
|
|
1067
792
|
description: "List the MCP servers the calling user reaches through the portal, as the portal itself names them. These are the handles an agent definition may delegate.",
|
package/dist/mcp/mcp.types.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import type { Authorized } from "@anchrd/gate-sdk";
|
|
2
|
-
import type { AgentCostsService } from "../agent-costs/agent-costs.types.js";
|
|
3
|
-
import type { AgentRuntimeService } from "../agent-runtime/agent-runtime.types.js";
|
|
4
2
|
import type { BundleService } from "../bundle/bundle.types.js";
|
|
5
3
|
import type { FlowService } from "../flows/flows.types.js";
|
|
6
4
|
import type { NodeService } from "../nodes/nodes.types.js";
|
|
@@ -8,15 +6,12 @@ import type { ToolService } from "../tools/tools.types.js";
|
|
|
8
6
|
export interface McpDeps {
|
|
9
7
|
authorization: Authorized;
|
|
10
8
|
/**
|
|
11
|
-
* The bearer this call was authorized with,
|
|
12
|
-
*
|
|
9
|
+
* The bearer this call was authorized with, passed on where Intel has to speak to Gate as the
|
|
10
|
+
* person asking rather than as itself. Nothing else reads it, and nothing writes it anywhere.
|
|
13
11
|
*/
|
|
14
12
|
bearer: string;
|
|
15
13
|
nodes: NodeService;
|
|
16
14
|
flows: FlowService;
|
|
17
15
|
tools: ToolService;
|
|
18
16
|
bundle: BundleService;
|
|
19
|
-
agents: AgentRuntimeService;
|
|
20
|
-
/** What an agent has cost, out of the AI Gateway log — the MCP half of the profile's figures (#251). */
|
|
21
|
-
agentCosts: AgentCostsService;
|
|
22
17
|
}
|
|
@@ -9,14 +9,12 @@
|
|
|
9
9
|
* newer editor wrote; a strict parse would turn "I do not recognise this block" into "this document
|
|
10
10
|
* cannot be saved". What it does not recognise contributes no link, which is the safe half.
|
|
11
11
|
*
|
|
12
|
-
* ⚠️
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* "what does this body point at" — the writer (`reconcileTextLinks`) asks this function and does
|
|
17
|
-
* not care which kind it is holding.
|
|
12
|
+
* ⚠️ ONE answer to "what does this body point at", whatever kind holds the body: the writer
|
|
13
|
+
* (`reconcileTextLinks`) asks this function and does not care. Until #390 a board answered here
|
|
14
|
+
* too, through the `references` of its tasks — a difference in where the ids were written and not
|
|
15
|
+
* in what they were.
|
|
18
16
|
*
|
|
19
|
-
* Content that is
|
|
20
|
-
* nothing to read.
|
|
17
|
+
* Content that is not a BlockNote document has no links at all: an attachment, a CSV table or plain
|
|
18
|
+
* markdown carries nothing to read.
|
|
21
19
|
*/
|
|
22
20
|
export declare function documentLinkTargets(mediaType: string, content: string): string[];
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { BlockNoteDocument, BlockNoteMediaType,
|
|
2
|
-
import { upgradeStoredBoard } from "../board/board.js";
|
|
1
|
+
import { BlockNoteDocument, BlockNoteMediaType, DocumentLinkInlineType, } from "@anchrd/intel-contract";
|
|
3
2
|
function isRecord(value) {
|
|
4
3
|
return typeof value === "object" && value !== null;
|
|
5
4
|
}
|
|
@@ -31,18 +30,16 @@ function collect(value, found) {
|
|
|
31
30
|
* newer editor wrote; a strict parse would turn "I do not recognise this block" into "this document
|
|
32
31
|
* cannot be saved". What it does not recognise contributes no link, which is the safe half.
|
|
33
32
|
*
|
|
34
|
-
* ⚠️
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* "what does this body point at" — the writer (`reconcileTextLinks`) asks this function and does
|
|
39
|
-
* not care which kind it is holding.
|
|
33
|
+
* ⚠️ ONE answer to "what does this body point at", whatever kind holds the body: the writer
|
|
34
|
+
* (`reconcileTextLinks`) asks this function and does not care. Until #390 a board answered here
|
|
35
|
+
* too, through the `references` of its tasks — a difference in where the ids were written and not
|
|
36
|
+
* in what they were.
|
|
40
37
|
*
|
|
41
|
-
* Content that is
|
|
42
|
-
* nothing to read.
|
|
38
|
+
* Content that is not a BlockNote document has no links at all: an attachment, a CSV table or plain
|
|
39
|
+
* markdown carries nothing to read.
|
|
43
40
|
*/
|
|
44
41
|
export function documentLinkTargets(mediaType, content) {
|
|
45
|
-
if (mediaType !== BlockNoteMediaType
|
|
42
|
+
if (mediaType !== BlockNoteMediaType)
|
|
46
43
|
return [];
|
|
47
44
|
let parsed;
|
|
48
45
|
try {
|
|
@@ -52,26 +49,6 @@ export function documentLinkTargets(mediaType, content) {
|
|
|
52
49
|
return [];
|
|
53
50
|
}
|
|
54
51
|
const found = new Set();
|
|
55
|
-
if (mediaType === BoardMediaType) {
|
|
56
|
-
/**
|
|
57
|
-
* ⚠️ Upgraded first, like `parseStoredBoard` and `indexing.ts` — this is the FOURTH place a
|
|
58
|
-
* board body is parsed, and the only one that cannot complain (anchrd/intel#321).
|
|
59
|
-
*
|
|
60
|
-
* A board it fails to parse simply has no links, which reads exactly like a board that points
|
|
61
|
-
* at nothing — and the reconciliation this feeds removes the links a body no longer names, so
|
|
62
|
-
* an unparsed old board would quietly leave the link graph. Every caller hands it a body written
|
|
63
|
-
* moments earlier today, so it has never met one; the upgrade is what keeps that from being the
|
|
64
|
-
* only reason.
|
|
65
|
-
*/
|
|
66
|
-
const board = BoardDocument.safeParse(upgradeStoredBoard(parsed));
|
|
67
|
-
if (!board.success)
|
|
68
|
-
return [];
|
|
69
|
-
for (const task of board.data.tasks) {
|
|
70
|
-
for (const reference of task.references)
|
|
71
|
-
found.add(reference);
|
|
72
|
-
}
|
|
73
|
-
return [...found];
|
|
74
|
-
}
|
|
75
52
|
const document = BlockNoteDocument.safeParse(parsed);
|
|
76
53
|
if (!document.success)
|
|
77
54
|
return [];
|