multicorn-shield 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/dist/multicorn-proxy.js +385 -470
- package/dist/multicorn-shield.js +118 -0
- package/dist/openclaw-hook/handler.js +2 -2
- package/dist/openclaw-plugin/multicorn-shield.js +2 -2
- package/dist/proxy.cjs +457 -0
- package/dist/proxy.d.cts +235 -0
- package/dist/proxy.d.ts +235 -0
- package/dist/proxy.js +438 -0
- package/dist/shield-extension.js +23858 -0
- package/package.json +25 -6
package/dist/proxy.d.cts
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { Writable } from 'node:stream';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* JSON-RPC message parsing and tool call interception for the MCP proxy.
|
|
5
|
+
*
|
|
6
|
+
* Handles the MCP stdio transport (newline-delimited JSON-RPC 2.0). Provides
|
|
7
|
+
* types, parsers, and response builders used by the proxy to intercept
|
|
8
|
+
* `tools/call` requests before they reach the wrapped MCP server.
|
|
9
|
+
*
|
|
10
|
+
* @module proxy/interceptor
|
|
11
|
+
*/
|
|
12
|
+
interface JsonRpcRequest {
|
|
13
|
+
readonly jsonrpc: "2.0";
|
|
14
|
+
readonly id: string | number | null;
|
|
15
|
+
readonly method: string;
|
|
16
|
+
readonly params?: unknown;
|
|
17
|
+
}
|
|
18
|
+
interface JsonRpcResponse {
|
|
19
|
+
readonly jsonrpc: "2.0";
|
|
20
|
+
readonly id: string | number | null;
|
|
21
|
+
readonly result?: unknown;
|
|
22
|
+
readonly error?: JsonRpcError;
|
|
23
|
+
}
|
|
24
|
+
interface JsonRpcError {
|
|
25
|
+
readonly code: number;
|
|
26
|
+
readonly message: string;
|
|
27
|
+
}
|
|
28
|
+
interface ToolCallParams {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly arguments: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
declare function parseJsonRpcLine(line: string): JsonRpcRequest | null;
|
|
33
|
+
declare function extractToolCallParams(request: JsonRpcRequest): ToolCallParams | null;
|
|
34
|
+
declare function buildBlockedResponse(id: string | number | null, service: string, permissionLevel: string, dashboardUrl: string): JsonRpcResponse;
|
|
35
|
+
declare function buildSpendingBlockedResponse(id: string | number | null, reason: string, dashboardUrl: string): JsonRpcResponse;
|
|
36
|
+
/**
|
|
37
|
+
* Internal error: Shield could not verify permissions due to an exception.
|
|
38
|
+
* Use when the proxy hits an unhandled error (fail-closed).
|
|
39
|
+
*/
|
|
40
|
+
declare function buildInternalErrorResponse(id: string | number | null): JsonRpcResponse;
|
|
41
|
+
/**
|
|
42
|
+
* Service unreachable: Shield could not verify permissions (DNS, network, timeout).
|
|
43
|
+
* Distinct code (-32003) so callers can tell apart from permission-denied (-32000).
|
|
44
|
+
*/
|
|
45
|
+
declare function buildServiceUnreachableResponse(id: string | number | null, dashboardUrl: string): JsonRpcResponse;
|
|
46
|
+
/**
|
|
47
|
+
* API key invalid or revoked (401/403 from service).
|
|
48
|
+
* Distinct code (-32004) so callers can tell apart from permission-denied (-32000).
|
|
49
|
+
*/
|
|
50
|
+
declare function buildAuthErrorResponse(id: string | number | null): JsonRpcResponse;
|
|
51
|
+
declare function extractServiceFromToolName(toolName: string): string;
|
|
52
|
+
declare function extractActionFromToolName(toolName: string): string;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Permission levels that can be granted on a service scope.
|
|
56
|
+
*
|
|
57
|
+
* - `read`: observe data without modification
|
|
58
|
+
* - `write`: create or modify data
|
|
59
|
+
* - `execute`: trigger side-effects (e.g. send an email, make a payment)
|
|
60
|
+
* - `publish`: make existing content publicly accessible (e.g. deploy, publish, make live)
|
|
61
|
+
* - `create`: create new content that is immediately public (e.g. tweet, public commit, forum post)
|
|
62
|
+
*/
|
|
63
|
+
declare const PERMISSION_LEVELS: {
|
|
64
|
+
readonly Read: "read";
|
|
65
|
+
readonly Write: "write";
|
|
66
|
+
readonly Execute: "execute";
|
|
67
|
+
readonly Publish: "publish";
|
|
68
|
+
readonly Create: "create";
|
|
69
|
+
};
|
|
70
|
+
type PermissionLevel = (typeof PERMISSION_LEVELS)[keyof typeof PERMISSION_LEVELS];
|
|
71
|
+
/**
|
|
72
|
+
* A single permission scope binding a service to an access level.
|
|
73
|
+
*
|
|
74
|
+
* For example, `{ service: "gmail", permissionLevel: "write" }` grants
|
|
75
|
+
* write access to the Gmail integration.
|
|
76
|
+
*/
|
|
77
|
+
interface Scope {
|
|
78
|
+
readonly service: string;
|
|
79
|
+
readonly permissionLevel: PermissionLevel;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Structured JSON logger for the MCP proxy.
|
|
84
|
+
*
|
|
85
|
+
* Writes to stderr only. Stdout is reserved for the MCP JSON-RPC transport.
|
|
86
|
+
* Log output is newline-delimited JSON so it can be parsed by log aggregators.
|
|
87
|
+
*
|
|
88
|
+
* @module proxy/logger
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
declare const LOG_LEVELS: {
|
|
92
|
+
readonly debug: 0;
|
|
93
|
+
readonly info: 1;
|
|
94
|
+
readonly warn: 2;
|
|
95
|
+
readonly error: 3;
|
|
96
|
+
};
|
|
97
|
+
type LogLevel = keyof typeof LOG_LEVELS;
|
|
98
|
+
interface ProxyLogger {
|
|
99
|
+
debug(msg: string, data?: Record<string, unknown>): void;
|
|
100
|
+
info(msg: string, data?: Record<string, unknown>): void;
|
|
101
|
+
warn(msg: string, data?: Record<string, unknown>): void;
|
|
102
|
+
error(msg: string, data?: Record<string, unknown>): void;
|
|
103
|
+
}
|
|
104
|
+
declare function createLogger(level: LogLevel, output?: Writable): ProxyLogger;
|
|
105
|
+
declare function isValidLogLevel(value: unknown): value is LogLevel;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Agent registration, scope fetching, consent flow, and scope caching.
|
|
109
|
+
*
|
|
110
|
+
* Handles the full lifecycle of connecting a proxy to the Multicorn service:
|
|
111
|
+
* finding or creating an agent record, fetching its granted scopes, and
|
|
112
|
+
* triggering the browser consent flow when the agent has no scopes yet.
|
|
113
|
+
*
|
|
114
|
+
* Scopes are cached in `~/.multicorn/scopes.json` for offline resilience
|
|
115
|
+
* and refreshed from the service every 60 seconds while the proxy runs.
|
|
116
|
+
* Cache is account-aware (keyed by agent name + API key).
|
|
117
|
+
*
|
|
118
|
+
* @module proxy/consent
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Derives the dashboard URL from the API base URL.
|
|
123
|
+
* - http://localhost:8080 -> http://localhost:5173
|
|
124
|
+
* - https://api.multicorn.ai -> https://app.multicorn.ai
|
|
125
|
+
* - Other URLs: replace "api" with "app" in the hostname, or use default
|
|
126
|
+
*/
|
|
127
|
+
declare function deriveDashboardUrl(baseUrl: string): string;
|
|
128
|
+
/**
|
|
129
|
+
* Thrown when the Shield API returns 401 or 403 (API key invalid or revoked).
|
|
130
|
+
* Used so resolveAgentRecord can detect auth failure without ad-hoc property casts.
|
|
131
|
+
*/
|
|
132
|
+
declare class ShieldAuthError extends Error {
|
|
133
|
+
constructor(message: string);
|
|
134
|
+
}
|
|
135
|
+
interface AgentRecord {
|
|
136
|
+
readonly id: string;
|
|
137
|
+
readonly name: string;
|
|
138
|
+
readonly scopes: readonly Scope[];
|
|
139
|
+
/** Set when the service returned 401/403; proxy must block with auth error message. */
|
|
140
|
+
readonly authInvalid?: boolean;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
declare function findAgentByName(agentName: string, apiKey: string, baseUrl: string): Promise<AgentRecord | null>;
|
|
144
|
+
declare function registerAgent(agentName: string, apiKey: string, baseUrl: string, platform?: string): Promise<string>;
|
|
145
|
+
declare function fetchGrantedScopes(agentId: string, apiKey: string, baseUrl: string): Promise<readonly Scope[]>;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Scope validation engine.
|
|
149
|
+
*
|
|
150
|
+
* Given a set of granted scopes and a requested action, determines whether
|
|
151
|
+
* the action is permitted. Returns a structured result with a human-readable
|
|
152
|
+
* reason when access is denied. No silent failures, no implicit grants.
|
|
153
|
+
*
|
|
154
|
+
* **Design principle (Jordan persona):** Every permission must be explicitly
|
|
155
|
+
* granted. `read` does **not** imply `write`; `write` does **not** imply
|
|
156
|
+
* `execute`. There is no wildcard or superuser bypass.
|
|
157
|
+
*
|
|
158
|
+
* @module scopes/scope-validator
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The outcome of a scope validation check.
|
|
163
|
+
*
|
|
164
|
+
* - When `allowed` is `true`, no `reason` is present.
|
|
165
|
+
* - When `allowed` is `false`, `reason` contains a descriptive explanation
|
|
166
|
+
* of why the action was denied.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* const result = validateScopeAccess(granted, requested);
|
|
171
|
+
* if (!result.allowed) {
|
|
172
|
+
* console.warn(`Denied: ${result.reason}`);
|
|
173
|
+
* }
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
interface ValidationResult {
|
|
177
|
+
/** Whether the requested action is permitted by the granted scopes. */
|
|
178
|
+
readonly allowed: boolean;
|
|
179
|
+
/**
|
|
180
|
+
* Human-readable explanation of why the action was denied.
|
|
181
|
+
* Only present when `allowed` is `false`.
|
|
182
|
+
*/
|
|
183
|
+
readonly reason?: string;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Check whether a single requested scope is covered by the granted set.
|
|
187
|
+
*
|
|
188
|
+
* Matching is **exact**: the granted set must contain an entry with the
|
|
189
|
+
* same `service` **and** `permissionLevel`. No implicit escalation is
|
|
190
|
+
* performed (e.g. `write` does not subsume `read`).
|
|
191
|
+
*
|
|
192
|
+
* @param grantedScopes - The scopes the agent has been granted via consent.
|
|
193
|
+
* @param requested - The scope required by the action the agent wants to perform.
|
|
194
|
+
* @returns A {@link ValidationResult} indicating whether access is allowed.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* const granted = [
|
|
199
|
+
* { service: "gmail", permissionLevel: "read" },
|
|
200
|
+
* { service: "gmail", permissionLevel: "write" },
|
|
201
|
+
* ];
|
|
202
|
+
*
|
|
203
|
+
* validateScopeAccess(granted, { service: "gmail", permissionLevel: "read" });
|
|
204
|
+
* // → { allowed: true }
|
|
205
|
+
*
|
|
206
|
+
* validateScopeAccess(granted, { service: "gmail", permissionLevel: "execute" });
|
|
207
|
+
* // → { allowed: false, reason: "Permission \"execute\" is not granted …" }
|
|
208
|
+
*
|
|
209
|
+
* validateScopeAccess(granted, { service: "slack", permissionLevel: "read" });
|
|
210
|
+
* // → { allowed: false, reason: "No permissions granted for service \"slack\" …" }
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
declare function validateScopeAccess(grantedScopes: readonly Scope[], requested: Scope): ValidationResult;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Maps MCP tool names (stdio servers, Claude Desktop) to Shield service and permission level.
|
|
217
|
+
*
|
|
218
|
+
* Uses explicit tables for common MCP servers (filesystem, terminal, browser) and the same
|
|
219
|
+
* integration-style prefix rules as OpenClaw's tool-mapper for names like `gmail_send_email`.
|
|
220
|
+
*
|
|
221
|
+
* @module mcp-tool-mapper
|
|
222
|
+
*/
|
|
223
|
+
|
|
224
|
+
interface McpToolScopeMapping {
|
|
225
|
+
readonly service: string;
|
|
226
|
+
readonly permissionLevel: PermissionLevel;
|
|
227
|
+
/** Original tool name for audit logs. */
|
|
228
|
+
readonly actionType: string;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Maps an MCP `tools/call` tool name to Shield `service` + `permissionLevel` for scope checks.
|
|
232
|
+
*/
|
|
233
|
+
declare function mapMcpToolToScope(toolName: string): McpToolScopeMapping;
|
|
234
|
+
|
|
235
|
+
export { type AgentRecord, type JsonRpcError, type JsonRpcRequest, type JsonRpcResponse, type LogLevel, type ProxyLogger, ShieldAuthError, type ToolCallParams, buildAuthErrorResponse, buildBlockedResponse, buildInternalErrorResponse, buildServiceUnreachableResponse, buildSpendingBlockedResponse, createLogger, deriveDashboardUrl, extractActionFromToolName, extractServiceFromToolName, extractToolCallParams, fetchGrantedScopes, findAgentByName, isValidLogLevel, mapMcpToolToScope, parseJsonRpcLine, registerAgent, validateScopeAccess };
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { Writable } from 'node:stream';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* JSON-RPC message parsing and tool call interception for the MCP proxy.
|
|
5
|
+
*
|
|
6
|
+
* Handles the MCP stdio transport (newline-delimited JSON-RPC 2.0). Provides
|
|
7
|
+
* types, parsers, and response builders used by the proxy to intercept
|
|
8
|
+
* `tools/call` requests before they reach the wrapped MCP server.
|
|
9
|
+
*
|
|
10
|
+
* @module proxy/interceptor
|
|
11
|
+
*/
|
|
12
|
+
interface JsonRpcRequest {
|
|
13
|
+
readonly jsonrpc: "2.0";
|
|
14
|
+
readonly id: string | number | null;
|
|
15
|
+
readonly method: string;
|
|
16
|
+
readonly params?: unknown;
|
|
17
|
+
}
|
|
18
|
+
interface JsonRpcResponse {
|
|
19
|
+
readonly jsonrpc: "2.0";
|
|
20
|
+
readonly id: string | number | null;
|
|
21
|
+
readonly result?: unknown;
|
|
22
|
+
readonly error?: JsonRpcError;
|
|
23
|
+
}
|
|
24
|
+
interface JsonRpcError {
|
|
25
|
+
readonly code: number;
|
|
26
|
+
readonly message: string;
|
|
27
|
+
}
|
|
28
|
+
interface ToolCallParams {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly arguments: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
declare function parseJsonRpcLine(line: string): JsonRpcRequest | null;
|
|
33
|
+
declare function extractToolCallParams(request: JsonRpcRequest): ToolCallParams | null;
|
|
34
|
+
declare function buildBlockedResponse(id: string | number | null, service: string, permissionLevel: string, dashboardUrl: string): JsonRpcResponse;
|
|
35
|
+
declare function buildSpendingBlockedResponse(id: string | number | null, reason: string, dashboardUrl: string): JsonRpcResponse;
|
|
36
|
+
/**
|
|
37
|
+
* Internal error: Shield could not verify permissions due to an exception.
|
|
38
|
+
* Use when the proxy hits an unhandled error (fail-closed).
|
|
39
|
+
*/
|
|
40
|
+
declare function buildInternalErrorResponse(id: string | number | null): JsonRpcResponse;
|
|
41
|
+
/**
|
|
42
|
+
* Service unreachable: Shield could not verify permissions (DNS, network, timeout).
|
|
43
|
+
* Distinct code (-32003) so callers can tell apart from permission-denied (-32000).
|
|
44
|
+
*/
|
|
45
|
+
declare function buildServiceUnreachableResponse(id: string | number | null, dashboardUrl: string): JsonRpcResponse;
|
|
46
|
+
/**
|
|
47
|
+
* API key invalid or revoked (401/403 from service).
|
|
48
|
+
* Distinct code (-32004) so callers can tell apart from permission-denied (-32000).
|
|
49
|
+
*/
|
|
50
|
+
declare function buildAuthErrorResponse(id: string | number | null): JsonRpcResponse;
|
|
51
|
+
declare function extractServiceFromToolName(toolName: string): string;
|
|
52
|
+
declare function extractActionFromToolName(toolName: string): string;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Permission levels that can be granted on a service scope.
|
|
56
|
+
*
|
|
57
|
+
* - `read`: observe data without modification
|
|
58
|
+
* - `write`: create or modify data
|
|
59
|
+
* - `execute`: trigger side-effects (e.g. send an email, make a payment)
|
|
60
|
+
* - `publish`: make existing content publicly accessible (e.g. deploy, publish, make live)
|
|
61
|
+
* - `create`: create new content that is immediately public (e.g. tweet, public commit, forum post)
|
|
62
|
+
*/
|
|
63
|
+
declare const PERMISSION_LEVELS: {
|
|
64
|
+
readonly Read: "read";
|
|
65
|
+
readonly Write: "write";
|
|
66
|
+
readonly Execute: "execute";
|
|
67
|
+
readonly Publish: "publish";
|
|
68
|
+
readonly Create: "create";
|
|
69
|
+
};
|
|
70
|
+
type PermissionLevel = (typeof PERMISSION_LEVELS)[keyof typeof PERMISSION_LEVELS];
|
|
71
|
+
/**
|
|
72
|
+
* A single permission scope binding a service to an access level.
|
|
73
|
+
*
|
|
74
|
+
* For example, `{ service: "gmail", permissionLevel: "write" }` grants
|
|
75
|
+
* write access to the Gmail integration.
|
|
76
|
+
*/
|
|
77
|
+
interface Scope {
|
|
78
|
+
readonly service: string;
|
|
79
|
+
readonly permissionLevel: PermissionLevel;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Structured JSON logger for the MCP proxy.
|
|
84
|
+
*
|
|
85
|
+
* Writes to stderr only. Stdout is reserved for the MCP JSON-RPC transport.
|
|
86
|
+
* Log output is newline-delimited JSON so it can be parsed by log aggregators.
|
|
87
|
+
*
|
|
88
|
+
* @module proxy/logger
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
declare const LOG_LEVELS: {
|
|
92
|
+
readonly debug: 0;
|
|
93
|
+
readonly info: 1;
|
|
94
|
+
readonly warn: 2;
|
|
95
|
+
readonly error: 3;
|
|
96
|
+
};
|
|
97
|
+
type LogLevel = keyof typeof LOG_LEVELS;
|
|
98
|
+
interface ProxyLogger {
|
|
99
|
+
debug(msg: string, data?: Record<string, unknown>): void;
|
|
100
|
+
info(msg: string, data?: Record<string, unknown>): void;
|
|
101
|
+
warn(msg: string, data?: Record<string, unknown>): void;
|
|
102
|
+
error(msg: string, data?: Record<string, unknown>): void;
|
|
103
|
+
}
|
|
104
|
+
declare function createLogger(level: LogLevel, output?: Writable): ProxyLogger;
|
|
105
|
+
declare function isValidLogLevel(value: unknown): value is LogLevel;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Agent registration, scope fetching, consent flow, and scope caching.
|
|
109
|
+
*
|
|
110
|
+
* Handles the full lifecycle of connecting a proxy to the Multicorn service:
|
|
111
|
+
* finding or creating an agent record, fetching its granted scopes, and
|
|
112
|
+
* triggering the browser consent flow when the agent has no scopes yet.
|
|
113
|
+
*
|
|
114
|
+
* Scopes are cached in `~/.multicorn/scopes.json` for offline resilience
|
|
115
|
+
* and refreshed from the service every 60 seconds while the proxy runs.
|
|
116
|
+
* Cache is account-aware (keyed by agent name + API key).
|
|
117
|
+
*
|
|
118
|
+
* @module proxy/consent
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Derives the dashboard URL from the API base URL.
|
|
123
|
+
* - http://localhost:8080 -> http://localhost:5173
|
|
124
|
+
* - https://api.multicorn.ai -> https://app.multicorn.ai
|
|
125
|
+
* - Other URLs: replace "api" with "app" in the hostname, or use default
|
|
126
|
+
*/
|
|
127
|
+
declare function deriveDashboardUrl(baseUrl: string): string;
|
|
128
|
+
/**
|
|
129
|
+
* Thrown when the Shield API returns 401 or 403 (API key invalid or revoked).
|
|
130
|
+
* Used so resolveAgentRecord can detect auth failure without ad-hoc property casts.
|
|
131
|
+
*/
|
|
132
|
+
declare class ShieldAuthError extends Error {
|
|
133
|
+
constructor(message: string);
|
|
134
|
+
}
|
|
135
|
+
interface AgentRecord {
|
|
136
|
+
readonly id: string;
|
|
137
|
+
readonly name: string;
|
|
138
|
+
readonly scopes: readonly Scope[];
|
|
139
|
+
/** Set when the service returned 401/403; proxy must block with auth error message. */
|
|
140
|
+
readonly authInvalid?: boolean;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
declare function findAgentByName(agentName: string, apiKey: string, baseUrl: string): Promise<AgentRecord | null>;
|
|
144
|
+
declare function registerAgent(agentName: string, apiKey: string, baseUrl: string, platform?: string): Promise<string>;
|
|
145
|
+
declare function fetchGrantedScopes(agentId: string, apiKey: string, baseUrl: string): Promise<readonly Scope[]>;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Scope validation engine.
|
|
149
|
+
*
|
|
150
|
+
* Given a set of granted scopes and a requested action, determines whether
|
|
151
|
+
* the action is permitted. Returns a structured result with a human-readable
|
|
152
|
+
* reason when access is denied. No silent failures, no implicit grants.
|
|
153
|
+
*
|
|
154
|
+
* **Design principle (Jordan persona):** Every permission must be explicitly
|
|
155
|
+
* granted. `read` does **not** imply `write`; `write` does **not** imply
|
|
156
|
+
* `execute`. There is no wildcard or superuser bypass.
|
|
157
|
+
*
|
|
158
|
+
* @module scopes/scope-validator
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The outcome of a scope validation check.
|
|
163
|
+
*
|
|
164
|
+
* - When `allowed` is `true`, no `reason` is present.
|
|
165
|
+
* - When `allowed` is `false`, `reason` contains a descriptive explanation
|
|
166
|
+
* of why the action was denied.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```ts
|
|
170
|
+
* const result = validateScopeAccess(granted, requested);
|
|
171
|
+
* if (!result.allowed) {
|
|
172
|
+
* console.warn(`Denied: ${result.reason}`);
|
|
173
|
+
* }
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
interface ValidationResult {
|
|
177
|
+
/** Whether the requested action is permitted by the granted scopes. */
|
|
178
|
+
readonly allowed: boolean;
|
|
179
|
+
/**
|
|
180
|
+
* Human-readable explanation of why the action was denied.
|
|
181
|
+
* Only present when `allowed` is `false`.
|
|
182
|
+
*/
|
|
183
|
+
readonly reason?: string;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Check whether a single requested scope is covered by the granted set.
|
|
187
|
+
*
|
|
188
|
+
* Matching is **exact**: the granted set must contain an entry with the
|
|
189
|
+
* same `service` **and** `permissionLevel`. No implicit escalation is
|
|
190
|
+
* performed (e.g. `write` does not subsume `read`).
|
|
191
|
+
*
|
|
192
|
+
* @param grantedScopes - The scopes the agent has been granted via consent.
|
|
193
|
+
* @param requested - The scope required by the action the agent wants to perform.
|
|
194
|
+
* @returns A {@link ValidationResult} indicating whether access is allowed.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* const granted = [
|
|
199
|
+
* { service: "gmail", permissionLevel: "read" },
|
|
200
|
+
* { service: "gmail", permissionLevel: "write" },
|
|
201
|
+
* ];
|
|
202
|
+
*
|
|
203
|
+
* validateScopeAccess(granted, { service: "gmail", permissionLevel: "read" });
|
|
204
|
+
* // → { allowed: true }
|
|
205
|
+
*
|
|
206
|
+
* validateScopeAccess(granted, { service: "gmail", permissionLevel: "execute" });
|
|
207
|
+
* // → { allowed: false, reason: "Permission \"execute\" is not granted …" }
|
|
208
|
+
*
|
|
209
|
+
* validateScopeAccess(granted, { service: "slack", permissionLevel: "read" });
|
|
210
|
+
* // → { allowed: false, reason: "No permissions granted for service \"slack\" …" }
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
declare function validateScopeAccess(grantedScopes: readonly Scope[], requested: Scope): ValidationResult;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Maps MCP tool names (stdio servers, Claude Desktop) to Shield service and permission level.
|
|
217
|
+
*
|
|
218
|
+
* Uses explicit tables for common MCP servers (filesystem, terminal, browser) and the same
|
|
219
|
+
* integration-style prefix rules as OpenClaw's tool-mapper for names like `gmail_send_email`.
|
|
220
|
+
*
|
|
221
|
+
* @module mcp-tool-mapper
|
|
222
|
+
*/
|
|
223
|
+
|
|
224
|
+
interface McpToolScopeMapping {
|
|
225
|
+
readonly service: string;
|
|
226
|
+
readonly permissionLevel: PermissionLevel;
|
|
227
|
+
/** Original tool name for audit logs. */
|
|
228
|
+
readonly actionType: string;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Maps an MCP `tools/call` tool name to Shield `service` + `permissionLevel` for scope checks.
|
|
232
|
+
*/
|
|
233
|
+
declare function mapMcpToolToScope(toolName: string): McpToolScopeMapping;
|
|
234
|
+
|
|
235
|
+
export { type AgentRecord, type JsonRpcError, type JsonRpcRequest, type JsonRpcResponse, type LogLevel, type ProxyLogger, ShieldAuthError, type ToolCallParams, buildAuthErrorResponse, buildBlockedResponse, buildInternalErrorResponse, buildServiceUnreachableResponse, buildSpendingBlockedResponse, createLogger, deriveDashboardUrl, extractActionFromToolName, extractServiceFromToolName, extractToolCallParams, fetchGrantedScopes, findAgentByName, isValidLogLevel, mapMcpToolToScope, parseJsonRpcLine, registerAgent, validateScopeAccess };
|