@wardby/cli 0.7.0 → 0.8.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/.env.example +5 -5
- package/README.md +6 -1
- package/deploy/local/docker-compose.quickstart-coding.yml +6 -0
- package/dist/claude-coding-worker/driver.d.ts +12 -0
- package/dist/claude-coding-worker/driver.js +29 -7
- package/dist/claude-coding-worker/main.js +5 -1
- package/dist/claude-coding-worker/sdk.js +17 -1
- package/dist/claude-plugin/command.d.ts +61 -0
- package/dist/claude-plugin/command.js +376 -0
- package/dist/cli-help.d.ts +1 -1
- package/dist/cli-help.js +2 -1
- package/dist/cli.js +47 -3
- package/dist/coding/code-host.d.ts +16 -0
- package/dist/coding/code-host.js +60 -0
- package/dist/coding/mcp-token.d.ts +12 -0
- package/dist/coding/mcp-token.js +26 -0
- package/dist/coding/protocol.js +2 -0
- package/dist/coding/redact.js +6 -1
- package/dist/coding/transcript/recorder.d.ts +5 -4
- package/dist/coding/transcript/recorder.js +1 -1
- package/dist/coding-worker/debug-trace.d.ts +3 -1
- package/dist/coding-worker/debug-trace.js +4 -2
- package/dist/coding-worker/driver.js +8 -1
- package/dist/coding-worker/main.js +5 -1
- package/dist/coding-worker/mcp-config.d.ts +27 -0
- package/dist/coding-worker/mcp-config.js +32 -0
- package/dist/coding-worker/sdk.d.ts +36 -0
- package/dist/coding-worker/sdk.js +28 -1
- package/dist/coding-worker/types.d.ts +4 -0
- package/dist/config/mcp-call-limit.d.ts +8 -0
- package/dist/config/mcp-call-limit.js +18 -0
- package/dist/config/mcp-servers.d.ts +8 -0
- package/dist/config/mcp-servers.js +12 -0
- package/dist/config/test-simulators.d.ts +10 -0
- package/dist/config/test-simulators.js +28 -0
- package/dist/config/tool-timeout.d.ts +1 -0
- package/dist/config/tool-timeout.js +18 -0
- package/dist/core/coding-mcp.d.ts +36 -0
- package/dist/core/coding-mcp.js +253 -0
- package/dist/core/coding-repository.d.ts +46 -0
- package/dist/core/coding-repository.js +92 -0
- package/dist/core/delegation-slots.d.ts +27 -0
- package/dist/core/delegation-slots.js +44 -0
- package/dist/core/dispatch.d.ts +6 -2
- package/dist/core/dispatch.js +36 -11
- package/dist/core/error-code.js +1 -0
- package/dist/core/grants.d.ts +28 -3
- package/dist/core/grants.js +46 -4
- package/dist/core/host-events.js +7 -3
- package/dist/core/host-status.js +2 -2
- package/dist/core/issue-status.js +4 -3
- package/dist/core/mark-ready.d.ts +1 -1
- package/dist/core/mark-ready.js +1 -1
- package/dist/core/mcp-client/client.d.ts +47 -0
- package/dist/core/mcp-client/client.js +141 -0
- package/dist/core/mcp-client/jsonrpc.d.ts +15 -0
- package/dist/core/mcp-client/jsonrpc.js +54 -0
- package/dist/core/mcp-client/results.d.ts +13 -0
- package/dist/core/mcp-client/results.js +42 -0
- package/dist/core/mcp-servers.d.ts +90 -0
- package/dist/core/mcp-servers.js +230 -0
- package/dist/core/mcp-tools.d.ts +108 -0
- package/dist/core/mcp-tools.js +287 -0
- package/dist/core/merge-order-check.js +38 -19
- package/dist/core/notification-dispatcher.js +2 -1
- package/dist/core/related-pull-requests.js +26 -10
- package/dist/core/review-fix.js +2 -2
- package/dist/core/review-host-tools.js +3 -1
- package/dist/core/runner.d.ts +66 -16
- package/dist/core/runner.js +281 -73
- package/dist/core/secret-broker-config.d.ts +2 -0
- package/dist/core/secret-broker-config.js +1 -1
- package/dist/core/secret-guard.d.ts +5 -2
- package/dist/core/secret-guard.js +22 -7
- package/dist/core/secrets.d.ts +11 -1
- package/dist/core/secrets.js +27 -1
- package/dist/core/serializable-retry.d.ts +18 -0
- package/dist/core/serializable-retry.js +43 -0
- package/dist/core/tool-admin.d.ts +5 -1
- package/dist/core/tool-admin.js +7 -2
- package/dist/core/tool-names.d.ts +13 -0
- package/dist/core/tool-names.js +20 -0
- package/dist/core/webhooks.d.ts +4 -0
- package/dist/core/webhooks.js +68 -49
- package/dist/core/workflow-run-events.js +2 -1
- package/dist/generated/prisma/browser.d.ts +31 -0
- package/dist/generated/prisma/client.d.ts +31 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +50 -50
- package/dist/generated/prisma/internal/class.d.ts +66 -0
- package/dist/generated/prisma/internal/class.js +4 -4
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +530 -1
- package/dist/generated/prisma/internal/prismaNamespace.js +75 -2
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +79 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +75 -2
- package/dist/generated/prisma/models/Agent.d.ts +271 -0
- package/dist/generated/prisma/models/AgentMcpServer.d.ts +1412 -0
- package/dist/generated/prisma/models/AgentMcpServer.js +1 -0
- package/dist/generated/prisma/models/AgentRepository.d.ts +40 -1
- package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
- package/dist/generated/prisma/models/CodingAgentProfile.d.ts +0 -7
- package/dist/generated/prisma/models/CodingProxySession.d.ts +119 -2
- package/dist/generated/prisma/models/CodingRun.d.ts +76 -1
- package/dist/generated/prisma/models/HostProvider.d.ts +2176 -0
- package/dist/generated/prisma/models/HostProvider.js +1 -0
- package/dist/generated/prisma/models/HostProviderCredential.d.ts +1285 -0
- package/dist/generated/prisma/models/HostProviderCredential.js +1 -0
- package/dist/generated/prisma/models/HostProviderOperation.d.ts +1470 -0
- package/dist/generated/prisma/models/HostProviderOperation.js +1 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.d.ts +1145 -0
- package/dist/generated/prisma/models/HostProviderTrustedAccount.js +1 -0
- package/dist/generated/prisma/models/McpServer.d.ts +1500 -0
- package/dist/generated/prisma/models/McpServer.js +1 -0
- package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
- package/dist/generated/prisma/models/Principal.d.ts +167 -0
- package/dist/generated/prisma/models/ResourceGrant.d.ts +1 -1
- package/dist/generated/prisma/models/Secret.d.ts +387 -0
- package/dist/generated/prisma/models/Tool.d.ts +223 -1
- package/dist/generated/prisma/models.d.ts +6 -0
- package/dist/help/search.js +12 -3
- package/dist/help-index.json +719 -47
- package/dist/import/create.js +16 -1
- package/dist/import/neutral-schema.d.ts +1 -0
- package/dist/import/neutral-schema.js +2 -0
- package/dist/mcp/auth/grants-cli.d.ts +6 -4
- package/dist/mcp/auth/grants-cli.js +42 -3
- package/dist/mcp/auth/resource-server.d.ts +3 -1
- package/dist/mcp/auth/resource-server.js +17 -2
- package/dist/mcp/context.d.ts +3 -0
- package/dist/mcp/index.js +11 -0
- package/dist/mcp/tools/agents.js +100 -15
- package/dist/mcp/tools/grants.js +66 -28
- package/dist/mcp/tools/host-providers.d.ts +2 -0
- package/dist/mcp/tools/host-providers.js +215 -0
- package/dist/mcp/tools/mcp-servers.d.ts +26 -0
- package/dist/mcp/tools/mcp-servers.js +481 -0
- package/dist/mcp/tools/repositories.js +50 -10
- package/dist/mcp/tools/runs.js +178 -18
- package/dist/mcp/tools/secret-elicitation.d.ts +2 -0
- package/dist/mcp/tools/secret-elicitation.js +25 -2
- package/dist/mcp/tools/secrets.js +38 -4
- package/dist/mcp/tools/tools.d.ts +6 -4
- package/dist/mcp/tools/tools.js +40 -8
- package/dist/mcp/tools/trigger.js +28 -6
- package/dist/mcp/transport/http-limits.d.ts +2 -0
- package/dist/mcp/transport/http-limits.js +6 -2
- package/dist/mcp/webhooks/ingress.js +10 -0
- package/dist/native-worker/gateway-server.d.ts +14 -0
- package/dist/native-worker/gateway-server.js +28 -7
- package/dist/native-worker/gateway.d.ts +2 -0
- package/dist/native-worker/gateway.js +5 -1
- package/dist/native-worker/http-server.d.ts +4 -0
- package/dist/native-worker/http-server.js +51 -0
- package/dist/native-worker/ledger.d.ts +6 -2
- package/dist/native-worker/ledger.js +5 -4
- package/dist/native-worker/protocol.d.ts +1 -0
- package/dist/native-worker/protocol.js +4 -1
- package/dist/providers/coding-proxy/capability-hash.d.ts +2 -0
- package/dist/providers/coding-proxy/capability-hash.js +5 -0
- package/dist/providers/coding-proxy/mcp-route.d.ts +34 -0
- package/dist/providers/coding-proxy/mcp-route.js +166 -0
- package/dist/providers/coding-proxy/memory-ledger.d.ts +2 -0
- package/dist/providers/coding-proxy/memory-ledger.js +15 -0
- package/dist/providers/coding-proxy/prisma-ledger.d.ts +1 -0
- package/dist/providers/coding-proxy/prisma-ledger.js +30 -2
- package/dist/providers/coding-proxy/proxy.d.ts +7 -1
- package/dist/providers/coding-proxy/proxy.js +134 -34
- package/dist/providers/coding-proxy/runtime.d.ts +6 -0
- package/dist/providers/coding-proxy/runtime.js +32 -1
- package/dist/providers/coding-proxy/server.d.ts +3 -0
- package/dist/providers/coding-proxy/server.js +10 -0
- package/dist/providers/coding-proxy/types.d.ts +13 -0
- package/dist/providers/executor/composition.js +5 -0
- package/dist/providers/executor/container.d.ts +33 -1
- package/dist/providers/executor/container.js +76 -7
- package/dist/providers/host-tools/invoke.d.ts +37 -0
- package/dist/providers/host-tools/invoke.js +117 -0
- package/dist/providers/host-tools/operations.d.ts +65 -0
- package/dist/providers/host-tools/operations.js +149 -0
- package/dist/providers/host-tools/registry.d.ts +79 -0
- package/dist/providers/host-tools/registry.js +362 -0
- package/dist/providers/host-tools/request-guard.d.ts +17 -0
- package/dist/providers/host-tools/request-guard.js +99 -0
- package/dist/providers/host-tools/self-test.d.ts +64 -0
- package/dist/providers/host-tools/self-test.js +142 -0
- package/dist/providers/jobs/docker-isolation.d.ts +2 -2
- package/dist/providers/jobs/docker-isolation.js +30 -8
- package/dist/providers/jobs/docker.d.ts +8 -0
- package/dist/providers/jobs/docker.js +33 -9
- package/dist/providers/jobs/kubernetes-isolation.d.ts +3 -1
- package/dist/providers/jobs/kubernetes-isolation.js +15 -2
- package/dist/providers/jobs/kubernetes.d.ts +5 -0
- package/dist/providers/jobs/kubernetes.js +6 -1
- package/dist/providers/jobs/types.d.ts +6 -0
- package/dist/providers/review-host/types.d.ts +6 -4
- package/dist/providers/review-host/types.js +1 -1
- package/dist/providers/vcs/git.d.ts +6 -2
- package/dist/providers/vcs/git.js +15 -4
- package/dist/providers/vcs/github.d.ts +2 -71
- package/dist/providers/vcs/github.js +8 -11
- package/dist/providers/vcs/index.d.ts +1 -1
- package/dist/providers/vcs/index.js +1 -1
- package/dist/providers/vcs/remote.d.ts +4 -0
- package/dist/providers/vcs/remote.js +7 -1
- package/dist/providers/vcs/routing.d.ts +12 -6
- package/dist/providers/vcs/routing.js +21 -6
- package/dist/providers/vcs/types.d.ts +71 -3
- package/dist/quickstart/index.d.ts +5 -0
- package/dist/quickstart/index.js +21 -0
- package/dist/quickstart-images.json +1 -1
- package/dist/sandbox/eval-core.d.ts +12 -2
- package/dist/sandbox/eval-core.js +20 -4
- package/dist/sandbox/host-functions.d.ts +4 -0
- package/dist/sandbox/host-functions.js +7 -2
- package/dist/sandbox/limits.d.ts +4 -1
- package/dist/sandbox/limits.js +4 -1
- package/dist/sandbox/run-in-sandbox.d.ts +6 -0
- package/dist/sandbox/run-in-sandbox.js +14 -2
- package/dist/sandbox/safe-fetch.d.ts +2 -0
- package/dist/sandbox/safe-fetch.js +1 -1
- package/dist/sandbox/secret-broker.d.ts +1 -0
- package/dist/sandbox/secret-broker.js +1 -1
- package/dist/sandbox/tool-timeout.d.ts +32 -0
- package/dist/sandbox/tool-timeout.js +52 -0
- package/dist/sandbox/user-tool.d.ts +8 -1
- package/dist/sandbox/user-tool.js +9 -3
- package/dist/viewer/graph.js +2 -1
- package/dist/wardby-bin.js +5 -0
- package/docs/README.md +11 -1
- package/docs/agent-recipes.md +37 -4
- package/docs/architecture-runtime.md +1 -1
- package/docs/claude-code-plugin.md +164 -0
- package/docs/code-review-agents.md +6 -0
- package/docs/coding-agent-setup.md +77 -1
- package/docs/coding-worker-byo-images.md +4 -2
- package/docs/coding-worker-isolation.md +78 -7
- package/docs/debugging-agent-runs.md +242 -0
- package/docs/getting-started-gke.md +5 -4
- package/docs/getting-started-identity-provider.md +43 -37
- package/docs/getting-started.md +8 -0
- package/docs/host-providers.md +294 -0
- package/docs/mcp-servers.md +254 -0
- package/docs/native-sandbox.md +25 -9
- package/docs/security-deployment.md +110 -31
- package/docs/tool-secrets.md +3 -0
- package/docs/viewer-api.md +6 -0
- package/docs/viewer-api.openapi.yml +1 -1
- package/help/admin-viewer.md +4 -0
- package/help/agent-recipes.md +33 -2
- package/help/brokered-secrets.md +3 -0
- package/help/build-claude-code-image.md +1 -1
- package/help/claude-code-run-pane.md +40 -0
- package/help/errors/coding-mcp-pins-unavailable.md +35 -0
- package/help/errors/database-auth-invalid.md +1 -1
- package/help/errors/host-provider-error.md +40 -0
- package/help/errors/host-tool-failed.md +36 -0
- package/help/errors/mcp-call-limit.md +22 -0
- package/help/errors/mcp-call-timeout.md +22 -0
- package/help/errors/mcp-server-auth-failed.md +22 -0
- package/help/errors/mcp-server-credentials-unavailable.md +45 -0
- package/help/errors/mcp-server-error.md +28 -0
- package/help/errors/mcp-server-unreachable.md +22 -0
- package/help/errors/mcp-servers-off.md +21 -0
- package/help/errors/mcp-tool-not-allowed.md +26 -0
- package/help/errors/mcp-tool-removed.md +22 -0
- package/help/errors/mcp-unavailable.md +25 -0
- package/help/errors/native-sandbox-capacity.md +1 -1
- package/help/errors/native-sandbox-deadline-exceeded.md +1 -1
- package/help/errors/native-sandbox-docker-failed.md +1 -1
- package/help/errors/native-sandbox-gateway-error.md +1 -1
- package/help/errors/native-sandbox-gateway-unavailable.md +1 -1
- package/help/errors/native-sandbox-image-not-pinned.md +1 -1
- package/help/errors/native-sandbox-isolation-mismatch.md +1 -1
- package/help/errors/native-sandbox-network-unenforced.md +1 -1
- package/help/errors/native-sandbox-requires-catalog.md +1 -1
- package/help/errors/native-sandbox-unavailable.md +1 -1
- package/help/errors/native-sandbox-warm-delivery-failed.md +1 -1
- package/help/errors/native-sandbox-worker-exited.md +1 -1
- package/help/errors/native-sandbox-worker-lost.md +1 -1
- package/help/errors/native-sandbox-worker-unready.md +1 -1
- package/help/errors/repository-not-listed.md +36 -0
- package/help/errors/repository-required.md +36 -0
- package/help/errors/tool-timeout.md +38 -0
- package/help/host-providers.md +40 -0
- package/help/identity-and-access.md +4 -1
- package/help/mcp-servers.md +73 -0
- package/help/mcp.md +7 -2
- package/help/native-capabilities.md +27 -1
- package/help/native-sandbox.md +1 -1
- package/help/operating-agents.md +16 -0
- package/help/related-pull-requests.md +7 -3
- package/help/repo-instructions-and-skills.md +3 -1
- package/help/resume-runs-locally.md +3 -0
- package/help/stop-run.md +1 -1
- package/help/troubleshooting/coding-workers.md +9 -1
- package/help/troubleshooting/debugging-agent-runs.md +29 -0
- package/package.json +2 -1
- package/prisma/migrations/20261102000000_host_providers/migration.sql +56 -0
- package/prisma/migrations/20261104000000_coding_agent_repository_list/migration.sql +5 -0
- package/prisma/migrations/20261106000000_tool_timeout/migration.sql +4 -0
- package/prisma/migrations/20261107000000_mcp_servers/migration.sql +33 -0
- package/prisma/migrations/20261108000000_coding_mcp/migration.sql +5 -0
- package/prisma/migrations/20261109000000_task_run_index/migration.sql +3 -0
- package/prisma/schema.prisma +159 -14
- package/scripts/git-askpass.sh +1 -1
package/dist/mcp/tools/grants.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* grant_access / revoke_access / list_access: the owner's sharing controls
|
|
3
|
-
* (resource-sharing grants spec §3.11).
|
|
4
|
-
* resource types are refused as "not shareable yet".
|
|
3
|
+
* (resource-sharing grants spec §3.11). Agents and MCP servers are shareable;
|
|
4
|
+
* other resource types are refused as "not shareable yet".
|
|
5
5
|
*
|
|
6
6
|
* - Only the resource's owner (or the stdio operator) grants and revokes.
|
|
7
7
|
* Admins get no implicit access: they may only list_access, for incident
|
|
8
8
|
* response ("who can reach this?"), and reassign with make_owner.
|
|
9
9
|
* - One grant per resource and grantee, so granting again replaces the
|
|
10
10
|
* level (also downgrades). Everyone grants are capped below write.
|
|
11
|
-
* - Takes effect on the next check; runs already in flight keep going.
|
|
11
|
+
* - Takes effect on the next check; runs already in flight keep going. For
|
|
12
|
+
* MCP servers the check is every tool call: revoking or lowering a grant
|
|
13
|
+
* below use also removes the attachments of agents whose owner can no
|
|
14
|
+
* longer use the server, in the same transaction.
|
|
12
15
|
*/
|
|
13
16
|
import { z } from "zod";
|
|
14
17
|
import { Prisma } from "#prisma";
|
|
15
|
-
import { EVERYONE_KEY, EVERYONE_MAX, LEVELS, agentOwnerBindings, SHAREABLE_TYPES, accessRank, atLeast, isLevel, isResourceType, principalGranteeKey, } from "../../core/grants.js";
|
|
18
|
+
import { EVERYONE_KEY, EVERYONE_MAX, LEVELS, agentOwnerBindings, SHAREABLE_TYPES, accessRank, atLeast, isLevel, isResourceType, principalGranteeKey, pruneUnusableMcpAttachments, } from "../../core/grants.js";
|
|
19
|
+
import { preferIndexScans, withSerializableRetry } from "../../core/serializable-retry.js";
|
|
16
20
|
import { agentNotFound, assertAgentAccess, agentAccess } from "../auth/access.js";
|
|
17
21
|
import { permissionsOf } from "../auth/resource-server.js";
|
|
18
22
|
import { McpError } from "../errors.js";
|
|
@@ -54,10 +58,17 @@ async function resolveGrantee(db, grantee) {
|
|
|
54
58
|
}
|
|
55
59
|
return principal;
|
|
56
60
|
}
|
|
57
|
-
/** Grant management is the owner's (or the stdio operator's).
|
|
61
|
+
/** Grant management is the owner's (or the stdio operator's). Agents and MCP servers. */
|
|
58
62
|
async function requireOwnedResource(ctx, db, type, id) {
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
if (type === "mcp_server") {
|
|
64
|
+
const server = await db.mcpServer.findUnique({ where: { id } });
|
|
65
|
+
// Missing and someone else's look the same. The stdio operator owns everything.
|
|
66
|
+
if (!server || (!ctx.operator && server.ownerId !== ctx.principal.id)) {
|
|
67
|
+
throw new McpError(404, `MCP server "${id}" not found.`);
|
|
68
|
+
}
|
|
69
|
+
return server;
|
|
70
|
+
}
|
|
71
|
+
// Only "agent" and "mcp_server" reach here (shareableType).
|
|
61
72
|
const row = await db.agent.findUnique({ where: { id } });
|
|
62
73
|
return (await assertAgentAccess(ctx, row, id, "owner", db)).agent;
|
|
63
74
|
}
|
|
@@ -65,17 +76,21 @@ async function requireOwnedResource(ctx, db, type, id) {
|
|
|
65
76
|
* The ownership check and the write in one Serializable transaction (review
|
|
66
77
|
* M3): a concurrent make_owner, which resets grants in its own transaction,
|
|
67
78
|
* conflicts instead of letting the old owner's grant land on the new
|
|
68
|
-
* owner's agent.
|
|
79
|
+
* owner's agent. A conflict is retried a bounded number of times
|
|
80
|
+
* (withSerializableRetry); the last one still reaches the caller as a 409.
|
|
69
81
|
*/
|
|
70
82
|
function inSerializable(ctx, fn) {
|
|
71
|
-
return ctx.db.$transaction((tx) =>
|
|
83
|
+
return withSerializableRetry(() => ctx.db.$transaction(async (tx) => {
|
|
84
|
+
await preferIndexScans(tx);
|
|
85
|
+
return fn(tx);
|
|
86
|
+
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }));
|
|
72
87
|
}
|
|
73
88
|
function isAdmin(ctx) {
|
|
74
89
|
return ctx.scopes.has("agents:admin") && permissionsOf(ctx.roles).has("agents:admin");
|
|
75
90
|
}
|
|
76
91
|
const TYPE_PROPERTIES = {
|
|
77
92
|
// No enum: another type gets a clear "not shareable yet" from the handler.
|
|
78
|
-
resourceType: { type: "string", description: '
|
|
93
|
+
resourceType: { type: "string", description: 'Agents and MCP servers are shareable: "agent" or "mcp_server".' },
|
|
79
94
|
resourceId: { type: "string" },
|
|
80
95
|
};
|
|
81
96
|
const GRANTEE_PROPERTY = {
|
|
@@ -100,21 +115,22 @@ export function registerGrantTools(mcp) {
|
|
|
100
115
|
mcp.registerTool({
|
|
101
116
|
name: "grant_access",
|
|
102
117
|
scope: "agents:write",
|
|
103
|
-
description: "Shares an agent you own with one principal or with everyone, at a level: " +
|
|
118
|
+
description: "Shares an agent or MCP server you own with one principal or with everyone, at a level: " +
|
|
104
119
|
"read (see its config, never secret values or other owners' tool code); " +
|
|
105
120
|
"execute (also trigger runs and see the runs they triggered); " +
|
|
106
121
|
"write (also change its name, prompt, model, budget amount, max turns, effort, memory on/off and schedule, attach tools they can use and detach tools, and create webhooks). " +
|
|
107
122
|
"Owner-only whatever the level: the coding profile (task, base ref, protected paths, task-override opt-in, image, packages, limits), kind, budget group, sub-agents, secret/datastore/repository bindings, tool capabilities, memory and datastore contents, grants, delete. " +
|
|
108
123
|
"Consequences: execute lets them run your agent with your tools, secrets, datastores and repository, and every run shares the agent's memory; they supply no task text unless it's a coding agent with allowWebhookTaskOverride. " +
|
|
109
124
|
"Write lets them rewrite what those runs do: the prompt can make a run use everything the agent can already reach (the capabilities you granted, its memory, its linked repositories), and on a coding agent the prompt is part of every coding task, so write directs work done with your GitHub access. " +
|
|
110
|
-
"
|
|
125
|
+
"MCP servers (resourceType mcp_server): read < use < write; use on an MCP server lets them attach it to their own agents; its tools then run with your server's credentials. Write adds nothing beyond use: only the owner updates, refreshes, deletes or re-grants a server. Lowering a grant to read (including everyone from use to read) detaches the server from every agent whose owner can no longer use it, and their runs' next call to it is refused. " +
|
|
126
|
+
"Granting again replaces the level. Everyone may get write only on a native agent with no secrets, datastores, repositories, sub-agents, MCP servers or tool capabilities. The grantee must have signed in once.",
|
|
111
127
|
inputSchema: {
|
|
112
128
|
type: "object",
|
|
113
129
|
additionalProperties: false,
|
|
114
130
|
properties: {
|
|
115
131
|
...TYPE_PROPERTIES,
|
|
116
132
|
grantee: GRANTEE_PROPERTY,
|
|
117
|
-
level: { type: "string", description: "Agents: read < execute < write." },
|
|
133
|
+
level: { type: "string", description: "Agents: read < execute < write. MCP servers: read < use < write." },
|
|
118
134
|
},
|
|
119
135
|
required: ["resourceType", "resourceId", "grantee", "level"],
|
|
120
136
|
},
|
|
@@ -124,7 +140,7 @@ export function registerGrantTools(mcp) {
|
|
|
124
140
|
if (!isLevel(type, args.level)) {
|
|
125
141
|
throw new McpError(400, `Level "${args.level}" is not valid for ${type} (${LEVELS[type].join(" < ")}).`);
|
|
126
142
|
}
|
|
127
|
-
const { resource, principal, grant } = await inSerializable(ctx, async (tx) => {
|
|
143
|
+
const { resource, principal, grant, attachmentsRemoved } = await inSerializable(ctx, async (tx) => {
|
|
128
144
|
const resource = await requireOwnedResource(ctx, tx, type, args.resourceId);
|
|
129
145
|
const principal = await resolveGrantee(tx, args.grantee);
|
|
130
146
|
if (principal && (principal.id === resource.ownerId || principal.id === ctx.principal.id)) {
|
|
@@ -154,7 +170,11 @@ export function registerGrantTools(mcp) {
|
|
|
154
170
|
},
|
|
155
171
|
update: { level: args.level, source: "owner", grantedById: ctx.principal.id },
|
|
156
172
|
});
|
|
157
|
-
|
|
173
|
+
// Below use, agents whose owner can no longer use the server lose it now (C1).
|
|
174
|
+
const attachmentsRemoved = type === "mcp_server" && !atLeast(type, args.level, "use")
|
|
175
|
+
? await pruneUnusableMcpAttachments(tx, resource.id)
|
|
176
|
+
: 0;
|
|
177
|
+
return { resource, principal, grant, attachmentsRemoved };
|
|
158
178
|
});
|
|
159
179
|
return textResult({
|
|
160
180
|
granted: true,
|
|
@@ -167,13 +187,14 @@ export function registerGrantTools(mcp) {
|
|
|
167
187
|
? { principalId: principal.id, subject: principal.subject }
|
|
168
188
|
: { principalId: principal.id },
|
|
169
189
|
level: grant.level,
|
|
190
|
+
...(attachmentsRemoved > 0 ? { attachmentsRemoved } : {}),
|
|
170
191
|
});
|
|
171
192
|
},
|
|
172
193
|
});
|
|
173
194
|
mcp.registerTool({
|
|
174
195
|
name: "revoke_access",
|
|
175
196
|
scope: "agents:write",
|
|
176
|
-
description: "Removes a grant on an agent you own. Idempotent. Takes effect on the next check (trigger, delegation, webhook fire); runs already in flight keep going.",
|
|
197
|
+
description: "Removes a grant on an agent or MCP server you own. Idempotent. Takes effect on the next check (trigger, delegation, webhook fire); runs already in flight keep going. For an MCP server the check is every tool call: the server is detached from every agent whose owner can no longer use it (your own agents are untouched), and a run's next call to it is refused with mcp_tool_not_allowed.",
|
|
177
198
|
inputSchema: {
|
|
178
199
|
type: "object",
|
|
179
200
|
additionalProperties: false,
|
|
@@ -183,20 +204,25 @@ export function registerGrantTools(mcp) {
|
|
|
183
204
|
handler: async (rawArgs, ctx) => {
|
|
184
205
|
const args = parse(RevokeSchema, "revoke_access", rawArgs);
|
|
185
206
|
const type = shareableType(args.resourceType);
|
|
186
|
-
const { count } = await inSerializable(ctx, async (tx) => {
|
|
207
|
+
const { count, attachmentsRemoved } = await inSerializable(ctx, async (tx) => {
|
|
187
208
|
const resource = await requireOwnedResource(ctx, tx, type, args.resourceId);
|
|
188
209
|
const granteeKey = args.grantee === "everyone"
|
|
189
210
|
? EVERYONE_KEY
|
|
190
211
|
: principalGranteeKey((await resolveGrantee(tx, args.grantee)).id);
|
|
191
|
-
|
|
212
|
+
const { count } = await tx.resourceGrant.deleteMany({
|
|
213
|
+
where: { resourceType: type, resourceId: resource.id, granteeKey },
|
|
214
|
+
});
|
|
215
|
+
// Agents whose owner can no longer use the server lose it now (C1).
|
|
216
|
+
const attachmentsRemoved = type === "mcp_server" ? await pruneUnusableMcpAttachments(tx, resource.id) : 0;
|
|
217
|
+
return { count, attachmentsRemoved };
|
|
192
218
|
});
|
|
193
|
-
return textResult({ revoked: count > 0 });
|
|
219
|
+
return textResult({ revoked: count > 0, ...(attachmentsRemoved > 0 ? { attachmentsRemoved } : {}) });
|
|
194
220
|
},
|
|
195
221
|
});
|
|
196
222
|
mcp.registerTool({
|
|
197
223
|
name: "list_access",
|
|
198
224
|
scope: "agents:read",
|
|
199
|
-
description: "Lists who an agent is shared with (grant metadata only). For its owner, the stdio operator, and admins (incident response).",
|
|
225
|
+
description: "Lists who an agent or MCP server is shared with (grant metadata only). For its owner, the stdio operator, and admins (incident response).",
|
|
200
226
|
inputSchema: {
|
|
201
227
|
type: "object",
|
|
202
228
|
additionalProperties: false,
|
|
@@ -206,16 +232,28 @@ export function registerGrantTools(mcp) {
|
|
|
206
232
|
handler: async (rawArgs, ctx) => {
|
|
207
233
|
const args = parse(ListSchema, "list_access", rawArgs);
|
|
208
234
|
const type = shareableType(args.resourceType);
|
|
209
|
-
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
235
|
+
let resource;
|
|
236
|
+
if (type === "mcp_server") {
|
|
237
|
+
const server = await ctx.db.mcpServer.findUnique({ where: { id: args.resourceId } });
|
|
238
|
+
// Owner, stdio operator or admin; anyone else sees a missing server.
|
|
239
|
+
if (!server || (!ctx.operator && !isAdmin(ctx) && server.ownerId !== ctx.principal.id)) {
|
|
240
|
+
throw new McpError(404, `MCP server "${args.resourceId}" not found.`);
|
|
241
|
+
}
|
|
242
|
+
resource = server;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
const agent = await ctx.db.agent.findUnique({ where: { id: args.resourceId } });
|
|
246
|
+
if (!agent)
|
|
215
247
|
throw agentNotFound(args.resourceId);
|
|
216
|
-
if (
|
|
217
|
-
|
|
248
|
+
if (!isAdmin(ctx)) {
|
|
249
|
+
const access = await agentAccess(ctx, agent);
|
|
250
|
+
if (!atLeast("agent", access, "read"))
|
|
251
|
+
throw agentNotFound(args.resourceId);
|
|
252
|
+
if (access !== "owner") {
|
|
253
|
+
throw new McpError(403, `Agent "${agent.id}": only its owner (or an admin) can list who it is shared with.`);
|
|
254
|
+
}
|
|
218
255
|
}
|
|
256
|
+
resource = agent;
|
|
219
257
|
}
|
|
220
258
|
const grants = await ctx.db.resourceGrant.findMany({
|
|
221
259
|
where: { resourceType: type, resourceId: resource.id },
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host providers over MCP (docs/host-providers.md). Everything here needs hosts:admin, a
|
|
3
|
+
* privileged scope honoured only with a role granting it (host-manager or admin; the stdio
|
|
4
|
+
* operator holds both): bound tools decide who has permission and who wrote a comment, and
|
|
5
|
+
* they run in the push and pull request flow. Results carry secret names, never values.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { codeProviderOf, normalizeHostedRepository } from "../../coding/code-host.js";
|
|
9
|
+
import { logger } from "../../core/logger.js";
|
|
10
|
+
import { approveHostProviderTools, deleteHostProvider, getHostProviderView, HostProviderError, registerHostProvider, RegisterHostProviderSchema, } from "../../providers/host-tools/registry.js";
|
|
11
|
+
import { hostToolFetch, loadProviderForCalls, selfTestHostProvider } from "../../providers/host-tools/self-test.js";
|
|
12
|
+
import { McpError } from "../errors.js";
|
|
13
|
+
import { textResult } from "./text-result.js";
|
|
14
|
+
const hostsLog = logger.child({ module: "mcp-host-providers" });
|
|
15
|
+
function audit(ctx, action, provider, extra = {}) {
|
|
16
|
+
hostsLog.info({ event: `hosts.provider.${action}`, provider, by: ctx.principal.id, ...extra }, "host provider changed");
|
|
17
|
+
}
|
|
18
|
+
function parse(schema, label, value) {
|
|
19
|
+
const result = schema.safeParse(value);
|
|
20
|
+
if (result.success)
|
|
21
|
+
return result.data;
|
|
22
|
+
const details = result.error.issues.map((i) => `${i.path.join(".") || "input"}: ${i.message}`).join("; ");
|
|
23
|
+
throw new McpError(400, `Invalid ${label}: ${details}`);
|
|
24
|
+
}
|
|
25
|
+
async function mapped(work) {
|
|
26
|
+
try {
|
|
27
|
+
return await work();
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
if (err instanceof HostProviderError) {
|
|
31
|
+
throw new McpError(err.code === "host_provider_not_found" ? 404 : 400, err.message);
|
|
32
|
+
}
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const ApproveSchema = z
|
|
37
|
+
.object({ provider: z.string().min(1).max(40), tools: z.array(z.string().min(1).max(100)).min(1) })
|
|
38
|
+
.strict();
|
|
39
|
+
const GetSchema = z.object({ provider: z.string().min(1).max(40), includeCode: z.boolean().optional() }).strict();
|
|
40
|
+
const TestSchema = z
|
|
41
|
+
.object({
|
|
42
|
+
provider: z.string().min(1).max(40),
|
|
43
|
+
repository: z.string().min(1).max(300),
|
|
44
|
+
pullRequest: z.number().int().positive().optional(),
|
|
45
|
+
headRef: z.string().min(1).max(255).optional(),
|
|
46
|
+
ref: z.string().min(1).max(255).optional(),
|
|
47
|
+
accountId: z
|
|
48
|
+
.string()
|
|
49
|
+
.regex(/^[A-Za-z0-9:{}_-]{1,128}$/)
|
|
50
|
+
.optional(),
|
|
51
|
+
})
|
|
52
|
+
.strict();
|
|
53
|
+
const DeleteSchema = z.object({ provider: z.string().min(1).max(40) }).strict();
|
|
54
|
+
export function registerHostProviderTools(mcp) {
|
|
55
|
+
mcp.registerTool({
|
|
56
|
+
name: "register_host_provider",
|
|
57
|
+
scope: "hosts:admin",
|
|
58
|
+
description: "Host managers (hosts:admin): register a code host implemented by your custom tools, or replace one. Each tool's code and schema are copied, and only that copy ever runs: update_tool changes nothing until approve_host_provider_tools. Credentials must be your brokered secrets, allowed on every API host and gitHost. Without permissionCredential, trustedAccounts decides who may start runs. No restart is needed today, because agents can't use a provider yet; a new provider will need one once they can.",
|
|
59
|
+
inputSchema: {
|
|
60
|
+
type: "object",
|
|
61
|
+
additionalProperties: false,
|
|
62
|
+
required: [
|
|
63
|
+
"provider",
|
|
64
|
+
"gitHost",
|
|
65
|
+
"apiHosts",
|
|
66
|
+
"gitUsername",
|
|
67
|
+
"pullRequestUrlTemplate",
|
|
68
|
+
"repositoryApiPath",
|
|
69
|
+
"webhook",
|
|
70
|
+
"credentials",
|
|
71
|
+
"tools",
|
|
72
|
+
],
|
|
73
|
+
properties: {
|
|
74
|
+
provider: { type: "string", description: "Prefix of its repository ids: <provider>:<workspace>/<repo>." },
|
|
75
|
+
gitHost: { type: "string" },
|
|
76
|
+
apiHosts: { type: "array", items: { type: "string" } },
|
|
77
|
+
gitUsername: { type: "string" },
|
|
78
|
+
pullRequestUrlTemplate: { type: "string", description: "https URL on gitHost with {repository} and {number}." },
|
|
79
|
+
repositoryApiPath: { type: "string", description: "API path of one repository, with {repository}." },
|
|
80
|
+
permissionApiPath: { type: "string", description: "Permission endpoint path, with {workspace} and {repo}." },
|
|
81
|
+
webhook: {
|
|
82
|
+
type: "object",
|
|
83
|
+
additionalProperties: false,
|
|
84
|
+
required: ["header", "algorithm", "deliveryHeader", "eventHeader", "secret"],
|
|
85
|
+
properties: {
|
|
86
|
+
header: { type: "string" },
|
|
87
|
+
algorithm: { type: "string", enum: ["sha256"] },
|
|
88
|
+
deliveryHeader: { type: "string" },
|
|
89
|
+
eventHeader: { type: "string" },
|
|
90
|
+
secret: { type: "string", description: "Name of your secret; not attached to any agent." },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
credentials: {
|
|
94
|
+
type: "array",
|
|
95
|
+
items: {
|
|
96
|
+
type: "object",
|
|
97
|
+
additionalProperties: false,
|
|
98
|
+
required: ["repositories", "secret"],
|
|
99
|
+
properties: {
|
|
100
|
+
repositories: { type: "string", description: '"workspace/repo" or "workspace/*".' },
|
|
101
|
+
secret: { type: "string" },
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
permissionCredential: { type: "string" },
|
|
106
|
+
trustedAccounts: { type: "array", items: { type: "string" } },
|
|
107
|
+
tools: {
|
|
108
|
+
type: "object",
|
|
109
|
+
additionalProperties: { type: "string" },
|
|
110
|
+
description: "Operation name to your tool's name.",
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
handler: async (rawArgs, ctx) => {
|
|
115
|
+
const input = parse(RegisterHostProviderSchema, "register_host_provider arguments", rawArgs);
|
|
116
|
+
const view = await mapped(() => registerHostProvider(ctx.db, ctx.providers.secrets, ctx.principal.id, input));
|
|
117
|
+
audit(ctx, "register", view.provider, { mode: view.mode, operations: view.operations.length });
|
|
118
|
+
return textResult({
|
|
119
|
+
...view,
|
|
120
|
+
note: view.mode === "permission"
|
|
121
|
+
? "Permission mode: once agents can use this provider, the host will decide who has access, and a failed lookup will refuse."
|
|
122
|
+
: "Allowlist mode: once agents can use this provider, only trustedAccounts will be able to start runs, at write level.",
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
mcp.registerTool({
|
|
127
|
+
name: "approve_host_provider_tools",
|
|
128
|
+
scope: "hosts:admin",
|
|
129
|
+
description: "Host managers (hosts:admin): copy the named operations' current tool code over their approved snapshots. Applies from the next call. Read the change first with get_host_provider and includeCode.",
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: "object",
|
|
132
|
+
additionalProperties: false,
|
|
133
|
+
required: ["provider", "tools"],
|
|
134
|
+
properties: {
|
|
135
|
+
provider: { type: "string" },
|
|
136
|
+
tools: { type: "array", items: { type: "string" }, description: "Operation names." },
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
handler: async (rawArgs, ctx) => {
|
|
140
|
+
const { provider, tools } = parse(ApproveSchema, "approve_host_provider_tools arguments", rawArgs);
|
|
141
|
+
const view = await mapped(() => approveHostProviderTools(ctx.db, ctx.principal.id, provider, tools));
|
|
142
|
+
audit(ctx, "approve", provider, { operations: tools });
|
|
143
|
+
return textResult(view);
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
mcp.registerTool({
|
|
147
|
+
name: "get_host_provider",
|
|
148
|
+
scope: "hosts:admin",
|
|
149
|
+
description: "Host managers (hosts:admin): a provider's config, active permission mode, secret names (never values), and which tools have unapproved changes. includeCode adds each approved snapshot's code and schema, and any newer code and schema, to review before approving.",
|
|
150
|
+
inputSchema: {
|
|
151
|
+
type: "object",
|
|
152
|
+
additionalProperties: false,
|
|
153
|
+
required: ["provider"],
|
|
154
|
+
properties: { provider: { type: "string" }, includeCode: { type: "boolean" } },
|
|
155
|
+
},
|
|
156
|
+
handler: async (rawArgs, ctx) => {
|
|
157
|
+
const { provider, includeCode } = parse(GetSchema, "get_host_provider arguments", rawArgs);
|
|
158
|
+
const view = await getHostProviderView(ctx.db, provider, { includeCode });
|
|
159
|
+
if (!view)
|
|
160
|
+
throw new McpError(404, `Host provider "${provider}" not found.`);
|
|
161
|
+
return textResult(view);
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
mcp.registerTool({
|
|
165
|
+
name: "delete_host_provider",
|
|
166
|
+
scope: "hosts:admin",
|
|
167
|
+
description: "Host managers (hosts:admin): remove a provider and its snapshots, freeing its secrets. Refused while an agent links to one of its repositories.",
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: "object",
|
|
170
|
+
additionalProperties: false,
|
|
171
|
+
required: ["provider"],
|
|
172
|
+
properties: { provider: { type: "string" } },
|
|
173
|
+
},
|
|
174
|
+
handler: async (rawArgs, ctx) => {
|
|
175
|
+
const { provider } = parse(DeleteSchema, "delete_host_provider arguments", rawArgs);
|
|
176
|
+
await mapped(() => deleteHostProvider(ctx.db, provider));
|
|
177
|
+
audit(ctx, "delete", provider);
|
|
178
|
+
return textResult({ deleted: provider });
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
mcp.registerTool({
|
|
182
|
+
name: "test_host_provider",
|
|
183
|
+
scope: "hosts:admin",
|
|
184
|
+
description: "Host managers (hosts:admin): run each approved read-only tool against one repository, with its real credential, and report pass, fail or skipped with a fixed code per operation. Changes nothing on the host: write operations are skipped, and a read-only tool can only send GET. Name a pullRequest, headRef, ref and accountId to reach the checks that need them. The report holds no response data, so it can be shared.",
|
|
185
|
+
inputSchema: {
|
|
186
|
+
type: "object",
|
|
187
|
+
additionalProperties: false,
|
|
188
|
+
required: ["provider", "repository"],
|
|
189
|
+
properties: {
|
|
190
|
+
provider: { type: "string" },
|
|
191
|
+
repository: { type: "string", description: "<provider>:<workspace>/<repo>" },
|
|
192
|
+
pullRequest: { type: "integer", minimum: 1 },
|
|
193
|
+
headRef: { type: "string", description: "A branch with an open pull request." },
|
|
194
|
+
ref: { type: "string", description: "A commit or branch. Defaults to the pull request's head." },
|
|
195
|
+
accountId: { type: "string", description: "A host account to look up, in permission mode." },
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
handler: async (rawArgs, ctx) => {
|
|
199
|
+
const { provider, ...input } = parse(TestSchema, "test_host_provider arguments", rawArgs);
|
|
200
|
+
const loaded = await loadProviderForCalls(ctx.db, provider);
|
|
201
|
+
if (!loaded)
|
|
202
|
+
throw new McpError(404, `Host provider "${provider}" not found.`);
|
|
203
|
+
try {
|
|
204
|
+
if (codeProviderOf(normalizeHostedRepository(input.repository)) !== provider)
|
|
205
|
+
throw new Error("mismatch");
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
throw new McpError(400, `repository must be ${provider}:<workspace>/<repo>`);
|
|
209
|
+
}
|
|
210
|
+
const report = await selfTestHostProvider(loaded, ctx.providers.secrets, input, hostToolFetch());
|
|
211
|
+
audit(ctx, "test", provider, { repository: report.repository });
|
|
212
|
+
return textResult(report);
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Prisma, type PrismaClient } from "#prisma";
|
|
2
|
+
import { MCP_SERVERS_OFF_MESSAGE } from "../../config/mcp-servers.js";
|
|
3
|
+
import type { WardbyMcpServer } from "../server.js";
|
|
4
|
+
export { MCP_SERVERS_OFF_MESSAGE };
|
|
5
|
+
/** get_agent's mcpServers field: each attachment with its exposed names and allowed tools gone from the snapshot. */
|
|
6
|
+
export declare function agentMcpServerViews(db: Pick<PrismaClient, "agentMcpServer">, agentId: string): Promise<{
|
|
7
|
+
serverId: string;
|
|
8
|
+
serverName: string;
|
|
9
|
+
allowedTools: string[];
|
|
10
|
+
exposedNames: string[];
|
|
11
|
+
callTimeoutMs: number | null;
|
|
12
|
+
missingTools: string[];
|
|
13
|
+
}[]>;
|
|
14
|
+
/** The update_agent warning when an agent becomes Codex with attached tools the server doesn't mark read-only. */
|
|
15
|
+
export declare function codexWriteToolsWarning(names: readonly string[]): string;
|
|
16
|
+
/**
|
|
17
|
+
* The exposed names of an agent's attached, allowed tools that the saved tool list doesn't mark read-only:
|
|
18
|
+
* a Codex run never gets them (loadMcpToolPins with readOnlyOnly). Tools no longer listed are left out.
|
|
19
|
+
*/
|
|
20
|
+
export declare function codexUnavailableMcpTools(db: Pick<PrismaClient, "agentMcpServer">, agentId: string): Promise<string[]>;
|
|
21
|
+
export declare function registerMcpServerTools(mcp: WardbyMcpServer): void;
|
|
22
|
+
/**
|
|
23
|
+
* 409 naming the first exposed name that clashes with a built-in, an attached tool or another MCP attachment.
|
|
24
|
+
* Exported for tests: no valid server and tool name maps to a built-in repository tool name.
|
|
25
|
+
*/
|
|
26
|
+
export declare function assertNoClash(tx: Prisma.TransactionClient, agentId: string, serverId: string, exposed: string[]): Promise<void>;
|