@stigmer/mcp-server 3.11.1-dev.20260812192248 → 3.12.1
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 +4 -22
- package/cli/mcp-server-stigmer.js +11 -11
- package/config.d.ts +1 -5
- package/config.d.ts.map +1 -1
- package/config.js +2 -2
- package/config.js.map +1 -1
- package/domains/agents/apply.d.ts.map +1 -1
- package/domains/agents/apply.js +4 -1
- package/domains/agents/apply.js.map +1 -1
- package/domains/apply-visibility.d.ts +19 -0
- package/domains/apply-visibility.d.ts.map +1 -0
- package/domains/apply-visibility.js +34 -0
- package/domains/apply-visibility.js.map +1 -0
- package/domains/environments/apply.d.ts.map +1 -1
- package/domains/environments/apply.js +4 -1
- package/domains/environments/apply.js.map +1 -1
- package/domains/mcpservers/apply.d.ts.map +1 -1
- package/domains/mcpservers/apply.js +4 -1
- package/domains/mcpservers/apply.js.map +1 -1
- package/domains/resourceuri.d.ts.map +1 -1
- package/domains/resourceuri.js +0 -1
- package/domains/resourceuri.js.map +1 -1
- package/domains/search/tools.d.ts.map +1 -1
- package/domains/search/tools.js +4 -5
- package/domains/search/tools.js.map +1 -1
- package/domains/workflows/apply.d.ts.map +1 -1
- package/domains/workflows/apply.js +4 -1
- package/domains/workflows/apply.js.map +1 -1
- package/gen/agent.d.ts +0 -56
- package/gen/agent.d.ts.map +1 -1
- package/gen/agent.js +2 -25
- package/gen/agent.js.map +1 -1
- package/gen/environment.js +1 -1
- package/gen/environment.js.map +1 -1
- package/gen/mcpserver.js +1 -1
- package/gen/mcpserver.js.map +1 -1
- package/gen/workflow.d.ts +2 -2
- package/gen/workflow.js +1 -1
- package/gen/workflow.js.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/package.json +3 -3
- package/server.d.ts +6 -18
- package/server.d.ts.map +1 -1
- package/server.js +6 -36
- package/server.js.map +1 -1
- package/src/config.ts +3 -7
- package/src/domains/agents/apply.ts +10 -1
- package/src/domains/apply-visibility.ts +47 -0
- package/src/domains/apply.integration.test.ts +58 -41
- package/src/domains/deletes.integration.test.ts +1 -25
- package/src/domains/environments/apply.ts +10 -1
- package/src/domains/mcpservers/apply.ts +10 -1
- package/src/domains/reads.integration.test.ts +1 -18
- package/src/domains/resources.integration.test.ts +0 -18
- package/src/domains/resourceuri.test.ts +0 -2
- package/src/domains/resourceuri.ts +0 -1
- package/src/domains/search/search.integration.test.ts +3 -5
- package/src/domains/search/tools.ts +4 -5
- package/src/domains/workflows/apply.ts +10 -1
- package/src/gen/agent.ts +2 -29
- package/src/gen/environment.ts +1 -1
- package/src/gen/mcpserver.ts +1 -1
- package/src/gen/workflow.ts +1 -1
- package/src/http.integration.test.ts +2 -3
- package/src/index.ts +0 -2
- package/src/server.ts +6 -36
- package/domains/datastores/apply.d.ts +0 -4
- package/domains/datastores/apply.d.ts.map +0 -1
- package/domains/datastores/apply.js +0 -28
- package/domains/datastores/apply.js.map +0 -1
- package/domains/datastores/delete.d.ts +0 -3
- package/domains/datastores/delete.d.ts.map +0 -1
- package/domains/datastores/delete.js +0 -38
- package/domains/datastores/delete.js.map +0 -1
- package/domains/datastores/fetch.d.ts +0 -3
- package/domains/datastores/fetch.d.ts.map +0 -1
- package/domains/datastores/fetch.js +0 -23
- package/domains/datastores/fetch.js.map +0 -1
- package/domains/datastores/resources.d.ts +0 -5
- package/domains/datastores/resources.d.ts.map +0 -1
- package/domains/datastores/resources.js +0 -16
- package/domains/datastores/resources.js.map +0 -1
- package/domains/datastores/tools.d.ts +0 -5
- package/domains/datastores/tools.d.ts.map +0 -1
- package/domains/datastores/tools.js +0 -46
- package/domains/datastores/tools.js.map +0 -1
- package/domains/records/calls.d.ts +0 -48
- package/domains/records/calls.d.ts.map +0 -1
- package/domains/records/calls.js +0 -118
- package/domains/records/calls.js.map +0 -1
- package/domains/records/errors.d.ts +0 -24
- package/domains/records/errors.d.ts.map +0 -1
- package/domains/records/errors.js +0 -79
- package/domains/records/errors.js.map +0 -1
- package/domains/records/tools.d.ts +0 -7
- package/domains/records/tools.d.ts.map +0 -1
- package/domains/records/tools.js +0 -139
- package/domains/records/tools.js.map +0 -1
- package/gen/datastore.d.ts +0 -861
- package/gen/datastore.d.ts.map +0 -1
- package/gen/datastore.js +0 -267
- package/gen/datastore.js.map +0 -1
- package/src/domains/datastores/apply.ts +0 -33
- package/src/domains/datastores/delete.ts +0 -47
- package/src/domains/datastores/fetch.ts +0 -32
- package/src/domains/datastores/resources.ts +0 -21
- package/src/domains/datastores/tools.ts +0 -76
- package/src/domains/records/calls.ts +0 -209
- package/src/domains/records/errors.ts +0 -98
- package/src/domains/records/records.integration.test.ts +0 -313
- package/src/domains/records/tools.ts +0 -210
- package/src/gen/datastore.ts +0 -264
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
7
7
|
import { AgentCommandController } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/command_pb";
|
|
8
8
|
|
|
9
|
+
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
10
|
+
|
|
9
11
|
import { agentInputToProto, type AgentInput } from "../../gen/agent.js";
|
|
12
|
+
import { applyDeclaredVisibility } from "../apply-visibility.js";
|
|
10
13
|
import { withClient } from "../client.js";
|
|
11
14
|
import { toProtoJson } from "../marshal.js";
|
|
12
15
|
import { rpcError } from "../rpcerr.js";
|
|
@@ -21,7 +24,13 @@ export async function applyAgent(
|
|
|
21
24
|
const desc = `agent "${agent.metadata?.slug ?? ""}" in org "${agent.metadata?.org ?? ""}"`;
|
|
22
25
|
return withClient(AgentCommandController, serverAddress, token, async (client, callOptions) => {
|
|
23
26
|
try {
|
|
24
|
-
const
|
|
27
|
+
const applied = await client.apply(agent, callOptions);
|
|
28
|
+
const result = await applyDeclaredVisibility(
|
|
29
|
+
client,
|
|
30
|
+
callOptions,
|
|
31
|
+
applied,
|
|
32
|
+
agent.metadata?.visibility ?? ApiResourceVisibility.api_resource_visibility_unspecified,
|
|
33
|
+
);
|
|
25
34
|
return toProtoJson(AgentSchema, result);
|
|
26
35
|
} catch (err) {
|
|
27
36
|
throw rpcError(err, desc);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Declared-visibility follow-up for the apply tools (oss#573).
|
|
2
|
+
//
|
|
3
|
+
// Plain updates preserve the stored visibility on both editions — the
|
|
4
|
+
// updateVisibility RPC is the only door for visibility changes, where the
|
|
5
|
+
// server-side guards live (per-kind level support, default-instance
|
|
6
|
+
// rejection). So when an apply tool's input declares a visibility and the
|
|
7
|
+
// applied resource comes back with a different level (i.e. the resource
|
|
8
|
+
// already existed), we follow up with one UpdateVisibility RPC on the same
|
|
9
|
+
// controller. The CLI's skill-push and manifest-apply flows do the same.
|
|
10
|
+
//
|
|
11
|
+
// No-ops are skipped (input omitted visibility, create honored it, or the
|
|
12
|
+
// stored level already matches), so an unchanged apply costs nothing extra.
|
|
13
|
+
// Guard rejections propagate to the caller's rpcError wrapper — the model
|
|
14
|
+
// sees the real FAILED_PRECONDITION / INVALID_ARGUMENT, not a silent lie.
|
|
15
|
+
|
|
16
|
+
import { create } from "@bufbuild/protobuf";
|
|
17
|
+
import type { CallOptions } from "@connectrpc/connect";
|
|
18
|
+
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
19
|
+
import { UpdateVisibilityInputSchema, type UpdateVisibilityInput } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
|
|
20
|
+
import type { ApiResourceMetadata } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
|
|
21
|
+
|
|
22
|
+
interface HasResourceMetadata {
|
|
23
|
+
metadata?: ApiResourceMetadata;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface UpdateVisibilityClient<T> {
|
|
27
|
+
updateVisibility(input: UpdateVisibilityInput, options?: CallOptions): Promise<T>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Land the input-declared visibility through the guarded door when the
|
|
32
|
+
* applied resource's stored level differs. Returns the resource carrying the
|
|
33
|
+
* final visibility (the follow-up's response, or `applied` unchanged when no
|
|
34
|
+
* follow-up was needed).
|
|
35
|
+
*/
|
|
36
|
+
export async function applyDeclaredVisibility<T extends HasResourceMetadata>(
|
|
37
|
+
client: UpdateVisibilityClient<T>,
|
|
38
|
+
callOptions: CallOptions,
|
|
39
|
+
applied: T,
|
|
40
|
+
declared: ApiResourceVisibility,
|
|
41
|
+
): Promise<T> {
|
|
42
|
+
if (declared === ApiResourceVisibility.api_resource_visibility_unspecified) return applied;
|
|
43
|
+
const resourceId = applied.metadata?.id ?? "";
|
|
44
|
+
if (resourceId === "") return applied;
|
|
45
|
+
if (applied.metadata?.visibility === declared) return applied;
|
|
46
|
+
return client.updateVisibility(create(UpdateVisibilityInputSchema, { resourceId, visibility: declared }), callOptions);
|
|
47
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// In-process integration test for the apply tools. Drives apply_agent,
|
|
2
|
-
// apply_mcp_server, apply_workflow, apply_environment
|
|
3
|
-
//
|
|
2
|
+
// apply_mcp_server, apply_workflow, and apply_environment through the
|
|
3
|
+
// full MCP boundary and asserts the codegen projection (src/gen/*)
|
|
4
4
|
// reconstitutes the proto correctly: metadata hoist + slug generation,
|
|
5
|
-
// enum-string conversion
|
|
5
|
+
// enum-string conversion,
|
|
6
6
|
// ApiResourceReference kind injection, the stdio/http oneof, the environment
|
|
7
7
|
// data map with secret flags, and the recursive workflow task_config expansion
|
|
8
8
|
// (http_call leaf, fork/for_each nesting).
|
|
9
9
|
|
|
10
|
+
import { clone } from "@bufbuild/protobuf";
|
|
10
11
|
import type { ConnectRouter } from "@connectrpc/connect";
|
|
11
12
|
import { connectNodeAdapter } from "@connectrpc/connect-node";
|
|
12
13
|
import {
|
|
@@ -18,11 +19,8 @@ import type { AddressInfo } from "node:net";
|
|
|
18
19
|
|
|
19
20
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
20
21
|
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
21
|
-
import type
|
|
22
|
+
import { type Agent, AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
22
23
|
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
24
|
import type { Environment } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
|
|
27
25
|
import { EnvironmentCommandController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/command_pb";
|
|
28
26
|
import type { McpServer as McpServerProto } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
|
|
@@ -32,6 +30,7 @@ import { WorkflowCommandController } from "@stigmer/protos/ai/stigmer/agentic/wo
|
|
|
32
30
|
import { WorkflowTaskKind } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/enum_pb";
|
|
33
31
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
34
32
|
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
33
|
+
import type { UpdateVisibilityInput } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
|
|
35
34
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
36
35
|
|
|
37
36
|
import { configureLogger } from "../logger";
|
|
@@ -45,7 +44,13 @@ let appliedAgent: Agent | undefined;
|
|
|
45
44
|
let appliedMcpServer: McpServerProto | undefined;
|
|
46
45
|
let appliedWorkflow: Workflow | undefined;
|
|
47
46
|
let appliedEnvironment: Environment | undefined;
|
|
48
|
-
|
|
47
|
+
const visibilityUpdates: UpdateVisibilityInput[] = [];
|
|
48
|
+
|
|
49
|
+
// The single-door contract (oss#573): a plain update preserves the STORED
|
|
50
|
+
// visibility, so the apply response can disagree with the request. This slug
|
|
51
|
+
// makes the mock backend simulate that — apply echoes back a pre-existing
|
|
52
|
+
// resource whose stored level is org, regardless of the requested one.
|
|
53
|
+
const STORED_ORG_SLUG = "stored-org-agent";
|
|
49
54
|
const openSessions = new Set<ServerHttp2Session>();
|
|
50
55
|
|
|
51
56
|
interface ToolResult {
|
|
@@ -62,8 +67,21 @@ beforeAll(async () => {
|
|
|
62
67
|
router.service(AgentCommandController, {
|
|
63
68
|
apply: (req) => {
|
|
64
69
|
appliedAgent = req;
|
|
70
|
+
if (req.metadata?.slug === STORED_ORG_SLUG) {
|
|
71
|
+
const stored = clone(AgentSchema, req);
|
|
72
|
+
stored.metadata!.id = "agent-stored-1";
|
|
73
|
+
stored.metadata!.visibility = ApiResourceVisibility.visibility_org;
|
|
74
|
+
return stored;
|
|
75
|
+
}
|
|
65
76
|
return req;
|
|
66
77
|
},
|
|
78
|
+
updateVisibility: (input) => {
|
|
79
|
+
visibilityUpdates.push(input);
|
|
80
|
+
const updated = clone(AgentSchema, appliedAgent!);
|
|
81
|
+
updated.metadata!.id = input.resourceId;
|
|
82
|
+
updated.metadata!.visibility = input.visibility;
|
|
83
|
+
return updated;
|
|
84
|
+
},
|
|
67
85
|
});
|
|
68
86
|
router.service(McpServerCommandController, {
|
|
69
87
|
apply: (req) => {
|
|
@@ -83,12 +101,6 @@ beforeAll(async () => {
|
|
|
83
101
|
return req;
|
|
84
102
|
},
|
|
85
103
|
});
|
|
86
|
-
router.service(DatastoreCommandController, {
|
|
87
|
-
apply: (req) => {
|
|
88
|
-
appliedDatastore = req;
|
|
89
|
-
return req;
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
104
|
};
|
|
93
105
|
backend = createHttp2Server(connectNodeAdapter({ routes }));
|
|
94
106
|
backend.on("session", (session) => {
|
|
@@ -119,7 +131,6 @@ describe("apply tools integration", () => {
|
|
|
119
131
|
"apply_mcp_server",
|
|
120
132
|
"apply_workflow",
|
|
121
133
|
"apply_environment",
|
|
122
|
-
"apply_datastore",
|
|
123
134
|
]),
|
|
124
135
|
);
|
|
125
136
|
});
|
|
@@ -245,6 +256,38 @@ describe("apply tools integration", () => {
|
|
|
245
256
|
expect(forCfg.do).toHaveLength(1);
|
|
246
257
|
});
|
|
247
258
|
|
|
259
|
+
it("apply_agent lands a declared visibility the update preserved away via UpdateVisibility (oss#573)", async () => {
|
|
260
|
+
const result = await callTool("apply_agent", {
|
|
261
|
+
name: "Stored Org Agent",
|
|
262
|
+
org: "acme",
|
|
263
|
+
visibility: "PUBLIC",
|
|
264
|
+
instructions: "i",
|
|
265
|
+
});
|
|
266
|
+
expect(result.isError).toBeFalsy();
|
|
267
|
+
|
|
268
|
+
// Server "preserved" stored org; the tool must follow up once through
|
|
269
|
+
// the guarded door and return the landed level, not the stale one.
|
|
270
|
+
expect(visibilityUpdates).toHaveLength(1);
|
|
271
|
+
expect(visibilityUpdates[0].resourceId).toBe("agent-stored-1");
|
|
272
|
+
expect(visibilityUpdates[0].visibility).toBe(ApiResourceVisibility.visibility_public);
|
|
273
|
+
const text = result.content.find((c) => c.type === "text")?.text ?? "";
|
|
274
|
+
expect(JSON.parse(text).metadata.visibility).toBe("visibility_public");
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("apply_agent skips the follow-up when the server already matches", async () => {
|
|
278
|
+
visibilityUpdates.length = 0;
|
|
279
|
+
const result = await callTool("apply_agent", {
|
|
280
|
+
name: "Plain Agent",
|
|
281
|
+
org: "acme",
|
|
282
|
+
visibility: "PUBLIC",
|
|
283
|
+
instructions: "i",
|
|
284
|
+
});
|
|
285
|
+
expect(result.isError).toBeFalsy();
|
|
286
|
+
// The echo backend returns the requested level (and no id) — no diff,
|
|
287
|
+
// no follow-up.
|
|
288
|
+
expect(visibilityUpdates).toHaveLength(0);
|
|
289
|
+
});
|
|
290
|
+
|
|
248
291
|
it("apply_environment rebuilds the data map with secret flags", async () => {
|
|
249
292
|
const result = await callTool("apply_environment", {
|
|
250
293
|
name: "GitHub Creds",
|
|
@@ -267,30 +310,4 @@ describe("apply tools integration", () => {
|
|
|
267
310
|
});
|
|
268
311
|
expect(env?.spec?.data?.REGION?.isSecret).toBe(false);
|
|
269
312
|
});
|
|
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
|
-
});
|
|
296
313
|
});
|
|
@@ -1,8 +1,7 @@
|
|
|
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
3
|
// delete-input shape: typed {value} for agent/skill/workflow, and
|
|
4
|
-
// ApiResourceDeleteInput {resource_id} for mcp_server
|
|
5
|
-
// datastore.
|
|
4
|
+
// ApiResourceDeleteInput {resource_id} for mcp_server and environment.
|
|
6
5
|
|
|
7
6
|
import { create, toJson } from "@bufbuild/protobuf";
|
|
8
7
|
import type { ConnectRouter } from "@connectrpc/connect";
|
|
@@ -19,9 +18,6 @@ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
|
19
18
|
import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
20
19
|
import { AgentCommandController } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/command_pb";
|
|
21
20
|
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
21
|
import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
|
|
26
22
|
import { EnvironmentCommandController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/command_pb";
|
|
27
23
|
import { EnvironmentQueryController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/query_pb";
|
|
@@ -50,18 +46,12 @@ const resolvedEnvironment = create(EnvironmentSchema, {
|
|
|
50
46
|
kind: "environment",
|
|
51
47
|
metadata: { name: "GitHub Creds", slug: "github-creds", org: "acme", id: "env-789" },
|
|
52
48
|
});
|
|
53
|
-
const resolvedDatastore = create(DatastoreSchema, {
|
|
54
|
-
apiVersion: "v1",
|
|
55
|
-
kind: "datastore",
|
|
56
|
-
metadata: { name: "Bookings", slug: "bookings", org: "acme", id: "dst-012" },
|
|
57
|
-
});
|
|
58
49
|
|
|
59
50
|
let backend: Http2Server;
|
|
60
51
|
let client: Client;
|
|
61
52
|
let deletedAgentId: string | undefined;
|
|
62
53
|
let deletedMcpResourceId: string | undefined;
|
|
63
54
|
let deletedEnvironmentResourceId: string | undefined;
|
|
64
|
-
let deletedDatastoreResourceId: string | undefined;
|
|
65
55
|
const openSessions = new Set<ServerHttp2Session>();
|
|
66
56
|
|
|
67
57
|
interface ToolResult {
|
|
@@ -96,13 +86,6 @@ beforeAll(async () => {
|
|
|
96
86
|
return resolvedEnvironment;
|
|
97
87
|
},
|
|
98
88
|
});
|
|
99
|
-
router.service(DatastoreQueryController, { getByReference: () => resolvedDatastore });
|
|
100
|
-
router.service(DatastoreCommandController, {
|
|
101
|
-
delete: (req) => {
|
|
102
|
-
deletedDatastoreResourceId = req.resourceId;
|
|
103
|
-
return resolvedDatastore;
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
89
|
};
|
|
107
90
|
backend = createHttp2Server(connectNodeAdapter({ routes }));
|
|
108
91
|
backend.on("session", (session) => {
|
|
@@ -134,7 +117,6 @@ describe("delete tools integration", () => {
|
|
|
134
117
|
"delete_workflow",
|
|
135
118
|
"delete_mcp_server",
|
|
136
119
|
"delete_environment",
|
|
137
|
-
"delete_datastore",
|
|
138
120
|
]),
|
|
139
121
|
);
|
|
140
122
|
});
|
|
@@ -162,10 +144,4 @@ describe("delete tools integration", () => {
|
|
|
162
144
|
expect(result.isError).toBeFalsy();
|
|
163
145
|
expect(deletedEnvironmentResourceId).toBe("env-789");
|
|
164
146
|
});
|
|
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
|
-
});
|
|
171
147
|
});
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
|
|
12
12
|
import { EnvironmentCommandController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/command_pb";
|
|
13
13
|
|
|
14
|
+
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
15
|
+
|
|
14
16
|
import { environmentInputToProto, type EnvironmentInput } from "../../gen/environment.js";
|
|
17
|
+
import { applyDeclaredVisibility } from "../apply-visibility.js";
|
|
15
18
|
import { withClient } from "../client.js";
|
|
16
19
|
import { toProtoJson } from "../marshal.js";
|
|
17
20
|
import { rpcError } from "../rpcerr.js";
|
|
@@ -30,7 +33,13 @@ export async function applyEnvironment(
|
|
|
30
33
|
token,
|
|
31
34
|
async (client, callOptions) => {
|
|
32
35
|
try {
|
|
33
|
-
const
|
|
36
|
+
const applied = await client.apply(environment, callOptions);
|
|
37
|
+
const result = await applyDeclaredVisibility(
|
|
38
|
+
client,
|
|
39
|
+
callOptions,
|
|
40
|
+
applied,
|
|
41
|
+
environment.metadata?.visibility ?? ApiResourceVisibility.api_resource_visibility_unspecified,
|
|
42
|
+
);
|
|
34
43
|
return toProtoJson(EnvironmentSchema, result);
|
|
35
44
|
} catch (err) {
|
|
36
45
|
throw rpcError(err, desc);
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
import { McpServerSchema } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
|
|
8
8
|
import { McpServerCommandController } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/command_pb";
|
|
9
9
|
|
|
10
|
+
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
11
|
+
|
|
10
12
|
import { mcpServerInputToProto, type McpServerInput } from "../../gen/mcpserver.js";
|
|
13
|
+
import { applyDeclaredVisibility } from "../apply-visibility.js";
|
|
11
14
|
import { withClient } from "../client.js";
|
|
12
15
|
import { toProtoJson } from "../marshal.js";
|
|
13
16
|
import { rpcError } from "../rpcerr.js";
|
|
@@ -26,7 +29,13 @@ export async function applyMcpServer(
|
|
|
26
29
|
token,
|
|
27
30
|
async (client, callOptions) => {
|
|
28
31
|
try {
|
|
29
|
-
const
|
|
32
|
+
const applied = await client.apply(server, callOptions);
|
|
33
|
+
const result = await applyDeclaredVisibility(
|
|
34
|
+
client,
|
|
35
|
+
callOptions,
|
|
36
|
+
applied,
|
|
37
|
+
server.metadata?.visibility ?? ApiResourceVisibility.api_resource_visibility_unspecified,
|
|
38
|
+
);
|
|
30
39
|
return toProtoJson(McpServerSchema, result);
|
|
31
40
|
} catch (err) {
|
|
32
41
|
throw rpcError(err, desc);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// In-process integration test for the read tools (get_mcp_server, get_skill,
|
|
2
|
-
// get_workflow, get_environment
|
|
2
|
+
// get_workflow, get_environment). Stands up a real Connect
|
|
3
3
|
// backend serving the query controllers, drives the MCP server through an
|
|
4
4
|
// in-memory client, and asserts each tool returns the backend's protojson
|
|
5
5
|
// verbatim (the parity contract) and that get_skill forwards its optional
|
|
@@ -17,8 +17,6 @@ import type { AddressInfo } from "node:net";
|
|
|
17
17
|
|
|
18
18
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
19
19
|
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
20
|
-
import { DatastoreSchema } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/api_pb";
|
|
21
|
-
import { DatastoreQueryController } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/query_pb";
|
|
22
20
|
import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
|
|
23
21
|
import { EnvironmentQueryController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/query_pb";
|
|
24
22
|
import { McpServerSchema } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
|
|
@@ -61,11 +59,6 @@ const knownEnvironment = create(EnvironmentSchema, {
|
|
|
61
59
|
spec: { data: { API_KEY: { value: "***REDACTED***", isSecret: true } } },
|
|
62
60
|
});
|
|
63
61
|
|
|
64
|
-
const knownDatastore = create(DatastoreSchema, {
|
|
65
|
-
apiVersion: "v1",
|
|
66
|
-
kind: "datastore",
|
|
67
|
-
metadata: { name: "Bookings", slug: "bookings", org: "acme", id: "dst-1" },
|
|
68
|
-
});
|
|
69
62
|
|
|
70
63
|
let backend: Http2Server;
|
|
71
64
|
let client: Client;
|
|
@@ -92,7 +85,6 @@ beforeAll(async () => {
|
|
|
92
85
|
});
|
|
93
86
|
router.service(WorkflowQueryController, { getByReference: () => knownWorkflow });
|
|
94
87
|
router.service(EnvironmentQueryController, { getByReference: () => knownEnvironment });
|
|
95
|
-
router.service(DatastoreQueryController, { getByReference: () => knownDatastore });
|
|
96
88
|
};
|
|
97
89
|
backend = createHttp2Server(connectNodeAdapter({ routes }));
|
|
98
90
|
// Force keep-alive sessions closed on teardown, else backend.close() blocks.
|
|
@@ -125,7 +117,6 @@ describe("read tools integration", () => {
|
|
|
125
117
|
"get_skill",
|
|
126
118
|
"get_workflow",
|
|
127
119
|
"get_environment",
|
|
128
|
-
"get_datastore",
|
|
129
120
|
]),
|
|
130
121
|
);
|
|
131
122
|
});
|
|
@@ -169,12 +160,4 @@ describe("read tools integration", () => {
|
|
|
169
160
|
const data = (body.spec as { data: Record<string, { value: string }> }).data;
|
|
170
161
|
expect(data.API_KEY?.value).toBe("***REDACTED***");
|
|
171
162
|
});
|
|
172
|
-
|
|
173
|
-
it("get_datastore returns the backend protojson", async () => {
|
|
174
|
-
const result = await callTool("get_datastore", { org: "acme", slug: "bookings" });
|
|
175
|
-
expect(result.isError).toBeFalsy();
|
|
176
|
-
expect(JSON.parse(result.content[0]?.text ?? "{}")).toEqual(
|
|
177
|
-
toJson(DatastoreSchema, knownDatastore, { useProtoFieldName: true }),
|
|
178
|
-
);
|
|
179
|
-
});
|
|
180
163
|
});
|
|
@@ -17,8 +17,6 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
17
17
|
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
18
18
|
import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
19
19
|
import { AgentQueryController } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/query_pb";
|
|
20
|
-
import { DatastoreSchema } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/api_pb";
|
|
21
|
-
import { DatastoreQueryController } from "@stigmer/protos/ai/stigmer/agentic/datastore/v1/query_pb";
|
|
22
20
|
import { EnvironmentSchema } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/api_pb";
|
|
23
21
|
import { EnvironmentQueryController } from "@stigmer/protos/ai/stigmer/agentic/environment/v1/query_pb";
|
|
24
22
|
import { McpServerSchema } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
|
|
@@ -51,11 +49,6 @@ const environment = create(EnvironmentSchema, {
|
|
|
51
49
|
kind: "environment",
|
|
52
50
|
metadata: { slug: "e", org: "acme" },
|
|
53
51
|
});
|
|
54
|
-
const datastore = create(DatastoreSchema, {
|
|
55
|
-
apiVersion: "v1",
|
|
56
|
-
kind: "datastore",
|
|
57
|
-
metadata: { slug: "d", org: "acme" },
|
|
58
|
-
});
|
|
59
52
|
|
|
60
53
|
let backend: Http2Server;
|
|
61
54
|
let client: Client;
|
|
@@ -78,7 +71,6 @@ beforeAll(async () => {
|
|
|
78
71
|
});
|
|
79
72
|
router.service(WorkflowQueryController, { getByReference: () => workflow });
|
|
80
73
|
router.service(EnvironmentQueryController, { getByReference: () => environment });
|
|
81
|
-
router.service(DatastoreQueryController, { getByReference: () => datastore });
|
|
82
74
|
};
|
|
83
75
|
backend = createHttp2Server(connectNodeAdapter({ routes }));
|
|
84
76
|
backend.on("session", (session) => {
|
|
@@ -111,7 +103,6 @@ describe("resource templates integration", () => {
|
|
|
111
103
|
"stigmer_skill_version",
|
|
112
104
|
"stigmer_workflow",
|
|
113
105
|
"stigmer_environment",
|
|
114
|
-
"stigmer_datastore",
|
|
115
106
|
]),
|
|
116
107
|
);
|
|
117
108
|
});
|
|
@@ -163,13 +154,4 @@ describe("resource templates integration", () => {
|
|
|
163
154
|
toJson(EnvironmentSchema, environment, { useProtoFieldName: true }),
|
|
164
155
|
);
|
|
165
156
|
});
|
|
166
|
-
|
|
167
|
-
it("reads a datastore resource", async () => {
|
|
168
|
-
const result = (await client.readResource({
|
|
169
|
-
uri: "stigmer://datastores/acme/d",
|
|
170
|
-
})) as ResourceResult;
|
|
171
|
-
expect(JSON.parse(result.contents[0]?.text ?? "{}")).toEqual(
|
|
172
|
-
toJson(DatastoreSchema, datastore, { useProtoFieldName: true }),
|
|
173
|
-
);
|
|
174
|
-
});
|
|
175
157
|
});
|
|
@@ -76,7 +76,6 @@ describe("buildResourceURI", () => {
|
|
|
76
76
|
expect(buildResourceURI("skill", "acme", "s")).toBe("stigmer://skills/acme/s");
|
|
77
77
|
expect(buildResourceURI("workflow", "acme", "w")).toBe("stigmer://workflows/acme/w");
|
|
78
78
|
expect(buildResourceURI("environment", "acme", "e")).toBe("stigmer://environments/acme/e");
|
|
79
|
-
expect(buildResourceURI("datastore", "acme", "d")).toBe("stigmer://datastores/acme/d");
|
|
80
79
|
});
|
|
81
80
|
|
|
82
81
|
it("round-trips with parseResourceURI", () => {
|
|
@@ -96,7 +95,6 @@ describe("buildResourceURI", () => {
|
|
|
96
95
|
it("covers exactly the templated kinds", () => {
|
|
97
96
|
expect(Object.keys(kindToAuthority).sort()).toEqual([
|
|
98
97
|
"agent",
|
|
99
|
-
"datastore",
|
|
100
98
|
"environment",
|
|
101
99
|
"mcp_server",
|
|
102
100
|
"skill",
|
|
@@ -81,7 +81,6 @@ export function parseVersionedResourceURI(uri: string): VersionedResourceRef {
|
|
|
81
81
|
*/
|
|
82
82
|
export const kindToAuthority: Readonly<Record<string, string>> = {
|
|
83
83
|
agent: "agents",
|
|
84
|
-
datastore: "datastores",
|
|
85
84
|
environment: "environments",
|
|
86
85
|
mcp_server: "mcp-servers",
|
|
87
86
|
skill: "skills",
|
|
@@ -40,7 +40,6 @@ const allKindsResponse = create(SearchResponseSchema, {
|
|
|
40
40
|
org: "acme",
|
|
41
41
|
slug: "github-creds",
|
|
42
42
|
}),
|
|
43
|
-
create(SearchResultSchema, { kind: ApiResourceKind.datastore, org: "acme", slug: "bookings" }),
|
|
44
43
|
],
|
|
45
44
|
});
|
|
46
45
|
const emptyResponse = create(SearchResponseSchema, { entries: [] });
|
|
@@ -103,7 +102,6 @@ describe("search tool integration", () => {
|
|
|
103
102
|
"stigmer://mcp-servers/acme/github",
|
|
104
103
|
"stigmer://workflows/acme/release",
|
|
105
104
|
"stigmer://environments/acme/github-creds",
|
|
106
|
-
"stigmer://datastores/acme/bookings",
|
|
107
105
|
]);
|
|
108
106
|
});
|
|
109
107
|
|
|
@@ -121,13 +119,13 @@ describe("search tool integration", () => {
|
|
|
121
119
|
expect(result.isError).toBe(true);
|
|
122
120
|
expect(result.content[0]?.text).toContain('unknown resource kind "bogus"');
|
|
123
121
|
expect(result.content[0]?.text).toContain(
|
|
124
|
-
"valid kinds: agent, skill, mcp_server, workflow, environment
|
|
122
|
+
"valid kinds: agent, skill, mcp_server, workflow, environment",
|
|
125
123
|
);
|
|
126
124
|
});
|
|
127
125
|
|
|
128
|
-
it("accepts the environment
|
|
126
|
+
it("accepts the environment kind", async () => {
|
|
129
127
|
nextResponse = emptyResponse;
|
|
130
|
-
const result = await callSearch({ kinds: ["environment"
|
|
128
|
+
const result = await callSearch({ kinds: ["environment"] });
|
|
131
129
|
expect(result.isError).toBeFalsy();
|
|
132
130
|
});
|
|
133
131
|
|
|
@@ -31,7 +31,6 @@ import { textOrError } from "../toolresult.js";
|
|
|
31
31
|
*/
|
|
32
32
|
const knownKinds: Readonly<Record<string, ApiResourceKind>> = {
|
|
33
33
|
agent: ApiResourceKind.agent,
|
|
34
|
-
datastore: ApiResourceKind.datastore,
|
|
35
34
|
environment: ApiResourceKind.environment,
|
|
36
35
|
skill: ApiResourceKind.skill,
|
|
37
36
|
mcp_server: ApiResourceKind.mcp_server,
|
|
@@ -53,15 +52,15 @@ export function registerSearchTools(server: McpServer, target: BackendTarget): s
|
|
|
53
52
|
"search",
|
|
54
53
|
{
|
|
55
54
|
description:
|
|
56
|
-
"Search and list Stigmer resources (agents, skills, MCP servers, workflows,
|
|
57
|
-
"
|
|
55
|
+
"Search and list Stigmer resources (agents, skills, MCP servers, workflows, " +
|
|
56
|
+
"environments). Set 'kinds' to filter by resource type. Set 'query' for full-text search. " +
|
|
58
57
|
"Set 'org' to scope to an organization. Omit 'query' to list all accessible resources.",
|
|
59
58
|
inputSchema: {
|
|
60
59
|
kinds: z
|
|
61
60
|
.array(z.string())
|
|
62
61
|
.optional()
|
|
63
62
|
.describe(
|
|
64
|
-
"Resource kinds to search. Valid: agent, skill, mcp_server, workflow, environment
|
|
63
|
+
"Resource kinds to search. Valid: agent, skill, mcp_server, workflow, environment. Empty searches all.",
|
|
65
64
|
),
|
|
66
65
|
query: z
|
|
67
66
|
.string()
|
|
@@ -130,7 +129,7 @@ function parseKinds(raw: string[] | undefined): ApiResourceKind[] {
|
|
|
130
129
|
const kind = knownKinds[s];
|
|
131
130
|
if (kind === undefined) {
|
|
132
131
|
throw new Error(
|
|
133
|
-
`unknown resource kind "${s}"; valid kinds: agent, skill, mcp_server, workflow, environment
|
|
132
|
+
`unknown resource kind "${s}"; valid kinds: agent, skill, mcp_server, workflow, environment`,
|
|
134
133
|
);
|
|
135
134
|
}
|
|
136
135
|
return kind;
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
import { WorkflowSchema } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
|
|
12
12
|
import { WorkflowCommandController } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/command_pb";
|
|
13
13
|
|
|
14
|
+
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
15
|
+
|
|
14
16
|
import { workflowInputToProto, type WorkflowInput } from "../../gen/workflow.js";
|
|
17
|
+
import { applyDeclaredVisibility } from "../apply-visibility.js";
|
|
15
18
|
import { withClient } from "../client.js";
|
|
16
19
|
import { toProtoJson } from "../marshal.js";
|
|
17
20
|
import { rpcError } from "../rpcerr.js";
|
|
@@ -30,7 +33,13 @@ export async function applyWorkflow(
|
|
|
30
33
|
token,
|
|
31
34
|
async (client, callOptions) => {
|
|
32
35
|
try {
|
|
33
|
-
const
|
|
36
|
+
const applied = await client.apply(workflow, callOptions);
|
|
37
|
+
const result = await applyDeclaredVisibility(
|
|
38
|
+
client,
|
|
39
|
+
callOptions,
|
|
40
|
+
applied,
|
|
41
|
+
workflow.metadata?.visibility ?? ApiResourceVisibility.api_resource_visibility_unspecified,
|
|
42
|
+
);
|
|
34
43
|
return toProtoJson(WorkflowSchema, result);
|
|
35
44
|
} catch (err) {
|
|
36
45
|
throw rpcError(err, desc);
|