Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pnpm build
- `src/db` - PGLite client and migrations
- `src/automation` - scheduler, hooks, automation runner
- `src/observability` - traces/transcripts and optional OTel
- `src/mcp` - MCP process client
- `src/mcp` - MCP process client and policy-gated bridge
- `tests` - unit/integration-facing tests for core behavior

## Configuration
Expand All @@ -90,6 +90,7 @@ Environment variables (BYOK):
- `DUBSBOT_ANTHROPIC_MODEL`
- `DUBSBOT_GOOGLE_MODEL` (defaults to `gemini-3.1-pro-preview`)
- `DUBSBOT_OTEL_ENABLED=1` to enable telemetry export hooks
- `DUBSBOT_MCP_SERVERS_JSON` to configure MCP servers for bridge discovery/connect/execute
- `DUBSBOT_EMBEDDING_STRATEGY_V2=1` to enable explicit embedding strategy resolution/fallback
- `DUBSBOT_EMBEDDING_STRATEGY_CONFIG_JSON` to provide explicit strategy config
- `DUBSBOT_EMBEDDING_PROVENANCE_LOG=1` to emit embedding provenance log lines
Expand All @@ -100,3 +101,4 @@ Environment variables (BYOK):
- This project intentionally uses Biome only (no ESLint/Prettier).
- Retrieval proofing benchmark schema/workflow docs: `docs/retrieval-proofing-benchmark-schema.md` and `docs/retrieval-proofing.md`.
- Embedding strategy rollout guide: `docs/embedding-strategy-rollout.md`.
- MCP bridge contracts/rollout guide: `docs/mcp-bridge-rollout.md`.
94 changes: 94 additions & 0 deletions docs/mcp-bridge-rollout.md
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.
Copy link

Copilot AI Mar 5, 2026

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 unavailable discovery records with misconfigured diagnostics, but if DUBSBOT_MCP_SERVERS_JSON is 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.

Suggested change
Invalid configurations are skipped from active use and surfaced as `unavailable` discovery records with `misconfigured` diagnostics.
If `DUBSBOT_MCP_SERVERS_JSON` is missing or malformed (e.g., invalid JSON or fails overall schema parsing), the loader returns no servers and discovery returns an empty list with no diagnostics. Once the env var is successfully parsed, invalid per-server configurations are skipped from active use and surfaced as `unavailable` discovery records with `misconfigured` diagnostics.

Copilot uses AI. Check for mistakes.

## 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.
34 changes: 34 additions & 0 deletions openspec/changes/archive/2026-03-04-full-mcp-tool-bridge/tasks.md
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
34 changes: 0 additions & 34 deletions openspec/changes/full-mcp-tool-bridge/tasks.md

This file was deleted.

26 changes: 26 additions & 0 deletions openspec/specs/mcp-policy-and-audit/spec.md
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

19 changes: 19 additions & 0 deletions openspec/specs/mcp-server-connection/spec.md
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

19 changes: 19 additions & 0 deletions openspec/specs/mcp-server-discovery/spec.md
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

19 changes: 19 additions & 0 deletions openspec/specs/mcp-tool-execution-pipeline/spec.md
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

20 changes: 19 additions & 1 deletion src/cli/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { AgentOrchestrator } from '../agent/orchestrator';
import { loadAgentsConfig } from '../config/agents-loader';
import { loadMcpServerConfig } from '../config/mcp-loader';
import { loadEmbeddingStrategyConfig } from '../context/embedding/config';
import { createDb } from '../db/client';
import { runMigrations } from '../db/migrate';
import { McpBridgeService } from '../mcp/bridge';
import { DefaultMcpServerAdapter } from '../mcp/default-adapter';
import { OptionalOtelExporter } from '../observability/otel';
import { TraceStore } from '../observability/traces';
import { TranscriptStore } from '../observability/transcripts';
Expand All @@ -18,6 +21,20 @@ export async function createRuntime() {
const agentsConfig = await loadAgentsConfig(process.cwd());
const provider = createProviderAdapter(detectProvider());
const policyEngine = new DefaultPolicyEngine(createDefaultApprovalPolicy());
const traces = new TraceStore();
const mcpBridge = new McpBridgeService({
adapter: new DefaultMcpServerAdapter(loadMcpServerConfig()),
policyEngine,
auditSink: {
append: async (event) => {
await traces.write({
timestamp: event.timing.endedAt,
type: `mcp.bridge.${event.operation}`,
payload: event as unknown as Record<string, unknown>,
});
},
},
});
const orchestrator = new AgentOrchestrator({
provider,
policyEngine,
Expand All @@ -29,8 +46,9 @@ export async function createRuntime() {
embeddingStrategyConfig,
provider,
policyEngine,
mcpBridge,
orchestrator,
traces: new TraceStore(),
traces,
transcripts: new TranscriptStore(),
otel: new OptionalOtelExporter(),
tools: new ToolRegistry({
Expand Down
28 changes: 28 additions & 0 deletions src/config/mcp-loader.ts
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 {
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loadMcpServerConfig() silently returns [] on JSON/schema parse failures. That makes MCP unexpectedly “disappear” with no diagnostic, and it’s inconsistent with other env config loaders like loadEmbeddingStrategyConfig() which throw actionable errors on invalid JSON. Consider surfacing a warning/trace (or throwing) when DUBSBOT_MCP_SERVERS_JSON is set but invalid, so misconfiguration is detectable.

Suggested change
} catch {
} catch (err) {
console.error(
'[loadMcpServerConfig] Failed to parse DUBSBOT_MCP_SERVERS_JSON; MCP servers will be disabled.',
err,
);

Copilot uses AI. Check for mistakes.
return [];
}
}
Loading