-
Notifications
You must be signed in to change notification settings - Fork 0
feat(mcp): add policy-gated MCP bridge with audit pipeline #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/context-graph-enrichment
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| # MCP Bridge Contracts And Rollout Guide | ||
|
|
||
| This guide documents the MCP bridge contracts introduced for `discover`, `connect`, and `execute`, plus a safe rollout and rollback procedure. | ||
|
|
||
| ## Bridge Contract | ||
|
|
||
| ### Discover | ||
|
|
||
| - Operation: `discover()` | ||
| - Response shape: | ||
| - `correlationId`: shared identifier for traceability | ||
| - `servers[]`: normalized records sorted by `serverId` | ||
| - `serverId` | ||
| - `displayName` | ||
| - `transport` (`stdio` | `http` | `sse` | `unknown`) | ||
| - `availability` (`available` | `unavailable`) | ||
| - optional `diagnostics` for unavailable servers | ||
|
|
||
| ### Connect | ||
|
|
||
| - Operation: `connect(serverId, correlationId?)` | ||
| - Success: | ||
| - `ok: true` | ||
| - `state: connected` | ||
| - `sessionId` | ||
| - `reusedSession` (health-validated reuse only) | ||
| - Failure: | ||
| - `ok: false` | ||
| - `state: failed` | ||
| - standardized error category (`validation`, `connection_failed`, `timeout`, `internal`, etc.) | ||
|
|
||
| ### Execute | ||
|
|
||
| - Operation: `execute({ serverId, toolName, input, ... })` | ||
| - Behavior: | ||
| - validates input | ||
| - evaluates policy before invocation | ||
| - denies with `policy_denied` without invoking provider when blocked | ||
| - returns normalized execution envelope for both success/failure | ||
| - Envelope fields: | ||
| - `ok` | ||
| - `correlationId` | ||
| - `serverId`, `toolName` | ||
| - `policyDecision` | ||
| - `error` (or `null`) | ||
| - `timing` (`startedAt`, `endedAt`, `durationMs`) | ||
| - `outputSummary` (bounded/redacted) | ||
|
|
||
| ## Audit Event Schema | ||
|
|
||
| Each bridge operation emits append-only audit records: | ||
|
|
||
| - `operation`: `discover` | `connect` | `execute` | ||
| - `outcome`: `success` | `failure` | `denied` | ||
| - `correlationId` | ||
| - optional `serverId`, `toolName` | ||
| - optional policy snapshot (`allowed`, `requiresApproval`, `decision`, `reason`, `sideEffect`) | ||
| - optional standardized `error` | ||
| - `timing`: start/end/duration | ||
| - operation metadata with redacted summaries of request/response payloads where relevant | ||
|
|
||
| ## MCP Server Configuration | ||
|
|
||
| Set `DUBSBOT_MCP_SERVERS_JSON` to a JSON array: | ||
|
|
||
| ```json | ||
| [ | ||
| { | ||
| "id": "local-tools", | ||
| "displayName": "Local Tools", | ||
| "transport": "stdio", | ||
| "command": "node", | ||
| "args": ["./scripts/mcp-server.js"], | ||
| "cwd": "/workspace/project" | ||
| } | ||
| ] | ||
| ``` | ||
|
|
||
| Invalid configurations are skipped from active use and surfaced as `unavailable` discovery records with `misconfigured` diagnostics. | ||
|
|
||
| ## Incremental Rollout | ||
|
|
||
| 1. Configure one known-safe MCP server in `DUBSBOT_MCP_SERVERS_JSON`. | ||
| 2. Run discovery and verify normalized server/diagnostic output. | ||
| 3. Run connect/execute against low-risk tools and inspect `mcp.bridge.*` trace entries. | ||
| 4. Expand server list gradually after confirming policy-denied flows and failure classification behavior in staging. | ||
| 5. Promote to production once success, denied, and failure audit records are all observed. | ||
|
|
||
| ## Rollback | ||
|
|
||
| 1. Remove or empty `DUBSBOT_MCP_SERVERS_JSON`. | ||
| 2. Restart the process so bridge discovery returns an empty server set. | ||
| 3. Verify no new `mcp.bridge.execute` success events are emitted. | ||
| 4. Restore config only after corrective fixes and staging verification. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| ## 1. Bridge Foundations | ||
|
|
||
| - [x] 1.1 Define `McpBridge` interfaces and shared response/error envelope types for `discover`, `connect`, and `execute` | ||
| - [x] 1.2 Add bridge module wiring to existing MCP client, policy service, and logging dependencies | ||
| - [x] 1.3 Implement correlation-id generation and propagation helpers used across bridge operations | ||
|
|
||
| ## 2. Discovery and Connection | ||
|
|
||
| - [x] 2.1 Implement server discovery adapter that returns normalized server metadata and availability status | ||
| - [x] 2.2 Add discovery diagnostics mapping for unreachable/misconfigured servers | ||
| - [x] 2.3 Implement connection lifecycle flow with explicit connected/failed states and standardized error categories | ||
| - [x] 2.4 Add connection health validation before session reuse | ||
|
|
||
| ## 3. Policy-Gated Tool Execution Pipeline | ||
|
|
||
| - [x] 3.1 Implement execution entry point that validates input and builds an execution envelope | ||
| - [x] 3.2 Add mandatory policy evaluation step before MCP tool invocation | ||
| - [x] 3.3 Implement provider invocation adapter and map provider failures into standardized bridge error categories | ||
| - [x] 3.4 Ensure denied policy decisions short-circuit invocation and return `policy_denied` | ||
|
|
||
| ## 4. Audit and Observability | ||
|
|
||
| - [x] 4.1 Define audit event schema for discovery/connect/execute attempts and outcomes | ||
| - [x] 4.2 Emit append-only audit records with correlation id, decision metadata, and outcome classification | ||
| - [x] 4.3 Capture and emit timing metadata (start, end, duration) for execution attempts | ||
| - [x] 4.4 Add bounded payload redaction/summarization to avoid large or sensitive audit entries | ||
|
|
||
| ## 5. Verification and Rollout | ||
|
|
||
| - [x] 5.1 Add unit tests for discovery normalization, connection failure classification, and execution envelope mapping | ||
| - [x] 5.2 Add policy enforcement tests proving denied requests never invoke tools | ||
| - [x] 5.3 Add audit emission tests for success, failure, and denied scenarios | ||
| - [x] 5.4 Add integration tests for end-to-end discover/connect/execute bridge flow | ||
| - [x] 5.5 Document bridge contracts and incremental rollout/rollback procedure |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # mcp-policy-and-audit Specification | ||
|
|
||
| ## Purpose | ||
| TBD - created by archiving change full-mcp-tool-bridge. Update Purpose after archive. | ||
| ## Requirements | ||
| ### Requirement: Policy SHALL be enforced before MCP tool invocation | ||
| The system SHALL evaluate policy for every tool execution request before invoking the remote tool and SHALL block execution when policy denies access. | ||
|
|
||
| #### Scenario: Policy denies tool execution | ||
| - **WHEN** a caller attempts to execute a tool that is not permitted by policy | ||
| - **THEN** the system returns a `policy_denied` result and does not invoke the MCP tool | ||
|
|
||
| ### Requirement: Bridge SHALL emit auditable execution records | ||
| The system SHALL emit append-only audit events for execution attempts, policy decisions, connection outcomes, and terminal tool results using a shared correlation identifier. | ||
|
|
||
| #### Scenario: Audit record is emitted for denied execution | ||
| - **WHEN** policy denies a tool execution request | ||
| - **THEN** the system writes an audit event containing request metadata, denial reason, and correlation id | ||
|
|
||
| ### Requirement: Audit records SHALL include outcome timing metadata | ||
| The system SHALL capture start timestamp, end timestamp, and duration for execution attempts to support operational analysis and incident reconstruction. | ||
|
|
||
| #### Scenario: Successful execution includes timing | ||
| - **WHEN** a tool execution completes successfully | ||
| - **THEN** the emitted audit event includes start time, end time, and calculated duration fields | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # mcp-server-connection Specification | ||
|
|
||
| ## Purpose | ||
| TBD - created by archiving change full-mcp-tool-bridge. Update Purpose after archive. | ||
| ## Requirements | ||
| ### Requirement: Bridge SHALL manage MCP connection lifecycle | ||
| The system SHALL expose a connect operation that validates the requested server, attempts session establishment, and returns an explicit connected or failed result with standardized error classification. | ||
|
|
||
| #### Scenario: Successful server connection | ||
| - **WHEN** a caller requests connection to a valid and reachable server identifier | ||
| - **THEN** the system returns a connected state with a session reference and connection metadata | ||
|
|
||
| ### Requirement: Connection failures SHALL be explicit and non-ambiguous | ||
| The system SHALL classify connection failures into standardized categories and SHALL include actionable failure context without leaking sensitive transport details. | ||
|
|
||
| #### Scenario: Connection fails due to timeout | ||
| - **WHEN** server connection cannot complete within configured timeout limits | ||
| - **THEN** the system returns a `connection_failed` classification with timeout context and no session reference | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # mcp-server-discovery Specification | ||
|
|
||
| ## Purpose | ||
| TBD - created by archiving change full-mcp-tool-bridge. Update Purpose after archive. | ||
| ## Requirements | ||
| ### Requirement: Bridge SHALL list discoverable MCP servers | ||
| The system SHALL provide a discovery operation that returns all configured or reachable MCP servers as normalized records including server identifier, display name, transport type, and availability status. | ||
|
|
||
| #### Scenario: Discovery succeeds with configured servers | ||
| - **WHEN** a caller invokes bridge discovery with valid runtime configuration | ||
| - **THEN** the system returns a deterministic list of normalized server records ordered by stable server identifier | ||
|
|
||
| ### Requirement: Discovery SHALL report diagnostics for unavailable servers | ||
| The system SHALL include diagnostic details for servers that cannot be reached or validated so callers can distinguish misconfiguration from temporary connectivity issues. | ||
|
|
||
| #### Scenario: One server is unavailable during discovery | ||
| - **WHEN** discovery encounters a server that fails validation or handshake | ||
| - **THEN** the returned record includes an unavailable status and machine-readable diagnostic metadata | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # mcp-tool-execution-pipeline Specification | ||
|
|
||
| ## Purpose | ||
| TBD - created by archiving change full-mcp-tool-bridge. Update Purpose after archive. | ||
| ## Requirements | ||
| ### Requirement: Bridge SHALL execute tools through a single pipeline | ||
| The system SHALL provide one execution entry point that accepts server id, tool name, and validated input payload, and returns a normalized execution envelope for both success and failure outcomes. | ||
|
|
||
| #### Scenario: Tool execution succeeds | ||
| - **WHEN** a caller executes a valid tool on a connected and authorized server | ||
| - **THEN** the system returns a success envelope containing correlation id, timing metadata, and structured tool output summary | ||
|
|
||
| ### Requirement: Pipeline SHALL standardize execution errors | ||
| The system SHALL map invocation failures to stable error categories so callers can reliably handle retryable and non-retryable outcomes. | ||
|
|
||
| #### Scenario: Tool invocation fails in provider layer | ||
| - **WHEN** the MCP provider returns an execution failure | ||
| - **THEN** the system returns a `tool_failed` envelope with stable error fields and preserved correlation id | ||
|
|
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,28 @@ | ||||||||||||||
| import { z } from 'zod'; | ||||||||||||||
| import type { McpServerConfig } from '../mcp/default-adapter'; | ||||||||||||||
|
|
||||||||||||||
| const McpServerConfigSchema = z.object({ | ||||||||||||||
| id: z.string().min(1), | ||||||||||||||
| displayName: z.string().optional(), | ||||||||||||||
| transport: z.enum(['stdio', 'http', 'sse', 'unknown']), | ||||||||||||||
| command: z.string().optional(), | ||||||||||||||
| args: z.array(z.string()).optional(), | ||||||||||||||
| cwd: z.string().optional(), | ||||||||||||||
| url: z.string().optional(), | ||||||||||||||
| metadata: z.record(z.string(), z.unknown()).optional(), | ||||||||||||||
| }); | ||||||||||||||
|
|
||||||||||||||
| const McpServerConfigListSchema = z.array(McpServerConfigSchema); | ||||||||||||||
|
|
||||||||||||||
| export function loadMcpServerConfig(raw = process.env.DUBSBOT_MCP_SERVERS_JSON): McpServerConfig[] { | ||||||||||||||
| if (!raw?.trim()) { | ||||||||||||||
| return []; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| try { | ||||||||||||||
| const parsed = JSON.parse(raw) as unknown; | ||||||||||||||
| return McpServerConfigListSchema.parse(parsed); | ||||||||||||||
| } catch { | ||||||||||||||
|
||||||||||||||
| } catch { | |
| } catch (err) { | |
| console.error( | |
| '[loadMcpServerConfig] Failed to parse DUBSBOT_MCP_SERVERS_JSON; MCP servers will be disabled.', | |
| err, | |
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This rollout guide says invalid configurations are surfaced as
unavailablediscovery records withmisconfigureddiagnostics, but ifDUBSBOT_MCP_SERVERS_JSONis invalid JSON or fails schema parsing,loadMcpServerConfig()currently returns an empty list and discovery will return no records (no diagnostics). Consider clarifying the doc (or changing the loader behavior) so operators know what to expect when the env var is malformed.