@kevinbermudezc/context-guard 1.1.1 → 1.2.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 CHANGED
@@ -145,7 +145,73 @@ That's it! When Claude Code attempts to run `Read` (or raw bash reads like `cat
145
145
 
146
146
  ---
147
147
 
148
- ## 🛠️ CLI Usage
148
+ ## 🔌 MCP Server — Universal Agent Integration (Phase 2)
149
+
150
+ ContextGuard ships a built-in **Model Context Protocol (MCP) server** compatible with any MCP host: **Cursor, Windsurf, Claude Desktop, Antigravity (AGY), Zed**, and more.
151
+
152
+ The MCP server exposes three tools:
153
+
154
+ | Tool | Description | Cost | Latency |
155
+ |---|---|---|---|
156
+ | `read_file_safe` | Protected file read — passthrough for small files, AST skeleton for large ones, block for binaries/lockfiles | **\$0.00** | **< 5ms** |
157
+ | `inspect_outline` | Instant structural outline (classes, functions, types, signals) with line numbers | **\$0.00** | **< 5ms** |
158
+ | `grep_distilled` | Pattern search returning only structural context around matches, never full file dumps | **\$0.00** | **< 50ms** |
159
+
160
+ ### Install & Run
161
+
162
+ ```bash
163
+ # One-shot (no install):
164
+ npx -y @kevinbermudezc/context-guard context-guard-mcp
165
+
166
+ # Or as a local dependency:
167
+ pnpm add -D @kevinbermudezc/context-guard
168
+ node ./node_modules/@kevinbermudezc/context-guard/dist/bin/mcp-server.js
169
+ ```
170
+
171
+ ### 🖱️ Cursor (`~/.cursor/mcp.json`)
172
+
173
+ ```json
174
+ {
175
+ "mcpServers": {
176
+ "context-guard": {
177
+ "command": "npx",
178
+ "args": ["-y", "@kevinbermudezc/context-guard@latest", "context-guard-mcp"]
179
+ }
180
+ }
181
+ }
182
+ ```
183
+
184
+ ### 🤖 Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json`)
185
+
186
+ ```json
187
+ {
188
+ "mcpServers": {
189
+ "context-guard": {
190
+ "command": "npx",
191
+ "args": ["-y", "@kevinbermudezc/context-guard@latest", "context-guard-mcp"]
192
+ }
193
+ }
194
+ }
195
+ ```
196
+
197
+ ### 🪁 Antigravity / Google AGY (`.agents/mcp.json` or `~/.config/agy/mcp.json`)
198
+
199
+ ```json
200
+ {
201
+ "mcpServers": {
202
+ "context-guard": {
203
+ "command": "npx",
204
+ "args": ["-y", "@kevinbermudezc/context-guard@latest", "context-guard-mcp"]
205
+ }
206
+ }
207
+ }
208
+ ```
209
+
210
+ > **Note:** Configuration examples are also available in [`examples/mcp-configs/`](./examples/mcp-configs/).
211
+
212
+ ---
213
+
214
+ ## 🛠️ CLI Usage (Claude Code Hook)
149
215
 
150
216
  You can also use ContextGuard directly as a command-line tool:
151
217
 
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=mcp-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../../bin/mcp-server.ts"],"names":[],"mappings":""}
@@ -0,0 +1,328 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ContextGuard MCP Server (Phase 2)
4
+ * Universal Model Context Protocol server exposing ContextGuard's context
5
+ * admission and token optimization engine as MCP tools.
6
+ *
7
+ * Compatible with: Claude Desktop, Cursor, Windsurf, Antigravity (AGY), Zed.
8
+ * Transport: stdio (JSON-RPC 2.0)
9
+ * SDK: @modelcontextprotocol/server v2 (2026-07-28 spec)
10
+ *
11
+ * Tools:
12
+ * read_file_safe — Protected file read with size guard + AST skeleton fallback
13
+ * inspect_outline — Instant structural outline of any source file (<5ms, $0.00)
14
+ * grep_distilled — Pattern search returning structural context around matches
15
+ */
16
+ import { McpServer } from '@modelcontextprotocol/server';
17
+ import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
18
+ import { z } from 'zod';
19
+ import fs from 'node:fs';
20
+ import path from 'node:path';
21
+ import { execSync } from 'node:child_process';
22
+ import { processWithContextGuard } from '../src/index.js';
23
+ import { extractCodeSkeleton, isSupportedExtension } from '../src/skeletonizer.js';
24
+ // ─── Constants ────────────────────────────────────────────────────────────────
25
+ const SERVER_NAME = 'context-guard';
26
+ const SERVER_VERSION = '1.2.0';
27
+ // Native Claude Code / multimodal formats — allow full passthrough
28
+ const NATIVE_MEDIA_EXTS = new Set([
29
+ '.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.ico', '.bmp', '.avif',
30
+ '.pdf', '.ipynb'
31
+ ]);
32
+ // ─── Server Setup ─────────────────────────────────────────────────────────────
33
+ const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
34
+ // ─── Tool: read_file_safe ─────────────────────────────────────────────────────
35
+ server.registerTool('read_file_safe', {
36
+ title: 'Read File (ContextGuard Protected)',
37
+ description: [
38
+ 'Reads a source file through the ContextGuard admission layer.',
39
+ '• If the file is small (≤300 lines / ≤25 KB): returns full content directly.',
40
+ '• If the file is large: returns an AST structural skeleton with line-tagged signatures.',
41
+ '• If the file is a binary (wasm/zip/sqlite/dll), lockfile, or minified bundle: returns an informative block message with CLI alternatives.',
42
+ '• If start_line / end_line are provided: returns the exact slice without applying the guard.',
43
+ 'This tool replaces raw "Read file" calls and prevents context window flooding from large files.'
44
+ ].join('\n'),
45
+ inputSchema: {
46
+ file_path: z.string().describe('Absolute or relative path to the file to read.'),
47
+ start_line: z.number().int().positive().optional().describe('First line to read (1-indexed). Bypasses size guard if set.'),
48
+ end_line: z.number().int().positive().optional().describe('Last line to read (1-indexed, inclusive). Bypasses size guard if set.'),
49
+ query: z.string().optional().describe('Optional semantic question. If provided, the file is passed to a worker model (Gemini Flash / Ollama) for summarization instead of AST extraction.')
50
+ }
51
+ }, async ({ file_path, start_line, end_line, query }) => {
52
+ try {
53
+ const resolved = path.resolve(process.cwd(), file_path);
54
+ // File existence check
55
+ if (!fs.existsSync(resolved)) {
56
+ return {
57
+ content: [{
58
+ type: 'text',
59
+ text: `⚠️ [CONTEXTGUARD] File not found: ${file_path}`
60
+ }]
61
+ };
62
+ }
63
+ if (fs.statSync(resolved).isDirectory()) {
64
+ return {
65
+ content: [{
66
+ type: 'text',
67
+ text: `⚠️ [CONTEXTGUARD] Path is a directory: ${file_path}. Use a file path.`
68
+ }]
69
+ };
70
+ }
71
+ const ext = path.extname(resolved).toLowerCase();
72
+ // Native multimodal / notebook formats — passthrough directly
73
+ if (NATIVE_MEDIA_EXTS.has(ext)) {
74
+ const raw = fs.readFileSync(resolved);
75
+ const isText = ext === '.ipynb' || ext === '.svg';
76
+ return {
77
+ content: [{
78
+ type: 'text',
79
+ text: isText
80
+ ? raw.toString('utf-8')
81
+ : `[Native binary format: ${ext}. Use multimodal vision tool or platform viewer to inspect.]`
82
+ }]
83
+ };
84
+ }
85
+ // Run through ContextGuard engine
86
+ const result = await processWithContextGuard({
87
+ filePath: file_path,
88
+ startLine: start_line,
89
+ endLine: end_line,
90
+ query: query ?? ''
91
+ });
92
+ // Format the response with status metadata header
93
+ const statusBadge = {
94
+ PASSTHROUGH_FULL: '✅ PASSTHROUGH',
95
+ PASSTHROUGH_SLICE: '✅ SLICE',
96
+ SHUNTED_LOCAL_AST: '🛡️ AST SKELETON',
97
+ SHUNTED_WORKER_MODEL: '🤖 WORKER SUMMARY',
98
+ SHUNT_FALLBACK: '⚠️ FALLBACK',
99
+ BLOCKED_BINARY: '🛑 BLOCKED (BINARY)',
100
+ BLOCKED_LOCKFILE: '🛑 BLOCKED (LOCKFILE)',
101
+ BLOCKED_MINIFIED: '🛑 BLOCKED (MINIFIED)',
102
+ ERROR: '❌ ERROR'
103
+ };
104
+ const badge = statusBadge[result.status] ?? result.status;
105
+ const meta = result.totalLines
106
+ ? `${result.totalLines} lines · ${((result.sizeBytes ?? 0) / 1024).toFixed(1)} KB`
107
+ : '';
108
+ const header = meta
109
+ ? `<!-- [CONTEXTGUARD ${badge}] ${meta} -->\n`
110
+ : `<!-- [CONTEXTGUARD ${badge}] -->\n`;
111
+ const isBlocked = ['BLOCKED_BINARY', 'BLOCKED_LOCKFILE', 'BLOCKED_MINIFIED', 'ERROR'].includes(result.status);
112
+ return {
113
+ content: [{
114
+ type: 'text',
115
+ text: isBlocked
116
+ ? `${badge}\n\n${result.content || result.error || result.reason}`
117
+ : header + result.content
118
+ }],
119
+ isError: isBlocked
120
+ };
121
+ }
122
+ catch (err) {
123
+ return {
124
+ content: [{ type: 'text', text: `❌ [CONTEXTGUARD ERROR] ${err.message}` }],
125
+ isError: true
126
+ };
127
+ }
128
+ });
129
+ // ─── Tool: inspect_outline ────────────────────────────────────────────────────
130
+ server.registerTool('inspect_outline', {
131
+ title: 'Inspect File Outline',
132
+ description: [
133
+ 'Returns a structural outline (classes, interfaces, functions, types, signals) of a source file.',
134
+ 'This is a zero-cost, <5ms operation using the Tier 0 local AST skeletonizer.',
135
+ 'Supports: TypeScript, JavaScript, Python, Go, Rust, Java, C#, Svelte 5, Vue 3, Angular, Astro.',
136
+ 'Use this to navigate large files before calling read_file_safe with targeted line ranges.',
137
+ 'Returns null if the file format is not supported for structural extraction.'
138
+ ].join('\n'),
139
+ inputSchema: {
140
+ file_path: z.string().describe('Absolute or relative path to the source file.')
141
+ }
142
+ }, async ({ file_path }) => {
143
+ try {
144
+ const resolved = path.resolve(process.cwd(), file_path);
145
+ if (!fs.existsSync(resolved)) {
146
+ return {
147
+ content: [{ type: 'text', text: `⚠️ File not found: ${file_path}` }],
148
+ isError: true
149
+ };
150
+ }
151
+ const ext = path.extname(resolved);
152
+ const langKey = isSupportedExtension(ext);
153
+ if (!langKey) {
154
+ return {
155
+ content: [{
156
+ type: 'text',
157
+ text: `ℹ️ [CONTEXTGUARD] File format "${ext}" is not supported for structural outline extraction.\nSupported: .ts, .tsx, .js, .jsx, .py, .go, .rs, .java, .cs, .svelte, .vue, .astro`
158
+ }]
159
+ };
160
+ }
161
+ const raw = fs.readFileSync(resolved, 'utf-8');
162
+ const stat = fs.statSync(resolved);
163
+ const totalLines = raw.split('\n').length;
164
+ const skeleton = extractCodeSkeleton(raw, ext);
165
+ if (!skeleton) {
166
+ return {
167
+ content: [{
168
+ type: 'text',
169
+ text: `ℹ️ [CONTEXTGUARD] No structural declarations found in "${path.basename(file_path)}" (${totalLines} lines).`
170
+ }]
171
+ };
172
+ }
173
+ return {
174
+ content: [{
175
+ type: 'text',
176
+ text: [
177
+ `<!-- [CONTEXTGUARD OUTLINE] ${path.basename(file_path)} · ${totalLines} lines · ${(stat.size / 1024).toFixed(1)} KB -->`,
178
+ `<!-- Use read_file_safe(file_path, start_line=N, end_line=M) to read any section. -->`,
179
+ '',
180
+ skeleton
181
+ ].join('\n')
182
+ }]
183
+ };
184
+ }
185
+ catch (err) {
186
+ return {
187
+ content: [{ type: 'text', text: `❌ [CONTEXTGUARD ERROR] ${err.message}` }],
188
+ isError: true
189
+ };
190
+ }
191
+ });
192
+ // ─── Tool: grep_distilled ─────────────────────────────────────────────────────
193
+ server.registerTool('grep_distilled', {
194
+ title: 'Grep (Distilled)',
195
+ description: [
196
+ 'Searches for a pattern in files and returns only the structural outline of matching files,',
197
+ 'not the full content. Prevents context flooding when searching across a large codebase.',
198
+ 'For each matching file, returns the filename, matching lines, and the structural skeleton.',
199
+ 'Pattern supports ripgrep syntax (fixed strings by default, pass is_regex=true for regex).',
200
+ 'Results are capped at 20 files to keep context manageable.'
201
+ ].join('\n'),
202
+ inputSchema: {
203
+ pattern: z.string().describe('Search pattern (string or regex if is_regex=true).'),
204
+ directory: z.string().optional().describe('Directory to search in. Defaults to current working directory.'),
205
+ include: z.string().optional().describe('Glob to filter files (e.g. "*.ts", "src/**/*.py"). Defaults to all text files.'),
206
+ is_regex: z.boolean().optional().describe('Treat pattern as a regex. Default false (fixed string search).')
207
+ }
208
+ }, async ({ pattern, directory, include, is_regex }) => {
209
+ try {
210
+ const searchDir = directory ? path.resolve(process.cwd(), directory) : process.cwd();
211
+ if (!fs.existsSync(searchDir)) {
212
+ return {
213
+ content: [{ type: 'text', text: `⚠️ Directory not found: ${directory}` }],
214
+ isError: true
215
+ };
216
+ }
217
+ // Build ripgrep command
218
+ const rgArgs = [
219
+ '--line-number',
220
+ '--with-filename',
221
+ '--no-heading',
222
+ '--color=never',
223
+ '--max-count=5', // max 5 matches per file
224
+ '-l' // list matching files first
225
+ ];
226
+ if (!is_regex)
227
+ rgArgs.push('--fixed-strings');
228
+ if (include)
229
+ rgArgs.push('--glob', include);
230
+ rgArgs.push('--', pattern, searchDir);
231
+ let matchingFiles = [];
232
+ try {
233
+ const output = execSync(`rg ${rgArgs.map(a => JSON.stringify(a)).join(' ')}`, {
234
+ encoding: 'utf-8',
235
+ maxBuffer: 1024 * 512
236
+ });
237
+ matchingFiles = output.trim().split('\n').filter(Boolean).slice(0, 20);
238
+ }
239
+ catch (rgErr) {
240
+ // rg exits with 1 if no matches
241
+ if (rgErr.status === 1) {
242
+ return {
243
+ content: [{ type: 'text', text: `ℹ️ [CONTEXTGUARD GREP] No matches for "${pattern}" in ${searchDir}` }]
244
+ };
245
+ }
246
+ // rg not installed — fall back to a Node.js glob scan
247
+ return {
248
+ content: [{ type: 'text', text: `⚠️ ripgrep (rg) is not available. Install it with: brew install ripgrep\nCannot perform distilled grep without rg.` }],
249
+ isError: true
250
+ };
251
+ }
252
+ if (matchingFiles.length === 0) {
253
+ return {
254
+ content: [{ type: 'text', text: `ℹ️ [CONTEXTGUARD GREP] No matches for "${pattern}"` }]
255
+ };
256
+ }
257
+ // For each matching file: get matching lines + structural outline
258
+ const results = [
259
+ `<!-- [CONTEXTGUARD GREP] Pattern: "${pattern}" | ${matchingFiles.length} matching files -->`,
260
+ ''
261
+ ];
262
+ for (const filePath of matchingFiles) {
263
+ const ext = path.extname(filePath);
264
+ const relPath = path.relative(process.cwd(), filePath);
265
+ // Get matching lines with context
266
+ let matchLines = '';
267
+ try {
268
+ const matchArgs = [
269
+ '--line-number', '--no-heading', '--color=never',
270
+ '--max-count=5', '--context=1'
271
+ ];
272
+ if (!is_regex)
273
+ matchArgs.push('--fixed-strings');
274
+ if (include)
275
+ matchArgs.push('--glob', include);
276
+ matchArgs.push('--', pattern, filePath);
277
+ matchLines = execSync(`rg ${matchArgs.map(a => JSON.stringify(a)).join(' ')}`, {
278
+ encoding: 'utf-8',
279
+ maxBuffer: 1024 * 64
280
+ }).trim();
281
+ }
282
+ catch {
283
+ matchLines = '(error reading match lines)';
284
+ }
285
+ // Get structural outline
286
+ const raw = fs.readFileSync(filePath, 'utf-8');
287
+ const skeleton = extractCodeSkeleton(raw, ext);
288
+ const stat = fs.statSync(filePath);
289
+ const totalLines = raw.split('\n').length;
290
+ results.push(`### ${relPath} (${totalLines} lines, ${(stat.size / 1024).toFixed(1)} KB)`);
291
+ results.push('**Matches:**');
292
+ results.push('```');
293
+ results.push(matchLines);
294
+ results.push('```');
295
+ if (skeleton) {
296
+ results.push('**Structural outline:**');
297
+ results.push('```');
298
+ results.push(skeleton);
299
+ results.push('```');
300
+ }
301
+ results.push('');
302
+ }
303
+ if (matchingFiles.length === 20) {
304
+ results.push('> ⚠️ Results capped at 20 files. Narrow your search with a more specific pattern or --include glob.');
305
+ }
306
+ return {
307
+ content: [{ type: 'text', text: results.join('\n') }]
308
+ };
309
+ }
310
+ catch (err) {
311
+ return {
312
+ content: [{ type: 'text', text: `❌ [CONTEXTGUARD ERROR] ${err.message}` }],
313
+ isError: true
314
+ };
315
+ }
316
+ });
317
+ // ─── Start ─────────────────────────────────────────────────────────────────────
318
+ async function main() {
319
+ const transport = new StdioServerTransport();
320
+ await server.connect(transport);
321
+ // Log to stderr — never to stdout (would corrupt JSON-RPC channel)
322
+ process.stderr.write(`[ContextGuard MCP] Server v${SERVER_VERSION} running on stdio\n`);
323
+ }
324
+ main().catch((err) => {
325
+ process.stderr.write(`[ContextGuard MCP] Fatal error: ${err.message}\n`);
326
+ process.exit(1);
327
+ });
328
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../../bin/mcp-server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEnF,iFAAiF;AAEjF,MAAM,WAAW,GAAG,eAAe,CAAC;AACpC,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B,mEAAmE;AACnE,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IAChC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;IACzE,MAAM,EAAE,QAAQ;CACjB,CAAC,CAAC;AAEH,iFAAiF;AAEjF,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;AAE7E,iFAAiF;AAEjF,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;IACE,KAAK,EAAE,oCAAoC;IAC3C,WAAW,EAAE;QACX,+DAA+D;QAC/D,8EAA8E;QAC9E,yFAAyF;QACzF,4IAA4I;QAC5I,8FAA8F;QAC9F,iGAAiG;KAClG,CAAC,IAAI,CAAC,IAAI,CAAC;IACZ,WAAW,EAAE;QACX,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gDAAgD,CAAC;QAChF,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6DAA6D,CAAC;QAC1H,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uEAAuE,CAAC;QAClI,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oJAAoJ,CAAC;KAC5L;CACF,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;IACnD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;QAExD,uBAAuB;QACvB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,qCAAqC,SAAS,EAAE;qBACvD,CAAC;aACH,CAAC;QACJ,CAAC;QAED,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YACxC,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,0CAA0C,SAAS,oBAAoB;qBAC9E,CAAC;aACH,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;QAEjD,8DAA8D;QAC9D,IAAI,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YACtC,MAAM,MAAM,GAAG,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,MAAM,CAAC;YAClD,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,MAAM;4BACV,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC;4BACvB,CAAC,CAAC,0BAA0B,GAAG,8DAA8D;qBAChG,CAAC;aACH,CAAC;QACJ,CAAC;QAED,kCAAkC;QAClC,MAAM,MAAM,GAAG,MAAM,uBAAuB,CAAC;YAC3C,QAAQ,EAAE,SAAS;YACnB,SAAS,EAAE,UAAU;YACrB,OAAO,EAAE,QAAQ;YACjB,KAAK,EAAE,KAAK,IAAI,EAAE;SACnB,CAAC,CAAC;QAEH,kDAAkD;QAClD,MAAM,WAAW,GAA2B;YAC1C,gBAAgB,EAAE,eAAe;YACjC,iBAAiB,EAAE,SAAS;YAC5B,iBAAiB,EAAE,kBAAkB;YACrC,oBAAoB,EAAE,mBAAmB;YACzC,cAAc,EAAE,aAAa;YAC7B,cAAc,EAAE,qBAAqB;YACrC,gBAAgB,EAAE,uBAAuB;YACzC,gBAAgB,EAAE,uBAAuB;YACzC,KAAK,EAAE,SAAS;SACjB,CAAC;QAEF,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC;QAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU;YAC5B,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,YAAY,CAAC,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;YAClF,CAAC,CAAC,EAAE,CAAC;QAEP,MAAM,MAAM,GAAG,IAAI;YACjB,CAAC,CAAC,sBAAsB,KAAK,KAAK,IAAI,QAAQ;YAC9C,CAAC,CAAC,sBAAsB,KAAK,SAAS,CAAC;QAEzC,MAAM,SAAS,GAAG,CAAC,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAE9G,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,SAAS;wBACb,CAAC,CAAC,GAAG,KAAK,OAAO,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE;wBAClE,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,OAAO;iBAC5B,CAAC;YACF,OAAO,EAAE,SAAS;SACnB,CAAC;IACJ,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;YAC1E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,iFAAiF;AAEjF,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;IACE,KAAK,EAAE,sBAAsB;IAC7B,WAAW,EAAE;QACX,iGAAiG;QACjG,8EAA8E;QAC9E,gGAAgG;QAChG,2FAA2F;QAC3F,6EAA6E;KAC9E,CAAC,IAAI,CAAC,IAAI,CAAC;IACZ,WAAW,EAAE;QACX,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+CAA+C,CAAC;KAChF;CACF,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;IACtB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;QAExD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,SAAS,EAAE,EAAE,CAAC;gBACpE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAE1C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,kCAAkC,GAAG,0IAA0I;qBACtL,CAAC;aACH,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;QAC1C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAE/C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,0DAA0D,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,UAAU,UAAU;qBACnH,CAAC;aACH,CAAC;QACJ,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE;wBACJ,+BAA+B,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,UAAU,YAAY,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;wBACzH,uFAAuF;wBACvF,EAAE;wBACF,QAAQ;qBACT,CAAC,IAAI,CAAC,IAAI,CAAC;iBACb,CAAC;SACH,CAAC;IACJ,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;YAC1E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,iFAAiF;AAEjF,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;IACE,KAAK,EAAE,kBAAkB;IACzB,WAAW,EAAE;QACX,4FAA4F;QAC5F,yFAAyF;QACzF,4FAA4F;QAC5F,2FAA2F;QAC3F,4DAA4D;KAC7D,CAAC,IAAI,CAAC,IAAI,CAAC;IACZ,WAAW,EAAE;QACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oDAAoD,CAAC;QAClF,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gEAAgE,CAAC;QAC3G,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gFAAgF,CAAC;QACzH,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gEAAgE,CAAC;KAC5G;CACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE;IAClD,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QAErF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,2BAA2B,SAAS,EAAE,EAAE,CAAC;gBACzE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,wBAAwB;QACxB,MAAM,MAAM,GAAa;YACvB,eAAe;YACf,iBAAiB;YACjB,cAAc;YACd,eAAe;YACf,eAAe,EAAO,yBAAyB;YAC/C,IAAI,CAAmB,4BAA4B;SACpD,CAAC;QAEF,IAAI,CAAC,QAAQ;YAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC9C,IAAI,OAAO;YAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;QAEtC,IAAI,aAAa,GAAa,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;gBAC5E,QAAQ,EAAE,OAAO;gBACjB,SAAS,EAAE,IAAI,GAAG,GAAG;aACtB,CAAC,CAAC;YACH,aAAa,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzE,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,gCAAgC;YAChC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0CAA0C,OAAO,QAAQ,SAAS,EAAE,EAAE,CAAC;iBACxG,CAAC;YACJ,CAAC;YACD,sDAAsD;YACtD,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,oHAAoH,EAAE,CAAC;gBACvJ,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0CAA0C,OAAO,GAAG,EAAE,CAAC;aACxF,CAAC;QACJ,CAAC;QAED,kEAAkE;QAClE,MAAM,OAAO,GAAa;YACxB,sCAAsC,OAAO,OAAO,aAAa,CAAC,MAAM,qBAAqB;YAC7F,EAAE;SACH,CAAC;QAEF,KAAK,MAAM,QAAQ,IAAI,aAAa,EAAE,CAAC;YACrC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;YAEvD,kCAAkC;YAClC,IAAI,UAAU,GAAG,EAAE,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG;oBAChB,eAAe,EAAE,cAAc,EAAE,eAAe;oBAChD,eAAe,EAAE,aAAa;iBAC/B,CAAC;gBACF,IAAI,CAAC,QAAQ;oBAAE,SAAS,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;gBACjD,IAAI,OAAO;oBAAE,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAC/C,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;gBACxC,UAAU,GAAG,QAAQ,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;oBAC7E,QAAQ,EAAE,OAAO;oBACjB,SAAS,EAAE,IAAI,GAAG,EAAE;iBACrB,CAAC,CAAC,IAAI,EAAE,CAAC;YACZ,CAAC;YAAC,MAAM,CAAC;gBACP,UAAU,GAAG,6BAA6B,CAAC;YAC7C,CAAC;YAED,yBAAyB;YACzB,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC/C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAC/C,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACnC,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;YAE1C,OAAO,CAAC,IAAI,CAAC,OAAO,OAAO,KAAK,UAAU,WAAW,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC1F,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;gBACxC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACvB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,CAAC;QAED,IAAI,aAAa,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC,qGAAqG,CAAC,CAAC;QACtH,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;SACtD,CAAC;IACJ,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;YAC1E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,kFAAkF;AAElF,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,mEAAmE;IACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,cAAc,qBAAqB,CAAC,CAAC;AAC1F,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;IACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mcp-server.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.test.d.ts","sourceRoot":"","sources":["../../tests/mcp-server.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,215 @@
1
+ /**
2
+ * MCP Server Integration Tests (Phase 2)
3
+ * Tests the ContextGuard MCP server by spawning it as a child process
4
+ * and sending JSON-RPC 2.0 messages over stdin/stdout.
5
+ */
6
+ import { describe, it, before, after } from 'node:test';
7
+ import assert from 'node:assert';
8
+ import { spawn } from 'node:child_process';
9
+ import path from 'node:path';
10
+ import fs from 'node:fs';
11
+ import os from 'node:os';
12
+ import { fileURLToPath } from 'node:url';
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+ const MCP_SERVER_BIN = path.resolve(__dirname, '../../dist/bin/mcp-server.js');
15
+ function sendRequest(proc, request) {
16
+ return new Promise((resolve, reject) => {
17
+ let buffer = '';
18
+ const onData = (chunk) => {
19
+ buffer += chunk.toString();
20
+ const lines = buffer.split('\n');
21
+ for (const line of lines) {
22
+ if (!line.trim())
23
+ continue;
24
+ try {
25
+ const parsed = JSON.parse(line);
26
+ if (parsed.id === request.id) {
27
+ proc.stdout.removeListener('data', onData);
28
+ resolve(parsed);
29
+ return;
30
+ }
31
+ }
32
+ catch {
33
+ // partial or unrelated line — continue
34
+ }
35
+ }
36
+ buffer = lines[lines.length - 1] ?? '';
37
+ };
38
+ proc.stdout.on('data', onData);
39
+ proc.on('error', reject);
40
+ proc.stdin.write(JSON.stringify(request) + '\n');
41
+ setTimeout(() => {
42
+ proc.stdout.removeListener('data', onData);
43
+ reject(new Error(`Timeout waiting for response to request id=${request.id}`));
44
+ }, 5000);
45
+ });
46
+ }
47
+ // ─── Test Suite ───────────────────────────────────────────────────────────────
48
+ describe('ContextGuard MCP Server (Phase 2)', () => {
49
+ let proc;
50
+ let nextId = 1;
51
+ const req = (method, params) => ({
52
+ jsonrpc: '2.0',
53
+ id: nextId++,
54
+ method,
55
+ params
56
+ });
57
+ before(() => {
58
+ proc = spawn('node', [MCP_SERVER_BIN], {
59
+ stdio: ['pipe', 'pipe', 'pipe']
60
+ });
61
+ proc.stderr.on('data', (d) => {
62
+ // Suppress server startup messages in test output
63
+ void d;
64
+ });
65
+ // Give the server a moment to initialize
66
+ return new Promise((r) => setTimeout(r, 300));
67
+ });
68
+ after(() => {
69
+ proc.stdin.end();
70
+ proc.kill();
71
+ });
72
+ // ── Initialization ──────────────────────────────────────────────────────────
73
+ it('should respond to initialize handshake', async () => {
74
+ const res = await sendRequest(proc, req('initialize', {
75
+ protocolVersion: '2024-11-05',
76
+ capabilities: {},
77
+ clientInfo: { name: 'test-client', version: '0.1.0' }
78
+ }));
79
+ assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
80
+ const result = res.result;
81
+ assert.ok(result.serverInfo, 'Must include serverInfo');
82
+ const info = result.serverInfo;
83
+ assert.strictEqual(info.name, 'context-guard');
84
+ assert.strictEqual(info.version, '1.2.0');
85
+ });
86
+ it('should list exactly 3 registered tools', async () => {
87
+ const res = await sendRequest(proc, req('tools/list'));
88
+ assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
89
+ const result = res.result;
90
+ const names = result.tools.map((t) => t.name);
91
+ assert.ok(names.includes('read_file_safe'), 'read_file_safe must be registered');
92
+ assert.ok(names.includes('inspect_outline'), 'inspect_outline must be registered');
93
+ assert.ok(names.includes('grep_distilled'), 'grep_distilled must be registered');
94
+ assert.strictEqual(names.length, 3, 'Must have exactly 3 tools');
95
+ });
96
+ // ── read_file_safe ──────────────────────────────────────────────────────────
97
+ it('read_file_safe: should return full content for a small file', async () => {
98
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
99
+ const tmpFile = path.join(tmpDir, 'small.ts');
100
+ fs.writeFileSync(tmpFile, 'export const foo = 42;\nexport const bar = "hello";\n');
101
+ const res = await sendRequest(proc, req('tools/call', {
102
+ name: 'read_file_safe',
103
+ arguments: { file_path: tmpFile }
104
+ }));
105
+ assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
106
+ const result = res.result;
107
+ const text = result.content[0].text;
108
+ assert.ok(text.includes('PASSTHROUGH'), 'Must include PASSTHROUGH badge for small file');
109
+ assert.ok(text.includes('foo') || text.includes('bar'), 'Must include file content');
110
+ fs.rmSync(tmpDir, { recursive: true });
111
+ });
112
+ it('read_file_safe: should return AST skeleton for a large TypeScript file', async () => {
113
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
114
+ const tmpFile = path.join(tmpDir, 'large.ts');
115
+ // Generate a file larger than 300 lines
116
+ const lines = [
117
+ 'import path from "node:path";',
118
+ 'import fs from "node:fs";',
119
+ '',
120
+ 'export interface UserService {',
121
+ ' getUser(id: string): Promise<User>;',
122
+ ' createUser(data: CreateUserDto): Promise<User>;',
123
+ '}',
124
+ '',
125
+ 'export class UserServiceImpl implements UserService {',
126
+ ' async getUser(id: string): Promise<User> { return null as any; }',
127
+ ' async createUser(data: CreateUserDto): Promise<User> { return null as any; }',
128
+ '}'
129
+ ];
130
+ // Pad to >300 lines
131
+ while (lines.length < 310)
132
+ lines.push(` // line ${lines.length}`);
133
+ fs.writeFileSync(tmpFile, lines.join('\n'));
134
+ const res = await sendRequest(proc, req('tools/call', {
135
+ name: 'read_file_safe',
136
+ arguments: { file_path: tmpFile }
137
+ }));
138
+ assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
139
+ const result = res.result;
140
+ const text = result.content[0].text;
141
+ assert.ok(text.includes('SKELETON') || text.includes('SHUNT') || text.includes('UserService'), 'Must return skeleton or structural content for large file');
142
+ fs.rmSync(tmpDir, { recursive: true });
143
+ });
144
+ it('read_file_safe: should block a .wasm binary with isError=true', async () => {
145
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
146
+ const wasmFile = path.join(tmpDir, 'module.wasm');
147
+ fs.writeFileSync(wasmFile, Buffer.from([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
148
+ const res = await sendRequest(proc, req('tools/call', {
149
+ name: 'read_file_safe',
150
+ arguments: { file_path: wasmFile }
151
+ }));
152
+ assert.ok(!res.error, 'JSON-RPC should not error — tool should handle it gracefully');
153
+ const result = res.result;
154
+ assert.ok(result.isError === true, 'isError must be true for blocked binary');
155
+ const text = result.content[0].text;
156
+ assert.ok(text.includes('BLOCKED') || text.includes('binario'), 'Must include block reason');
157
+ fs.rmSync(tmpDir, { recursive: true });
158
+ });
159
+ it('read_file_safe: should return a scoped slice when start_line/end_line given', async () => {
160
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
161
+ const tmpFile = path.join(tmpDir, 'ranged.ts');
162
+ const content = Array.from({ length: 50 }, (_, i) => `// line ${i + 1}`).join('\n');
163
+ fs.writeFileSync(tmpFile, content);
164
+ const res = await sendRequest(proc, req('tools/call', {
165
+ name: 'read_file_safe',
166
+ arguments: { file_path: tmpFile, start_line: 5, end_line: 10 }
167
+ }));
168
+ assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
169
+ const result = res.result;
170
+ const text = result.content[0].text;
171
+ assert.ok(text.includes('line 5') || text.includes('SLICE'), 'Must contain sliced content');
172
+ fs.rmSync(tmpDir, { recursive: true });
173
+ });
174
+ // ── inspect_outline ─────────────────────────────────────────────────────────
175
+ it('inspect_outline: should return structural outline for a TypeScript file', async () => {
176
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
177
+ const tmpFile = path.join(tmpDir, 'service.ts');
178
+ fs.writeFileSync(tmpFile, [
179
+ 'export interface AuthService {',
180
+ ' login(email: string, password: string): Promise<string>;',
181
+ ' logout(token: string): void;',
182
+ '}',
183
+ '',
184
+ 'export class AuthServiceImpl implements AuthService {',
185
+ ' async login(email: string, password: string): Promise<string> { return "token"; }',
186
+ ' logout(token: string): void {}',
187
+ '}'
188
+ ].join('\n'));
189
+ const res = await sendRequest(proc, req('tools/call', {
190
+ name: 'inspect_outline',
191
+ arguments: { file_path: tmpFile }
192
+ }));
193
+ assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
194
+ const result = res.result;
195
+ const text = result.content[0].text;
196
+ assert.ok(text.includes('AuthService') || text.includes('login'), 'Must include structural declarations');
197
+ assert.ok(text.includes('OUTLINE'), 'Must include OUTLINE badge');
198
+ fs.rmSync(tmpDir, { recursive: true });
199
+ });
200
+ // ── grep_distilled ──────────────────────────────────────────────────────────
201
+ it('grep_distilled: should return no-match message when pattern is not found', async () => {
202
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
203
+ fs.writeFileSync(path.join(tmpDir, 'a.ts'), 'export const x = 1;');
204
+ const res = await sendRequest(proc, req('tools/call', {
205
+ name: 'grep_distilled',
206
+ arguments: { pattern: 'PATTERN_THAT_DOES_NOT_EXIST_XYZ123', directory: tmpDir }
207
+ }));
208
+ assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
209
+ const result = res.result;
210
+ const text = result.content[0].text;
211
+ assert.ok(text.includes('No matches') || text.includes('not available'), 'Must indicate no matches');
212
+ fs.rmSync(tmpDir, { recursive: true });
213
+ });
214
+ });
215
+ //# sourceMappingURL=mcp-server.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.test.js","sourceRoot":"","sources":["../../tests/mcp-server.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Read|Bash",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "node ./node_modules/@kevinbermudezc/context-guard/dist/bin/claude-hook.js"
10
+ }
11
+ ]
12
+ }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "context-guard": {
4
+ "command": "npx",
5
+ "args": [
6
+ "-y",
7
+ "@kevinbermudezc/context-guard@latest",
8
+ "context-guard-mcp"
9
+ ]
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "context-guard": {
4
+ "command": "npx",
5
+ "args": [
6
+ "-y",
7
+ "@kevinbermudezc/context-guard@latest",
8
+ "context-guard-mcp"
9
+ ]
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "context-guard": {
4
+ "command": "npx",
5
+ "args": [
6
+ "-y",
7
+ "@kevinbermudezc/context-guard@latest",
8
+ "context-guard-mcp"
9
+ ]
10
+ }
11
+ }
12
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kevinbermudezc/context-guard",
3
- "version": "1.1.1",
4
- "description": "Intelligent context admission control and token optimization for Claude Code, Cursor, and Agentic workflows.",
3
+ "version": "1.2.0",
4
+ "description": "Intelligent context admission control and token optimization for Claude Code, Cursor, Windsurf, Antigravity, and Agentic AI workflows.",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -10,19 +10,26 @@
10
10
  "types": "./dist/src/index.d.ts",
11
11
  "bin": {
12
12
  "context-guard": "./dist/bin/context-guard.js",
13
- "context-guard-hook": "./dist/bin/claude-hook.js"
13
+ "context-guard-hook": "./dist/bin/claude-hook.js",
14
+ "context-guard-mcp": "./dist/bin/mcp-server.js"
14
15
  },
15
16
  "files": [
16
17
  "dist",
17
18
  "README.md",
18
- "LICENSE"
19
+ "LICENSE",
20
+ "examples"
19
21
  ],
20
22
  "devDependencies": {
21
23
  "@types/node": "^22.13.9",
22
- "typescript": "^7.0.2"
24
+ "typescript": "^7.0.2",
25
+ "zod": "^4.6.5"
23
26
  },
24
27
  "keywords": [
25
28
  "claude-code",
29
+ "cursor",
30
+ "windsurf",
31
+ "mcp",
32
+ "model-context-protocol",
26
33
  "pretooluse-hook",
27
34
  "ai-agent",
28
35
  "token-optimization",
@@ -43,6 +50,9 @@
43
50
  "engines": {
44
51
  "node": ">=18.0.0"
45
52
  },
53
+ "dependencies": {
54
+ "@modelcontextprotocol/server": "2.2.0"
55
+ },
46
56
  "scripts": {
47
57
  "build": "tsc && chmod +x dist/bin/*.js",
48
58
  "test": "pnpm run build && node --test dist/tests/**/*.test.js"