@stigmer/mcp-server 3.2.3 → 3.4.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/README.md +60 -13
- package/cli/mcp-server-stigmer.js +13 -9
- package/config.d.ts +12 -0
- package/config.d.ts.map +1 -1
- package/config.js +5 -0
- package/config.js.map +1 -1
- package/domains/agentexecutions/approve.d.ts +9 -0
- package/domains/agentexecutions/approve.d.ts.map +1 -0
- package/domains/agentexecutions/approve.js +46 -0
- package/domains/agentexecutions/approve.js.map +1 -0
- package/domains/agentexecutions/fetch.d.ts +28 -0
- package/domains/agentexecutions/fetch.d.ts.map +1 -0
- package/domains/agentexecutions/fetch.js +82 -0
- package/domains/agentexecutions/fetch.js.map +1 -0
- package/domains/agentexecutions/run.d.ts +27 -0
- package/domains/agentexecutions/run.d.ts.map +1 -0
- package/domains/agentexecutions/run.js +87 -0
- package/domains/agentexecutions/run.js.map +1 -0
- package/domains/agentexecutions/tools.d.ts +5 -0
- package/domains/agentexecutions/tools.d.ts.map +1 -0
- package/domains/agentexecutions/tools.js +97 -0
- package/domains/agentexecutions/tools.js.map +1 -0
- package/domains/datastores/apply.d.ts +4 -0
- package/domains/datastores/apply.d.ts.map +1 -0
- package/domains/datastores/apply.js +28 -0
- package/domains/datastores/apply.js.map +1 -0
- package/domains/datastores/delete.d.ts +3 -0
- package/domains/datastores/delete.d.ts.map +1 -0
- package/domains/datastores/delete.js +38 -0
- package/domains/datastores/delete.js.map +1 -0
- package/domains/datastores/fetch.d.ts +3 -0
- package/domains/datastores/fetch.d.ts.map +1 -0
- package/domains/datastores/fetch.js +23 -0
- package/domains/datastores/fetch.js.map +1 -0
- package/domains/datastores/resources.d.ts +5 -0
- package/domains/datastores/resources.d.ts.map +1 -0
- package/domains/datastores/resources.js +16 -0
- package/domains/datastores/resources.js.map +1 -0
- package/domains/datastores/tools.d.ts +5 -0
- package/domains/datastores/tools.d.ts.map +1 -0
- package/domains/datastores/tools.js +46 -0
- package/domains/datastores/tools.js.map +1 -0
- package/domains/environments/apply.d.ts +4 -0
- package/domains/environments/apply.d.ts.map +1 -0
- package/domains/environments/apply.js +30 -0
- package/domains/environments/apply.js.map +1 -0
- package/domains/environments/delete.d.ts +3 -0
- package/domains/environments/delete.d.ts.map +1 -0
- package/domains/environments/delete.js +36 -0
- package/domains/environments/delete.js.map +1 -0
- package/domains/environments/fetch.d.ts +6 -0
- package/domains/environments/fetch.d.ts.map +1 -0
- package/domains/environments/fetch.js +30 -0
- package/domains/environments/fetch.js.map +1 -0
- package/domains/environments/resources.d.ts +5 -0
- package/domains/environments/resources.d.ts.map +1 -0
- package/domains/environments/resources.js +16 -0
- package/domains/environments/resources.js.map +1 -0
- package/domains/environments/tools.d.ts +5 -0
- package/domains/environments/tools.d.ts.map +1 -0
- package/domains/environments/tools.js +51 -0
- package/domains/environments/tools.js.map +1 -0
- package/domains/executions/cancel.d.ts +9 -0
- package/domains/executions/cancel.d.ts.map +1 -0
- package/domains/executions/cancel.js +114 -0
- package/domains/executions/cancel.js.map +1 -0
- package/domains/executions/tools.d.ts +5 -0
- package/domains/executions/tools.d.ts.map +1 -0
- package/domains/executions/tools.js +28 -0
- package/domains/executions/tools.js.map +1 -0
- package/domains/records/calls.d.ts +48 -0
- package/domains/records/calls.d.ts.map +1 -0
- package/domains/records/calls.js +118 -0
- package/domains/records/calls.js.map +1 -0
- package/domains/records/errors.d.ts +24 -0
- package/domains/records/errors.d.ts.map +1 -0
- package/domains/records/errors.js +79 -0
- package/domains/records/errors.js.map +1 -0
- package/domains/records/tools.d.ts +7 -0
- package/domains/records/tools.d.ts.map +1 -0
- package/domains/records/tools.js +139 -0
- package/domains/records/tools.js.map +1 -0
- package/domains/resourceuri.d.ts.map +1 -1
- package/domains/resourceuri.js +2 -0
- package/domains/resourceuri.js.map +1 -1
- package/domains/search/tools.d.ts.map +1 -1
- package/domains/search/tools.js +10 -7
- package/domains/search/tools.js.map +1 -1
- package/domains/skills/tools.d.ts.map +1 -1
- package/domains/skills/tools.js +25 -1
- package/domains/skills/tools.js.map +1 -1
- package/domains/skills/versions.d.ts +9 -0
- package/domains/skills/versions.d.ts.map +1 -0
- package/domains/skills/versions.js +31 -0
- package/domains/skills/versions.js.map +1 -0
- package/domains/workflowexecutions/approvals.d.ts +17 -0
- package/domains/workflowexecutions/approvals.d.ts.map +1 -0
- package/domains/workflowexecutions/approvals.js +61 -0
- package/domains/workflowexecutions/approvals.js.map +1 -0
- package/domains/workflowexecutions/run.d.ts +12 -0
- package/domains/workflowexecutions/run.d.ts.map +1 -0
- package/domains/workflowexecutions/run.js +69 -0
- package/domains/workflowexecutions/run.js.map +1 -0
- package/domains/workflowexecutions/tools.d.ts.map +1 -1
- package/domains/workflowexecutions/tools.js +89 -6
- package/domains/workflowexecutions/tools.js.map +1 -1
- package/domains/workflows/tools.d.ts.map +1 -1
- package/domains/workflows/tools.js +69 -5
- package/domains/workflows/tools.js.map +1 -1
- package/domains/workflows/versions.d.ts +29 -0
- package/domains/workflows/versions.d.ts.map +1 -0
- package/domains/workflows/versions.js +99 -0
- package/domains/workflows/versions.js.map +1 -0
- package/gen/agent.d.ts +56 -0
- package/gen/agent.d.ts.map +1 -1
- package/gen/agent.js +24 -1
- package/gen/agent.js.map +1 -1
- package/gen/datastore.d.ts +861 -0
- package/gen/datastore.d.ts.map +1 -0
- package/gen/datastore.js +267 -0
- package/gen/datastore.js.map +1 -0
- package/gen/environment.d.ts +77 -0
- package/gen/environment.d.ts.map +1 -0
- package/gen/environment.js +62 -0
- package/gen/environment.js.map +1 -0
- package/gen/workflow.d.ts +4 -4
- package/index.d.ts +2 -2
- package/index.d.ts.map +1 -1
- package/index.js +4 -4
- package/index.js.map +1 -1
- package/package.json +3 -3
- package/server.d.ts +31 -1
- package/server.d.ts.map +1 -1
- package/server.js +53 -3
- package/server.js.map +1 -1
- package/src/config.ts +19 -0
- package/src/domains/agentexecutions/approve.ts +67 -0
- package/src/domains/agentexecutions/fetch.ts +112 -0
- package/src/domains/agentexecutions/run.ts +112 -0
- package/src/domains/agentexecutions/tools.ts +139 -0
- package/src/domains/apply.integration.test.ts +82 -6
- package/src/domains/datastores/apply.ts +33 -0
- package/src/domains/datastores/delete.ts +47 -0
- package/src/domains/datastores/fetch.ts +32 -0
- package/src/domains/datastores/resources.ts +21 -0
- package/src/domains/datastores/tools.ts +76 -0
- package/src/domains/deletes.integration.test.ts +49 -2
- package/src/domains/environments/apply.ts +40 -0
- package/src/domains/environments/delete.ts +45 -0
- package/src/domains/environments/fetch.ts +39 -0
- package/src/domains/environments/resources.ts +21 -0
- package/src/domains/environments/tools.ts +81 -0
- package/src/domains/executions/cancel.ts +148 -0
- package/src/domains/executions/tools.ts +45 -0
- package/src/domains/executions.integration.test.ts +376 -0
- package/src/domains/reads.integration.test.ts +51 -5
- package/src/domains/records/calls.ts +209 -0
- package/src/domains/records/errors.ts +98 -0
- package/src/domains/records/records.integration.test.ts +313 -0
- package/src/domains/records/tools.ts +210 -0
- package/src/domains/resources.integration.test.ts +38 -2
- package/src/domains/resourceuri.test.ts +11 -2
- package/src/domains/resourceuri.ts +2 -0
- package/src/domains/search/search.integration.test.ts +18 -2
- package/src/domains/search/tools.ts +12 -7
- package/src/domains/skills/tools.ts +34 -1
- package/src/domains/skills/versions.ts +47 -0
- package/src/domains/versions.integration.test.ts +212 -0
- package/src/domains/workflowexecutions/approvals.ts +102 -0
- package/src/domains/workflowexecutions/run.ts +87 -0
- package/src/domains/workflowexecutions/tools.ts +120 -6
- package/src/domains/workflows/tools.ts +90 -7
- package/src/domains/workflows/versions.ts +147 -0
- package/src/gen/agent.ts +28 -1
- package/src/gen/datastore.ts +264 -0
- package/src/gen/environment.ts +66 -0
- package/src/http.integration.test.ts +1 -0
- package/src/index.ts +12 -5
- package/src/server.ts +71 -5
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Environment read path: the single RPC both the get_environment tool and the
|
|
2
|
+
// environment resource template delegate to.
|
|
3
|
+
//
|
|
4
|
+
// Secret handling happens entirely server-side: the response pipeline replaces
|
|
5
|
+
// every is_secret value with the ***REDACTED*** sentinel before it leaves the
|
|
6
|
+
// server (backend environment domain, preserve_redacted_secrets.go), so this
|
|
7
|
+
// layer adds no redaction logic of its own — the server is the single
|
|
8
|
+
// enforcement point.
|
|
9
|
+
|
|
10
|
+
import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
|
|
11
|
+
import { EnvironmentQueryController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/query_pb";
|
|
12
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
13
|
+
|
|
14
|
+
import { withClient } from "../client.js";
|
|
15
|
+
import { toProtoJson } from "../marshal.js";
|
|
16
|
+
import { rpcError } from "../rpcerr.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Retrieve an environment by org and slug, returning its protojson
|
|
20
|
+
* representation (secret values arrive already redacted by the server).
|
|
21
|
+
*/
|
|
22
|
+
export async function fetchEnvironment(
|
|
23
|
+
serverAddress: string,
|
|
24
|
+
token: string,
|
|
25
|
+
org: string,
|
|
26
|
+
slug: string,
|
|
27
|
+
): Promise<string> {
|
|
28
|
+
return withClient(EnvironmentQueryController, serverAddress, token, async (client, callOptions) => {
|
|
29
|
+
try {
|
|
30
|
+
const environment = await client.getByReference(
|
|
31
|
+
{ org, kind: ApiResourceKind.environment, slug },
|
|
32
|
+
callOptions,
|
|
33
|
+
);
|
|
34
|
+
return toProtoJson(EnvironmentSchema, environment);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
throw rpcError(err, `environment "${slug}" in org "${org}"`);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Environment resource template (stigmer://environments/{org}/{slug}).
|
|
2
|
+
|
|
3
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
|
|
5
|
+
import type { BackendTarget } from "../client.js";
|
|
6
|
+
import { registerResource } from "../resourcehandler.js";
|
|
7
|
+
import { fetchEnvironment } from "./fetch.js";
|
|
8
|
+
|
|
9
|
+
/** Register the environment resource template; returns the registered resource names. */
|
|
10
|
+
export function registerEnvironmentResources(server: McpServer, target: BackendTarget): string[] {
|
|
11
|
+
registerResource(server, target, {
|
|
12
|
+
name: "stigmer_environment",
|
|
13
|
+
title: "Stigmer Environment",
|
|
14
|
+
description:
|
|
15
|
+
"Full definition of a Stigmer environment, identified by organization and slug. " +
|
|
16
|
+
"Secret values are redacted.",
|
|
17
|
+
template: "stigmer://environments/{org}/{slug}",
|
|
18
|
+
fetch: fetchEnvironment,
|
|
19
|
+
});
|
|
20
|
+
return ["stigmer_environment"];
|
|
21
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// MCP tools for the Environment domain. Environments hold the configuration
|
|
2
|
+
// and secrets that agents, workflows, and MCP servers resolve at runtime —
|
|
3
|
+
// without these tools, an assistant authoring an agent hits a dead end the
|
|
4
|
+
// moment env config is involved.
|
|
5
|
+
//
|
|
6
|
+
// The secret contract every description teaches: reads return secret values
|
|
7
|
+
// redacted to ***REDACTED*** (server-enforced); an apply that echoes the
|
|
8
|
+
// marker back preserves the existing secret, so get → edit → apply is always
|
|
9
|
+
// safe. Secret values are only ever sent when setting or rotating them, and
|
|
10
|
+
// they can never be read back through MCP.
|
|
11
|
+
|
|
12
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
|
|
15
|
+
import { EnvironmentInputShape } from "../../gen/environment.js";
|
|
16
|
+
import { resolveToken, type BackendTarget } from "../client.js";
|
|
17
|
+
import { textOrError } from "../toolresult.js";
|
|
18
|
+
import { applyEnvironment } from "./apply.js";
|
|
19
|
+
import { deleteEnvironment } from "./delete.js";
|
|
20
|
+
import { fetchEnvironment } from "./fetch.js";
|
|
21
|
+
|
|
22
|
+
/** Register every Environment-domain tool; returns the registered tool names. */
|
|
23
|
+
export function registerEnvironmentTools(server: McpServer, target: BackendTarget): string[] {
|
|
24
|
+
server.registerTool(
|
|
25
|
+
"get_environment",
|
|
26
|
+
{
|
|
27
|
+
description:
|
|
28
|
+
"Get full details of a Stigmer environment by its org and slug, including its variable " +
|
|
29
|
+
"declarations. Secret values are redacted to ***REDACTED*** — they can never be read " +
|
|
30
|
+
"back through this tool.",
|
|
31
|
+
inputSchema: {
|
|
32
|
+
org: z.string().describe("Organization slug that owns the environment (e.g. stigmer)."),
|
|
33
|
+
slug: z
|
|
34
|
+
.string()
|
|
35
|
+
.describe("Environment slug — the unique identifier within the org (e.g. github-creds)."),
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
(args, extra) =>
|
|
39
|
+
textOrError(() =>
|
|
40
|
+
fetchEnvironment(target.serverAddress, resolveToken(extra, target.apiKey), args.org, args.slug),
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
server.registerTool(
|
|
45
|
+
"apply_environment",
|
|
46
|
+
{
|
|
47
|
+
description:
|
|
48
|
+
"Create or update a Stigmer environment (idempotent). The spec is applied in full, so " +
|
|
49
|
+
"include every variable the environment should keep. When updating, secret values read " +
|
|
50
|
+
"back as ***REDACTED*** — echoing that marker preserves the existing secret, so a " +
|
|
51
|
+
"get → edit → apply round-trip never destroys secrets; send a real value only to set " +
|
|
52
|
+
"or rotate one.",
|
|
53
|
+
inputSchema: EnvironmentInputShape,
|
|
54
|
+
},
|
|
55
|
+
(args, extra) =>
|
|
56
|
+
textOrError(() =>
|
|
57
|
+
applyEnvironment(target.serverAddress, resolveToken(extra, target.apiKey), args),
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
server.registerTool(
|
|
62
|
+
"delete_environment",
|
|
63
|
+
{
|
|
64
|
+
description:
|
|
65
|
+
"Delete a Stigmer environment by its org and slug. Returns the deleted environment. " +
|
|
66
|
+
"Agents and workflows referencing it will fail to resolve their variables at run time.",
|
|
67
|
+
inputSchema: {
|
|
68
|
+
org: z.string().describe("Organization slug that owns the environment (e.g. stigmer)."),
|
|
69
|
+
slug: z
|
|
70
|
+
.string()
|
|
71
|
+
.describe("Environment slug — the unique identifier within the org (e.g. github-creds)."),
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
(args, extra) =>
|
|
75
|
+
textOrError(() =>
|
|
76
|
+
deleteEnvironment(target.serverAddress, resolveToken(extra, target.apiKey), args.org, args.slug),
|
|
77
|
+
),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return ["get_environment", "apply_environment", "delete_environment"];
|
|
81
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Cross-kind execution cancellation for the cancel_execution tool.
|
|
2
|
+
//
|
|
3
|
+
// Executions are the one place the MCP surface routes on ID prefix rather
|
|
4
|
+
// than a kind argument: agent (aex_*) and workflow (wex_*) executions live on
|
|
5
|
+
// dedicated controllers but cancel with identical arguments, so one tool
|
|
6
|
+
// serves both — mirroring the CLI's routing seam
|
|
7
|
+
// (client-apps/cli/src/resources/execution.ts), including its cancel
|
|
8
|
+
// semantics: read the execution first and short-circuit with
|
|
9
|
+
// "already terminal" instead of issuing a futile cancel, so the
|
|
10
|
+
// success/no-op distinction comes from authoritative state.
|
|
11
|
+
|
|
12
|
+
import { createClient } from "@connectrpc/connect";
|
|
13
|
+
import { type AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
14
|
+
import { AgentExecutionCommandController } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/command_pb";
|
|
15
|
+
import { ExecutionPhase as AgentExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
16
|
+
import { AgentExecutionQueryController } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/query_pb";
|
|
17
|
+
import {
|
|
18
|
+
WorkflowExecutionSchema,
|
|
19
|
+
type WorkflowExecution,
|
|
20
|
+
} from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
|
|
21
|
+
import { WorkflowExecutionCommandController } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/command_pb";
|
|
22
|
+
import { ExecutionPhase as WorkflowExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/enum_pb";
|
|
23
|
+
import { WorkflowExecutionQueryController } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/query_pb";
|
|
24
|
+
|
|
25
|
+
import { compactExecution, DEFAULT_MESSAGE_LIMIT } from "../agentexecutions/fetch.js";
|
|
26
|
+
import { withTransport } from "../client.js";
|
|
27
|
+
import { toProtoJson } from "../marshal.js";
|
|
28
|
+
import { rpcError } from "../rpcerr.js";
|
|
29
|
+
|
|
30
|
+
// Terminal phases per kind. The two ExecutionPhase enums live in different
|
|
31
|
+
// proto packages with different numeric values (agent TERMINATED is 8,
|
|
32
|
+
// workflow TERMINATED is 6), so these are deliberately separate sets.
|
|
33
|
+
const TERMINAL_AGENT_PHASES: ReadonlySet<AgentExecutionPhase> = new Set([
|
|
34
|
+
AgentExecutionPhase.EXECUTION_COMPLETED,
|
|
35
|
+
AgentExecutionPhase.EXECUTION_FAILED,
|
|
36
|
+
AgentExecutionPhase.EXECUTION_CANCELLED,
|
|
37
|
+
AgentExecutionPhase.EXECUTION_TERMINATED,
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const TERMINAL_WORKFLOW_PHASES: ReadonlySet<WorkflowExecutionPhase> = new Set([
|
|
41
|
+
WorkflowExecutionPhase.EXECUTION_COMPLETED,
|
|
42
|
+
WorkflowExecutionPhase.EXECUTION_FAILED,
|
|
43
|
+
WorkflowExecutionPhase.EXECUTION_CANCELLED,
|
|
44
|
+
WorkflowExecutionPhase.EXECUTION_TERMINATED,
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Resolve an execution ID to its controller family by prefix. Matching is
|
|
49
|
+
* case-sensitive and accepts both separators the backend does ("_" canonical,
|
|
50
|
+
* "-" legacy), mirroring the CLI's resolveExecutionType.
|
|
51
|
+
*/
|
|
52
|
+
function resolveExecutionType(id: string): "agent" | "workflow" {
|
|
53
|
+
const trimmed = id.trim();
|
|
54
|
+
if (trimmed.startsWith("aex_") || trimmed.startsWith("aex-")) return "agent";
|
|
55
|
+
if (trimmed.startsWith("wex_") || trimmed.startsWith("wex-")) return "workflow";
|
|
56
|
+
throw new Error(
|
|
57
|
+
`unrecognized execution ID format: ${id}\n\n` +
|
|
58
|
+
"Expected formats:\n" +
|
|
59
|
+
" Agent execution: aex_<26-char-ulid>\n" +
|
|
60
|
+
" Workflow execution: wex_<26-char-ulid>",
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Cancel an execution of either kind. Returns a wrapper documenting whether a
|
|
66
|
+
* cancel was actually issued: `{"already_terminal": bool, "execution": …}`.
|
|
67
|
+
* Agent executions are returned in the compact projection (their status embeds
|
|
68
|
+
* the full message history); workflow executions as plain protojson, matching
|
|
69
|
+
* get_workflow_execution.
|
|
70
|
+
*/
|
|
71
|
+
export async function cancelExecution(
|
|
72
|
+
serverAddress: string,
|
|
73
|
+
token: string,
|
|
74
|
+
executionId: string,
|
|
75
|
+
reason: string,
|
|
76
|
+
): Promise<string> {
|
|
77
|
+
return resolveExecutionType(executionId) === "agent"
|
|
78
|
+
? cancelAgentExecution(serverAddress, token, executionId, reason)
|
|
79
|
+
: cancelWorkflowExecution(serverAddress, token, executionId, reason);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function cancelAgentExecution(
|
|
83
|
+
serverAddress: string,
|
|
84
|
+
token: string,
|
|
85
|
+
id: string,
|
|
86
|
+
reason: string,
|
|
87
|
+
): Promise<string> {
|
|
88
|
+
const desc = `agent execution "${id}"`;
|
|
89
|
+
return withTransport(serverAddress, token, async (transport, callOptions) => {
|
|
90
|
+
try {
|
|
91
|
+
const query = createClient(AgentExecutionQueryController, transport);
|
|
92
|
+
const current = await query.get({ value: id }, callOptions);
|
|
93
|
+
const phase = current.status?.phase ?? AgentExecutionPhase.EXECUTION_PHASE_UNSPECIFIED;
|
|
94
|
+
if (TERMINAL_AGENT_PHASES.has(phase)) {
|
|
95
|
+
return wrapAgent(current, true);
|
|
96
|
+
}
|
|
97
|
+
const command = createClient(AgentExecutionCommandController, transport);
|
|
98
|
+
const cancelled = await command.cancel({ id, reason }, callOptions);
|
|
99
|
+
return wrapAgent(cancelled, false);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
throw rpcError(err, desc);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function cancelWorkflowExecution(
|
|
107
|
+
serverAddress: string,
|
|
108
|
+
token: string,
|
|
109
|
+
id: string,
|
|
110
|
+
reason: string,
|
|
111
|
+
): Promise<string> {
|
|
112
|
+
const desc = `workflow execution "${id}"`;
|
|
113
|
+
return withTransport(serverAddress, token, async (transport, callOptions) => {
|
|
114
|
+
try {
|
|
115
|
+
const query = createClient(WorkflowExecutionQueryController, transport);
|
|
116
|
+
const current = await query.get({ value: id }, callOptions);
|
|
117
|
+
const phase = current.status?.phase ?? WorkflowExecutionPhase.EXECUTION_PHASE_UNSPECIFIED;
|
|
118
|
+
if (TERMINAL_WORKFLOW_PHASES.has(phase)) {
|
|
119
|
+
return wrapWorkflow(current, true);
|
|
120
|
+
}
|
|
121
|
+
const command = createClient(WorkflowExecutionCommandController, transport);
|
|
122
|
+
const cancelled = await command.cancel({ id, reason }, callOptions);
|
|
123
|
+
return wrapWorkflow(cancelled, false);
|
|
124
|
+
} catch (err) {
|
|
125
|
+
throw rpcError(err, desc);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function wrapAgent(execution: AgentExecution, alreadyTerminal: boolean): string {
|
|
131
|
+
const { totalMessages, data } = compactExecution(execution, DEFAULT_MESSAGE_LIMIT);
|
|
132
|
+
return JSON.stringify(
|
|
133
|
+
{ already_terminal: alreadyTerminal, view: "compact", total_messages: totalMessages, execution: data },
|
|
134
|
+
null,
|
|
135
|
+
2,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function wrapWorkflow(execution: WorkflowExecution, alreadyTerminal: boolean): string {
|
|
140
|
+
return JSON.stringify(
|
|
141
|
+
{
|
|
142
|
+
already_terminal: alreadyTerminal,
|
|
143
|
+
execution: JSON.parse(toProtoJson(WorkflowExecutionSchema, execution)),
|
|
144
|
+
},
|
|
145
|
+
null,
|
|
146
|
+
2,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Cross-kind execution-control tools. Currently just cancel_execution — the
|
|
2
|
+
// one control verb whose argument shape is identical for agent and workflow
|
|
3
|
+
// executions, so a single ID-prefix-dispatched tool serves both (the
|
|
4
|
+
// "crisp schemas over fewer tools" principle cuts the other way for
|
|
5
|
+
// approvals, whose shapes are disjoint). pause/resume/terminate stay
|
|
6
|
+
// CLI-only until a real MCP need shows up.
|
|
7
|
+
|
|
8
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
import { resolveToken, type BackendTarget } from "../client.js";
|
|
12
|
+
import { textOrError } from "../toolresult.js";
|
|
13
|
+
import { cancelExecution } from "./cancel.js";
|
|
14
|
+
|
|
15
|
+
/** Register the cross-kind execution-control tools; returns the registered tool names. */
|
|
16
|
+
export function registerExecutionControlTools(server: McpServer, target: BackendTarget): string[] {
|
|
17
|
+
server.registerTool(
|
|
18
|
+
"cancel_execution",
|
|
19
|
+
{
|
|
20
|
+
description:
|
|
21
|
+
"Gracefully cancel a running agent (aex_*) or workflow (wex_*) execution — the kind is " +
|
|
22
|
+
"inferred from the ID prefix. Cancellation is terminal: the run stops after cleanup and " +
|
|
23
|
+
"cannot be resumed. Executions already in a terminal phase are returned with " +
|
|
24
|
+
"already_terminal=true instead of an error.",
|
|
25
|
+
inputSchema: {
|
|
26
|
+
execution_id: z.string().describe("Execution ID to cancel (aex_* or wex_* format)."),
|
|
27
|
+
reason: z
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("Human-readable reason for the cancellation, stored in the audit trail."),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
(args, extra) =>
|
|
34
|
+
textOrError(() =>
|
|
35
|
+
cancelExecution(
|
|
36
|
+
target.serverAddress,
|
|
37
|
+
resolveToken(extra, target.apiKey),
|
|
38
|
+
args.execution_id,
|
|
39
|
+
args.reason ?? "",
|
|
40
|
+
),
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return ["cancel_execution"];
|
|
45
|
+
}
|