mcp-sentinel 0.1.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/LICENSE +21 -0
- package/README.md +290 -0
- package/bin/mcp-sentinel.js +2 -0
- package/dist/aguara.d.ts +4 -0
- package/dist/aguara.d.ts.map +1 -0
- package/dist/aguara.js +105 -0
- package/dist/aguara.js.map +1 -0
- package/dist/analyzer.d.ts +9 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +42 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +176 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +55 -0
- package/dist/config.js.map +1 -0
- package/dist/diff.d.ts +3 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +110 -0
- package/dist/diff.js.map +1 -0
- package/dist/formatter.d.ts +7 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +184 -0
- package/dist/formatter.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +165 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown.d.ts +3 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +106 -0
- package/dist/markdown.js.map +1 -0
- package/dist/policy.d.ts +5 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +162 -0
- package/dist/policy.js.map +1 -0
- package/dist/scanner.d.ts +17 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +147 -0
- package/dist/scanner.js.map +1 -0
- package/dist/types.d.ts +135 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { parseArgs } from "./cli.js";
|
|
4
|
+
import { connectToServer, getServerInfo, getServerCapabilities, listTools, listResources, listResourceTemplates, listPrompts, getInstructions, disconnect, } from "./scanner.js";
|
|
5
|
+
import { analyzeTools, summarize } from "./analyzer.js";
|
|
6
|
+
import { scanWithAguara } from "./aguara.js";
|
|
7
|
+
import { formatOutput, formatJson, formatDiff, formatPolicyResult, formatError } from "./formatter.js";
|
|
8
|
+
import { formatMarkdown } from "./markdown.js";
|
|
9
|
+
import { diffScans } from "./diff.js";
|
|
10
|
+
import { discoverServers } from "./config.js";
|
|
11
|
+
import { loadPolicy, findPolicy, evaluatePolicy } from "./policy.js";
|
|
12
|
+
function targetLabel(target) {
|
|
13
|
+
if (target.type === "stdio") {
|
|
14
|
+
return `${target.command} ${target.args.join(" ")}`.trim();
|
|
15
|
+
}
|
|
16
|
+
return target.url;
|
|
17
|
+
}
|
|
18
|
+
async function scanServer(target, timeout) {
|
|
19
|
+
const startTime = Date.now();
|
|
20
|
+
const connection = await connectToServer(target, timeout);
|
|
21
|
+
try {
|
|
22
|
+
const server = getServerInfo(connection.client);
|
|
23
|
+
const capabilities = getServerCapabilities(connection.client);
|
|
24
|
+
const [rawTools, resources, resourceTemplates, prompts] = await Promise.all([
|
|
25
|
+
listTools(connection.client, capabilities.tools),
|
|
26
|
+
listResources(connection.client, capabilities.resources),
|
|
27
|
+
listResourceTemplates(connection.client, capabilities.resources),
|
|
28
|
+
listPrompts(connection.client, capabilities.prompts),
|
|
29
|
+
]);
|
|
30
|
+
const instructions = getInstructions(connection.client);
|
|
31
|
+
const tools = analyzeTools(rawTools);
|
|
32
|
+
const toolSummary = summarize(tools);
|
|
33
|
+
const aguara = await scanWithAguara(rawTools);
|
|
34
|
+
const scanDuration = Date.now() - startTime;
|
|
35
|
+
return {
|
|
36
|
+
server, capabilities, tools, toolSummary,
|
|
37
|
+
resources, resourceTemplates, prompts, instructions,
|
|
38
|
+
aguara, scanDuration,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
await disconnect(connection);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function loadBaseline(filePath) {
|
|
46
|
+
const raw = await readFile(filePath, "utf-8");
|
|
47
|
+
return JSON.parse(raw);
|
|
48
|
+
}
|
|
49
|
+
async function main() {
|
|
50
|
+
const options = parseArgs(process.argv);
|
|
51
|
+
if (options === null)
|
|
52
|
+
return;
|
|
53
|
+
if (options.noColor) {
|
|
54
|
+
process.env["FORCE_COLOR"] = "0";
|
|
55
|
+
}
|
|
56
|
+
// Discover servers from config files if --config is set
|
|
57
|
+
if (options.config) {
|
|
58
|
+
const discovered = await discoverServers();
|
|
59
|
+
if (discovered.length === 0) {
|
|
60
|
+
console.error(formatError("No MCP servers found in config files."));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
console.log(chalk.bold(`Found ${discovered.length} server(s) in config files:\n`));
|
|
64
|
+
for (const s of discovered) {
|
|
65
|
+
console.log(` ${chalk.dim(s.source)} ${chalk.cyan(s.name)}`);
|
|
66
|
+
options.targets.push(s.target);
|
|
67
|
+
}
|
|
68
|
+
console.log("");
|
|
69
|
+
}
|
|
70
|
+
const results = [];
|
|
71
|
+
for (const target of options.targets) {
|
|
72
|
+
try {
|
|
73
|
+
const result = await scanServer(target, options.timeout);
|
|
74
|
+
results.push(result);
|
|
75
|
+
if (!options.json && options.diff === false) {
|
|
76
|
+
console.log(formatOutput(result));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
const label = targetLabel(target);
|
|
81
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
82
|
+
console.error(formatError(`[${label}] ${message}`));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (results.length === 0) {
|
|
86
|
+
console.error(formatError("No servers were scanned successfully."));
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
// Diff mode
|
|
90
|
+
if (options.diff !== false) {
|
|
91
|
+
const baseline = await loadBaseline(options.diff);
|
|
92
|
+
for (const current of results) {
|
|
93
|
+
const diff = diffScans(baseline, current);
|
|
94
|
+
if (options.json) {
|
|
95
|
+
console.log(JSON.stringify(diff, null, 2));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log(formatDiff(diff));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (options.json) {
|
|
104
|
+
const output = results.length === 1 ? results[0] : results;
|
|
105
|
+
console.log(formatJson(output));
|
|
106
|
+
}
|
|
107
|
+
if (options.markdown !== false) {
|
|
108
|
+
const md = formatMarkdown(results);
|
|
109
|
+
await writeFile(options.markdown, md, "utf-8");
|
|
110
|
+
console.log(`Report saved to ${options.markdown}`);
|
|
111
|
+
}
|
|
112
|
+
// Policy enforcement
|
|
113
|
+
let policy = null;
|
|
114
|
+
if (options.policy !== false) {
|
|
115
|
+
const policyPath = options.policy === "auto" ? await findPolicy() : options.policy;
|
|
116
|
+
if (policyPath !== null) {
|
|
117
|
+
policy = await loadPolicy(policyPath);
|
|
118
|
+
if (!options.json) {
|
|
119
|
+
console.log(`\n${chalk.bold("\u{1F6E1}\uFE0F Policy:")} ${policyPath}\n`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else if (options.policy === "auto") {
|
|
123
|
+
// No policy file found, skip silently
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
console.error(formatError(`Policy file not found: ${options.policy}`));
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (policy !== null) {
|
|
131
|
+
let anyFailed = false;
|
|
132
|
+
const policyResults = [];
|
|
133
|
+
for (const scanResult of results) {
|
|
134
|
+
const pr = evaluatePolicy(policy, scanResult);
|
|
135
|
+
policyResults.push({ server: scanResult.server.name, result: pr });
|
|
136
|
+
if (!pr.passed)
|
|
137
|
+
anyFailed = true;
|
|
138
|
+
if (!options.json) {
|
|
139
|
+
console.log(formatPolicyResult(pr, scanResult.server.name));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (!options.json) {
|
|
143
|
+
console.log("");
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
console.log(JSON.stringify(policyResults, null, 2));
|
|
147
|
+
}
|
|
148
|
+
if (anyFailed) {
|
|
149
|
+
process.exit(2);
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// CI exit code: exit 2 if aguara found issues
|
|
154
|
+
if (options.failOnFindings) {
|
|
155
|
+
const hasFindings = results.some((r) => r.aguara.findings.length > 0);
|
|
156
|
+
if (hasFindings) {
|
|
157
|
+
process.exit(2);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
main().catch((err) => {
|
|
162
|
+
console.error(formatError(err instanceof Error ? err.message : "Unexpected error"));
|
|
163
|
+
process.exit(1);
|
|
164
|
+
});
|
|
165
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAe3D,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAkG5D"}
|
package/dist/markdown.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
function truncate(text, max) {
|
|
2
|
+
return text.length > max ? text.slice(0, max - 3) + "..." : text;
|
|
3
|
+
}
|
|
4
|
+
function formatToolRow(analyzed) {
|
|
5
|
+
const desc = truncate(analyzed.tool.description, 80);
|
|
6
|
+
const params = analyzed.tool.parameters.map((p) => {
|
|
7
|
+
const req = p.required ? "**" : "";
|
|
8
|
+
return `${req}${p.name}${req} (${p.type})`;
|
|
9
|
+
}).join(", ");
|
|
10
|
+
return `| \`${analyzed.tool.name}\` | ${analyzed.category} | ${desc} | ${params} |`;
|
|
11
|
+
}
|
|
12
|
+
export function formatMarkdown(results) {
|
|
13
|
+
const lines = [];
|
|
14
|
+
const timestamp = new Date().toISOString().split("T")[0];
|
|
15
|
+
lines.push("# MCP Sentinel Report");
|
|
16
|
+
lines.push("");
|
|
17
|
+
lines.push(`Generated: ${timestamp}`);
|
|
18
|
+
lines.push("");
|
|
19
|
+
for (const result of results) {
|
|
20
|
+
lines.push(`## ${result.server.name} v${result.server.version}`);
|
|
21
|
+
lines.push("");
|
|
22
|
+
// Capabilities
|
|
23
|
+
const caps = [];
|
|
24
|
+
if (result.capabilities.tools)
|
|
25
|
+
caps.push("tools");
|
|
26
|
+
if (result.capabilities.resources)
|
|
27
|
+
caps.push("resources");
|
|
28
|
+
if (result.capabilities.prompts)
|
|
29
|
+
caps.push("prompts");
|
|
30
|
+
if (result.capabilities.logging)
|
|
31
|
+
caps.push("logging");
|
|
32
|
+
lines.push(`- **Capabilities:** ${caps.join(", ") || "none"}`);
|
|
33
|
+
lines.push(`- **Scan time:** ${result.scanDuration}ms`);
|
|
34
|
+
lines.push("");
|
|
35
|
+
// Tools
|
|
36
|
+
if (result.tools.length > 0) {
|
|
37
|
+
lines.push(`### Tools (${result.tools.length}: ${result.toolSummary.read} read, ${result.toolSummary.write} write, ${result.toolSummary.admin} admin)`);
|
|
38
|
+
lines.push("");
|
|
39
|
+
lines.push("| Tool | Category | Description | Parameters |");
|
|
40
|
+
lines.push("|------|----------|-------------|------------|");
|
|
41
|
+
for (const tool of result.tools) {
|
|
42
|
+
lines.push(formatToolRow(tool));
|
|
43
|
+
}
|
|
44
|
+
lines.push("");
|
|
45
|
+
}
|
|
46
|
+
// Resources
|
|
47
|
+
if (result.resources.length > 0 || result.resourceTemplates.length > 0) {
|
|
48
|
+
const total = result.resources.length + result.resourceTemplates.length;
|
|
49
|
+
lines.push(`### Resources (${total})`);
|
|
50
|
+
lines.push("");
|
|
51
|
+
lines.push("| URI | Name | Type |");
|
|
52
|
+
lines.push("|-----|------|------|");
|
|
53
|
+
for (const r of result.resources) {
|
|
54
|
+
lines.push(`| \`${r.uri}\` | ${r.name} | ${r.mimeType} |`);
|
|
55
|
+
}
|
|
56
|
+
for (const r of result.resourceTemplates) {
|
|
57
|
+
lines.push(`| \`${r.uriTemplate}\` | ${r.name} | ${r.mimeType} |`);
|
|
58
|
+
}
|
|
59
|
+
lines.push("");
|
|
60
|
+
}
|
|
61
|
+
// Prompts
|
|
62
|
+
if (result.prompts.length > 0) {
|
|
63
|
+
lines.push(`### Prompts (${result.prompts.length})`);
|
|
64
|
+
lines.push("");
|
|
65
|
+
lines.push("| Prompt | Description | Arguments |");
|
|
66
|
+
lines.push("|--------|-------------|-----------|");
|
|
67
|
+
for (const p of result.prompts) {
|
|
68
|
+
const args = p.arguments.map((a) => `${a.required ? "**" : ""}${a.name}${a.required ? "**" : ""}`).join(", ");
|
|
69
|
+
lines.push(`| \`${p.name}\` | ${truncate(p.description, 60)} | ${args} |`);
|
|
70
|
+
}
|
|
71
|
+
lines.push("");
|
|
72
|
+
}
|
|
73
|
+
// Instructions
|
|
74
|
+
if (result.instructions !== null) {
|
|
75
|
+
lines.push("### Server Instructions");
|
|
76
|
+
lines.push("");
|
|
77
|
+
lines.push("```");
|
|
78
|
+
lines.push(result.instructions);
|
|
79
|
+
lines.push("```");
|
|
80
|
+
lines.push("");
|
|
81
|
+
}
|
|
82
|
+
// Aguara
|
|
83
|
+
if (result.aguara.available && result.aguara.findings.length > 0) {
|
|
84
|
+
lines.push("### Security Findings (Aguara)");
|
|
85
|
+
lines.push("");
|
|
86
|
+
lines.push("| Severity | Rule | Description |");
|
|
87
|
+
lines.push("|----------|------|-------------|");
|
|
88
|
+
for (const f of result.aguara.findings) {
|
|
89
|
+
lines.push(`| ${f.severity} | ${f.ruleId} | ${f.ruleName} |`);
|
|
90
|
+
}
|
|
91
|
+
lines.push("");
|
|
92
|
+
lines.push(`> ${result.aguara.summary}`);
|
|
93
|
+
lines.push("");
|
|
94
|
+
}
|
|
95
|
+
else if (!result.aguara.available) {
|
|
96
|
+
lines.push(`> ${result.aguara.summary}`);
|
|
97
|
+
lines.push("");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
lines.push("---");
|
|
101
|
+
lines.push("");
|
|
102
|
+
lines.push("Deep scan: [aguarascan.com](https://aguarascan.com)");
|
|
103
|
+
lines.push("");
|
|
104
|
+
return lines.join("\n");
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"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"}
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Policy, PolicyResult, ScanResult } from "./types.js";
|
|
2
|
+
export declare function evaluatePolicy(policy: Policy, result: ScanResult): PolicyResult;
|
|
3
|
+
export declare function loadPolicy(filePath: string): Promise<Policy>;
|
|
4
|
+
export declare function findPolicy(): Promise<string | null>;
|
|
5
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAA+B,YAAY,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAwF9G,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,YAAY,CAqE/E;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKlE;AAID,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUzD"}
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
const VALID_CATEGORIES = new Set(["read", "write", "admin"]);
|
|
5
|
+
function validatePolicy(raw) {
|
|
6
|
+
if (typeof raw !== "object" || raw === null) {
|
|
7
|
+
throw new Error("Policy file must be a YAML object");
|
|
8
|
+
}
|
|
9
|
+
const obj = raw;
|
|
10
|
+
const rules = {};
|
|
11
|
+
if (obj["deny"] !== undefined) {
|
|
12
|
+
if (typeof obj["deny"] !== "object" || obj["deny"] === null) {
|
|
13
|
+
throw new Error("Policy 'deny' must be an object");
|
|
14
|
+
}
|
|
15
|
+
const deny = obj["deny"];
|
|
16
|
+
rules.deny = {};
|
|
17
|
+
if (deny["categories"] !== undefined) {
|
|
18
|
+
if (!Array.isArray(deny["categories"])) {
|
|
19
|
+
throw new Error("Policy 'deny.categories' must be an array");
|
|
20
|
+
}
|
|
21
|
+
for (const c of deny["categories"]) {
|
|
22
|
+
if (!VALID_CATEGORIES.has(String(c))) {
|
|
23
|
+
throw new Error(`Invalid category '${String(c)}' in deny.categories. Valid: read, write, admin`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
rules.deny.categories = deny["categories"];
|
|
27
|
+
}
|
|
28
|
+
if (deny["tools"] !== undefined) {
|
|
29
|
+
if (!Array.isArray(deny["tools"])) {
|
|
30
|
+
throw new Error("Policy 'deny.tools' must be an array");
|
|
31
|
+
}
|
|
32
|
+
rules.deny.tools = deny["tools"].map(String);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (obj["require"] !== undefined) {
|
|
36
|
+
if (typeof obj["require"] !== "object" || obj["require"] === null) {
|
|
37
|
+
throw new Error("Policy 'require' must be an object");
|
|
38
|
+
}
|
|
39
|
+
const req = obj["require"];
|
|
40
|
+
rules.require = {};
|
|
41
|
+
if (req["aguara"] !== undefined) {
|
|
42
|
+
if (req["aguara"] !== "clean") {
|
|
43
|
+
throw new Error("Policy 'require.aguara' must be 'clean'");
|
|
44
|
+
}
|
|
45
|
+
rules.require.aguara = "clean";
|
|
46
|
+
}
|
|
47
|
+
if (req["maxTools"] !== undefined) {
|
|
48
|
+
const n = Number(req["maxTools"]);
|
|
49
|
+
if (!Number.isInteger(n) || n < 0) {
|
|
50
|
+
throw new Error("Policy 'require.maxTools' must be a non-negative integer");
|
|
51
|
+
}
|
|
52
|
+
rules.require.maxTools = n;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (obj["allow"] !== undefined) {
|
|
56
|
+
if (typeof obj["allow"] !== "object" || obj["allow"] === null) {
|
|
57
|
+
throw new Error("Policy 'allow' must be an object");
|
|
58
|
+
}
|
|
59
|
+
const allow = obj["allow"];
|
|
60
|
+
rules.allow = {};
|
|
61
|
+
if (allow["tools"] !== undefined) {
|
|
62
|
+
if (!Array.isArray(allow["tools"])) {
|
|
63
|
+
throw new Error("Policy 'allow.tools' must be an array");
|
|
64
|
+
}
|
|
65
|
+
rules.allow.tools = allow["tools"].map(String);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { rules };
|
|
69
|
+
}
|
|
70
|
+
function matchesPattern(name, pattern) {
|
|
71
|
+
if (pattern.includes("*")) {
|
|
72
|
+
const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
|
|
73
|
+
return regex.test(name);
|
|
74
|
+
}
|
|
75
|
+
return name === pattern;
|
|
76
|
+
}
|
|
77
|
+
export function evaluatePolicy(policy, result) {
|
|
78
|
+
const violations = [];
|
|
79
|
+
const { rules } = policy;
|
|
80
|
+
const allowedTools = rules.allow?.tools;
|
|
81
|
+
// deny.categories
|
|
82
|
+
if (rules.deny?.categories !== undefined) {
|
|
83
|
+
for (const tool of result.tools) {
|
|
84
|
+
if (rules.deny.categories.includes(tool.category)) {
|
|
85
|
+
// Check if explicitly allowed
|
|
86
|
+
if (allowedTools !== undefined && allowedTools.some((p) => matchesPattern(tool.tool.name, p))) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
violations.push({
|
|
90
|
+
rule: "deny.categories",
|
|
91
|
+
message: `Tool '${tool.tool.name}' has denied category '${tool.category}'`,
|
|
92
|
+
severity: "error",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// deny.tools
|
|
98
|
+
if (rules.deny?.tools !== undefined) {
|
|
99
|
+
for (const tool of result.tools) {
|
|
100
|
+
for (const pattern of rules.deny.tools) {
|
|
101
|
+
if (matchesPattern(tool.tool.name, pattern)) {
|
|
102
|
+
if (allowedTools !== undefined && allowedTools.some((p) => matchesPattern(tool.tool.name, p))) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
violations.push({
|
|
106
|
+
rule: "deny.tools",
|
|
107
|
+
message: `Tool '${tool.tool.name}' matches denied pattern '${pattern}'`,
|
|
108
|
+
severity: "error",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// require.aguara
|
|
115
|
+
if (rules.require?.aguara === "clean") {
|
|
116
|
+
if (!result.aguara.available) {
|
|
117
|
+
violations.push({
|
|
118
|
+
rule: "require.aguara",
|
|
119
|
+
message: "Policy requires aguara but it is not installed",
|
|
120
|
+
severity: "error",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
else if (result.aguara.findings.length > 0) {
|
|
124
|
+
violations.push({
|
|
125
|
+
rule: "require.aguara",
|
|
126
|
+
message: `Aguara found ${result.aguara.findings.length} security issue(s)`,
|
|
127
|
+
severity: "error",
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// require.maxTools
|
|
132
|
+
if (rules.require?.maxTools !== undefined) {
|
|
133
|
+
if (result.tools.length > rules.require.maxTools) {
|
|
134
|
+
violations.push({
|
|
135
|
+
rule: "require.maxTools",
|
|
136
|
+
message: `Server exposes ${result.tools.length} tools, policy allows max ${rules.require.maxTools}`,
|
|
137
|
+
severity: "error",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { passed: violations.length === 0, violations };
|
|
142
|
+
}
|
|
143
|
+
export async function loadPolicy(filePath) {
|
|
144
|
+
const resolved = resolve(filePath);
|
|
145
|
+
const raw = await readFile(resolved, "utf-8");
|
|
146
|
+
const parsed = parseYaml(raw);
|
|
147
|
+
return validatePolicy(parsed);
|
|
148
|
+
}
|
|
149
|
+
const DEFAULT_PATHS = [".mcp-policy.yml", ".mcp-policy.yaml"];
|
|
150
|
+
export async function findPolicy() {
|
|
151
|
+
for (const path of DEFAULT_PATHS) {
|
|
152
|
+
try {
|
|
153
|
+
await readFile(resolve(path), "utf-8");
|
|
154
|
+
return path;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAG1C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAS,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAErE,SAAS,cAAc,CAAC,GAAY;IAClC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,MAAM,KAAK,GAAe,EAAE,CAAC;IAE7B,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;YAC5D,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAA4B,CAAC;QACpD,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC;QAEhB,IAAI,IAAI,CAAC,YAAY,CAAC,KAAK,SAAS,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;gBACvC,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAC/D,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;gBACnC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBACrC,MAAM,IAAI,KAAK,CAAC,qBAAqB,MAAM,CAAC,CAAC,CAAC,iDAAiD,CAAC,CAAC;gBACnG,CAAC;YACH,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,YAAY,CAAmB,CAAC;QAC/D,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;gBAClC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;YAC1D,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,SAAS,CAAC,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC;YAClE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACxD,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,CAAC,SAAS,CAA4B,CAAC;QACtD,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;QAEnB,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,SAAS,EAAE,CAAC;YAChC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,OAAO,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAC7D,CAAC;YACD,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC;QACjC,CAAC;QAED,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,SAAS,EAAE,CAAC;YAClC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;YAClC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;YAC9E,CAAC;YACD,KAAK,CAAC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;QAC/B,IAAI,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC9D,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACtD,CAAC;QACD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAA4B,CAAC;QACtD,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;QAEjB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;YAC3D,CAAC;YACD,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC;AAED,SAAS,cAAc,CAAC,IAAY,EAAE,OAAe;IACnD,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;QACnE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,KAAK,OAAO,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,MAAkB;IAC/D,MAAM,UAAU,GAAsB,EAAE,CAAC;IACzC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IACzB,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC;IAExC,kBAAkB;IAClB,IAAI,KAAK,CAAC,IAAI,EAAE,UAAU,KAAK,SAAS,EAAE,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAChC,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAClD,8BAA8B;gBAC9B,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC9F,SAAS;gBACX,CAAC;gBACD,UAAU,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,iBAAiB;oBACvB,OAAO,EAAE,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,0BAA0B,IAAI,CAAC,QAAQ,GAAG;oBAC1E,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,aAAa;IACb,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;QACpC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAChC,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACvC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;oBAC5C,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;wBAC9F,SAAS;oBACX,CAAC;oBACD,UAAU,CAAC,IAAI,CAAC;wBACd,IAAI,EAAE,YAAY;wBAClB,OAAO,EAAE,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,6BAA6B,OAAO,GAAG;wBACvE,QAAQ,EAAE,OAAO;qBAClB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,iBAAiB;IACjB,IAAI,KAAK,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YAC7B,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,gBAAgB;gBACtB,OAAO,EAAE,gDAAgD;gBACzD,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7C,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,gBAAgB;gBACtB,OAAO,EAAE,gBAAgB,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,oBAAoB;gBAC1E,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,mBAAmB;IACnB,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC1C,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YACjD,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,kBAAkB,MAAM,CAAC,KAAK,CAAC,MAAM,6BAA6B,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE;gBACnG,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;AACzD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAY,SAAS,CAAC,GAAG,CAAC,CAAC;IACvC,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,aAAa,GAAG,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAAC;AAE9D,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;YACvC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
3
|
+
import type { ServerInfo, ServerCapabilities, ServerTarget, ToolInfo, ResourceInfo, ResourceTemplateInfo, PromptInfo } from "./types.js";
|
|
4
|
+
export interface ScanConnection {
|
|
5
|
+
client: Client;
|
|
6
|
+
transport: Transport;
|
|
7
|
+
}
|
|
8
|
+
export declare function connectToServer(target: ServerTarget, timeout: number): Promise<ScanConnection>;
|
|
9
|
+
export declare function getServerInfo(client: Client): ServerInfo;
|
|
10
|
+
export declare function getServerCapabilities(client: Client): ServerCapabilities;
|
|
11
|
+
export declare function listTools(client: Client, supported: boolean): Promise<ToolInfo[]>;
|
|
12
|
+
export declare function listResources(client: Client, supported: boolean): Promise<ResourceInfo[]>;
|
|
13
|
+
export declare function listResourceTemplates(client: Client, supported: boolean): Promise<ResourceTemplateInfo[]>;
|
|
14
|
+
export declare function listPrompts(client: Client, supported: boolean): Promise<PromptInfo[]>;
|
|
15
|
+
export declare function getInstructions(client: Client): string | null;
|
|
16
|
+
export declare function disconnect(connection: ScanConnection): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAInE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,QAAQ,EAER,YAAY,EACZ,oBAAoB,EACpB,UAAU,EAEX,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;CACtB;AAiBD,wBAAsB,eAAe,CACnC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,cAAc,CAAC,CAezB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAMxD;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,CAQxE;AAwBD,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAUvF;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAc/F;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAc/G;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAiB3F;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO7D;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1E"}
|
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
3
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
4
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5
|
+
function createTransport(target) {
|
|
6
|
+
switch (target.type) {
|
|
7
|
+
case "stdio":
|
|
8
|
+
return new StdioClientTransport({
|
|
9
|
+
command: target.command,
|
|
10
|
+
args: target.args,
|
|
11
|
+
stderr: "pipe",
|
|
12
|
+
});
|
|
13
|
+
case "sse":
|
|
14
|
+
return new SSEClientTransport(new URL(target.url));
|
|
15
|
+
case "streamable-http":
|
|
16
|
+
return new StreamableHTTPClientTransport(new URL(target.url));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function connectToServer(target, timeout) {
|
|
20
|
+
const transport = createTransport(target);
|
|
21
|
+
const client = new Client({
|
|
22
|
+
name: "mcp-sentinel",
|
|
23
|
+
version: "0.1.0",
|
|
24
|
+
});
|
|
25
|
+
const connectPromise = client.connect(transport);
|
|
26
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
27
|
+
setTimeout(() => reject(new Error(`Connection timed out after ${timeout}ms`)), timeout);
|
|
28
|
+
});
|
|
29
|
+
await Promise.race([connectPromise, timeoutPromise]);
|
|
30
|
+
return { client, transport };
|
|
31
|
+
}
|
|
32
|
+
export function getServerInfo(client) {
|
|
33
|
+
const info = client.getServerVersion();
|
|
34
|
+
return {
|
|
35
|
+
name: info?.name ?? "unknown",
|
|
36
|
+
version: info?.version ?? "unknown",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function getServerCapabilities(client) {
|
|
40
|
+
const caps = client.getServerCapabilities();
|
|
41
|
+
return {
|
|
42
|
+
tools: caps?.tools !== undefined,
|
|
43
|
+
resources: caps?.resources !== undefined,
|
|
44
|
+
prompts: caps?.prompts !== undefined,
|
|
45
|
+
logging: caps?.logging !== undefined,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function extractSchemaProperties(schema) {
|
|
49
|
+
const properties = schema["properties"];
|
|
50
|
+
if (typeof properties !== "object" || properties === null) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
const requiredArr = Array.isArray(schema["required"]) ? schema["required"] : [];
|
|
54
|
+
const props = properties;
|
|
55
|
+
const result = [];
|
|
56
|
+
for (const [name, def] of Object.entries(props)) {
|
|
57
|
+
result.push({
|
|
58
|
+
name,
|
|
59
|
+
type: typeof def["type"] === "string" ? def["type"] : "unknown",
|
|
60
|
+
required: requiredArr.includes(name),
|
|
61
|
+
description: typeof def["description"] === "string" ? def["description"] : "",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
export async function listTools(client, supported) {
|
|
67
|
+
if (!supported)
|
|
68
|
+
return [];
|
|
69
|
+
const result = await client.listTools();
|
|
70
|
+
return result.tools.map((tool) => ({
|
|
71
|
+
name: tool.name,
|
|
72
|
+
description: tool.description ?? "",
|
|
73
|
+
parameters: extractSchemaProperties(tool.inputSchema),
|
|
74
|
+
rawInputSchema: tool.inputSchema,
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
export async function listResources(client, supported) {
|
|
78
|
+
if (!supported)
|
|
79
|
+
return [];
|
|
80
|
+
try {
|
|
81
|
+
const result = await client.listResources();
|
|
82
|
+
return result.resources.map((r) => ({
|
|
83
|
+
uri: r.uri,
|
|
84
|
+
name: r.name ?? "",
|
|
85
|
+
description: typeof r.description === "string" ? r.description : "",
|
|
86
|
+
mimeType: typeof r.mimeType === "string" ? r.mimeType : "",
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export async function listResourceTemplates(client, supported) {
|
|
94
|
+
if (!supported)
|
|
95
|
+
return [];
|
|
96
|
+
try {
|
|
97
|
+
const result = await client.listResourceTemplates();
|
|
98
|
+
return result.resourceTemplates.map((r) => ({
|
|
99
|
+
uriTemplate: r.uriTemplate,
|
|
100
|
+
name: r.name ?? "",
|
|
101
|
+
description: typeof r.description === "string" ? r.description : "",
|
|
102
|
+
mimeType: typeof r.mimeType === "string" ? r.mimeType : "",
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export async function listPrompts(client, supported) {
|
|
110
|
+
if (!supported)
|
|
111
|
+
return [];
|
|
112
|
+
try {
|
|
113
|
+
const result = await client.listPrompts();
|
|
114
|
+
return result.prompts.map((p) => ({
|
|
115
|
+
name: p.name,
|
|
116
|
+
description: typeof p.description === "string" ? p.description : "",
|
|
117
|
+
arguments: Array.isArray(p.arguments) ? p.arguments.map((a) => ({
|
|
118
|
+
name: a.name,
|
|
119
|
+
description: typeof a.description === "string" ? a.description : "",
|
|
120
|
+
required: a.required === true,
|
|
121
|
+
})) : [],
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export function getInstructions(client) {
|
|
129
|
+
try {
|
|
130
|
+
const instructions = client.getInstructions();
|
|
131
|
+
return typeof instructions === "string" && instructions.length > 0 ? instructions : null;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export async function disconnect(connection) {
|
|
138
|
+
try {
|
|
139
|
+
await connection.client.close();
|
|
140
|
+
}
|
|
141
|
+
catch { /* cleanup */ }
|
|
142
|
+
try {
|
|
143
|
+
await connection.transport.close();
|
|
144
|
+
}
|
|
145
|
+
catch { /* cleanup */ }
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=scanner.js.map
|