@kevinbermudezc/context-guard 1.1.1 → 1.2.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/README.md +76 -1
- package/dist/bin/mcp-server.d.ts +3 -0
- package/dist/bin/mcp-server.d.ts.map +1 -0
- package/dist/bin/mcp-server.js +446 -0
- package/dist/bin/mcp-server.js.map +1 -0
- package/dist/tests/mcp-server.test.d.ts +2 -0
- package/dist/tests/mcp-server.test.d.ts.map +1 -0
- package/dist/tests/mcp-server.test.js +248 -0
- package/dist/tests/mcp-server.test.js.map +1 -0
- package/examples/claude-settings.json +15 -0
- package/examples/mcp-configs/antigravity-mcp.json +13 -0
- package/examples/mcp-configs/claude-desktop.json +13 -0
- package/examples/mcp-configs/cursor-mcp.json +13 -0
- package/package.json +15 -5
package/README.md
CHANGED
|
@@ -145,7 +145,82 @@ That's it! When Claude Code attempts to run `Read` (or raw bash reads like `cat
|
|
|
145
145
|
|
|
146
146
|
---
|
|
147
147
|
|
|
148
|
-
##
|
|
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 tools, passive resources, and optimized prompt templates:
|
|
153
|
+
|
|
154
|
+
### 🛠️ Tools
|
|
155
|
+
|
|
156
|
+
| Tool | Description | Cost | Latency |
|
|
157
|
+
|---|---|---|---|
|
|
158
|
+
| `read_file_safe` | Protected file read — passthrough for small files, AST skeleton for large ones, block for binaries/lockfiles | **\$0.00** | **< 5ms** |
|
|
159
|
+
| `inspect_outline` | Instant structural outline (classes, functions, types, signals) with line numbers | **\$0.00** | **< 5ms** |
|
|
160
|
+
| `grep_distilled` | Pattern search returning only structural context around matches (uses ripgrep with pure Node fallback) | **\$0.00** | **< 50ms** |
|
|
161
|
+
|
|
162
|
+
### 📦 Resources & Prompts
|
|
163
|
+
|
|
164
|
+
| Type | URI / Name | Description |
|
|
165
|
+
|---|---|---|
|
|
166
|
+
| **Resource** | `contextguard://config` | Live inspection of active thresholds, bypass formats, and worker provider |
|
|
167
|
+
| **Prompt** | `investigate_codebase_safely` | System prompt template enforcing token preservation and outline-first navigation |
|
|
168
|
+
|
|
169
|
+
### Install & Run
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# One-shot (no install):
|
|
173
|
+
npx -y -p @kevinbermudezc/context-guard context-guard-mcp
|
|
174
|
+
|
|
175
|
+
# Or as a local dependency:
|
|
176
|
+
pnpm add -D @kevinbermudezc/context-guard
|
|
177
|
+
node ./node_modules/@kevinbermudezc/context-guard/dist/bin/mcp-server.js
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 🖱️ Cursor (`~/.cursor/mcp.json`)
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"mcpServers": {
|
|
185
|
+
"context-guard": {
|
|
186
|
+
"command": "npx",
|
|
187
|
+
"args": ["-y", "-p", "@kevinbermudezc/context-guard@latest", "context-guard-mcp"]
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 🤖 Claude Desktop (`~/Library/Application Support/Claude/claude_desktop_config.json`)
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"mcpServers": {
|
|
198
|
+
"context-guard": {
|
|
199
|
+
"command": "npx",
|
|
200
|
+
"args": ["-y", "-p", "@kevinbermudezc/context-guard@latest", "context-guard-mcp"]
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### 🪁 Antigravity / Google AGY (`.agents/mcp.json` or `~/.config/agy/mcp.json`)
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"mcpServers": {
|
|
211
|
+
"context-guard": {
|
|
212
|
+
"command": "npx",
|
|
213
|
+
"args": ["-y", "-p", "@kevinbermudezc/context-guard@latest", "context-guard-mcp"]
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
> **Note:** Configuration examples are also available in [`examples/mcp-configs/`](./examples/mcp-configs/).
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 🛠️ CLI Usage (Claude Code Hook)
|
|
149
224
|
|
|
150
225
|
You can also use ContextGuard directly as a command-line tool:
|
|
151
226
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../../bin/mcp-server.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,446 @@
|
|
|
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 { processWithContextGuard } from '../src/index.js';
|
|
22
|
+
import { CONFIG } from '../src/config.js';
|
|
23
|
+
import { extractCodeSkeleton, isSupportedExtension } from '../src/skeletonizer.js';
|
|
24
|
+
import { spawnSync } from 'node:child_process';
|
|
25
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
26
|
+
const SERVER_NAME = 'context-guard';
|
|
27
|
+
const SERVER_VERSION = '1.2.0';
|
|
28
|
+
// Native Claude Code / multimodal formats — allow full passthrough
|
|
29
|
+
const NATIVE_MEDIA_EXTS = new Set([
|
|
30
|
+
'.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.ico', '.bmp', '.avif',
|
|
31
|
+
'.pdf', '.ipynb'
|
|
32
|
+
]);
|
|
33
|
+
// Ignored directories for pure Node recursive scan fallback
|
|
34
|
+
const IGNORED_DIRS = new Set([
|
|
35
|
+
'node_modules', '.git', 'dist', 'build', '.next', '.turbo', '.cache',
|
|
36
|
+
'coverage', '.vscode', '.idea'
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* Pure Node.js fallback scanner when ripgrep (rg) is not installed on the system.
|
|
40
|
+
*/
|
|
41
|
+
function safeNodeGrep(searchDir, pattern, isRegex, includeGlob, maxFiles = 20) {
|
|
42
|
+
const results = [];
|
|
43
|
+
let matcher;
|
|
44
|
+
if (isRegex) {
|
|
45
|
+
try {
|
|
46
|
+
const re = new RegExp(pattern);
|
|
47
|
+
matcher = (l) => re.test(l);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
matcher = (l) => l.includes(pattern);
|
|
55
|
+
}
|
|
56
|
+
// Simple extension or filename filter
|
|
57
|
+
const filterExt = includeGlob?.startsWith('*.') ? includeGlob.slice(1).toLowerCase() : null;
|
|
58
|
+
function walk(currentDir) {
|
|
59
|
+
if (results.length >= maxFiles)
|
|
60
|
+
return;
|
|
61
|
+
let entries = [];
|
|
62
|
+
try {
|
|
63
|
+
entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
for (const entry of entries) {
|
|
69
|
+
if (results.length >= maxFiles)
|
|
70
|
+
break;
|
|
71
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
72
|
+
if (entry.isDirectory()) {
|
|
73
|
+
if (!IGNORED_DIRS.has(entry.name) && !entry.name.startsWith('.')) {
|
|
74
|
+
walk(fullPath);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else if (entry.isFile()) {
|
|
78
|
+
if (filterExt && !entry.name.toLowerCase().endsWith(filterExt)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
83
|
+
const lines = content.split('\n');
|
|
84
|
+
const matchingLines = [];
|
|
85
|
+
for (let i = 0; i < lines.length; i++) {
|
|
86
|
+
if (matcher(lines[i])) {
|
|
87
|
+
const start = Math.max(0, i - 1);
|
|
88
|
+
const end = Math.min(lines.length - 1, i + 1);
|
|
89
|
+
for (let c = start; c <= end; c++) {
|
|
90
|
+
matchingLines.push(`${c + 1}:${lines[c]}`);
|
|
91
|
+
}
|
|
92
|
+
if (matchingLines.length >= 10)
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (matchingLines.length > 0) {
|
|
97
|
+
results.push({ filePath: fullPath, matches: matchingLines });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// ignore binary or unreadable files
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
walk(searchDir);
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
109
|
+
// ─── Server Setup ─────────────────────────────────────────────────────────────
|
|
110
|
+
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
|
|
111
|
+
// ─── MCP Resources (Static/Live System Inspection) ───────────────────────────
|
|
112
|
+
server.registerResource('config', 'contextguard://config', {
|
|
113
|
+
title: 'ContextGuard Active Configuration',
|
|
114
|
+
description: 'Current thresholds, active worker provider, and buffer safety settings in effect.',
|
|
115
|
+
mimeType: 'application/json'
|
|
116
|
+
}, async (uri) => {
|
|
117
|
+
return {
|
|
118
|
+
contents: [{
|
|
119
|
+
uri: uri.href,
|
|
120
|
+
text: JSON.stringify({
|
|
121
|
+
version: SERVER_VERSION,
|
|
122
|
+
maxLinesThreshold: CONFIG.maxLines,
|
|
123
|
+
maxBytesThreshold: CONFIG.maxBytes,
|
|
124
|
+
workerProvider: CONFIG.provider,
|
|
125
|
+
supportedSfcFrameworks: ['svelte', 'vue', 'angular', 'astro'],
|
|
126
|
+
nativeMediaBypass: Array.from(NATIVE_MEDIA_EXTS)
|
|
127
|
+
}, null, 2),
|
|
128
|
+
mimeType: 'application/json'
|
|
129
|
+
}]
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
// ─── MCP Prompts (Optimized System Prompt Templates) ─────────────────────────
|
|
133
|
+
server.registerPrompt('investigate_codebase_safely', {
|
|
134
|
+
title: 'Investigate Codebase Safely',
|
|
135
|
+
description: 'Standard system instruction guiding the AI agent to avoid full-file dumps by prioritizing inspect_outline and targeted read_file_safe ranges.'
|
|
136
|
+
}, async () => {
|
|
137
|
+
return {
|
|
138
|
+
messages: [{
|
|
139
|
+
role: 'user',
|
|
140
|
+
content: {
|
|
141
|
+
type: 'text',
|
|
142
|
+
text: [
|
|
143
|
+
'Follow ContextGuard FinOps & token preservation rules:',
|
|
144
|
+
'1. Do not perform unrestricted open reads of files over 300 lines.',
|
|
145
|
+
'2. Call "inspect_outline" first to discover method and type signatures with their exact line tags [L#].',
|
|
146
|
+
'3. Read only targeted slices with "read_file_safe(file_path, start_line, end_line)".',
|
|
147
|
+
'4. When searching for symbols, use "grep_distilled" to examine matching signatures instead of dumping entire source files.'
|
|
148
|
+
].join('\n')
|
|
149
|
+
}
|
|
150
|
+
}]
|
|
151
|
+
};
|
|
152
|
+
});
|
|
153
|
+
// ─── Tool: read_file_safe ─────────────────────────────────────────────────────
|
|
154
|
+
server.registerTool('read_file_safe', {
|
|
155
|
+
title: 'Read File (ContextGuard Protected)',
|
|
156
|
+
description: [
|
|
157
|
+
'Reads a source file through the ContextGuard admission layer.',
|
|
158
|
+
'• If the file is small (≤300 lines / ≤25 KB): returns full content directly.',
|
|
159
|
+
'• If the file is large: returns an AST structural skeleton with line-tagged signatures.',
|
|
160
|
+
'• If the file is a binary (wasm/zip/sqlite/dll), lockfile, or minified bundle: returns an informative block message with CLI alternatives.',
|
|
161
|
+
'• If start_line / end_line are provided: returns the exact slice without applying the guard.',
|
|
162
|
+
'This tool replaces raw "Read file" calls and prevents context window flooding from large files.'
|
|
163
|
+
].join('\n'),
|
|
164
|
+
inputSchema: {
|
|
165
|
+
file_path: z.string().describe('Absolute or relative path to the file to read.'),
|
|
166
|
+
start_line: z.number().int().positive().optional().describe('First line to read (1-indexed). Bypasses size guard if set.'),
|
|
167
|
+
end_line: z.number().int().positive().optional().describe('Last line to read (1-indexed, inclusive). Bypasses size guard if set.'),
|
|
168
|
+
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.')
|
|
169
|
+
}
|
|
170
|
+
}, async ({ file_path, start_line, end_line, query }) => {
|
|
171
|
+
try {
|
|
172
|
+
const resolved = path.resolve(process.cwd(), file_path);
|
|
173
|
+
// File existence check
|
|
174
|
+
if (!fs.existsSync(resolved)) {
|
|
175
|
+
return {
|
|
176
|
+
content: [{
|
|
177
|
+
type: 'text',
|
|
178
|
+
text: `⚠️ [CONTEXTGUARD] File not found: ${file_path}`
|
|
179
|
+
}]
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (fs.statSync(resolved).isDirectory()) {
|
|
183
|
+
return {
|
|
184
|
+
content: [{
|
|
185
|
+
type: 'text',
|
|
186
|
+
text: `⚠️ [CONTEXTGUARD] Path is a directory: ${file_path}. Use a file path.`
|
|
187
|
+
}]
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const ext = path.extname(resolved).toLowerCase();
|
|
191
|
+
// Native multimodal / notebook formats — passthrough directly
|
|
192
|
+
if (NATIVE_MEDIA_EXTS.has(ext)) {
|
|
193
|
+
const raw = fs.readFileSync(resolved);
|
|
194
|
+
const isText = ext === '.ipynb' || ext === '.svg';
|
|
195
|
+
return {
|
|
196
|
+
content: [{
|
|
197
|
+
type: 'text',
|
|
198
|
+
text: isText
|
|
199
|
+
? raw.toString('utf-8')
|
|
200
|
+
: `[Native binary format: ${ext}. Use multimodal vision tool or platform viewer to inspect.]`
|
|
201
|
+
}]
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
// Run through ContextGuard engine
|
|
205
|
+
const result = await processWithContextGuard({
|
|
206
|
+
filePath: file_path,
|
|
207
|
+
startLine: start_line,
|
|
208
|
+
endLine: end_line,
|
|
209
|
+
query: query ?? ''
|
|
210
|
+
});
|
|
211
|
+
// Format the response with status metadata header
|
|
212
|
+
const statusBadge = {
|
|
213
|
+
PASSTHROUGH_FULL: '✅ PASSTHROUGH',
|
|
214
|
+
PASSTHROUGH_SLICE: '✅ SLICE',
|
|
215
|
+
SHUNTED_LOCAL_AST: '🛡️ AST SKELETON',
|
|
216
|
+
SHUNTED_WORKER_MODEL: '🤖 WORKER SUMMARY',
|
|
217
|
+
SHUNT_FALLBACK: '⚠️ FALLBACK',
|
|
218
|
+
BLOCKED_BINARY: '🛑 BLOCKED (BINARY)',
|
|
219
|
+
BLOCKED_LOCKFILE: '🛑 BLOCKED (LOCKFILE)',
|
|
220
|
+
BLOCKED_MINIFIED: '🛑 BLOCKED (MINIFIED)',
|
|
221
|
+
ERROR: '❌ ERROR'
|
|
222
|
+
};
|
|
223
|
+
const badge = statusBadge[result.status] ?? result.status;
|
|
224
|
+
const meta = result.totalLines
|
|
225
|
+
? `${result.totalLines} lines · ${((result.sizeBytes ?? 0) / 1024).toFixed(1)} KB`
|
|
226
|
+
: '';
|
|
227
|
+
const header = meta
|
|
228
|
+
? `<!-- [CONTEXTGUARD ${badge}] ${meta} -->\n`
|
|
229
|
+
: `<!-- [CONTEXTGUARD ${badge}] -->\n`;
|
|
230
|
+
const isBlocked = ['BLOCKED_BINARY', 'BLOCKED_LOCKFILE', 'BLOCKED_MINIFIED', 'ERROR'].includes(result.status);
|
|
231
|
+
return {
|
|
232
|
+
content: [{
|
|
233
|
+
type: 'text',
|
|
234
|
+
text: isBlocked
|
|
235
|
+
? `${badge}\n\n${result.content || result.error || result.reason}`
|
|
236
|
+
: header + result.content
|
|
237
|
+
}],
|
|
238
|
+
isError: isBlocked
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
return {
|
|
243
|
+
content: [{ type: 'text', text: `❌ [CONTEXTGUARD ERROR] ${err.message}` }],
|
|
244
|
+
isError: true
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
// ─── Tool: inspect_outline ────────────────────────────────────────────────────
|
|
249
|
+
server.registerTool('inspect_outline', {
|
|
250
|
+
title: 'Inspect File Outline',
|
|
251
|
+
description: [
|
|
252
|
+
'Returns a structural outline (classes, interfaces, functions, types, signals) of a source file.',
|
|
253
|
+
'This is a zero-cost, <5ms operation using the Tier 0 local AST skeletonizer.',
|
|
254
|
+
'Supports: TypeScript, JavaScript, Python, Go, Rust, Java, C#, Svelte 5, Vue 3, Angular, Astro.',
|
|
255
|
+
'Use this to navigate large files before calling read_file_safe with targeted line ranges.',
|
|
256
|
+
'Returns null if the file format is not supported for structural extraction.'
|
|
257
|
+
].join('\n'),
|
|
258
|
+
inputSchema: {
|
|
259
|
+
file_path: z.string().describe('Absolute or relative path to the source file.')
|
|
260
|
+
}
|
|
261
|
+
}, async ({ file_path }) => {
|
|
262
|
+
try {
|
|
263
|
+
const resolved = path.resolve(process.cwd(), file_path);
|
|
264
|
+
if (!fs.existsSync(resolved)) {
|
|
265
|
+
return {
|
|
266
|
+
content: [{ type: 'text', text: `⚠️ File not found: ${file_path}` }],
|
|
267
|
+
isError: true
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const ext = path.extname(resolved);
|
|
271
|
+
const langKey = isSupportedExtension(ext);
|
|
272
|
+
if (!langKey) {
|
|
273
|
+
return {
|
|
274
|
+
content: [{
|
|
275
|
+
type: 'text',
|
|
276
|
+
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`
|
|
277
|
+
}]
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
const raw = fs.readFileSync(resolved, 'utf-8');
|
|
281
|
+
const stat = fs.statSync(resolved);
|
|
282
|
+
const totalLines = raw.split('\n').length;
|
|
283
|
+
const skeleton = extractCodeSkeleton(raw, ext);
|
|
284
|
+
if (!skeleton) {
|
|
285
|
+
return {
|
|
286
|
+
content: [{
|
|
287
|
+
type: 'text',
|
|
288
|
+
text: `ℹ️ [CONTEXTGUARD] No structural declarations found in "${path.basename(file_path)}" (${totalLines} lines).`
|
|
289
|
+
}]
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
content: [{
|
|
294
|
+
type: 'text',
|
|
295
|
+
text: [
|
|
296
|
+
`<!-- [CONTEXTGUARD OUTLINE] ${path.basename(file_path)} · ${totalLines} lines · ${(stat.size / 1024).toFixed(1)} KB -->`,
|
|
297
|
+
`<!-- Use read_file_safe(file_path, start_line=N, end_line=M) to read any section. -->`,
|
|
298
|
+
'',
|
|
299
|
+
skeleton
|
|
300
|
+
].join('\n')
|
|
301
|
+
}]
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
return {
|
|
306
|
+
content: [{ type: 'text', text: `❌ [CONTEXTGUARD ERROR] ${err.message}` }],
|
|
307
|
+
isError: true
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
// ─── Tool: grep_distilled ─────────────────────────────────────────────────────
|
|
312
|
+
server.registerTool('grep_distilled', {
|
|
313
|
+
title: 'Grep (Distilled)',
|
|
314
|
+
description: [
|
|
315
|
+
'Searches for a pattern in files and returns only the structural outline of matching files,',
|
|
316
|
+
'not the full content. Prevents context flooding when searching across a large codebase.',
|
|
317
|
+
'For each matching file, returns the filename, matching lines, and the structural skeleton.',
|
|
318
|
+
'Uses ripgrep if available, with an automatic fallback to pure Node.js scanning.',
|
|
319
|
+
'Results are capped at 20 files to keep context manageable.'
|
|
320
|
+
].join('\n'),
|
|
321
|
+
inputSchema: {
|
|
322
|
+
pattern: z.string().describe('Search pattern (string or regex if is_regex=true).'),
|
|
323
|
+
directory: z.string().optional().describe('Directory to search in. Defaults to current working directory.'),
|
|
324
|
+
include: z.string().optional().describe('Glob to filter files (e.g. "*.ts", "src/**/*.py"). Defaults to all text files.'),
|
|
325
|
+
is_regex: z.boolean().optional().describe('Treat pattern as a regex. Default false (fixed string search).')
|
|
326
|
+
}
|
|
327
|
+
}, async ({ pattern, directory, include, is_regex }) => {
|
|
328
|
+
try {
|
|
329
|
+
const searchDir = directory ? path.resolve(process.cwd(), directory) : process.cwd();
|
|
330
|
+
if (!fs.existsSync(searchDir)) {
|
|
331
|
+
return {
|
|
332
|
+
content: [{ type: 'text', text: `⚠️ Directory not found: ${directory}` }],
|
|
333
|
+
isError: true
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
let matchingEntries = [];
|
|
337
|
+
// 1. Attempt ripgrep using safe spawnSync (no shell injection)
|
|
338
|
+
const rgArgs = [
|
|
339
|
+
'--line-number',
|
|
340
|
+
'--with-filename',
|
|
341
|
+
'--no-heading',
|
|
342
|
+
'--color=never',
|
|
343
|
+
'--max-count=5',
|
|
344
|
+
'-l'
|
|
345
|
+
];
|
|
346
|
+
if (!is_regex)
|
|
347
|
+
rgArgs.push('--fixed-strings');
|
|
348
|
+
if (include)
|
|
349
|
+
rgArgs.push('--glob', include);
|
|
350
|
+
rgArgs.push('--', pattern, searchDir);
|
|
351
|
+
const rgResult = spawnSync('rg', rgArgs, { encoding: 'utf-8', maxBuffer: 1024 * 512 });
|
|
352
|
+
if (rgResult.error && rgResult.error.code === 'ENOENT') {
|
|
353
|
+
// ripgrep is not installed — use pure Node.js fallback!
|
|
354
|
+
const nodeMatches = safeNodeGrep(searchDir, pattern, !!is_regex, include, 20);
|
|
355
|
+
matchingEntries = nodeMatches.map(m => ({
|
|
356
|
+
filePath: m.filePath,
|
|
357
|
+
matchLines: m.matches.slice(0, 5).join('\n')
|
|
358
|
+
}));
|
|
359
|
+
}
|
|
360
|
+
else if (rgResult.status === 0 && rgResult.stdout) {
|
|
361
|
+
const filePaths = rgResult.stdout.trim().split('\n').filter(Boolean).slice(0, 20);
|
|
362
|
+
for (const fp of filePaths) {
|
|
363
|
+
const matchArgs = [
|
|
364
|
+
'--line-number', '--no-heading', '--color=never',
|
|
365
|
+
'--max-count=5', '--context=1'
|
|
366
|
+
];
|
|
367
|
+
if (!is_regex)
|
|
368
|
+
matchArgs.push('--fixed-strings');
|
|
369
|
+
if (include)
|
|
370
|
+
matchArgs.push('--glob', include);
|
|
371
|
+
matchArgs.push('--', pattern, fp);
|
|
372
|
+
const matchRes = spawnSync('rg', matchArgs, { encoding: 'utf-8', maxBuffer: 1024 * 64 });
|
|
373
|
+
matchingEntries.push({
|
|
374
|
+
filePath: fp,
|
|
375
|
+
matchLines: (matchRes.stdout || '').trim() || '(matches found)'
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
else if (rgResult.status === 1) {
|
|
380
|
+
// No matches found by ripgrep
|
|
381
|
+
return {
|
|
382
|
+
content: [{ type: 'text', text: `ℹ️ [CONTEXTGUARD GREP] No matches for "${pattern}" in ${searchDir}` }]
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
if (matchingEntries.length === 0) {
|
|
386
|
+
return {
|
|
387
|
+
content: [{ type: 'text', text: `ℹ️ [CONTEXTGUARD GREP] No matches for "${pattern}" in ${searchDir}` }]
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
// Format results with outlines
|
|
391
|
+
const results = [
|
|
392
|
+
`<!-- [CONTEXTGUARD GREP] Pattern: "${pattern}" | ${matchingEntries.length} matching files -->`,
|
|
393
|
+
''
|
|
394
|
+
];
|
|
395
|
+
for (const entry of matchingEntries) {
|
|
396
|
+
const ext = path.extname(entry.filePath);
|
|
397
|
+
const relPath = path.relative(process.cwd(), entry.filePath);
|
|
398
|
+
let skeleton = null;
|
|
399
|
+
let totalLines = 0;
|
|
400
|
+
let sizeBytes = 0;
|
|
401
|
+
try {
|
|
402
|
+
const raw = fs.readFileSync(entry.filePath, 'utf-8');
|
|
403
|
+
skeleton = extractCodeSkeleton(raw, ext);
|
|
404
|
+
totalLines = raw.split('\n').length;
|
|
405
|
+
sizeBytes = fs.statSync(entry.filePath).size;
|
|
406
|
+
}
|
|
407
|
+
catch { }
|
|
408
|
+
results.push(`### ${relPath} (${totalLines} lines, ${(sizeBytes / 1024).toFixed(1)} KB)`);
|
|
409
|
+
results.push('**Matches:**');
|
|
410
|
+
results.push('```');
|
|
411
|
+
results.push(entry.matchLines);
|
|
412
|
+
results.push('```');
|
|
413
|
+
if (skeleton) {
|
|
414
|
+
results.push('**Structural outline:**');
|
|
415
|
+
results.push('```');
|
|
416
|
+
results.push(skeleton);
|
|
417
|
+
results.push('```');
|
|
418
|
+
}
|
|
419
|
+
results.push('');
|
|
420
|
+
}
|
|
421
|
+
if (matchingEntries.length === 20) {
|
|
422
|
+
results.push('> ⚠️ Results capped at 20 files. Narrow your search with a more specific pattern or --include glob.');
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
content: [{ type: 'text', text: results.join('\n') }]
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
catch (err) {
|
|
429
|
+
return {
|
|
430
|
+
content: [{ type: 'text', text: `❌ [CONTEXTGUARD ERROR] ${err.message}` }],
|
|
431
|
+
isError: true
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
// ─── Start ─────────────────────────────────────────────────────────────────────
|
|
436
|
+
async function main() {
|
|
437
|
+
const transport = new StdioServerTransport();
|
|
438
|
+
await server.connect(transport);
|
|
439
|
+
// Log to stderr — never to stdout (would corrupt JSON-RPC channel)
|
|
440
|
+
process.stderr.write(`[ContextGuard MCP] Server v${SERVER_VERSION} running on stdio\n`);
|
|
441
|
+
}
|
|
442
|
+
main().catch((err) => {
|
|
443
|
+
process.stderr.write(`[ContextGuard MCP] Fatal error: ${err.message}\n`);
|
|
444
|
+
process.exit(1);
|
|
445
|
+
});
|
|
446
|
+
//# sourceMappingURL=mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../../bin/mcp-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server.test.d.ts","sourceRoot":"","sources":["../../tests/mcp-server.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,248 @@
|
|
|
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 onError = (err) => {
|
|
19
|
+
cleanup();
|
|
20
|
+
reject(err);
|
|
21
|
+
};
|
|
22
|
+
const onData = (chunk) => {
|
|
23
|
+
buffer += chunk.toString();
|
|
24
|
+
const lines = buffer.split('\n');
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
if (!line.trim())
|
|
27
|
+
continue;
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(line);
|
|
30
|
+
if (parsed.id === request.id) {
|
|
31
|
+
cleanup();
|
|
32
|
+
resolve(parsed);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// partial or unrelated line — continue
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
buffer = lines[lines.length - 1] ?? '';
|
|
41
|
+
};
|
|
42
|
+
const cleanup = () => {
|
|
43
|
+
proc.stdout.removeListener('data', onData);
|
|
44
|
+
proc.removeListener('error', onError);
|
|
45
|
+
};
|
|
46
|
+
proc.stdout.on('data', onData);
|
|
47
|
+
proc.on('error', onError);
|
|
48
|
+
proc.stdin.write(JSON.stringify(request) + '\n');
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
cleanup();
|
|
51
|
+
reject(new Error(`Timeout waiting for response to request id=${request.id}`));
|
|
52
|
+
}, 5000);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// ─── Test Suite ───────────────────────────────────────────────────────────────
|
|
56
|
+
describe('ContextGuard MCP Server (Phase 2)', () => {
|
|
57
|
+
let proc;
|
|
58
|
+
let nextId = 1;
|
|
59
|
+
const req = (method, params) => ({
|
|
60
|
+
jsonrpc: '2.0',
|
|
61
|
+
id: nextId++,
|
|
62
|
+
method,
|
|
63
|
+
params
|
|
64
|
+
});
|
|
65
|
+
before(() => {
|
|
66
|
+
proc = spawn('node', [MCP_SERVER_BIN], {
|
|
67
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
68
|
+
});
|
|
69
|
+
proc.stderr.on('data', (d) => {
|
|
70
|
+
// Suppress server startup messages in test output
|
|
71
|
+
void d;
|
|
72
|
+
});
|
|
73
|
+
// Give the server a moment to initialize
|
|
74
|
+
return new Promise((r) => setTimeout(r, 300));
|
|
75
|
+
});
|
|
76
|
+
after(() => {
|
|
77
|
+
proc.stdin.end();
|
|
78
|
+
proc.kill();
|
|
79
|
+
});
|
|
80
|
+
// ── Initialization ──────────────────────────────────────────────────────────
|
|
81
|
+
it('should respond to initialize handshake', async () => {
|
|
82
|
+
const res = await sendRequest(proc, req('initialize', {
|
|
83
|
+
protocolVersion: '2024-11-05',
|
|
84
|
+
capabilities: {},
|
|
85
|
+
clientInfo: { name: 'test-client', version: '0.1.0' }
|
|
86
|
+
}));
|
|
87
|
+
assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
|
|
88
|
+
const result = res.result;
|
|
89
|
+
assert.ok(result.serverInfo, 'Must include serverInfo');
|
|
90
|
+
const info = result.serverInfo;
|
|
91
|
+
assert.strictEqual(info.name, 'context-guard');
|
|
92
|
+
assert.strictEqual(info.version, '1.2.0');
|
|
93
|
+
});
|
|
94
|
+
it('should list exactly 3 registered tools', async () => {
|
|
95
|
+
const res = await sendRequest(proc, req('tools/list'));
|
|
96
|
+
assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
|
|
97
|
+
const result = res.result;
|
|
98
|
+
const names = result.tools.map((t) => t.name);
|
|
99
|
+
assert.ok(names.includes('read_file_safe'), 'read_file_safe must be registered');
|
|
100
|
+
assert.ok(names.includes('inspect_outline'), 'inspect_outline must be registered');
|
|
101
|
+
assert.ok(names.includes('grep_distilled'), 'grep_distilled must be registered');
|
|
102
|
+
assert.strictEqual(names.length, 3, 'Must have exactly 3 tools');
|
|
103
|
+
});
|
|
104
|
+
// ── read_file_safe ──────────────────────────────────────────────────────────
|
|
105
|
+
it('read_file_safe: should return full content for a small file', async () => {
|
|
106
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
|
|
107
|
+
const tmpFile = path.join(tmpDir, 'small.ts');
|
|
108
|
+
fs.writeFileSync(tmpFile, 'export const foo = 42;\nexport const bar = "hello";\n');
|
|
109
|
+
const res = await sendRequest(proc, req('tools/call', {
|
|
110
|
+
name: 'read_file_safe',
|
|
111
|
+
arguments: { file_path: tmpFile }
|
|
112
|
+
}));
|
|
113
|
+
assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
|
|
114
|
+
const result = res.result;
|
|
115
|
+
const text = result.content[0].text;
|
|
116
|
+
assert.ok(text.includes('PASSTHROUGH'), 'Must include PASSTHROUGH badge for small file');
|
|
117
|
+
assert.ok(text.includes('foo') || text.includes('bar'), 'Must include file content');
|
|
118
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
119
|
+
});
|
|
120
|
+
it('read_file_safe: should return AST skeleton for a large TypeScript file', async () => {
|
|
121
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
|
|
122
|
+
const tmpFile = path.join(tmpDir, 'large.ts');
|
|
123
|
+
// Generate a file larger than 300 lines
|
|
124
|
+
const lines = [
|
|
125
|
+
'import path from "node:path";',
|
|
126
|
+
'import fs from "node:fs";',
|
|
127
|
+
'',
|
|
128
|
+
'export interface UserService {',
|
|
129
|
+
' getUser(id: string): Promise<User>;',
|
|
130
|
+
' createUser(data: CreateUserDto): Promise<User>;',
|
|
131
|
+
'}',
|
|
132
|
+
'',
|
|
133
|
+
'export class UserServiceImpl implements UserService {',
|
|
134
|
+
' async getUser(id: string): Promise<User> { return null as any; }',
|
|
135
|
+
' async createUser(data: CreateUserDto): Promise<User> { return null as any; }',
|
|
136
|
+
'}'
|
|
137
|
+
];
|
|
138
|
+
// Pad to >300 lines
|
|
139
|
+
while (lines.length < 310)
|
|
140
|
+
lines.push(` // line ${lines.length}`);
|
|
141
|
+
fs.writeFileSync(tmpFile, lines.join('\n'));
|
|
142
|
+
const res = await sendRequest(proc, req('tools/call', {
|
|
143
|
+
name: 'read_file_safe',
|
|
144
|
+
arguments: { file_path: tmpFile }
|
|
145
|
+
}));
|
|
146
|
+
assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
|
|
147
|
+
const result = res.result;
|
|
148
|
+
const text = result.content[0].text;
|
|
149
|
+
assert.ok(text.includes('SKELETON') || text.includes('SHUNT') || text.includes('UserService'), 'Must return skeleton or structural content for large file');
|
|
150
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
151
|
+
});
|
|
152
|
+
it('read_file_safe: should block a .wasm binary with isError=true', async () => {
|
|
153
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
|
|
154
|
+
const wasmFile = path.join(tmpDir, 'module.wasm');
|
|
155
|
+
fs.writeFileSync(wasmFile, Buffer.from([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
|
|
156
|
+
const res = await sendRequest(proc, req('tools/call', {
|
|
157
|
+
name: 'read_file_safe',
|
|
158
|
+
arguments: { file_path: wasmFile }
|
|
159
|
+
}));
|
|
160
|
+
assert.ok(!res.error, 'JSON-RPC should not error — tool should handle it gracefully');
|
|
161
|
+
const result = res.result;
|
|
162
|
+
assert.ok(result.isError === true, 'isError must be true for blocked binary');
|
|
163
|
+
const text = result.content[0].text;
|
|
164
|
+
assert.ok(text.includes('BLOCKED') || text.includes('binario'), 'Must include block reason');
|
|
165
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
166
|
+
});
|
|
167
|
+
it('read_file_safe: should return a scoped slice when start_line/end_line given', async () => {
|
|
168
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
|
|
169
|
+
const tmpFile = path.join(tmpDir, 'ranged.ts');
|
|
170
|
+
const content = Array.from({ length: 50 }, (_, i) => `// line ${i + 1}`).join('\n');
|
|
171
|
+
fs.writeFileSync(tmpFile, content);
|
|
172
|
+
const res = await sendRequest(proc, req('tools/call', {
|
|
173
|
+
name: 'read_file_safe',
|
|
174
|
+
arguments: { file_path: tmpFile, start_line: 5, end_line: 10 }
|
|
175
|
+
}));
|
|
176
|
+
assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
|
|
177
|
+
const result = res.result;
|
|
178
|
+
const text = result.content[0].text;
|
|
179
|
+
assert.ok(text.includes('line 5') || text.includes('SLICE'), 'Must contain sliced content');
|
|
180
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
181
|
+
});
|
|
182
|
+
// ── inspect_outline ─────────────────────────────────────────────────────────
|
|
183
|
+
it('inspect_outline: should return structural outline for a TypeScript file', async () => {
|
|
184
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
|
|
185
|
+
const tmpFile = path.join(tmpDir, 'service.ts');
|
|
186
|
+
fs.writeFileSync(tmpFile, [
|
|
187
|
+
'export interface AuthService {',
|
|
188
|
+
' login(email: string, password: string): Promise<string>;',
|
|
189
|
+
' logout(token: string): void;',
|
|
190
|
+
'}',
|
|
191
|
+
'',
|
|
192
|
+
'export class AuthServiceImpl implements AuthService {',
|
|
193
|
+
' async login(email: string, password: string): Promise<string> { return "token"; }',
|
|
194
|
+
' logout(token: string): void {}',
|
|
195
|
+
'}'
|
|
196
|
+
].join('\n'));
|
|
197
|
+
const res = await sendRequest(proc, req('tools/call', {
|
|
198
|
+
name: 'inspect_outline',
|
|
199
|
+
arguments: { file_path: tmpFile }
|
|
200
|
+
}));
|
|
201
|
+
assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
|
|
202
|
+
const result = res.result;
|
|
203
|
+
const text = result.content[0].text;
|
|
204
|
+
assert.ok(text.includes('AuthService') || text.includes('login'), 'Must include structural declarations');
|
|
205
|
+
assert.ok(text.includes('OUTLINE'), 'Must include OUTLINE badge');
|
|
206
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
207
|
+
});
|
|
208
|
+
it('grep_distilled: should find matches and extract structural outline via ripgrep or node fallback', async () => {
|
|
209
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-mcp-'));
|
|
210
|
+
const testFile = path.join(tmpDir, 'controller.ts');
|
|
211
|
+
fs.writeFileSync(testFile, [
|
|
212
|
+
'export class UserController {',
|
|
213
|
+
' async findUserById(id: string) {',
|
|
214
|
+
' const targetId = id;',
|
|
215
|
+
' return targetId;',
|
|
216
|
+
' }',
|
|
217
|
+
'}'
|
|
218
|
+
].join('\n'));
|
|
219
|
+
const res = await sendRequest(proc, req('tools/call', {
|
|
220
|
+
name: 'grep_distilled',
|
|
221
|
+
arguments: { pattern: 'targetId', directory: tmpDir }
|
|
222
|
+
}));
|
|
223
|
+
assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
|
|
224
|
+
const result = res.result;
|
|
225
|
+
const text = result.content[0].text;
|
|
226
|
+
assert.ok(text.includes('controller.ts'), 'Must find controller.ts file');
|
|
227
|
+
assert.ok(text.includes('targetId'), 'Must contain match line');
|
|
228
|
+
fs.rmSync(tmpDir, { recursive: true });
|
|
229
|
+
});
|
|
230
|
+
// ── Resources & Prompts (MCP Best Practices) ────────────────────────────────
|
|
231
|
+
it('resources: should list contextguard://config and read active configuration', async () => {
|
|
232
|
+
const listRes = await sendRequest(proc, req('resources/list'));
|
|
233
|
+
assert.ok(!listRes.error, `Unexpected error: ${JSON.stringify(listRes.error)}`);
|
|
234
|
+
const listResult = listRes.result;
|
|
235
|
+
assert.ok(listResult.resources.some((r) => r.uri === 'contextguard://config'), 'Must list config resource');
|
|
236
|
+
const readRes = await sendRequest(proc, req('resources/read', { uri: 'contextguard://config' }));
|
|
237
|
+
assert.ok(!readRes.error, `Unexpected error: ${JSON.stringify(readRes.error)}`);
|
|
238
|
+
const readResult = readRes.result;
|
|
239
|
+
assert.ok(readResult.contents[0].text.includes('maxLinesThreshold'), 'Must return valid config JSON');
|
|
240
|
+
});
|
|
241
|
+
it('prompts: should list investigate_codebase_safely prompt', async () => {
|
|
242
|
+
const res = await sendRequest(proc, req('prompts/list'));
|
|
243
|
+
assert.ok(!res.error, `Unexpected error: ${JSON.stringify(res.error)}`);
|
|
244
|
+
const result = res.result;
|
|
245
|
+
assert.ok(result.prompts.some((p) => p.name === 'investigate_codebase_safely'), 'Must expose prompt template');
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
//# 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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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kevinbermudezc/context-guard",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Intelligent context admission control and token optimization for Claude Code, Cursor, and Agentic workflows.",
|
|
3
|
+
"version": "1.2.1",
|
|
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"
|