@anchrd/intel-api 0.14.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 +0 -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.js +16 -113
- package/dist/bundle/bundle.js +28 -141
- package/dist/cli/cli.js +3 -9
- package/dist/http/http.js +1 -206
- package/dist/http/http.types.d.ts +0 -8
- package/dist/indexing/indexing.js +18 -89
- package/dist/intel/intel.js +4 -9
- package/dist/intel/intel.types.d.ts +0 -6
- package/dist/mcp/mcp.js +3 -292
- 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 +20 -886
- package/dist/nodes/nodes.types.d.ts +10 -169
- package/dist/tools/tools.js +37 -148
- package/dist/tools/tools.types.d.ts +0 -21
- package/migrations/0018_no_context_policy_at_last.sql +13 -6
- 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 -306
- 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 -61
- package/dist/nodes/board/board.js +0 -826
- package/dist/nodes/board/board.types.d.ts +0 -38
- 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";
|
|
@@ -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
|
|
@@ -213,33 +207,6 @@ export async function handleMcp(request, deps) {
|
|
|
213
207
|
openWorldHint: false,
|
|
214
208
|
},
|
|
215
209
|
}, async (input) => text(await deps.nodes.search(actor, input)));
|
|
216
|
-
// ⚠️ Plain `registerTool` and no alias: these tools are new with #139 and never had a
|
|
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
210
|
server.registerTool("node_table_get", {
|
|
244
211
|
title: "Get node table",
|
|
245
212
|
description: "Read one authorized table as column names and rows.",
|
|
@@ -252,21 +219,6 @@ 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
|
-
// A board answers whole: the statuses and every task, in one read. There is no per-task read
|
|
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
222
|
server.registerTool("node_links_list", {
|
|
271
223
|
title: "List node links",
|
|
272
224
|
description: "List authorized outgoing links and backlinks for one node.",
|
|
@@ -356,65 +308,8 @@ 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("agent_update", {
|
|
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
313
|
server.registerTool("node_save", {
|
|
419
314
|
title: "Save node version",
|
|
420
315
|
description: "Append an immutable content version using an optimistic base version.",
|
|
@@ -528,93 +423,6 @@ 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
|
-
server.registerTool("board_repair_task_ids", {
|
|
605
|
-
title: "Repair repeated board task ids",
|
|
606
|
-
description: "Give a fresh id to every task entry on a board that repeats an id another entry already used. Only a board imported from a hand-written or foreign bundle can hold such a pair, and it cannot be repaired with the other calls: they address a task by id, so they would read, write or remove both entries at once. The first entry under an id keeps it — that is the one every parentId and dependsOn on the board already means — and each later one becomes its own task with everything else it carries, so nothing is lost and no edge is invented. The answer names each pair. A board that names every task once is refused rather than rewritten. Repeated STATUS ids are repaired with board_configure instead, by writing the status list with distinct ids.",
|
|
607
|
-
inputSchema: RepairBoardTaskIdsInput,
|
|
608
|
-
annotations: {
|
|
609
|
-
title: "Repair repeated board task ids",
|
|
610
|
-
readOnlyHint: false,
|
|
611
|
-
// Nothing is removed and nothing is overwritten: one entry gains an id of its own and
|
|
612
|
-
// every other field on every task stays exactly as it stood.
|
|
613
|
-
destructiveHint: false,
|
|
614
|
-
idempotentHint: true,
|
|
615
|
-
openWorldHint: false,
|
|
616
|
-
},
|
|
617
|
-
}, async (input) => text(await deps.nodes.repairBoardTaskIds(actor, input)));
|
|
618
426
|
server.registerTool("node_update", {
|
|
619
427
|
title: "Update node",
|
|
620
428
|
description: "Rename, move, or describe a node.",
|
|
@@ -965,101 +773,6 @@ export async function handleMcp(request, deps) {
|
|
|
965
773
|
},
|
|
966
774
|
}, async (input) => text(await deps.flows.cancelRun(flowActor, input)));
|
|
967
775
|
}
|
|
968
|
-
/**
|
|
969
|
-
* Driving an agent, over the same door the screen uses.
|
|
970
|
-
*
|
|
971
|
-
* ⚠️ The three below are the MCP half of the two head actions on the agent page — the repository's
|
|
972
|
-
* "one business layer, three thin surfaces" rule, kept in the same delivery. They call the runtime
|
|
973
|
-
* through `deps.agents`, which resolves the resource ACL and forwards the CALLER's bearer; a tool
|
|
974
|
-
* that reached the runtime with anything else would be Intel acting as somebody it is not.
|
|
975
|
-
*/
|
|
976
|
-
if (permits(deps.authorization, "agents", "run")) {
|
|
977
|
-
server.registerTool("agent_pause", {
|
|
978
|
-
title: "Pause agent",
|
|
979
|
-
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.",
|
|
980
|
-
inputSchema: PauseAgentInput,
|
|
981
|
-
annotations: {
|
|
982
|
-
title: "Pause agent",
|
|
983
|
-
readOnlyHint: false,
|
|
984
|
-
// Nothing is deleted, and yet work that would have happened does not — a client deciding
|
|
985
|
-
// whether to ask first has to be told that.
|
|
986
|
-
destructiveHint: true,
|
|
987
|
-
idempotentHint: true,
|
|
988
|
-
openWorldHint: false,
|
|
989
|
-
},
|
|
990
|
-
}, async (input) => text(await deps.agents.request({
|
|
991
|
-
actor: agentActor,
|
|
992
|
-
token: deps.bearer,
|
|
993
|
-
agentId: input.nodeId,
|
|
994
|
-
method: "POST",
|
|
995
|
-
path: "/pause",
|
|
996
|
-
body: "{}",
|
|
997
|
-
})));
|
|
998
|
-
server.registerTool("agent_resume", {
|
|
999
|
-
title: "Resume agent",
|
|
1000
|
-
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.",
|
|
1001
|
-
inputSchema: PauseAgentInput,
|
|
1002
|
-
annotations: {
|
|
1003
|
-
title: "Resume agent",
|
|
1004
|
-
readOnlyHint: false,
|
|
1005
|
-
destructiveHint: false,
|
|
1006
|
-
idempotentHint: true,
|
|
1007
|
-
openWorldHint: false,
|
|
1008
|
-
},
|
|
1009
|
-
}, async (input) => text(await deps.agents.request({
|
|
1010
|
-
actor: agentActor,
|
|
1011
|
-
token: deps.bearer,
|
|
1012
|
-
agentId: input.nodeId,
|
|
1013
|
-
method: "POST",
|
|
1014
|
-
path: "/resume",
|
|
1015
|
-
body: "{}",
|
|
1016
|
-
})));
|
|
1017
|
-
/**
|
|
1018
|
-
* ⚠️ The MCP half of the profile's cost figures, in the same delivery slice — the repository's
|
|
1019
|
-
* "every user-visible capability has an Intel MCP equivalent" rule.
|
|
1020
|
-
*
|
|
1021
|
-
* ⚠️ Under `agents/run` beside pause and resume, not under `knowledge/read`. What an agent
|
|
1022
|
-
* spends is as sensitive as what it did, and this answer stands beside the run list rather than
|
|
1023
|
-
* beside the node.
|
|
1024
|
-
*
|
|
1025
|
-
* ⚠️ `status` travels with the numbers and is not flattened away. A client that read an empty
|
|
1026
|
-
* `runs` list as "this agent is free" would be reporting a Cloudflare outage as a saving.
|
|
1027
|
-
*/
|
|
1028
|
-
server.registerTool("agent_costs", {
|
|
1029
|
-
title: "Agent costs",
|
|
1030
|
-
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.",
|
|
1031
|
-
inputSchema: PauseAgentInput,
|
|
1032
|
-
annotations: {
|
|
1033
|
-
title: "Agent costs",
|
|
1034
|
-
readOnlyHint: true,
|
|
1035
|
-
destructiveHint: false,
|
|
1036
|
-
idempotentHint: true,
|
|
1037
|
-
openWorldHint: false,
|
|
1038
|
-
},
|
|
1039
|
-
}, async (input) => text(await deps.agentCosts.read(agentActor, input.nodeId)));
|
|
1040
|
-
server.registerTool("agent_run_now", {
|
|
1041
|
-
title: "Run agent now",
|
|
1042
|
-
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.",
|
|
1043
|
-
inputSchema: RunAgentNowInput,
|
|
1044
|
-
annotations: {
|
|
1045
|
-
title: "Run agent now",
|
|
1046
|
-
readOnlyHint: false,
|
|
1047
|
-
destructiveHint: false,
|
|
1048
|
-
// Every run is a new run, so asking twice runs it twice.
|
|
1049
|
-
idempotentHint: false,
|
|
1050
|
-
// The agent may reach whatever its own definition grants it, so what a run touches is not
|
|
1051
|
-
// knowable from this schema.
|
|
1052
|
-
openWorldHint: true,
|
|
1053
|
-
},
|
|
1054
|
-
}, async (input) => text(await deps.agents.request({
|
|
1055
|
-
actor: agentActor,
|
|
1056
|
-
token: deps.bearer,
|
|
1057
|
-
agentId: input.nodeId,
|
|
1058
|
-
method: "POST",
|
|
1059
|
-
path: "/run",
|
|
1060
|
-
body: JSON.stringify({ target: input.target }),
|
|
1061
|
-
})));
|
|
1062
|
-
}
|
|
1063
776
|
if (permits(deps.authorization, "tools", "read")) {
|
|
1064
777
|
server.registerTool("tools_catalog_list", {
|
|
1065
778
|
title: "List tools",
|
|
@@ -1073,9 +786,7 @@ export async function handleMcp(request, deps) {
|
|
|
1073
786
|
openWorldHint: false,
|
|
1074
787
|
},
|
|
1075
788
|
}, async () => text(await deps.tools.catalog(toolActor)));
|
|
1076
|
-
// The
|
|
1077
|
-
// given tools from a client instead of from the screen. Assigning and removing is
|
|
1078
|
-
// `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.
|
|
1079
790
|
server.registerTool("tools_servers_list", {
|
|
1080
791
|
title: "List tool servers",
|
|
1081
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 [];
|