@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,139 @@
|
|
|
1
|
+
// MCP tools for the AgentExecution domain: start a run, poll it, and answer
|
|
2
|
+
// its approval requests. Together with cancel_execution (executions domain)
|
|
3
|
+
// these close the agent iteration loop: author with apply_agent, run with
|
|
4
|
+
// run_agent, observe with get_agent_execution, steer with
|
|
5
|
+
// submit_agent_execution_approval.
|
|
6
|
+
//
|
|
7
|
+
// The execution model is asynchronous by design: run_agent returns as soon as
|
|
8
|
+
// the backend accepts the execution; progress is observed by polling. Agent
|
|
9
|
+
// executions have no event-log RPC, so get_agent_execution IS the poll loop —
|
|
10
|
+
// which is why it defaults to the compact view (see fetch.ts).
|
|
11
|
+
|
|
12
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
|
|
15
|
+
import { resolveToken, type BackendTarget } from "../client.js";
|
|
16
|
+
import { textOrError } from "../toolresult.js";
|
|
17
|
+
import { submitAgentApproval } from "./approve.js";
|
|
18
|
+
import { DEFAULT_MESSAGE_LIMIT, fetchAgentExecution } from "./fetch.js";
|
|
19
|
+
import { runAgent } from "./run.js";
|
|
20
|
+
|
|
21
|
+
/** Register every AgentExecution-domain tool; returns the registered tool names. */
|
|
22
|
+
export function registerAgentExecutionTools(server: McpServer, target: BackendTarget): string[] {
|
|
23
|
+
server.registerTool(
|
|
24
|
+
"run_agent",
|
|
25
|
+
{
|
|
26
|
+
description:
|
|
27
|
+
"Start an agent execution (asynchronous). Returns immediately with the created execution " +
|
|
28
|
+
"(aex_* ID) while the run continues in the background — poll get_agent_execution to observe " +
|
|
29
|
+
"progress, pending approvals, and the final result. Omit session_id to start a fresh " +
|
|
30
|
+
"conversation; pass one to send a follow-up message into an existing session.",
|
|
31
|
+
inputSchema: {
|
|
32
|
+
org: z.string().describe("Organization slug that owns the agent (e.g. stigmer)."),
|
|
33
|
+
agent: z
|
|
34
|
+
.string()
|
|
35
|
+
.describe("Agent slug — the unique identifier within the org (e.g. code-reviewer)."),
|
|
36
|
+
message: z.string().describe("The instruction or message for the agent to act on."),
|
|
37
|
+
session_id: z
|
|
38
|
+
.string()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe(
|
|
41
|
+
"Existing session ID to continue a conversation (from a previous execution's " +
|
|
42
|
+
"spec.session_id). Omit to start a new session.",
|
|
43
|
+
),
|
|
44
|
+
runtime_env: z
|
|
45
|
+
.record(z.string())
|
|
46
|
+
.optional()
|
|
47
|
+
.describe(
|
|
48
|
+
"Non-secret runtime environment values (name → value) injected into the run. " +
|
|
49
|
+
"Secrets must come from Environments attached to the agent, never through this tool.",
|
|
50
|
+
),
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
(args, extra) =>
|
|
54
|
+
textOrError(() =>
|
|
55
|
+
runAgent(target.serverAddress, resolveToken(extra, target.apiKey), {
|
|
56
|
+
org: args.org,
|
|
57
|
+
agent: args.agent,
|
|
58
|
+
message: args.message,
|
|
59
|
+
sessionId: args.session_id,
|
|
60
|
+
runtimeEnv: args.runtime_env,
|
|
61
|
+
}),
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
server.registerTool(
|
|
66
|
+
"get_agent_execution",
|
|
67
|
+
{
|
|
68
|
+
description:
|
|
69
|
+
"Get an agent execution's status: phase, messages, pending approvals, errors, timing. " +
|
|
70
|
+
"Agent executions have no event log — poll this tool to track a run started with run_agent " +
|
|
71
|
+
"(terminal phases: completed, failed, cancelled, terminated). The default compact view " +
|
|
72
|
+
"returns the last few messages and omits bulky bookkeeping fields (resolved context, " +
|
|
73
|
+
"approval ledger, sub-agent transcripts); total_messages tells you when the tail is a " +
|
|
74
|
+
"window. Use view=full for the complete record.",
|
|
75
|
+
inputSchema: {
|
|
76
|
+
execution_id: z.string().describe("Agent execution ID (aex_* format)."),
|
|
77
|
+
view: z
|
|
78
|
+
.enum(["compact", "full"])
|
|
79
|
+
.optional()
|
|
80
|
+
.describe("Response shape: compact (default, bounded message tail) or full protojson."),
|
|
81
|
+
message_limit: z
|
|
82
|
+
.number()
|
|
83
|
+
.int()
|
|
84
|
+
.min(1)
|
|
85
|
+
.optional()
|
|
86
|
+
.describe(`Compact view only: number of trailing messages to return (default ${DEFAULT_MESSAGE_LIMIT}).`),
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
(args, extra) =>
|
|
90
|
+
textOrError(() =>
|
|
91
|
+
fetchAgentExecution(
|
|
92
|
+
target.serverAddress,
|
|
93
|
+
resolveToken(extra, target.apiKey),
|
|
94
|
+
args.execution_id,
|
|
95
|
+
args.view ?? "compact",
|
|
96
|
+
args.message_limit ?? DEFAULT_MESSAGE_LIMIT,
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
server.registerTool(
|
|
102
|
+
"submit_agent_execution_approval",
|
|
103
|
+
{
|
|
104
|
+
description:
|
|
105
|
+
"Approve, skip, or reject a tool call an agent execution is waiting on (phase " +
|
|
106
|
+
"waiting-for-approval). Find pending requests in get_agent_execution's " +
|
|
107
|
+
"status.pending_approvals — tool_call_id must match exactly. reject denies the single " +
|
|
108
|
+
"tool call and feeds your comment back to the agent, which then continues; to stop the " +
|
|
109
|
+
"whole run use cancel_execution instead.",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
execution_id: z.string().describe("Agent execution ID (aex_* format)."),
|
|
112
|
+
tool_call_id: z
|
|
113
|
+
.string()
|
|
114
|
+
.describe("Tool call awaiting the decision (status.pending_approvals[].tool_call_id)."),
|
|
115
|
+
action: z
|
|
116
|
+
.enum(["approve", "skip", "reject"])
|
|
117
|
+
.describe(
|
|
118
|
+
"approve: execute the tool. skip: don't execute, agent proceeds without it. " +
|
|
119
|
+
"reject: don't execute, agent is told why (see comment) and adapts.",
|
|
120
|
+
),
|
|
121
|
+
comment: z
|
|
122
|
+
.string()
|
|
123
|
+
.optional()
|
|
124
|
+
.describe("Reason for the decision; on reject it is fed back to the agent."),
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
(args, extra) =>
|
|
128
|
+
textOrError(() =>
|
|
129
|
+
submitAgentApproval(target.serverAddress, resolveToken(extra, target.apiKey), {
|
|
130
|
+
executionId: args.execution_id,
|
|
131
|
+
toolCallId: args.tool_call_id,
|
|
132
|
+
action: args.action,
|
|
133
|
+
comment: args.comment,
|
|
134
|
+
}),
|
|
135
|
+
),
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
return ["run_agent", "get_agent_execution", "submit_agent_execution_approval"];
|
|
139
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// In-process integration test for the apply tools. Drives apply_agent,
|
|
2
|
-
// apply_mcp_server,
|
|
3
|
-
// the codegen projection (src/gen/*)
|
|
4
|
-
// hoist + slug generation,
|
|
5
|
-
//
|
|
6
|
-
//
|
|
2
|
+
// apply_mcp_server, apply_workflow, apply_environment, and apply_datastore
|
|
3
|
+
// through the full MCP boundary and asserts the codegen projection (src/gen/*)
|
|
4
|
+
// reconstitutes the proto correctly: metadata hoist + slug generation,
|
|
5
|
+
// enum-string conversion (including repeated enums — datastore grant verbs),
|
|
6
|
+
// ApiResourceReference kind injection, the stdio/http oneof, the environment
|
|
7
|
+
// data map with secret flags, and the recursive workflow task_config expansion
|
|
8
|
+
// (http_call leaf, fork/for_each nesting).
|
|
7
9
|
|
|
8
10
|
import type { ConnectRouter } from "@connectrpc/connect";
|
|
9
11
|
import { connectNodeAdapter } from "@connectrpc/connect-node";
|
|
@@ -18,6 +20,11 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
18
20
|
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
19
21
|
import type { Agent } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
20
22
|
import { AgentCommandController } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/command_pb";
|
|
23
|
+
import type { Datastore } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/api_pb";
|
|
24
|
+
import { DatastoreCommandController } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/command_pb";
|
|
25
|
+
import { DatastoreVerb, FieldType } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/spec_pb";
|
|
26
|
+
import type { Environment } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
|
|
27
|
+
import { EnvironmentCommandController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/command_pb";
|
|
21
28
|
import type { McpServer as McpServerProto } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
|
|
22
29
|
import { McpServerCommandController } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/command_pb";
|
|
23
30
|
import type { Workflow } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
|
|
@@ -37,6 +44,8 @@ let client: Client;
|
|
|
37
44
|
let appliedAgent: Agent | undefined;
|
|
38
45
|
let appliedMcpServer: McpServerProto | undefined;
|
|
39
46
|
let appliedWorkflow: Workflow | undefined;
|
|
47
|
+
let appliedEnvironment: Environment | undefined;
|
|
48
|
+
let appliedDatastore: Datastore | undefined;
|
|
40
49
|
const openSessions = new Set<ServerHttp2Session>();
|
|
41
50
|
|
|
42
51
|
interface ToolResult {
|
|
@@ -68,6 +77,18 @@ beforeAll(async () => {
|
|
|
68
77
|
return req;
|
|
69
78
|
},
|
|
70
79
|
});
|
|
80
|
+
router.service(EnvironmentCommandController, {
|
|
81
|
+
apply: (req) => {
|
|
82
|
+
appliedEnvironment = req;
|
|
83
|
+
return req;
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
router.service(DatastoreCommandController, {
|
|
87
|
+
apply: (req) => {
|
|
88
|
+
appliedDatastore = req;
|
|
89
|
+
return req;
|
|
90
|
+
},
|
|
91
|
+
});
|
|
71
92
|
};
|
|
72
93
|
backend = createHttp2Server(connectNodeAdapter({ routes }));
|
|
73
94
|
backend.on("session", (session) => {
|
|
@@ -93,7 +114,13 @@ describe("apply tools integration", () => {
|
|
|
93
114
|
it("advertises every apply tool", async () => {
|
|
94
115
|
const { tools } = await client.listTools();
|
|
95
116
|
expect(tools.map((t) => t.name)).toEqual(
|
|
96
|
-
expect.arrayContaining([
|
|
117
|
+
expect.arrayContaining([
|
|
118
|
+
"apply_agent",
|
|
119
|
+
"apply_mcp_server",
|
|
120
|
+
"apply_workflow",
|
|
121
|
+
"apply_environment",
|
|
122
|
+
"apply_datastore",
|
|
123
|
+
]),
|
|
97
124
|
);
|
|
98
125
|
});
|
|
99
126
|
|
|
@@ -217,4 +244,53 @@ describe("apply tools integration", () => {
|
|
|
217
244
|
expect(forCfg.each).toBe("item");
|
|
218
245
|
expect(forCfg.do).toHaveLength(1);
|
|
219
246
|
});
|
|
247
|
+
|
|
248
|
+
it("apply_environment rebuilds the data map with secret flags", async () => {
|
|
249
|
+
const result = await callTool("apply_environment", {
|
|
250
|
+
name: "GitHub Creds",
|
|
251
|
+
org: "acme",
|
|
252
|
+
data: {
|
|
253
|
+
API_KEY: { value: "sk-real-value", is_secret: true, description: "GitHub PAT" },
|
|
254
|
+
REGION: { value: "us-east-1" },
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
expect(result.isError).toBeFalsy();
|
|
258
|
+
|
|
259
|
+
const env = appliedEnvironment;
|
|
260
|
+
expect(env?.apiVersion).toBe("agentic.stigmer.ai/v1");
|
|
261
|
+
expect(env?.kind).toBe("Environment");
|
|
262
|
+
expect(env?.metadata?.slug).toBe("github-creds"); // auto-generated from name
|
|
263
|
+
expect(env?.spec?.data?.API_KEY).toMatchObject({
|
|
264
|
+
value: "sk-real-value",
|
|
265
|
+
isSecret: true,
|
|
266
|
+
description: "GitHub PAT",
|
|
267
|
+
});
|
|
268
|
+
expect(env?.spec?.data?.REGION?.isSecret).toBe(false);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("apply_datastore maps field types and repeated grant verbs to enums", async () => {
|
|
272
|
+
const result = await callTool("apply_datastore", {
|
|
273
|
+
name: "Bookings",
|
|
274
|
+
org: "acme",
|
|
275
|
+
collections: [
|
|
276
|
+
{
|
|
277
|
+
name: "appointments",
|
|
278
|
+
fields: [{ name: "patient", type: "string", required: true }],
|
|
279
|
+
grants: [{ role: "assistant", verbs: ["read", "insert"], scope: "own" }],
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
});
|
|
283
|
+
expect(result.isError).toBeFalsy();
|
|
284
|
+
|
|
285
|
+
const ds = appliedDatastore;
|
|
286
|
+
expect(ds?.kind).toBe("Datastore");
|
|
287
|
+
expect(ds?.metadata?.slug).toBe("bookings");
|
|
288
|
+
|
|
289
|
+
const collection = ds?.spec?.collections?.[0];
|
|
290
|
+
expect(collection?.fields?.[0]?.type).toBe(FieldType.string);
|
|
291
|
+
|
|
292
|
+
// Repeated enum: verb strings map to DatastoreVerb values, not raw strings.
|
|
293
|
+
const grant = collection?.grants?.[0];
|
|
294
|
+
expect(grant?.verbs).toEqual([DatastoreVerb.read, DatastoreVerb.insert]);
|
|
295
|
+
});
|
|
220
296
|
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Datastore apply path: create-or-update via DatastoreCommandController.apply.
|
|
2
|
+
// The flat MCP input is projected into a fully-formed Datastore proto by the
|
|
3
|
+
// generated datastoreInputToProto bridge (codegen, src/gen/datastore.ts).
|
|
4
|
+
//
|
|
5
|
+
// The manifest is authoritative for structure, never for data: schema changes
|
|
6
|
+
// sync on apply (the server enforces its additive-plus change matrix), and
|
|
7
|
+
// records enter exclusively through the record tools.
|
|
8
|
+
|
|
9
|
+
import { DatastoreSchema } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/api_pb";
|
|
10
|
+
import { DatastoreCommandController } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/command_pb";
|
|
11
|
+
|
|
12
|
+
import { datastoreInputToProto, type DatastoreInput } from "../../gen/datastore.js";
|
|
13
|
+
import { withClient } from "../client.js";
|
|
14
|
+
import { toProtoJson } from "../marshal.js";
|
|
15
|
+
import { rpcError } from "../rpcerr.js";
|
|
16
|
+
|
|
17
|
+
/** Create or update a datastore, returning the persisted resource as protojson. */
|
|
18
|
+
export async function applyDatastore(
|
|
19
|
+
serverAddress: string,
|
|
20
|
+
token: string,
|
|
21
|
+
input: DatastoreInput,
|
|
22
|
+
): Promise<string> {
|
|
23
|
+
const datastore = datastoreInputToProto(input);
|
|
24
|
+
const desc = `datastore "${datastore.metadata?.slug ?? ""}" in org "${datastore.metadata?.org ?? ""}"`;
|
|
25
|
+
return withClient(DatastoreCommandController, serverAddress, token, async (client, callOptions) => {
|
|
26
|
+
try {
|
|
27
|
+
const result = await client.apply(datastore, callOptions);
|
|
28
|
+
return toProtoJson(DatastoreSchema, result);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
throw rpcError(err, desc);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Datastore delete path: resolve org/slug → id via the Query controller, then
|
|
2
|
+
// delete via the Command controller, both over a single shared transport. Like
|
|
3
|
+
// McpServer, the command controller takes ApiResourceDeleteInput{resource_id}.
|
|
4
|
+
//
|
|
5
|
+
// This is the only path that destroys collections — record tools can delete
|
|
6
|
+
// individual records but never structure.
|
|
7
|
+
|
|
8
|
+
import { createClient } from "@connectrpc/connect";
|
|
9
|
+
import { DatastoreSchema } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/api_pb";
|
|
10
|
+
import { DatastoreCommandController } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/command_pb";
|
|
11
|
+
import { DatastoreQueryController } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/query_pb";
|
|
12
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
13
|
+
|
|
14
|
+
import { withTransport } from "../client.js";
|
|
15
|
+
import { toProtoJson } from "../marshal.js";
|
|
16
|
+
import { rpcError } from "../rpcerr.js";
|
|
17
|
+
|
|
18
|
+
/** Delete a datastore by org and slug, returning the deleted resource as protojson. */
|
|
19
|
+
export async function deleteDatastore(
|
|
20
|
+
serverAddress: string,
|
|
21
|
+
token: string,
|
|
22
|
+
org: string,
|
|
23
|
+
slug: string,
|
|
24
|
+
): Promise<string> {
|
|
25
|
+
const desc = `datastore "${slug}" in org "${org}"`;
|
|
26
|
+
return withTransport(serverAddress, token, async (transport, callOptions) => {
|
|
27
|
+
const query = createClient(DatastoreQueryController, transport);
|
|
28
|
+
let id: string;
|
|
29
|
+
try {
|
|
30
|
+
const datastore = await query.getByReference(
|
|
31
|
+
{ org, kind: ApiResourceKind.datastore, slug },
|
|
32
|
+
callOptions,
|
|
33
|
+
);
|
|
34
|
+
id = datastore.metadata?.id ?? "";
|
|
35
|
+
} catch (err) {
|
|
36
|
+
throw rpcError(err, desc);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const command = createClient(DatastoreCommandController, transport);
|
|
40
|
+
try {
|
|
41
|
+
const deleted = await command.delete({ resourceId: id }, callOptions);
|
|
42
|
+
return toProtoJson(DatastoreSchema, deleted);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
throw rpcError(err, desc);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Datastore-definition read path: the single RPC both the get_datastore tool
|
|
2
|
+
// and the datastore resource template delegate to. This is the *structure*
|
|
3
|
+
// surface (collections, constraints, grants); living records are served by the
|
|
4
|
+
// record tools (records/), whose agent-facing summary is describe_datastore.
|
|
5
|
+
|
|
6
|
+
import { DatastoreSchema } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/api_pb";
|
|
7
|
+
import { DatastoreQueryController } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/query_pb";
|
|
8
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
9
|
+
|
|
10
|
+
import { withClient } from "../client.js";
|
|
11
|
+
import { toProtoJson } from "../marshal.js";
|
|
12
|
+
import { rpcError } from "../rpcerr.js";
|
|
13
|
+
|
|
14
|
+
/** Retrieve a datastore by org and slug, returning its protojson representation. */
|
|
15
|
+
export async function fetchDatastore(
|
|
16
|
+
serverAddress: string,
|
|
17
|
+
token: string,
|
|
18
|
+
org: string,
|
|
19
|
+
slug: string,
|
|
20
|
+
): Promise<string> {
|
|
21
|
+
return withClient(DatastoreQueryController, serverAddress, token, async (client, callOptions) => {
|
|
22
|
+
try {
|
|
23
|
+
const datastore = await client.getByReference(
|
|
24
|
+
{ org, kind: ApiResourceKind.datastore, slug },
|
|
25
|
+
callOptions,
|
|
26
|
+
);
|
|
27
|
+
return toProtoJson(DatastoreSchema, datastore);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
throw rpcError(err, `datastore "${slug}" in org "${org}"`);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Datastore resource template (stigmer://datastores/{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 { fetchDatastore } from "./fetch.js";
|
|
8
|
+
|
|
9
|
+
/** Register the datastore resource template; returns the registered resource names. */
|
|
10
|
+
export function registerDatastoreResources(server: McpServer, target: BackendTarget): string[] {
|
|
11
|
+
registerResource(server, target, {
|
|
12
|
+
name: "stigmer_datastore",
|
|
13
|
+
title: "Stigmer Datastore",
|
|
14
|
+
description:
|
|
15
|
+
"Full definition of a Stigmer datastore (collections, constraints, grants), " +
|
|
16
|
+
"identified by organization and slug.",
|
|
17
|
+
template: "stigmer://datastores/{org}/{slug}",
|
|
18
|
+
fetch: fetchDatastore,
|
|
19
|
+
});
|
|
20
|
+
return ["stigmer_datastore"];
|
|
21
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// MCP tools for the Datastore *definition* domain — the structure surface
|
|
2
|
+
// (collections, fields, constraints, roles, grants). The record tools
|
|
3
|
+
// (records/tools.ts) operate on the living data inside these structures;
|
|
4
|
+
// before this domain existed, an assistant could insert records into a
|
|
5
|
+
// datastore it had no way to create.
|
|
6
|
+
|
|
7
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
import { DatastoreInputShape } from "../../gen/datastore.js";
|
|
11
|
+
import { resolveToken, type BackendTarget } from "../client.js";
|
|
12
|
+
import { textOrError } from "../toolresult.js";
|
|
13
|
+
import { applyDatastore } from "./apply.js";
|
|
14
|
+
import { deleteDatastore } from "./delete.js";
|
|
15
|
+
import { fetchDatastore } from "./fetch.js";
|
|
16
|
+
|
|
17
|
+
/** Register every Datastore-domain tool; returns the registered tool names. */
|
|
18
|
+
export function registerDatastoreTools(server: McpServer, target: BackendTarget): string[] {
|
|
19
|
+
server.registerTool(
|
|
20
|
+
"get_datastore",
|
|
21
|
+
{
|
|
22
|
+
description:
|
|
23
|
+
"Get the full definition of a Stigmer datastore by its org and slug: collections, field " +
|
|
24
|
+
"declarations, constraints, roles, and grants. For the record-facing view of the same " +
|
|
25
|
+
"structure (allowed verbs, readable fields), use describe_datastore; for the data " +
|
|
26
|
+
"itself, use the record tools.",
|
|
27
|
+
inputSchema: {
|
|
28
|
+
org: z.string().describe("Organization slug that owns the datastore (e.g. stigmer)."),
|
|
29
|
+
slug: z
|
|
30
|
+
.string()
|
|
31
|
+
.describe("Datastore slug — the unique identifier within the org (e.g. clinic-bookings)."),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
(args, extra) =>
|
|
35
|
+
textOrError(() =>
|
|
36
|
+
fetchDatastore(target.serverAddress, resolveToken(extra, target.apiKey), args.org, args.slug),
|
|
37
|
+
),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
server.registerTool(
|
|
41
|
+
"apply_datastore",
|
|
42
|
+
{
|
|
43
|
+
description:
|
|
44
|
+
"Create or update a Stigmer datastore definition (idempotent). The manifest is " +
|
|
45
|
+
"authoritative for structure only — schema changes sync on apply (the server enforces " +
|
|
46
|
+
"its additive-plus change rules), and records are never touched; use the record tools " +
|
|
47
|
+
"to manage data.",
|
|
48
|
+
inputSchema: DatastoreInputShape,
|
|
49
|
+
},
|
|
50
|
+
(args, extra) =>
|
|
51
|
+
textOrError(() =>
|
|
52
|
+
applyDatastore(target.serverAddress, resolveToken(extra, target.apiKey), args),
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
server.registerTool(
|
|
57
|
+
"delete_datastore",
|
|
58
|
+
{
|
|
59
|
+
description:
|
|
60
|
+
"Delete a Stigmer datastore by its org and slug, destroying its collections and all " +
|
|
61
|
+
"records in them. This is irreversible. Returns the deleted datastore definition.",
|
|
62
|
+
inputSchema: {
|
|
63
|
+
org: z.string().describe("Organization slug that owns the datastore (e.g. stigmer)."),
|
|
64
|
+
slug: z
|
|
65
|
+
.string()
|
|
66
|
+
.describe("Datastore slug — the unique identifier within the org (e.g. clinic-bookings)."),
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
(args, extra) =>
|
|
70
|
+
textOrError(() =>
|
|
71
|
+
deleteDatastore(target.serverAddress, resolveToken(extra, target.apiKey), args.org, args.slug),
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return ["get_datastore", "apply_datastore", "delete_datastore"];
|
|
76
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// In-process integration test for the delete tools. Verifies the two-step
|
|
2
2
|
// resolve→delete flow forwards the resolved id into the correct per-domain
|
|
3
|
-
// delete-input shape: typed {value} for agent/skill/workflow, and
|
|
4
|
-
// ApiResourceDeleteInput {resource_id}
|
|
3
|
+
// delete-input shape: typed {value} for agent/skill/workflow, and
|
|
4
|
+
// ApiResourceDeleteInput {resource_id} for mcp_server, environment, and
|
|
5
|
+
// datastore.
|
|
5
6
|
|
|
6
7
|
import { create, toJson } from "@bufbuild/protobuf";
|
|
7
8
|
import type { ConnectRouter } from "@connectrpc/connect";
|
|
@@ -18,6 +19,12 @@ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
|
18
19
|
import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
19
20
|
import { AgentCommandController } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/command_pb";
|
|
20
21
|
import { AgentQueryController } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/query_pb";
|
|
22
|
+
import { DatastoreSchema } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/api_pb";
|
|
23
|
+
import { DatastoreCommandController } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/command_pb";
|
|
24
|
+
import { DatastoreQueryController } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/query_pb";
|
|
25
|
+
import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
|
|
26
|
+
import { EnvironmentCommandController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/command_pb";
|
|
27
|
+
import { EnvironmentQueryController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/query_pb";
|
|
21
28
|
import { McpServerSchema } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
|
|
22
29
|
import { McpServerCommandController } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/command_pb";
|
|
23
30
|
import { McpServerQueryController } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/query_pb";
|
|
@@ -38,11 +45,23 @@ const resolvedMcpServer = create(McpServerSchema, {
|
|
|
38
45
|
kind: "mcp_server",
|
|
39
46
|
metadata: { name: "GitHub", slug: "github", org: "acme", id: "mcp-456" },
|
|
40
47
|
});
|
|
48
|
+
const resolvedEnvironment = create(EnvironmentSchema, {
|
|
49
|
+
apiVersion: "v1",
|
|
50
|
+
kind: "environment",
|
|
51
|
+
metadata: { name: "GitHub Creds", slug: "github-creds", org: "acme", id: "env-789" },
|
|
52
|
+
});
|
|
53
|
+
const resolvedDatastore = create(DatastoreSchema, {
|
|
54
|
+
apiVersion: "v1",
|
|
55
|
+
kind: "datastore",
|
|
56
|
+
metadata: { name: "Bookings", slug: "bookings", org: "acme", id: "dst-012" },
|
|
57
|
+
});
|
|
41
58
|
|
|
42
59
|
let backend: Http2Server;
|
|
43
60
|
let client: Client;
|
|
44
61
|
let deletedAgentId: string | undefined;
|
|
45
62
|
let deletedMcpResourceId: string | undefined;
|
|
63
|
+
let deletedEnvironmentResourceId: string | undefined;
|
|
64
|
+
let deletedDatastoreResourceId: string | undefined;
|
|
46
65
|
const openSessions = new Set<ServerHttp2Session>();
|
|
47
66
|
|
|
48
67
|
interface ToolResult {
|
|
@@ -70,6 +89,20 @@ beforeAll(async () => {
|
|
|
70
89
|
return resolvedMcpServer;
|
|
71
90
|
},
|
|
72
91
|
});
|
|
92
|
+
router.service(EnvironmentQueryController, { getByReference: () => resolvedEnvironment });
|
|
93
|
+
router.service(EnvironmentCommandController, {
|
|
94
|
+
delete: (req) => {
|
|
95
|
+
deletedEnvironmentResourceId = req.resourceId;
|
|
96
|
+
return resolvedEnvironment;
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
router.service(DatastoreQueryController, { getByReference: () => resolvedDatastore });
|
|
100
|
+
router.service(DatastoreCommandController, {
|
|
101
|
+
delete: (req) => {
|
|
102
|
+
deletedDatastoreResourceId = req.resourceId;
|
|
103
|
+
return resolvedDatastore;
|
|
104
|
+
},
|
|
105
|
+
});
|
|
73
106
|
};
|
|
74
107
|
backend = createHttp2Server(connectNodeAdapter({ routes }));
|
|
75
108
|
backend.on("session", (session) => {
|
|
@@ -100,6 +133,8 @@ describe("delete tools integration", () => {
|
|
|
100
133
|
"delete_skill",
|
|
101
134
|
"delete_workflow",
|
|
102
135
|
"delete_mcp_server",
|
|
136
|
+
"delete_environment",
|
|
137
|
+
"delete_datastore",
|
|
103
138
|
]),
|
|
104
139
|
);
|
|
105
140
|
});
|
|
@@ -121,4 +156,16 @@ describe("delete tools integration", () => {
|
|
|
121
156
|
toJson(McpServerSchema, resolvedMcpServer, { useProtoFieldName: true }),
|
|
122
157
|
);
|
|
123
158
|
});
|
|
159
|
+
|
|
160
|
+
it("delete_environment resolves the id then deletes via ApiResourceDeleteInput", async () => {
|
|
161
|
+
const result = await callTool("delete_environment", { org: "acme", slug: "github-creds" });
|
|
162
|
+
expect(result.isError).toBeFalsy();
|
|
163
|
+
expect(deletedEnvironmentResourceId).toBe("env-789");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("delete_datastore resolves the id then deletes via ApiResourceDeleteInput", async () => {
|
|
167
|
+
const result = await callTool("delete_datastore", { org: "acme", slug: "bookings" });
|
|
168
|
+
expect(result.isError).toBeFalsy();
|
|
169
|
+
expect(deletedDatastoreResourceId).toBe("dst-012");
|
|
170
|
+
});
|
|
124
171
|
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Environment apply path: create-or-update via EnvironmentCommandController.apply.
|
|
2
|
+
// The flat MCP input is projected into a fully-formed Environment proto by the
|
|
3
|
+
// generated environmentInputToProto bridge (codegen, src/gen/environment.ts).
|
|
4
|
+
//
|
|
5
|
+
// Secret round-trip contract: a get → edit → apply loop is safe even when
|
|
6
|
+
// secrets are present. The server redacts secret values to ***REDACTED*** on
|
|
7
|
+
// read, and its update pipeline treats an echoed marker as "preserve the
|
|
8
|
+
// existing secret" — so callers only ever send real secret values when
|
|
9
|
+
// setting or rotating them.
|
|
10
|
+
|
|
11
|
+
import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
|
|
12
|
+
import { EnvironmentCommandController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/command_pb";
|
|
13
|
+
|
|
14
|
+
import { environmentInputToProto, type EnvironmentInput } from "../../gen/environment.js";
|
|
15
|
+
import { withClient } from "../client.js";
|
|
16
|
+
import { toProtoJson } from "../marshal.js";
|
|
17
|
+
import { rpcError } from "../rpcerr.js";
|
|
18
|
+
|
|
19
|
+
/** Create or update an environment, returning the persisted resource as protojson. */
|
|
20
|
+
export async function applyEnvironment(
|
|
21
|
+
serverAddress: string,
|
|
22
|
+
token: string,
|
|
23
|
+
input: EnvironmentInput,
|
|
24
|
+
): Promise<string> {
|
|
25
|
+
const environment = environmentInputToProto(input);
|
|
26
|
+
const desc = `environment "${environment.metadata?.slug ?? ""}" in org "${environment.metadata?.org ?? ""}"`;
|
|
27
|
+
return withClient(
|
|
28
|
+
EnvironmentCommandController,
|
|
29
|
+
serverAddress,
|
|
30
|
+
token,
|
|
31
|
+
async (client, callOptions) => {
|
|
32
|
+
try {
|
|
33
|
+
const result = await client.apply(environment, callOptions);
|
|
34
|
+
return toProtoJson(EnvironmentSchema, result);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
throw rpcError(err, desc);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Environment delete path: resolve org/slug → id via the Query controller,
|
|
2
|
+
// then delete via the Command controller, both over a single shared transport.
|
|
3
|
+
// Like McpServer, the command controller takes the generic
|
|
4
|
+
// ApiResourceDeleteInput{resource_id}.
|
|
5
|
+
|
|
6
|
+
import { createClient } from "@connectrpc/connect";
|
|
7
|
+
import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
|
|
8
|
+
import { EnvironmentCommandController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/command_pb";
|
|
9
|
+
import { EnvironmentQueryController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/query_pb";
|
|
10
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
11
|
+
|
|
12
|
+
import { withTransport } from "../client.js";
|
|
13
|
+
import { toProtoJson } from "../marshal.js";
|
|
14
|
+
import { rpcError } from "../rpcerr.js";
|
|
15
|
+
|
|
16
|
+
/** Delete an environment by org and slug, returning the deleted resource as protojson. */
|
|
17
|
+
export async function deleteEnvironment(
|
|
18
|
+
serverAddress: string,
|
|
19
|
+
token: string,
|
|
20
|
+
org: string,
|
|
21
|
+
slug: string,
|
|
22
|
+
): Promise<string> {
|
|
23
|
+
const desc = `environment "${slug}" in org "${org}"`;
|
|
24
|
+
return withTransport(serverAddress, token, async (transport, callOptions) => {
|
|
25
|
+
const query = createClient(EnvironmentQueryController, transport);
|
|
26
|
+
let id: string;
|
|
27
|
+
try {
|
|
28
|
+
const environment = await query.getByReference(
|
|
29
|
+
{ org, kind: ApiResourceKind.environment, slug },
|
|
30
|
+
callOptions,
|
|
31
|
+
);
|
|
32
|
+
id = environment.metadata?.id ?? "";
|
|
33
|
+
} catch (err) {
|
|
34
|
+
throw rpcError(err, desc);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const command = createClient(EnvironmentCommandController, transport);
|
|
38
|
+
try {
|
|
39
|
+
const deleted = await command.delete({ resourceId: id }, callOptions);
|
|
40
|
+
return toProtoJson(EnvironmentSchema, deleted);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
throw rpcError(err, desc);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|