@sammorrowdrums/mcpi-ext 0.2.0 → 1.0.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.
Files changed (145) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -47
  3. package/dist/code-mode/eligibility.d.ts +35 -0
  4. package/dist/code-mode/eligibility.js +64 -0
  5. package/dist/code-mode/executor.d.ts +53 -0
  6. package/dist/code-mode/executor.js +185 -0
  7. package/dist/code-mode/index.d.ts +107 -0
  8. package/dist/code-mode/index.js +267 -0
  9. package/dist/code-mode/isolated-vm.d.ts +65 -0
  10. package/dist/code-mode/isolated-vm.js +116 -0
  11. package/dist/code-mode/tools.d.ts +48 -0
  12. package/dist/code-mode/tools.js +73 -0
  13. package/dist/code-mode/type-hints.d.ts +20 -0
  14. package/dist/code-mode/type-hints.js +205 -0
  15. package/dist/docker-e2e.d.ts +54 -0
  16. package/dist/docker-e2e.js +234 -0
  17. package/dist/index.d.ts +11 -0
  18. package/dist/index.js +227 -0
  19. package/dist/mcp/call-tool-result.d.ts +10 -0
  20. package/dist/mcp/call-tool-result.js +70 -0
  21. package/dist/mcp/client-factory.d.ts +59 -0
  22. package/dist/mcp/client-factory.js +76 -0
  23. package/dist/mcp/client-manager.d.ts +88 -0
  24. package/dist/mcp/client-manager.js +307 -0
  25. package/dist/mcp/config-loader.d.ts +7 -0
  26. package/dist/mcp/config-loader.js +42 -0
  27. package/dist/mcp/config.d.ts +56 -0
  28. package/dist/mcp/config.js +42 -0
  29. package/dist/mcp/gateway-defaults.d.ts +14 -0
  30. package/dist/mcp/gateway-defaults.js +21 -0
  31. package/dist/mcp/host-approval.d.ts +16 -0
  32. package/dist/mcp/host-approval.js +19 -0
  33. package/dist/mcp/host-elicitation.d.ts +19 -0
  34. package/dist/mcp/host-elicitation.js +94 -0
  35. package/dist/mcp/index.d.ts +8 -0
  36. package/dist/mcp/index.js +7 -0
  37. package/dist/mcp/policy.d.ts +314 -0
  38. package/dist/mcp/policy.js +848 -0
  39. package/dist/routing/facilities.d.ts +109 -0
  40. package/dist/routing/facilities.js +175 -0
  41. package/dist/routing/format.d.ts +24 -0
  42. package/dist/routing/format.js +64 -0
  43. package/dist/routing/index.d.ts +4 -0
  44. package/dist/routing/index.js +4 -0
  45. package/dist/routing/seam.d.ts +35 -0
  46. package/dist/routing/seam.js +28 -0
  47. package/dist/routing/tripwire.d.ts +42 -0
  48. package/dist/routing/tripwire.js +80 -0
  49. package/dist/skills/discover.d.ts +12 -0
  50. package/dist/skills/discover.js +85 -0
  51. package/dist/skills/format.d.ts +13 -0
  52. package/dist/skills/format.js +42 -0
  53. package/dist/skills/index.d.ts +6 -0
  54. package/dist/skills/index.js +6 -0
  55. package/dist/skills/load-skill-tool.d.ts +56 -0
  56. package/dist/skills/load-skill-tool.js +183 -0
  57. package/dist/skills/mcp-tool-proxy.d.ts +7 -0
  58. package/dist/skills/mcp-tool-proxy.js +41 -0
  59. package/dist/skills/sep2640/client.d.ts +91 -0
  60. package/dist/skills/sep2640/client.js +160 -0
  61. package/dist/skills/sep2640/discover.d.ts +33 -0
  62. package/dist/skills/sep2640/discover.js +82 -0
  63. package/dist/skills/sep2640/index.d.ts +13 -0
  64. package/dist/skills/sep2640/index.js +13 -0
  65. package/dist/skills/sep2640/integrity.d.ts +58 -0
  66. package/dist/skills/sep2640/integrity.js +185 -0
  67. package/dist/skills/sep2640/load.d.ts +61 -0
  68. package/dist/skills/sep2640/load.js +90 -0
  69. package/dist/skills/sep2640/protocol.d.ts +146 -0
  70. package/dist/skills/sep2640/protocol.js +214 -0
  71. package/dist/skills/sep2640/spec.d.ts +76 -0
  72. package/dist/skills/sep2640/spec.js +88 -0
  73. package/dist/skills/skill-registry.d.ts +85 -0
  74. package/dist/skills/skill-registry.js +85 -0
  75. package/dist/tool-cli/bridge.d.ts +14 -0
  76. package/dist/tool-cli/bridge.js +196 -0
  77. package/dist/tool-cli/format.d.ts +20 -0
  78. package/dist/tool-cli/format.js +73 -0
  79. package/dist/tool-cli/index.d.ts +7 -0
  80. package/dist/tool-cli/index.js +5 -0
  81. package/dist/tool-cli/provider.d.ts +21 -0
  82. package/dist/tool-cli/provider.js +114 -0
  83. package/dist/tool-cli/startup.d.ts +22 -0
  84. package/dist/tool-cli/startup.js +47 -0
  85. package/package.json +78 -26
  86. package/.dockerignore +0 -6
  87. package/.github/dependabot.yml +0 -25
  88. package/.github/workflows/ci.yml +0 -50
  89. package/.hadolint.yaml +0 -6
  90. package/.prettierignore +0 -4
  91. package/.prettierrc +0 -7
  92. package/AGENTS.md +0 -118
  93. package/DECISIONS.md +0 -99
  94. package/Dockerfile +0 -39
  95. package/aube-lock.yaml +0 -4146
  96. package/docs/code-mode.md +0 -38
  97. package/docs/skills.md +0 -57
  98. package/docs/tool-cli.md +0 -54
  99. package/eslint.config.js +0 -27
  100. package/images/banner.webp +0 -0
  101. package/images/code-c-maude.webp +0 -0
  102. package/images/code-mode-histogram.png +0 -0
  103. package/images/nuclear-mcp-football.webp +0 -0
  104. package/images/skills-enabling-mcp-tools.png +0 -0
  105. package/images/the-skill-dealer.webp +0 -0
  106. package/images/tool-cli-grep.png +0 -0
  107. package/mise.toml +0 -53
  108. package/scripts/docker-entrypoint.sh +0 -29
  109. package/scripts/test-echo-server.mjs +0 -28
  110. package/scripts/test-mcp-integration.mjs +0 -59
  111. package/scripts/test-tool-cli-smoke.mjs +0 -93
  112. package/src/code-mode/code-mode-integration.test.ts +0 -135
  113. package/src/code-mode/eligibility.test.ts +0 -60
  114. package/src/code-mode/eligibility.ts +0 -16
  115. package/src/code-mode/executor.test.ts +0 -168
  116. package/src/code-mode/executor.ts +0 -163
  117. package/src/code-mode/index.ts +0 -208
  118. package/src/code-mode/tools.ts +0 -110
  119. package/src/code-mode/type-hints.test.ts +0 -187
  120. package/src/code-mode/type-hints.ts +0 -249
  121. package/src/docker-e2e.test.ts +0 -195
  122. package/src/docker-e2e.ts +0 -277
  123. package/src/index.test.ts +0 -8
  124. package/src/index.ts +0 -179
  125. package/src/mcp/client-manager.test.ts +0 -250
  126. package/src/mcp/client-manager.ts +0 -199
  127. package/src/mcp/config-loader.test.ts +0 -115
  128. package/src/mcp/config-loader.ts +0 -46
  129. package/src/mcp/config.ts +0 -40
  130. package/src/mcp/index.ts +0 -4
  131. package/src/skills/discover.ts +0 -81
  132. package/src/skills/format.test.ts +0 -70
  133. package/src/skills/format.ts +0 -42
  134. package/src/skills/index.ts +0 -9
  135. package/src/skills/load-skill-tool.ts +0 -136
  136. package/src/skills/mcp-tool-proxy.ts +0 -143
  137. package/src/skills/skill-integration.test.ts +0 -139
  138. package/src/skills/skill-registry.test.ts +0 -83
  139. package/src/skills/skill-registry.ts +0 -69
  140. package/src/test-servers/weather-server.ts +0 -171
  141. package/src/test-servers/weather-stdio.ts +0 -11
  142. package/src/tool-cli/format.test.ts +0 -33
  143. package/src/tool-cli/format.ts +0 -56
  144. package/src/tool-cli/index.ts +0 -3
  145. package/tsconfig.json +0 -21
@@ -0,0 +1,16 @@
1
+ import type { ExtensionContext } from "@sammorrowdrums/mcpi";
2
+ import type { McpApprovalPrompt, McpApprovalRequest } from "./policy.js";
3
+ type ApprovalContext = Pick<ExtensionContext, "hasUI" | "ui">;
4
+ /**
5
+ * Bridges policy approval requests to mcpi's explicit user-facing confirmation.
6
+ *
7
+ * Mirrors {@link McpiHostElicitation}: without an interactive UI the request is
8
+ * reported as undecided rather than approved, so nothing is ever silently
9
+ * enabled.
10
+ */
11
+ export declare class McpiHostApproval implements McpApprovalPrompt {
12
+ private context;
13
+ setContext(context: ApprovalContext | undefined): void;
14
+ confirm(request: McpApprovalRequest): Promise<boolean | undefined>;
15
+ }
16
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Bridges policy approval requests to mcpi's explicit user-facing confirmation.
3
+ *
4
+ * Mirrors {@link McpiHostElicitation}: without an interactive UI the request is
5
+ * reported as undecided rather than approved, so nothing is ever silently
6
+ * enabled.
7
+ */
8
+ export class McpiHostApproval {
9
+ context;
10
+ setContext(context) {
11
+ this.context = context;
12
+ }
13
+ async confirm(request) {
14
+ const context = this.context;
15
+ if (!context?.hasUI)
16
+ return undefined;
17
+ return context.ui.confirm(request.title, request.message, request.signal ? { signal: request.signal } : undefined);
18
+ }
19
+ }
@@ -0,0 +1,19 @@
1
+ import type { ElicitRequestParams, ElicitResult } from "@modelcontextprotocol/client";
2
+ import type { ExtensionContext } from "@sammorrowdrums/mcpi";
3
+ import type { McpElicitationHandler } from "./client-factory.js";
4
+ type ElicitationContext = Pick<ExtensionContext, "hasUI" | "ui">;
5
+ export declare class McpHostElicitationError extends Error {
6
+ constructor(message: string);
7
+ }
8
+ /**
9
+ * Bridges MCP form elicitation to mcpi's explicit user-facing UI.
10
+ *
11
+ * URL elicitation is intentionally unsupported and is not advertised by the
12
+ * client. Form requests never receive an implicit approval.
13
+ */
14
+ export declare class McpiHostElicitation implements McpElicitationHandler {
15
+ private context;
16
+ setContext(context: ElicitationContext | undefined): void;
17
+ elicit(params: ElicitRequestParams): Promise<ElicitResult>;
18
+ }
19
+ export {};
@@ -0,0 +1,94 @@
1
+ export class McpHostElicitationError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "McpHostElicitationError";
5
+ }
6
+ }
7
+ /**
8
+ * Bridges MCP form elicitation to mcpi's explicit user-facing UI.
9
+ *
10
+ * URL elicitation is intentionally unsupported and is not advertised by the
11
+ * client. Form requests never receive an implicit approval.
12
+ */
13
+ export class McpiHostElicitation {
14
+ context;
15
+ setContext(context) {
16
+ this.context = context;
17
+ }
18
+ async elicit(params) {
19
+ if (params.mode === "url") {
20
+ throw new McpHostElicitationError(`MCP URL elicitation is unsupported. Open the requested URL manually only after reviewing it: ${params.url}`);
21
+ }
22
+ const context = this.context;
23
+ if (!context?.hasUI) {
24
+ throw new McpHostElicitationError("The MCP server requested user input, but mcpi has no interactive UI. Re-run this call in interactive mode; the request was not approved.");
25
+ }
26
+ const decision = await context.ui.select(params.message, [
27
+ "Provide input",
28
+ "Decline",
29
+ "Cancel",
30
+ ]);
31
+ if (decision === "Decline") {
32
+ return { action: "decline" };
33
+ }
34
+ if (decision !== "Provide input") {
35
+ return { action: "cancel" };
36
+ }
37
+ let draft = JSON.stringify(buildInitialContent(params), null, 2);
38
+ while (true) {
39
+ const edited = await context.ui.editor("MCP input required (JSON)", draft);
40
+ if (edited === undefined) {
41
+ return { action: "cancel" };
42
+ }
43
+ try {
44
+ const content = JSON.parse(edited);
45
+ if (!isElicitationContent(content)) {
46
+ context.ui.notify("MCP elicitation input must be a JSON object containing primitive values or string arrays.", "error");
47
+ draft = edited;
48
+ continue;
49
+ }
50
+ return { action: "accept", content };
51
+ }
52
+ catch {
53
+ context.ui.notify("MCP elicitation input must be valid JSON.", "error");
54
+ draft = edited;
55
+ }
56
+ }
57
+ }
58
+ }
59
+ function buildInitialContent(params) {
60
+ return Object.fromEntries(Object.entries(params.requestedSchema.properties).map(([name, definition]) => [
61
+ name,
62
+ initialValue(definition),
63
+ ]));
64
+ }
65
+ function initialValue(definition) {
66
+ if (definition.default !== undefined) {
67
+ return definition.default;
68
+ }
69
+ if (definition.type === "boolean") {
70
+ return false;
71
+ }
72
+ if (definition.type === "number" || definition.type === "integer") {
73
+ return definition.minimum ?? 0;
74
+ }
75
+ if (definition.type === "array") {
76
+ return [];
77
+ }
78
+ if ("enum" in definition) {
79
+ return definition.enum[0] ?? "";
80
+ }
81
+ if ("oneOf" in definition) {
82
+ return definition.oneOf[0]?.const ?? "";
83
+ }
84
+ return "";
85
+ }
86
+ function isElicitationContent(value) {
87
+ return (typeof value === "object" &&
88
+ value !== null &&
89
+ !Array.isArray(value) &&
90
+ Object.values(value).every((entry) => typeof entry === "string" ||
91
+ typeof entry === "number" ||
92
+ typeof entry === "boolean" ||
93
+ (Array.isArray(entry) && entry.every((item) => typeof item === "string"))));
94
+ }
@@ -0,0 +1,8 @@
1
+ export type { McpConfig, ServerConfig, StdioServerConfig, RemoteServerConfig } from "./config.js";
2
+ export { McpConfig as McpConfigSchema } from "./config.js";
3
+ export { loadMcpConfig } from "./config-loader.js";
4
+ export { isSkillsExtensionEnabled } from "./config.js";
5
+ export { McpClientManager, type McpTool } from "./client-manager.js";
6
+ export { McpPolicy, McpPolicyError, isReadOnlyToolCall, validateToolArguments, type McpApprovalOutcome, type McpApprovalPrompt, type McpApprovalRequest, type McpAuditRecord, type McpCallSource, type McpPolicyDenialReason, type McpPolicyGateway, type McpPolicyOptions, type McpPolicySkill, type McpResourceReadRequest, type McpResourceSource, type McpToolCallRequest, type SkillGrantOutcome, } from "./policy.js";
7
+ export { McpiHostApproval } from "./host-approval.js";
8
+ export { noSkillsExtensionGateway } from "./gateway-defaults.js";
@@ -0,0 +1,7 @@
1
+ export { McpConfig as McpConfigSchema } from "./config.js";
2
+ export { loadMcpConfig } from "./config-loader.js";
3
+ export { isSkillsExtensionEnabled } from "./config.js";
4
+ export { McpClientManager } from "./client-manager.js";
5
+ export { McpPolicy, McpPolicyError, isReadOnlyToolCall, validateToolArguments, } from "./policy.js";
6
+ export { McpiHostApproval } from "./host-approval.js";
7
+ export { noSkillsExtensionGateway } from "./gateway-defaults.js";
@@ -0,0 +1,314 @@
1
+ import type { ReadResourceResult, Resource, ResourceTemplateType } from "@modelcontextprotocol/client";
2
+ import type { DirectoryReadResult, SkillsGetResult, SkillsListResult } from "../skills/sep2640/protocol.js";
3
+ import type { TerminalCallToolResult } from "./call-tool-result.js";
4
+ import type { McpTool } from "./client-manager.js";
5
+ /**
6
+ * Which execution path asked for an MCP operation. Recorded on every audit
7
+ * record so a denial can be attributed to the surface that produced it.
8
+ */
9
+ export type McpCallSource = "proxy" | "code-mode" | "tool-cli";
10
+ /**
11
+ * Which skill workflow asked for a resource read. Resource authorization is
12
+ * narrower than tool authorization: only skill discovery and skill loading
13
+ * currently need it, and each is bound to a single server origin.
14
+ *
15
+ * `skills-extension` covers the draft SEP-2640 surface. It is a distinct source
16
+ * so a read authorized by a skill's declared `resources` set can never be
17
+ * confused with one authorized by the legacy `skill://…/SKILL.md` convention,
18
+ * and so the audit log says which contract produced each read.
19
+ */
20
+ export type McpResourceSource = "skill-discovery" | "skill-load" | "skills-extension" | "tool-cli";
21
+ export type McpPolicyDenialReason = "server_not_connected" | "tool_not_discovered" | "tool_gated" | "invalid_arguments" | "permission_denied" | "approval_declined" | "approval_unavailable" | "cancelled" | "resource_not_discovered" | "resource_origin_mismatch"
22
+ /** The server never declared the skills extension, so its methods are off-limits. */
23
+ | "extension_not_declared"
24
+ /** The server declared the extension but not `directoryRead`. */
25
+ | "directory_read_unavailable";
26
+ export interface McpPolicyErrorInit {
27
+ reason: McpPolicyDenialReason;
28
+ message: string;
29
+ source: McpCallSource | McpResourceSource;
30
+ serverName: string;
31
+ toolName?: string;
32
+ uri?: string;
33
+ alternatives?: readonly string[];
34
+ }
35
+ /** A denial raised by the policy boundary before any upstream MCP request. */
36
+ export declare class McpPolicyError extends Error {
37
+ readonly reason: McpPolicyDenialReason;
38
+ readonly source: McpCallSource | McpResourceSource;
39
+ readonly serverName: string;
40
+ readonly toolName?: string;
41
+ readonly uri?: string;
42
+ readonly alternatives: readonly string[];
43
+ constructor(init: McpPolicyErrorInit);
44
+ }
45
+ export type McpApprovalOutcome = "granted" | "reused" | "declined" | "unavailable";
46
+ export interface McpAuditRecord {
47
+ readonly id: string;
48
+ readonly source: McpCallSource | McpResourceSource;
49
+ readonly operation: "tool" | "resource" | "resource-list" | "resource-templates" | "skill-grant" | "skill-list" | "skill-get" | "skill-directory";
50
+ readonly serverName: string;
51
+ readonly toolName?: string;
52
+ readonly uri?: string;
53
+ readonly decision: "allowed" | "denied";
54
+ readonly reason?: McpPolicyDenialReason;
55
+ readonly approval?: McpApprovalOutcome;
56
+ readonly timestampMs: number;
57
+ }
58
+ /** Describes a pending confirmation so hosts can render an accurate prompt. */
59
+ export interface McpApprovalRequest {
60
+ readonly kind: "tool-call" | "skill-grant";
61
+ readonly source: McpCallSource;
62
+ readonly serverName: string;
63
+ readonly toolName?: string;
64
+ readonly skillName?: string;
65
+ readonly grantedTools?: readonly string[];
66
+ readonly title: string;
67
+ readonly message: string;
68
+ readonly signal?: AbortSignal;
69
+ }
70
+ /**
71
+ * Host confirmation seam. Returning `undefined` means no decision could be
72
+ * obtained (no interactive UI, or the prompt was cancelled); it is never
73
+ * treated as approval.
74
+ */
75
+ export interface McpApprovalPrompt {
76
+ confirm(request: McpApprovalRequest): Promise<boolean | undefined>;
77
+ }
78
+ /** The upstream surface the policy is allowed to dispatch to. */
79
+ export interface McpPolicyGateway {
80
+ getConnectedServers(): string[];
81
+ getToolsForServer(name: string): McpTool[];
82
+ callTool(serverName: string, toolName: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<TerminalCallToolResult>;
83
+ listResources(serverName: string, signal?: AbortSignal): Promise<Resource[]>;
84
+ listResourceTemplates(serverName: string, signal?: AbortSignal): Promise<ResourceTemplateType[]>;
85
+ readResource(serverName: string, uri: string, signal?: AbortSignal): Promise<ReadResourceResult>;
86
+ /**
87
+ * The settings a server declared for one MCP extension in its `initialize`
88
+ * response, or `undefined` when it did not declare that extension at all.
89
+ *
90
+ * Read fresh on every call rather than cached as a boolean: the policy asks
91
+ * this immediately before each extension request so a reconnect that drops
92
+ * the extension cannot leave a stale "enabled" flag behind.
93
+ */
94
+ getExtensionCapability(serverName: string, extensionName: string): Record<string, unknown> | undefined;
95
+ /** Draft SEP-2640 `skills/list`. */
96
+ requestSkillsList(serverName: string, cursor?: string, signal?: AbortSignal): Promise<SkillsListResult>;
97
+ /** Draft SEP-2640 `skills/get`. */
98
+ requestSkillsGet(serverName: string, uri: string, signal?: AbortSignal): Promise<SkillsGetResult>;
99
+ /** Draft SEP-2640 `resources/directory/read`. */
100
+ requestDirectoryRead(serverName: string, uri: string, cursor?: string, signal?: AbortSignal): Promise<DirectoryReadResult>;
101
+ }
102
+ export interface McpToolCallRequest {
103
+ source: McpCallSource;
104
+ serverName: string;
105
+ toolName: string;
106
+ args: Record<string, unknown>;
107
+ signal?: AbortSignal;
108
+ }
109
+ export interface McpResourceReadRequest {
110
+ source: McpResourceSource;
111
+ serverName: string;
112
+ uri: string;
113
+ /**
114
+ * Required for `skills-extension` reads: which skill's declared `resources`
115
+ * set authorizes this URI. Naming the skill is what keeps the allowlist
116
+ * per-skill instead of pooling every declared file on the server.
117
+ */
118
+ skillUri?: string;
119
+ signal?: AbortSignal;
120
+ }
121
+ export interface McpResourceListRequest {
122
+ source: "tool-cli";
123
+ serverName: string;
124
+ signal?: AbortSignal;
125
+ }
126
+ /** Minimum skill shape the policy needs; mirrors `McpSkillMetadata`. */
127
+ export interface McpPolicySkill {
128
+ readonly name: string;
129
+ readonly uri: string;
130
+ readonly serverName: string;
131
+ readonly allowedTools: readonly string[];
132
+ /**
133
+ * A digest over the exact content this grant covers, when the origin can
134
+ * supply one (SEP-2640 servers can; the legacy `skill://` convention cannot).
135
+ *
136
+ * Folding it into the grant key is what makes an approval content-bound: if a
137
+ * later listing rotates a digest, adds a file, or drops one, the key changes,
138
+ * the prior approval no longer matches, and the user is asked again rather
139
+ * than having yesterday's consent silently applied to today's bytes.
140
+ */
141
+ readonly contentFingerprint?: string;
142
+ }
143
+ export type SkillGrantOutcome = {
144
+ readonly status: "granted";
145
+ readonly activatedTools: readonly string[];
146
+ } | {
147
+ readonly status: "reused";
148
+ readonly activatedTools: readonly string[];
149
+ } | {
150
+ readonly status: "declined";
151
+ readonly message: string;
152
+ } | {
153
+ readonly status: "unavailable";
154
+ readonly message: string;
155
+ };
156
+ export interface McpPolicyOptions {
157
+ gateway: McpPolicyGateway;
158
+ approvals?: McpApprovalPrompt;
159
+ /** Bounded audit ring buffer size. */
160
+ auditLimit?: number;
161
+ onAudit?: (record: McpAuditRecord) => void;
162
+ }
163
+ /**
164
+ * The single MCP policy and dispatch boundary.
165
+ *
166
+ * Every execution path — deferred/direct proxy tools, Code Mode dispatch,
167
+ * tool-cli provider RPC, and skill resource reads — crosses this class exactly
168
+ * once per operation. Discovery, gating, argument validation, permission
169
+ * semantics, approval, cancellation, and audit all happen here, and nothing
170
+ * reaches an MCP server without passing through `dispatchToolCall` /
171
+ * `dispatchResourceRead` below.
172
+ */
173
+ export declare class McpPolicy {
174
+ private readonly gateway;
175
+ private readonly approvals;
176
+ private readonly auditLimit;
177
+ private readonly onAudit;
178
+ /** Tools that require an approved skill grant before any path may call them. */
179
+ private readonly gatedTools;
180
+ /** Tools unlocked by an approved, activated skill grant. */
181
+ private readonly enabledTools;
182
+ /** Approved skill grant keys, bound to server origin plus grant content. */
183
+ private readonly approvedGrants;
184
+ /** Skill URIs registered per server origin, for `skill-load` authorization. */
185
+ private readonly skillUrisByServer;
186
+ /** URIs a server itself listed in the current discovery pass. */
187
+ private readonly discoveredUrisByServer;
188
+ /**
189
+ * Per-skill SEP-2640 resource allowlists, keyed by `serverName\0skillUri`.
190
+ *
191
+ * A skill's declared `resources` set is the *only* thing that authorizes a
192
+ * `skills-extension` read. Storing it per skill rather than per server means a
193
+ * file listed by skill A cannot be read while "loading" skill B.
194
+ */
195
+ private readonly extensionResourceUris;
196
+ private readonly audit;
197
+ private sequence;
198
+ constructor(options: McpPolicyOptions);
199
+ /** Register discovered skills so their tools are gated and their URIs bound. */
200
+ registerSkills(skills: readonly McpPolicySkill[]): void;
201
+ /**
202
+ * Bind a SEP-2640 skill's declared resource URIs so they become readable
203
+ * under the `skills-extension` source — and nothing else does.
204
+ *
205
+ * The set replaces any previous one for that skill, so a rotated listing
206
+ * narrows access immediately instead of accumulating stale grants.
207
+ */
208
+ registerSkillResources(serverName: string, skillUri: string, resourceUris: readonly string[]): void;
209
+ /** Every tool name currently gated behind an unapproved skill grant. */
210
+ getGatedToolNames(): string[];
211
+ /** Skills that gate `toolName`, for actionable block messages. */
212
+ getGatingSkills(toolName: string): string[];
213
+ /** True when a tool is discovered but still requires an approved skill grant. */
214
+ isGated(toolName: string): boolean;
215
+ reset(): void;
216
+ getVisibleServers(): string[];
217
+ /**
218
+ * The exact discovered schema set a discovery surface may see. Gated tools
219
+ * are omitted entirely, so a surface cannot learn a hidden tool's name or
220
+ * schema and then try to call it.
221
+ */
222
+ getVisibleTools(serverName: string): McpTool[];
223
+ /**
224
+ * Activate an MCP-origin skill's `allowed-tools` grant.
225
+ *
226
+ * The grant is origin- and content-bound: the key covers the server name, the
227
+ * skill URI, and a digest of the exact tool list being granted. A previously
228
+ * approved identical grant is reused without re-prompting, so no path ever
229
+ * asks the user twice for the same authority. Declined, cancelled, and
230
+ * unavailable outcomes all leave the tools gated.
231
+ */
232
+ activateSkillGrant(skill: McpPolicySkill, signal?: AbortSignal): Promise<SkillGrantOutcome>;
233
+ /**
234
+ * Authorize and dispatch a tool call. Every execution path funnels here, and
235
+ * upstream is reached only after all checks below pass.
236
+ */
237
+ callTool(request: McpToolCallRequest): Promise<TerminalCallToolResult>;
238
+ /**
239
+ * The server's declared settings for the draft skills extension, or
240
+ * `undefined` when it did not declare it.
241
+ *
242
+ * Resolved fresh on every call. The extension is negotiated in `initialize`,
243
+ * but a reconnect can change the answer, and caching "this server supports
244
+ * skills" would let a stale yes outlive the negotiation that produced it.
245
+ */
246
+ getSkillsExtension(serverName: string): Record<string, unknown> | undefined;
247
+ /** True when the server declared `directoryRead: true` on the extension. */
248
+ supportsSkillDirectoryRead(serverName: string): boolean;
249
+ /**
250
+ * Draft SEP-2640 `skills/list`.
251
+ *
252
+ * Refused unless the server declared the extension: an undeclared method is
253
+ * not a method this host is entitled to probe for.
254
+ */
255
+ listMcpSkills(serverName: string, cursor?: string, signal?: AbortSignal): Promise<SkillsListResult>;
256
+ /** Draft SEP-2640 `skills/get`. */
257
+ getMcpSkill(serverName: string, uri: string, signal?: AbortSignal): Promise<SkillsGetResult>;
258
+ /**
259
+ * Draft SEP-2640 `resources/directory/read`.
260
+ *
261
+ * Refused when the server declared the extension without `directoryRead`.
262
+ * The setting defaults to false, so silence means no.
263
+ */
264
+ readSkillDirectory(serverName: string, uri: string, cursor?: string, signal?: AbortSignal): Promise<DirectoryReadResult>;
265
+ /**
266
+ * List the resources visible to tool-cli.
267
+ *
268
+ * `skill://` is a separate authorization domain: those resources carry
269
+ * workflow instructions and grants, so they remain reachable only through
270
+ * skill discovery/load and can never be enumerated through the shell bridge.
271
+ */
272
+ listResources(request: McpResourceListRequest): Promise<Resource[]>;
273
+ /** List policy-visible resource templates for tool-cli. */
274
+ listResourceTemplates(request: McpResourceListRequest): Promise<ResourceTemplateType[]>;
275
+ /**
276
+ * List a server's skill resources and remember them as this pass's discovery
277
+ * candidates. Only URIs seen here become readable under `skill-discovery`.
278
+ */
279
+ listSkillResources(serverName: string, signal?: AbortSignal): Promise<Resource[]>;
280
+ /**
281
+ * Authorize and dispatch a resource read using the same policy discipline as
282
+ * tool calls: origin binding, discovery, cancellation, and audit.
283
+ */
284
+ readResource(request: McpResourceReadRequest): Promise<ReadResourceResult>;
285
+ getAuditLog(): readonly McpAuditRecord[];
286
+ private enableTools;
287
+ private allowedReadUris;
288
+ private isSkillOwnedResource;
289
+ private findOtherOrigin;
290
+ /**
291
+ * Refuse an extension method unless the server is connected and declared the
292
+ * extension. Records the denial before throwing, like every other refusal.
293
+ */
294
+ private assertSkillsExtension;
295
+ private denyExtension;
296
+ private requestApproval;
297
+ private record;
298
+ }
299
+ /**
300
+ * A tool call is treated as read-only only when the server explicitly declares
301
+ * it read-only and does not also declare it destructive. This is the same
302
+ * predicate Code Mode uses for dispatch eligibility.
303
+ */
304
+ export declare function isReadOnlyToolCall(tool: McpTool): boolean;
305
+ /**
306
+ * Conservative, dependency-free structural validation of tool arguments.
307
+ *
308
+ * It rejects only what a server's own declared schema unambiguously forbids —
309
+ * missing required properties, undeclared properties under
310
+ * `additionalProperties: false`, and clearly mismatched primitive types — so
311
+ * schemas using `$ref`, `allOf`, or other composition keywords are not
312
+ * incorrectly refused. Returns a message when invalid, otherwise `undefined`.
313
+ */
314
+ export declare function validateToolArguments(schema: McpTool["inputSchema"] | undefined, args: unknown): string | undefined;