@sanctuary-framework/mcp-server 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -255,9 +255,11 @@ declare class StateStore {
255
255
  /**
256
256
  * Import a previously exported state bundle.
257
257
  */
258
- import(bundleBase64: string, conflictResolution?: "skip" | "overwrite" | "version"): Promise<{
258
+ import(bundleBase64: string, conflictResolution: "skip" | "overwrite" | "version" | undefined, publicKeyResolver: (kid: string) => Uint8Array | null): Promise<{
259
259
  imported_keys: number;
260
260
  skipped_keys: number;
261
+ skipped_invalid_sig: number;
262
+ skipped_unknown_kid: number;
261
263
  conflicts: number;
262
264
  namespaces: string[];
263
265
  imported_at: string;
@@ -1187,7 +1189,13 @@ interface Tier2Config {
1187
1189
  interface ApprovalChannelConfig {
1188
1190
  type: "stderr" | "webhook" | "callback";
1189
1191
  timeout_seconds: number;
1190
- auto_deny: boolean;
1192
+ /**
1193
+ * SEC-002: auto_deny is hardcoded to true and not configurable.
1194
+ * Timeout on any approval channel ALWAYS results in denial.
1195
+ * This field is retained for backward compatibility with existing
1196
+ * policy files but is ignored — timeout always denies.
1197
+ */
1198
+ auto_deny?: boolean;
1191
1199
  webhook_url?: string;
1192
1200
  webhook_secret?: string;
1193
1201
  }
@@ -1215,7 +1223,7 @@ interface ApprovalRequest {
1215
1223
  interface ApprovalResponse {
1216
1224
  decision: "approve" | "deny";
1217
1225
  decided_at: string;
1218
- decided_by: "human" | "timeout" | "auto";
1226
+ decided_by: "human" | "timeout" | "auto" | "stderr:non-interactive";
1219
1227
  }
1220
1228
  /** Result of the approval gate evaluation */
1221
1229
  interface GateResult {
@@ -1258,22 +1266,25 @@ interface ApprovalChannel {
1258
1266
  requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
1259
1267
  }
1260
1268
  /**
1261
- * Stderr approval channel — writes prompts to stderr, waits for response.
1269
+ * Stderr approval channel — non-interactive informational channel.
1262
1270
  *
1263
1271
  * In the MCP stdio model:
1264
1272
  * - stdin/stdout carry the MCP protocol (JSON-RPC)
1265
1273
  * - stderr is available for out-of-band human communication
1266
1274
  *
1267
- * Since many harnesses do not support interactive stdin during tool calls,
1268
- * this channel uses a timeout-based model: the prompt is displayed, and
1269
- * if no response is received within the timeout, the default action applies.
1275
+ * Because stdin is consumed by the MCP JSON-RPC transport, this channel
1276
+ * CANNOT read interactive human input. It is strictly informational:
1277
+ * the prompt is displayed so the human sees what is happening, and the
1278
+ * operation is denied immediately.
1270
1279
  *
1271
- * For MVS, the channel auto-resolves based on the auto_deny setting.
1272
- * Interactive stdin reading is deferred to a future version with harness support.
1280
+ * SEC-002 + SEC-016 invariants:
1281
+ * - This channel ALWAYS denies. No configuration can change this.
1282
+ * - There is no timeout or async delay — denial is synchronous.
1283
+ * - The `auto_deny` config field is ignored (SEC-002).
1284
+ * - For interactive approval, use the dashboard or webhook channel.
1273
1285
  */
1274
1286
  declare class StderrApprovalChannel implements ApprovalChannel {
1275
- private config;
1276
- constructor(config: ApprovalChannelConfig);
1287
+ constructor(_config: ApprovalChannelConfig);
1277
1288
  requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
1278
1289
  private formatPrompt;
1279
1290
  }
@@ -1458,7 +1469,8 @@ interface DashboardConfig {
1458
1469
  port: number;
1459
1470
  host: string;
1460
1471
  timeout_seconds: number;
1461
- auto_deny: boolean;
1472
+ /** SEC-002: auto_deny is always true. Field retained for interface compat but ignored. */
1473
+ auto_deny?: boolean;
1462
1474
  /** Bearer token for API authentication. If omitted, auth is disabled. */
1463
1475
  auth_token?: string;
1464
1476
  /** TLS configuration for HTTPS. If omitted, plain HTTP is used. */
@@ -1478,6 +1490,9 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
1478
1490
  private dashboardHTML;
1479
1491
  private authToken;
1480
1492
  private useTLS;
1493
+ /** SEC-012: Short-lived session store. Sessions replace URL query tokens. */
1494
+ private sessions;
1495
+ private sessionCleanupTimer;
1481
1496
  constructor(config: DashboardConfig);
1482
1497
  /**
1483
1498
  * Inject dependencies after construction.
@@ -1503,11 +1518,39 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
1503
1518
  requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
1504
1519
  /**
1505
1520
  * Verify bearer token authentication.
1506
- * Checks Authorization header first, falls back to ?token= query param.
1521
+ *
1522
+ * SEC-012: The long-lived auth token is ONLY accepted via the Authorization
1523
+ * header — never in URL query strings. For SSE and page loads that cannot
1524
+ * set headers, a short-lived session token (obtained via POST /auth/session)
1525
+ * is accepted via ?session= query parameter.
1526
+ *
1507
1527
  * Returns true if auth passes, false if blocked (response already sent).
1508
1528
  */
1509
1529
  private checkAuth;
1530
+ /**
1531
+ * Create a short-lived session by exchanging the long-lived auth token
1532
+ * (provided in the Authorization header) for a session ID.
1533
+ */
1534
+ private createSession;
1535
+ /**
1536
+ * Validate a session ID — must exist and not be expired.
1537
+ */
1538
+ private validateSession;
1539
+ /**
1540
+ * Remove all expired sessions.
1541
+ */
1542
+ private cleanupSessions;
1510
1543
  private handleRequest;
1544
+ /**
1545
+ * SEC-012: Exchange a long-lived auth token (in Authorization header)
1546
+ * for a short-lived session ID. The session ID can be used in URL
1547
+ * query parameters without exposing the long-lived credential.
1548
+ *
1549
+ * This endpoint performs its OWN auth check (header-only) because it
1550
+ * must reject query-parameter tokens and is called before the
1551
+ * normal checkAuth flow.
1552
+ */
1553
+ private handleSessionExchange;
1511
1554
  private serveDashboard;
1512
1555
  private handleSSE;
1513
1556
  private handleStatus;
@@ -1568,8 +1611,8 @@ interface WebhookConfig {
1568
1611
  callback_host: string;
1569
1612
  /** Seconds to wait for a callback before timeout */
1570
1613
  timeout_seconds: number;
1571
- /** Whether to deny (true) or approve (false) on timeout */
1572
- auto_deny: boolean;
1614
+ /** SEC-002: auto_deny is always true. Field retained for interface compat but ignored. */
1615
+ auto_deny?: boolean;
1573
1616
  }
1574
1617
  /** Outbound webhook payload */
1575
1618
  interface WebhookPayload {
package/dist/index.d.ts CHANGED
@@ -255,9 +255,11 @@ declare class StateStore {
255
255
  /**
256
256
  * Import a previously exported state bundle.
257
257
  */
258
- import(bundleBase64: string, conflictResolution?: "skip" | "overwrite" | "version"): Promise<{
258
+ import(bundleBase64: string, conflictResolution: "skip" | "overwrite" | "version" | undefined, publicKeyResolver: (kid: string) => Uint8Array | null): Promise<{
259
259
  imported_keys: number;
260
260
  skipped_keys: number;
261
+ skipped_invalid_sig: number;
262
+ skipped_unknown_kid: number;
261
263
  conflicts: number;
262
264
  namespaces: string[];
263
265
  imported_at: string;
@@ -1187,7 +1189,13 @@ interface Tier2Config {
1187
1189
  interface ApprovalChannelConfig {
1188
1190
  type: "stderr" | "webhook" | "callback";
1189
1191
  timeout_seconds: number;
1190
- auto_deny: boolean;
1192
+ /**
1193
+ * SEC-002: auto_deny is hardcoded to true and not configurable.
1194
+ * Timeout on any approval channel ALWAYS results in denial.
1195
+ * This field is retained for backward compatibility with existing
1196
+ * policy files but is ignored — timeout always denies.
1197
+ */
1198
+ auto_deny?: boolean;
1191
1199
  webhook_url?: string;
1192
1200
  webhook_secret?: string;
1193
1201
  }
@@ -1215,7 +1223,7 @@ interface ApprovalRequest {
1215
1223
  interface ApprovalResponse {
1216
1224
  decision: "approve" | "deny";
1217
1225
  decided_at: string;
1218
- decided_by: "human" | "timeout" | "auto";
1226
+ decided_by: "human" | "timeout" | "auto" | "stderr:non-interactive";
1219
1227
  }
1220
1228
  /** Result of the approval gate evaluation */
1221
1229
  interface GateResult {
@@ -1258,22 +1266,25 @@ interface ApprovalChannel {
1258
1266
  requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
1259
1267
  }
1260
1268
  /**
1261
- * Stderr approval channel — writes prompts to stderr, waits for response.
1269
+ * Stderr approval channel — non-interactive informational channel.
1262
1270
  *
1263
1271
  * In the MCP stdio model:
1264
1272
  * - stdin/stdout carry the MCP protocol (JSON-RPC)
1265
1273
  * - stderr is available for out-of-band human communication
1266
1274
  *
1267
- * Since many harnesses do not support interactive stdin during tool calls,
1268
- * this channel uses a timeout-based model: the prompt is displayed, and
1269
- * if no response is received within the timeout, the default action applies.
1275
+ * Because stdin is consumed by the MCP JSON-RPC transport, this channel
1276
+ * CANNOT read interactive human input. It is strictly informational:
1277
+ * the prompt is displayed so the human sees what is happening, and the
1278
+ * operation is denied immediately.
1270
1279
  *
1271
- * For MVS, the channel auto-resolves based on the auto_deny setting.
1272
- * Interactive stdin reading is deferred to a future version with harness support.
1280
+ * SEC-002 + SEC-016 invariants:
1281
+ * - This channel ALWAYS denies. No configuration can change this.
1282
+ * - There is no timeout or async delay — denial is synchronous.
1283
+ * - The `auto_deny` config field is ignored (SEC-002).
1284
+ * - For interactive approval, use the dashboard or webhook channel.
1273
1285
  */
1274
1286
  declare class StderrApprovalChannel implements ApprovalChannel {
1275
- private config;
1276
- constructor(config: ApprovalChannelConfig);
1287
+ constructor(_config: ApprovalChannelConfig);
1277
1288
  requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
1278
1289
  private formatPrompt;
1279
1290
  }
@@ -1458,7 +1469,8 @@ interface DashboardConfig {
1458
1469
  port: number;
1459
1470
  host: string;
1460
1471
  timeout_seconds: number;
1461
- auto_deny: boolean;
1472
+ /** SEC-002: auto_deny is always true. Field retained for interface compat but ignored. */
1473
+ auto_deny?: boolean;
1462
1474
  /** Bearer token for API authentication. If omitted, auth is disabled. */
1463
1475
  auth_token?: string;
1464
1476
  /** TLS configuration for HTTPS. If omitted, plain HTTP is used. */
@@ -1478,6 +1490,9 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
1478
1490
  private dashboardHTML;
1479
1491
  private authToken;
1480
1492
  private useTLS;
1493
+ /** SEC-012: Short-lived session store. Sessions replace URL query tokens. */
1494
+ private sessions;
1495
+ private sessionCleanupTimer;
1481
1496
  constructor(config: DashboardConfig);
1482
1497
  /**
1483
1498
  * Inject dependencies after construction.
@@ -1503,11 +1518,39 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
1503
1518
  requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
1504
1519
  /**
1505
1520
  * Verify bearer token authentication.
1506
- * Checks Authorization header first, falls back to ?token= query param.
1521
+ *
1522
+ * SEC-012: The long-lived auth token is ONLY accepted via the Authorization
1523
+ * header — never in URL query strings. For SSE and page loads that cannot
1524
+ * set headers, a short-lived session token (obtained via POST /auth/session)
1525
+ * is accepted via ?session= query parameter.
1526
+ *
1507
1527
  * Returns true if auth passes, false if blocked (response already sent).
1508
1528
  */
1509
1529
  private checkAuth;
1530
+ /**
1531
+ * Create a short-lived session by exchanging the long-lived auth token
1532
+ * (provided in the Authorization header) for a session ID.
1533
+ */
1534
+ private createSession;
1535
+ /**
1536
+ * Validate a session ID — must exist and not be expired.
1537
+ */
1538
+ private validateSession;
1539
+ /**
1540
+ * Remove all expired sessions.
1541
+ */
1542
+ private cleanupSessions;
1510
1543
  private handleRequest;
1544
+ /**
1545
+ * SEC-012: Exchange a long-lived auth token (in Authorization header)
1546
+ * for a short-lived session ID. The session ID can be used in URL
1547
+ * query parameters without exposing the long-lived credential.
1548
+ *
1549
+ * This endpoint performs its OWN auth check (header-only) because it
1550
+ * must reject query-parameter tokens and is called before the
1551
+ * normal checkAuth flow.
1552
+ */
1553
+ private handleSessionExchange;
1511
1554
  private serveDashboard;
1512
1555
  private handleSSE;
1513
1556
  private handleStatus;
@@ -1568,8 +1611,8 @@ interface WebhookConfig {
1568
1611
  callback_host: string;
1569
1612
  /** Seconds to wait for a callback before timeout */
1570
1613
  timeout_seconds: number;
1571
- /** Whether to deny (true) or approve (false) on timeout */
1572
- auto_deny: boolean;
1614
+ /** SEC-002: auto_deny is always true. Field retained for interface compat but ignored. */
1615
+ auto_deny?: boolean;
1573
1616
  }
1574
1617
  /** Outbound webhook payload */
1575
1618
  interface WebhookPayload {