How the MCP Server Implements HTTP Transport with Origin-Guard Security in Code Review Graph
The MCP server in the Code Review Graph VS Code extension implements HTTP transport through a message-passing channel secured by nonce-based CSP headers and strict origin verification on every incoming message.
The Code Review Graph extension by tirth8205 embeds an MCP (Micro-Code-Protocol) server directly inside the VS Code extension host rather than exposing a network port. This architecture requires a secure HTTP-like transport mechanism to communicate with the webview UI. The implementation combines Content-Security-Policy nonces with origin-guard validation to prevent cross-origin attacks.
Webview Creation with Nonce-Based CSP Protection
The transport security begins when the extension opens a graph view. In graphWebview.ts (around line 300), the code generates a vscode.WebviewPanel and injects a strict Content-Security-Policy header using a cryptographically secure nonce:
const nonce = getNonce();
panel.webview.html = `
<meta http-equiv="Content-Security-Policy"
content="default-src 'none';
script-src 'nonce-${nonce}';
style-src 'unsafe-inline';
img-src ${webview.cspSource};">
`;
This CSP configuration achieves three critical security properties:
default-src 'none'— Blocks all resource loading by defaultscript-src 'nonce-${nonce}'— Permits execution only of scripts bearing the matching nonceimg-src ${webview.cspSource}— Restricts images to the webview's own CSP source
The getNonce() utility generates a unique value per webview instance, ensuring that injected scripts cannot be replayed across sessions.
HTTP Transport via VS Code Message Passing
Rather than raw HTTP, the MCP server uses VS Code's built-in message-passing API as its transport layer. The webview JavaScript obtains the API through acquireVsCodeApi():
<script nonce="${nonce}">
const vscode = acquireVsCodeApi();
// Requests travel through postMessage, not fetch/XHR
vscode.postMessage({ command: 'buildGraph', args: [] });
</script>
The extension registers a listener on webview.onDidReceiveMessage to handle incoming requests. This creates an HTTP-like request/response pattern where messages carry commands, arguments, and metadata.
Origin-Guard Enforcement on Every Message
The critical security layer—the origin guard—validates each message before processing. In graphWebview.ts, the message handler performs this verification:
panel.webview.onDidReceiveMessage(message => {
// Origin-guard check: reject messages from unexpected origins
if (panel.webview.cspSource !== message.origin) {
console.warn('Rejected message with invalid origin');
return;
}
// Process validated commands...
});
This check couples the CSP source with runtime origin verification. Only scripts that were injected with the correct nonce—proving they originated from the extension's trusted HTML generation—can successfully post messages that pass validation.
Debounced Database Notifications
The MCP server also pushes updates from the backend to the UI. The GraphWatcher class in watcher.ts monitors the on-disk SQLite database (graph.db) and forwards change notifications through the same secured channel:
const debouncedOnChanged = debounce(onChanged, 500);
this.watcher.onDidChange(() => debouncedOnChanged());
- Lines 37–49 implement a 500ms debounce to prevent UI flooding during rapid file changes
- Notifications traverse the
postMessagechannel, inheriting the same origin-guard protection
The debounce utility itself:
function debounce<T extends (...args: any[]) => any>(fn: T, ms: number): T {
let timer: ReturnType<typeof setTimeout> | undefined;
const debounced = (...args: Parameters<T>) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => { timer = undefined; fn(...args); }, ms);
};
return debounced as unknown as T;
}
Complete Implementation Example
Here's the integrated pattern from graphWebview.ts:
import * as vscode from 'vscode';
import { getNonce } from './utils';
export function createGraphWebview(context: vscode.ExtensionContext) {
const panel = vscode.window.createWebviewPanel(
'codeReviewGraph',
'Code Review Graph',
vscode.ViewColumn.One,
{ enableScripts: true }
);
const nonce = getNonce();
panel.webview.html = `
<meta http-equiv="Content-Security-Policy"
content="default-src 'none';
script-src 'nonce-${nonce}';
style-src 'unsafe-inline';
img-src ${panel.webview.cspSource};">
<script nonce="${nonce}">
const vscode = acquireVsCodeApi();
vscode.postMessage({ command: 'buildGraph', args: [] });
</script>
`;
panel.webview.onDidReceiveMessage(message => {
if (panel.webview.cspSource !== message.origin) {
console.warn('Rejected message with invalid origin');
return;
}
// Command handling logic...
});
}
Key Source Files
| File | Security Responsibility |
|---|---|
graphWebview.ts |
CSP generation, nonce injection, origin-guard verification |
watcher.ts |
Debounced file-system monitoring, secured change notifications |
extension.ts |
Command registration, extension host entry points |
Summary
- Nonce-based CSP in
graphWebview.tsprevents execution of unauthorized scripts acquireVsCodeApi()provides a controlled message-passing transport layer- Runtime origin verification rejects messages whose origin mismatches
cspSource - Debounced watchers in
watcher.tspush updates without overwhelming the UI - No network port exposure eliminates entire classes of remote attack vectors
Frequently Asked Questions
What makes this transport "HTTP-like" rather than true HTTP?
The channel mimics HTTP semantics—requests carry commands and payloads, responses return data—but uses postMessage over VS Code's internal bridge instead of TCP sockets or HTTP requests. There's no localhost port, no CORS headers, and no browser networking stack involved.
Why does the origin guard check cspSource instead of a hardcoded value?
webview.cspSource is generated per-webview by VS Code itself, incorporating the extension's identifier and session context. Using this dynamic value prevents hardcoded bypasses and automatically invalidates if the webview context changes.
Can malicious extensions bypass the origin guard?
Extensions running in the same VS Code instance operate in separate extension hosts with isolated webviews. The nonce and CSP source are not exposed through any API, so another extension cannot forge valid messages without compromising VS Code's core webview isolation.
What happens if the SQLite database changes rapidly?
The debounce utility with a 500ms window in watcher.ts collapses multiple rapid changes into a single notification, preventing UI thrashing while maintaining responsiveness for legitimate updates.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →