@toolbaux/guardian 0.1.11 → 0.1.13
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 +52 -0
- package/dist/commands/mcp-serve.js +197 -157
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,6 +79,58 @@ Guardian auto-injects architecture context into `CLAUDE.md` so your AI tool read
|
|
|
79
79
|
|
|
80
80
|
The block between markers is replaced on every save (VSCode extension) and every commit (pre-commit hook). Your manual content outside the markers is never touched.
|
|
81
81
|
|
|
82
|
+
## MCP Server — AI Tools Connect Directly
|
|
83
|
+
|
|
84
|
+
Guardian includes an MCP server that Claude Code and Cursor connect to automatically. The VSCode extension sets this up on first activation — no manual config needed.
|
|
85
|
+
|
|
86
|
+
**6 compact tools available to AI:**
|
|
87
|
+
|
|
88
|
+
| Tool | Tokens | Purpose |
|
|
89
|
+
|------|--------|---------|
|
|
90
|
+
| `guardian_orient` | ~100 | Project summary at session start |
|
|
91
|
+
| `guardian_context` | ~50-80 | File or endpoint dependencies before editing |
|
|
92
|
+
| `guardian_impact` | ~30 | What breaks if you change a file |
|
|
93
|
+
| `guardian_search` | ~70 | Find endpoints, models, modules by keyword |
|
|
94
|
+
| `guardian_model` | ~90 | Full field details (only when needed) |
|
|
95
|
+
| `guardian_metrics` | ~50 | Session usage stats |
|
|
96
|
+
|
|
97
|
+
All responses are compact JSON — no pretty-printing, no verbose keys. Repeated calls are cached (30s TTL). Usage metrics tracked per session.
|
|
98
|
+
|
|
99
|
+
**Manual setup** (if the extension doesn't auto-configure):
|
|
100
|
+
|
|
101
|
+
Create `.mcp.json` at your project root:
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"mcpServers": {
|
|
105
|
+
"guardian": {
|
|
106
|
+
"command": "guardian",
|
|
107
|
+
"args": ["mcp-serve", "--specs", ".specs"]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## VSCode Extension
|
|
114
|
+
|
|
115
|
+
Install from [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=toolbaux.toolbaux-guardian):
|
|
116
|
+
|
|
117
|
+
Search "ToolBaux Guardian" in Extensions, or:
|
|
118
|
+
```
|
|
119
|
+
Cmd+Shift+P → "Extensions: Install from VSIX"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**What it does automatically:**
|
|
123
|
+
- Creates `.specs/`, config, and pre-commit hook on first activation
|
|
124
|
+
- Configures MCP server for Claude Code and Cursor (`.mcp.json`)
|
|
125
|
+
- Extracts architecture on every file save (5s debounce)
|
|
126
|
+
- Shows drift status in status bar: `✓ Guardian: stable · 35 ep · 8 pg`
|
|
127
|
+
|
|
128
|
+
**Commands** (Cmd+Shift+P):
|
|
129
|
+
- Guardian: Initialize Project
|
|
130
|
+
- Guardian: Generate AI Context
|
|
131
|
+
- Guardian: Drift Check
|
|
132
|
+
- Guardian: Generate Constraints
|
|
133
|
+
|
|
82
134
|
## Key Commands
|
|
83
135
|
|
|
84
136
|
```bash
|
|
@@ -98,208 +98,248 @@ async function loadIntel() {
|
|
|
98
98
|
}
|
|
99
99
|
return intel;
|
|
100
100
|
}
|
|
101
|
-
// ──
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
101
|
+
// ── Helpers ──
|
|
102
|
+
const SKIP_SERVICES = new Set(["str", "dict", "int", "len", "float", "max", "join", "getattr", "lower", "open", "params.append", "updates.append"]);
|
|
103
|
+
function compact(obj) {
|
|
104
|
+
return JSON.stringify(obj);
|
|
105
|
+
}
|
|
106
|
+
function normalize(p) {
|
|
107
|
+
return p.replace(/^\.\//, "").replace(/\/\//g, "/");
|
|
108
|
+
}
|
|
109
|
+
function findModule(data, file) {
|
|
110
|
+
const f = normalize(file);
|
|
111
|
+
return data.service_map?.find((m) => {
|
|
112
|
+
const mp = normalize(m.path || "");
|
|
113
|
+
return mp && (f.startsWith(mp + "/") || f === mp);
|
|
114
|
+
}) || data.service_map?.find((m) => {
|
|
115
|
+
// Fallback: match by module ID (handles doubled paths)
|
|
116
|
+
const mid = normalize(m.id || "");
|
|
117
|
+
return mid && f.includes(mid);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function findEndpointsInFile(data, file) {
|
|
121
|
+
const f = normalize(file);
|
|
122
|
+
const basename = path.basename(f);
|
|
123
|
+
return Object.values(data.api_registry || {}).filter((ep) => {
|
|
124
|
+
const ef = normalize(ep.file || "");
|
|
125
|
+
return ef && (f.includes(ef) || ef.includes(f) || ef.endsWith(basename));
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function findModelsInFile(data, file) {
|
|
129
|
+
const f = normalize(file);
|
|
130
|
+
const basename = path.basename(f);
|
|
131
|
+
return Object.values(data.model_registry || {}).filter((m) => {
|
|
132
|
+
const mf = normalize(m.file || "");
|
|
133
|
+
return mf && (f.includes(mf) || mf.includes(f) || mf.endsWith(basename));
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
// ── Tool implementations (compact JSON, no redundancy) ──
|
|
137
|
+
async function orient() {
|
|
138
|
+
// Read architecture-context.md first — it has the richest summary
|
|
139
|
+
const contextPath = path.join(path.dirname(intelPath), "architecture-context.md");
|
|
140
|
+
try {
|
|
141
|
+
const raw = await fs.readFile(contextPath, "utf8");
|
|
142
|
+
// Extract the content between guardian:context markers
|
|
143
|
+
const match = raw.match(/<!-- guardian:context[^>]*-->([\s\S]*?)<!-- \/guardian:context -->/);
|
|
144
|
+
if (match) {
|
|
145
|
+
// Parse the markdown into compact structured data
|
|
146
|
+
const content = match[1];
|
|
147
|
+
const lines = content.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
148
|
+
// Extract key sections
|
|
149
|
+
const desc = raw.match(/Description: (.+)/)?.[1] || "";
|
|
150
|
+
const codeMap = lines.find((l) => l.startsWith("**Backend:**")) || "";
|
|
151
|
+
// Module map with exports
|
|
152
|
+
const moduleLines = lines.filter((l) => l.startsWith("- **backend/") || l.startsWith("- **frontend/"));
|
|
153
|
+
const modules = moduleLines.map((l) => {
|
|
154
|
+
const m = l.match(/\*\*([^*]+)\*\*\s*\(([^)]+)\)\s*[—–-]\s*(.*)/);
|
|
155
|
+
return m ? [m[1], m[2], m[3].slice(0, 60)] : null;
|
|
156
|
+
}).filter(Boolean);
|
|
157
|
+
// Dependencies
|
|
158
|
+
const deps = lines.filter((l) => l.includes("→")).map((l) => l.replace(/^- /, ""));
|
|
159
|
+
// High-coupling files
|
|
160
|
+
const coupling = lines.filter((l) => l.match(/score \d/)).map((l) => l.replace(/^- /, ""));
|
|
161
|
+
// Structural intelligence
|
|
162
|
+
const si = lines.filter((l) => l.includes("depth=")).map((l) => l.replace(/^- /, ""));
|
|
163
|
+
// Model-endpoint map
|
|
164
|
+
const modelEp = lines.filter((l) => l.includes("endpoints) ->")).map((l) => l.replace(/^- /, ""));
|
|
165
|
+
return compact({
|
|
166
|
+
desc: desc.slice(0, 120),
|
|
167
|
+
map: codeMap,
|
|
168
|
+
modules,
|
|
169
|
+
deps,
|
|
170
|
+
coupling: coupling.slice(0, 5),
|
|
171
|
+
si: si.slice(0, 5),
|
|
172
|
+
modelEp,
|
|
173
|
+
});
|
|
118
174
|
}
|
|
119
175
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
models_in_file: models.map((m) => `${m.name} (${m.framework}, ${m.fields?.length || 0} fields)`),
|
|
129
|
-
calls_downstream: [...new Set(calls)],
|
|
130
|
-
called_by_upstream: calledBy.slice(0, 10),
|
|
131
|
-
frontend_pages_using: pages.map((p) => p.path),
|
|
132
|
-
coupling: module?.coupling_score ?? null,
|
|
133
|
-
}, null, 2);
|
|
134
|
-
}
|
|
135
|
-
async function search(args) {
|
|
136
|
-
const data = await loadIntel();
|
|
137
|
-
const q = args.query.toLowerCase();
|
|
138
|
-
const types = (args.types || "models,endpoints,modules").split(",").map((t) => t.trim());
|
|
139
|
-
const results = {};
|
|
140
|
-
if (types.includes("endpoints")) {
|
|
141
|
-
results.endpoints = Object.values(data.api_registry || {})
|
|
142
|
-
.filter((ep) => ep.path?.toLowerCase().includes(q) ||
|
|
143
|
-
ep.handler?.toLowerCase().includes(q) ||
|
|
144
|
-
ep.service_calls?.some((s) => s.toLowerCase().includes(q)))
|
|
145
|
-
.slice(0, 10)
|
|
146
|
-
.map((ep) => `${ep.method} ${ep.path} → ${ep.handler} [${ep.module}]`);
|
|
176
|
+
catch { }
|
|
177
|
+
// Fallback: build from codebase-intelligence.json
|
|
178
|
+
const d = await loadIntel();
|
|
179
|
+
const c = d.meta?.counts || {};
|
|
180
|
+
// Compute endpoint counts from api_registry (service_map counts are often 0)
|
|
181
|
+
const epByMod = {};
|
|
182
|
+
for (const ep of Object.values(d.api_registry || {})) {
|
|
183
|
+
epByMod[ep.module] = (epByMod[ep.module] || 0) + 1;
|
|
147
184
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
185
|
+
const mods = (d.service_map || []).filter((m) => m.file_count > 0);
|
|
186
|
+
const topMods = mods
|
|
187
|
+
.map((m) => ({ ...m, ep_count: epByMod[m.id] || 0 }))
|
|
188
|
+
.sort((a, b) => b.ep_count - a.ep_count)
|
|
189
|
+
.slice(0, 6);
|
|
190
|
+
return compact({
|
|
191
|
+
p: d.meta?.project,
|
|
192
|
+
ep: c.endpoints, mod: c.models, pg: c.pages, m: c.modules,
|
|
193
|
+
top: topMods.map((m) => [m.id, m.ep_count, m.layer]),
|
|
194
|
+
pages: (d.frontend_pages || []).map((p) => p.path),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
async function context(args) {
|
|
198
|
+
const d = await loadIntel();
|
|
199
|
+
const t = args.target;
|
|
200
|
+
// Check if target is an endpoint (e.g. "POST /sessions/start")
|
|
201
|
+
const epMatch = t.match(/^(GET|POST|PUT|DELETE|PATCH)\s+(.+)$/i);
|
|
202
|
+
if (epMatch) {
|
|
203
|
+
const ep = d.api_registry?.[`${epMatch[1].toUpperCase()} ${epMatch[2]}`]
|
|
204
|
+
|| Object.values(d.api_registry || {}).find((e) => e.method === epMatch[1].toUpperCase() && e.path === epMatch[2]);
|
|
205
|
+
if (!ep)
|
|
206
|
+
return compact({ err: "not found" });
|
|
207
|
+
const svcs = (ep.service_calls || []).filter((s) => !SKIP_SERVICES.has(s));
|
|
208
|
+
return compact({
|
|
209
|
+
ep: `${ep.method} ${ep.path}`, h: ep.handler, f: ep.file, m: ep.module,
|
|
210
|
+
req: ep.request_schema, res: ep.response_schema,
|
|
211
|
+
calls: svcs, ai: ep.ai_operations?.length || 0,
|
|
212
|
+
});
|
|
154
213
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
214
|
+
// Otherwise treat as file path
|
|
215
|
+
const file = t.replace(/^\.\//, "");
|
|
216
|
+
const mod = findModule(d, file);
|
|
217
|
+
const eps = findEndpointsInFile(d, file);
|
|
218
|
+
const models = findModelsInFile(d, file);
|
|
219
|
+
const fileName = path.basename(file, path.extname(file));
|
|
220
|
+
const calledBy = [];
|
|
221
|
+
for (const ep of Object.values(d.api_registry || {})) {
|
|
222
|
+
if (ep.service_calls?.some((s) => s.toLowerCase().includes(fileName.toLowerCase()))) {
|
|
223
|
+
calledBy.push(`${ep.method} ${ep.path}`);
|
|
224
|
+
}
|
|
161
225
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const frontendCallers = (data.frontend_pages || []).filter((p) => p.api_calls?.some((call) => call.includes(args.path.split("{")[0])));
|
|
172
|
-
// Find what models this endpoint uses
|
|
173
|
-
const models = Object.values(data.model_registry || {}).filter((m) => ep.request_schema === m.name || ep.response_schema === m.name);
|
|
174
|
-
return JSON.stringify({
|
|
175
|
-
endpoint: `${ep.method} ${ep.path}`,
|
|
176
|
-
handler: ep.handler,
|
|
177
|
-
file: ep.file,
|
|
178
|
-
module: ep.module,
|
|
179
|
-
request_schema: ep.request_schema,
|
|
180
|
-
response_schema: ep.response_schema,
|
|
181
|
-
service_calls: ep.service_calls,
|
|
182
|
-
ai_operations: ep.ai_operations,
|
|
183
|
-
patterns: ep.patterns,
|
|
184
|
-
models_used: models.map((m) => ({ name: m.name, fields: m.fields })),
|
|
185
|
-
frontend_callers: frontendCallers.map((p) => p.path),
|
|
186
|
-
}, null, 2);
|
|
226
|
+
const calls = eps.flatMap((ep) => (ep.service_calls || []).filter((s) => !SKIP_SERVICES.has(s)));
|
|
227
|
+
return compact({
|
|
228
|
+
f: file,
|
|
229
|
+
mod: mod ? [mod.id, mod.layer] : null,
|
|
230
|
+
ep: eps.map((e) => `${e.method} ${e.path}`),
|
|
231
|
+
models: models.map((m) => [m.name, m.fields?.length || 0]),
|
|
232
|
+
calls: [...new Set(calls)],
|
|
233
|
+
calledBy: calledBy.slice(0, 8),
|
|
234
|
+
});
|
|
187
235
|
}
|
|
188
|
-
async function
|
|
189
|
-
const
|
|
190
|
-
const file = args.
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
// Find all models in this file
|
|
194
|
-
const models = Object.values(data.model_registry || {}).filter((m) => m.file && file.includes(m.file.replace(/^\.\//, "")));
|
|
195
|
-
// Find endpoints that USE these models
|
|
236
|
+
async function impact(args) {
|
|
237
|
+
const d = await loadIntel();
|
|
238
|
+
const file = args.target.replace(/^\.\//, "");
|
|
239
|
+
const eps = findEndpointsInFile(d, file);
|
|
240
|
+
const models = findModelsInFile(d, file);
|
|
196
241
|
const modelNames = new Set(models.map((m) => m.name));
|
|
197
|
-
const
|
|
198
|
-
ep.response_schema && modelNames.has(ep.response_schema));
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
affected_frontend_pages: affectedPages.map((p) => p.path),
|
|
213
|
-
risk: endpoints.length + affectedEndpoints.length + dependentModules.length > 5 ? "HIGH" : "LOW",
|
|
214
|
-
}, null, 2);
|
|
242
|
+
const affectedEps = Object.values(d.api_registry || {}).filter((ep) => (ep.request_schema && modelNames.has(ep.request_schema)) ||
|
|
243
|
+
(ep.response_schema && modelNames.has(ep.response_schema)));
|
|
244
|
+
const mod = findModule(d, file);
|
|
245
|
+
const depMods = mod ? (d.service_map || []).filter((m) => m.imports?.includes(mod.id)) : [];
|
|
246
|
+
const affectedPages = (d.frontend_pages || []).filter((p) => p.api_calls?.some((call) => eps.some((ep) => call.includes(ep.path?.split("{")[0]))));
|
|
247
|
+
const total = eps.length + affectedEps.length + depMods.length + affectedPages.length;
|
|
248
|
+
return compact({
|
|
249
|
+
f: file,
|
|
250
|
+
risk: total > 5 ? "HIGH" : total > 2 ? "MED" : "LOW",
|
|
251
|
+
ep: eps.map((e) => `${e.method} ${e.path}`),
|
|
252
|
+
models: models.map((m) => m.name),
|
|
253
|
+
affectedEp: affectedEps.map((e) => `${e.method} ${e.path}`),
|
|
254
|
+
depMods: depMods.map((m) => m.id),
|
|
255
|
+
pages: affectedPages.map((p) => p.path),
|
|
256
|
+
});
|
|
215
257
|
}
|
|
216
|
-
async function
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
258
|
+
async function search(args) {
|
|
259
|
+
const d = await loadIntel();
|
|
260
|
+
const q = args.query.toLowerCase();
|
|
261
|
+
const eps = Object.values(d.api_registry || {}).filter((ep) => ep.path?.toLowerCase().includes(q) || ep.handler?.toLowerCase().includes(q) ||
|
|
262
|
+
ep.service_calls?.some((s) => s.toLowerCase().includes(q))).slice(0, 8).map((ep) => `${ep.method} ${ep.path} [${ep.module}]`);
|
|
263
|
+
const models = Object.values(d.model_registry || {}).filter((m) => m.name?.toLowerCase().includes(q) || m.fields?.some((f) => f.toLowerCase().includes(q))).slice(0, 8).map((m) => `${m.name}:${m.fields?.length}f`);
|
|
264
|
+
const mods = (d.service_map || []).filter((m) => m.id?.toLowerCase().includes(q)).slice(0, 5).map((m) => `${m.id}:${m.endpoint_count}ep`);
|
|
265
|
+
return compact({ ep: eps, mod: models, m: mods });
|
|
266
|
+
}
|
|
267
|
+
async function model(args) {
|
|
268
|
+
const d = await loadIntel();
|
|
269
|
+
const m = d.model_registry?.[args.name];
|
|
270
|
+
if (!m)
|
|
271
|
+
return compact({ err: "not found" });
|
|
272
|
+
const usedBy = Object.values(d.api_registry || {}).filter((ep) => ep.request_schema === args.name || ep.response_schema === args.name).map((ep) => `${ep.method} ${ep.path}`);
|
|
273
|
+
return compact({
|
|
274
|
+
name: m.name, fw: m.framework, f: m.file,
|
|
275
|
+
fields: m.fields, rels: m.relationships,
|
|
276
|
+
usedBy,
|
|
277
|
+
});
|
|
230
278
|
}
|
|
231
279
|
// ── MCP protocol ──
|
|
232
280
|
const TOOLS = [
|
|
233
281
|
{
|
|
234
|
-
name: "
|
|
235
|
-
description: "
|
|
236
|
-
inputSchema: {
|
|
237
|
-
type: "object",
|
|
238
|
-
properties: {
|
|
239
|
-
file: { type: "string", description: "File path relative to project root (e.g. 'backend/service-conversation/engine.py')" },
|
|
240
|
-
},
|
|
241
|
-
required: ["file"],
|
|
242
|
-
},
|
|
282
|
+
name: "guardian_orient",
|
|
283
|
+
description: "Compact project summary. Call at session start. Returns: project name, counts, top modules, page routes.",
|
|
284
|
+
inputSchema: { type: "object", properties: {} },
|
|
243
285
|
},
|
|
244
286
|
{
|
|
245
|
-
name: "
|
|
246
|
-
description: "
|
|
287
|
+
name: "guardian_context",
|
|
288
|
+
description: "Get dependencies for a file or endpoint. Pass a file path (e.g. 'backend/service-conversation/engine.py') or an endpoint (e.g. 'POST /sessions/start').",
|
|
247
289
|
inputSchema: {
|
|
248
290
|
type: "object",
|
|
249
291
|
properties: {
|
|
250
|
-
|
|
251
|
-
types: { type: "string", description: "Comma-separated: models,endpoints,modules (default: all)" },
|
|
292
|
+
target: { type: "string", description: "File path or 'METHOD /path' endpoint" },
|
|
252
293
|
},
|
|
253
|
-
required: ["
|
|
294
|
+
required: ["target"],
|
|
254
295
|
},
|
|
255
296
|
},
|
|
256
297
|
{
|
|
257
|
-
name: "
|
|
258
|
-
description: "
|
|
298
|
+
name: "guardian_impact",
|
|
299
|
+
description: "What breaks if you change this file? Returns affected endpoints, models, modules, pages, and risk level.",
|
|
259
300
|
inputSchema: {
|
|
260
301
|
type: "object",
|
|
261
302
|
properties: {
|
|
262
|
-
|
|
263
|
-
path: { type: "string", description: "Endpoint path (e.g. '/sessions/start')" },
|
|
303
|
+
target: { type: "string", description: "File path to check" },
|
|
264
304
|
},
|
|
265
|
-
required: ["
|
|
305
|
+
required: ["target"],
|
|
266
306
|
},
|
|
267
307
|
},
|
|
268
308
|
{
|
|
269
|
-
name: "
|
|
270
|
-
description: "
|
|
309
|
+
name: "guardian_search",
|
|
310
|
+
description: "Find endpoints, models, modules by keyword. Returns compact one-line results.",
|
|
271
311
|
inputSchema: {
|
|
272
312
|
type: "object",
|
|
273
313
|
properties: {
|
|
274
|
-
|
|
314
|
+
query: { type: "string", description: "Search keyword" },
|
|
275
315
|
},
|
|
276
|
-
required: ["
|
|
316
|
+
required: ["query"],
|
|
277
317
|
},
|
|
278
318
|
},
|
|
279
319
|
{
|
|
280
|
-
name: "
|
|
281
|
-
description: "Get
|
|
320
|
+
name: "guardian_model",
|
|
321
|
+
description: "Get full field list and usage for a specific model. Only call when you need field details.",
|
|
282
322
|
inputSchema: {
|
|
283
323
|
type: "object",
|
|
284
|
-
properties: {
|
|
324
|
+
properties: {
|
|
325
|
+
name: { type: "string", description: "Model name (e.g. 'StartSessionRequest')" },
|
|
326
|
+
},
|
|
327
|
+
required: ["name"],
|
|
285
328
|
},
|
|
286
329
|
},
|
|
287
330
|
{
|
|
288
331
|
name: "guardian_metrics",
|
|
289
|
-
description: "
|
|
290
|
-
inputSchema: {
|
|
291
|
-
type: "object",
|
|
292
|
-
properties: {},
|
|
293
|
-
},
|
|
332
|
+
description: "MCP usage stats for this session. Call at end to evaluate guardian's usefulness.",
|
|
333
|
+
inputSchema: { type: "object", properties: {} },
|
|
294
334
|
},
|
|
295
335
|
];
|
|
296
336
|
const TOOL_HANDLERS = {
|
|
297
|
-
|
|
337
|
+
guardian_orient: orient,
|
|
338
|
+
guardian_context: context,
|
|
339
|
+
guardian_impact: impact,
|
|
298
340
|
guardian_search: search,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
guardian_overview: overview,
|
|
302
|
-
guardian_metrics: async () => JSON.stringify(metrics.summary(), null, 2),
|
|
341
|
+
guardian_model: model,
|
|
342
|
+
guardian_metrics: async () => compact(metrics.summary()),
|
|
303
343
|
};
|
|
304
344
|
function respond(id, result) {
|
|
305
345
|
const msg = JSON.stringify({ jsonrpc: "2.0", id, result });
|
package/package.json
CHANGED