agentaudit 3.3.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -12
- package/cli.mjs +140 -14
- package/index.mjs +79 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,16 +15,52 @@ MCP server for agents + standalone CLI for humans.
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## Getting Started
|
|
19
|
+
|
|
20
|
+
There are two ways to use AgentAudit:
|
|
21
|
+
|
|
22
|
+
### Option A: MCP Server in your AI editor (recommended)
|
|
23
|
+
|
|
24
|
+
Add AgentAudit to Claude Desktop, Cursor, or Windsurf. **No API key needed** — your editor's agent runs audits using its own LLM.
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"agentaudit": {
|
|
30
|
+
"command": "npx",
|
|
31
|
+
"args": ["-y", "agentaudit"]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then just ask your agent: *"Check which MCP servers I have installed and audit any unaudited ones."*
|
|
38
|
+
|
|
39
|
+
### Option B: CLI
|
|
19
40
|
|
|
20
41
|
```bash
|
|
42
|
+
# Install
|
|
43
|
+
npm install -g agentaudit # or use npx agentaudit <command>
|
|
44
|
+
|
|
45
|
+
# 1. Discover your MCP servers
|
|
21
46
|
npx agentaudit discover
|
|
47
|
+
|
|
48
|
+
# 2. Audit unaudited packages (needs an LLM API key)
|
|
49
|
+
export ANTHROPIC_API_KEY=sk-ant-... # or OPENAI_API_KEY=sk-...
|
|
50
|
+
npx agentaudit audit https://github.com/owner/repo
|
|
51
|
+
|
|
52
|
+
# 3. (Optional) Register to upload reports to the public registry
|
|
53
|
+
npx agentaudit setup
|
|
22
54
|
```
|
|
23
55
|
|
|
24
|
-
|
|
56
|
+
> **Note:** The `audit` command requires an LLM API key (`ANTHROPIC_API_KEY` or `OPENAI_API_KEY`) to analyze code. The `discover`, `scan`, and `check` commands work without one. If you don't have an API key, use `--export` to generate a markdown file you can paste into any LLM, or use AgentAudit as an MCP server (Option A) where no extra key is needed.
|
|
57
|
+
|
|
58
|
+
### Quick example
|
|
25
59
|
|
|
26
60
|
```
|
|
27
|
-
|
|
61
|
+
$ npx agentaudit discover
|
|
62
|
+
|
|
63
|
+
AgentAudit v3.3.0
|
|
28
64
|
Security scanner for AI packages
|
|
29
65
|
|
|
30
66
|
• Scanning Claude Desktop ~/.claude/mcp.json found 2 servers
|
|
@@ -32,21 +68,15 @@ AgentAudit v3.2.0
|
|
|
32
68
|
├── fastmcp-demo npm:fastmcp
|
|
33
69
|
│ SAFE Risk 0 ✔ official https://agentaudit.dev/skills/fastmcp
|
|
34
70
|
└── my-tool npm:some-mcp-tool
|
|
35
|
-
⚠ not audited Run: agentaudit audit
|
|
71
|
+
⚠ not audited Run: agentaudit audit https://github.com/user/some-mcp-tool
|
|
36
72
|
|
|
37
|
-
────────────────────────────────────────────────────────
|
|
38
73
|
Summary 2 servers across 1 config
|
|
39
74
|
|
|
40
75
|
✔ 1 audited
|
|
41
76
|
⚠ 1 not audited
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Install
|
|
45
77
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# or use directly:
|
|
49
|
-
npx agentaudit <command>
|
|
78
|
+
To audit unaudited servers:
|
|
79
|
+
agentaudit audit https://github.com/user/some-mcp-tool (my-tool)
|
|
50
80
|
```
|
|
51
81
|
|
|
52
82
|
---
|
package/cli.mjs
CHANGED
|
@@ -157,17 +157,26 @@ async function setupCommand() {
|
|
|
157
157
|
|
|
158
158
|
console.log();
|
|
159
159
|
console.log(` ${c.bold}Ready!${c.reset} You can now:`);
|
|
160
|
-
console.log(` ${c.dim}•${c.reset}
|
|
161
|
-
console.log(` ${c.dim}•${c.reset}
|
|
160
|
+
console.log(` ${c.dim}•${c.reset} Discover servers: ${c.cyan}agentaudit discover${c.reset}`);
|
|
161
|
+
console.log(` ${c.dim}•${c.reset} Audit packages: ${c.cyan}agentaudit audit <repo-url>${c.reset} ${c.dim}(deep LLM analysis)${c.reset}`);
|
|
162
|
+
console.log(` ${c.dim}•${c.reset} Quick scan: ${c.cyan}agentaudit scan <repo-url>${c.reset} ${c.dim}(regex-based)${c.reset}`);
|
|
163
|
+
console.log(` ${c.dim}•${c.reset} Check registry: ${c.cyan}agentaudit check <name>${c.reset}`);
|
|
162
164
|
console.log(` ${c.dim}•${c.reset} Submit reports via MCP in Claude/Cursor/Windsurf`);
|
|
163
165
|
console.log();
|
|
164
166
|
}
|
|
165
167
|
|
|
166
168
|
// ── Helpers ──────────────────────────────────────────────
|
|
167
169
|
|
|
170
|
+
function getVersion() {
|
|
171
|
+
try {
|
|
172
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
|
|
173
|
+
return pkg.version || '0.0.0';
|
|
174
|
+
} catch { return '0.0.0'; }
|
|
175
|
+
}
|
|
176
|
+
|
|
168
177
|
function banner() {
|
|
169
178
|
console.log();
|
|
170
|
-
console.log(` ${c.bold}${c.cyan}AgentAudit${c.reset} ${c.dim}
|
|
179
|
+
console.log(` ${c.bold}${c.cyan}AgentAudit${c.reset} ${c.dim}v${getVersion()}${c.reset}`);
|
|
171
180
|
console.log(` ${c.dim}Security scanner for AI packages${c.reset}`);
|
|
172
181
|
console.log();
|
|
173
182
|
}
|
|
@@ -693,6 +702,19 @@ function extractServersFromConfig(config) {
|
|
|
693
702
|
const pyMatch = allArgs.match(/(?:uvx|pip run|python -m)\s+(@?[a-z0-9][\w./-]*)/i);
|
|
694
703
|
if (pyMatch) info.pyPackage = pyMatch[1];
|
|
695
704
|
|
|
705
|
+
// URL-based MCP server (remote HTTP)
|
|
706
|
+
if (info.url && !info.npmPackage && !info.pyPackage) {
|
|
707
|
+
try {
|
|
708
|
+
const parsed = new URL(info.url);
|
|
709
|
+
// Extract service name from hostname: mcp.supabase.com → supabase
|
|
710
|
+
const hostParts = parsed.hostname.split('.');
|
|
711
|
+
if (hostParts.length >= 2) {
|
|
712
|
+
const serviceName = hostParts.length === 3 ? hostParts[1] : hostParts[0];
|
|
713
|
+
info.remoteService = serviceName;
|
|
714
|
+
}
|
|
715
|
+
} catch {}
|
|
716
|
+
}
|
|
717
|
+
|
|
696
718
|
result.push(info);
|
|
697
719
|
}
|
|
698
720
|
return result;
|
|
@@ -705,6 +727,81 @@ function serverSlug(server) {
|
|
|
705
727
|
return server.name.toLowerCase().replace(/[^a-z0-9-]/gi, '-');
|
|
706
728
|
}
|
|
707
729
|
|
|
730
|
+
async function resolveSourceUrl(server) {
|
|
731
|
+
// Already have it
|
|
732
|
+
if (server.sourceUrl) return server.sourceUrl;
|
|
733
|
+
|
|
734
|
+
// Try npm registry
|
|
735
|
+
if (server.npmPackage) {
|
|
736
|
+
try {
|
|
737
|
+
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(server.npmPackage)}`, {
|
|
738
|
+
signal: AbortSignal.timeout(5000),
|
|
739
|
+
});
|
|
740
|
+
if (res.ok) {
|
|
741
|
+
const data = await res.json();
|
|
742
|
+
let repoUrl = data.repository?.url;
|
|
743
|
+
if (repoUrl) {
|
|
744
|
+
repoUrl = repoUrl.replace(/^git\+/, '').replace(/\.git$/, '').replace(/^ssh:\/\/git@github\.com/, 'https://github.com');
|
|
745
|
+
if (repoUrl.startsWith('http')) return repoUrl;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
} catch {}
|
|
749
|
+
// Fallback: npm page
|
|
750
|
+
return `https://www.npmjs.com/package/${server.npmPackage}`;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Try PyPI
|
|
754
|
+
if (server.pyPackage) {
|
|
755
|
+
try {
|
|
756
|
+
const res = await fetch(`https://pypi.org/pypi/${encodeURIComponent(server.pyPackage)}/json`, {
|
|
757
|
+
signal: AbortSignal.timeout(5000),
|
|
758
|
+
});
|
|
759
|
+
if (res.ok) {
|
|
760
|
+
const data = await res.json();
|
|
761
|
+
const urls = data.info?.project_urls || {};
|
|
762
|
+
const source = urls.Source || urls.Repository || urls.Homepage || urls['Source Code'] || data.info?.home_page;
|
|
763
|
+
if (source && source.startsWith('http')) return source;
|
|
764
|
+
}
|
|
765
|
+
} catch {}
|
|
766
|
+
return `https://pypi.org/project/${server.pyPackage}/`;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// URL-based remote MCP server — try GitHub search by service name
|
|
770
|
+
if (server.remoteService) {
|
|
771
|
+
// Try npm registry with common MCP naming patterns
|
|
772
|
+
for (const tryName of [
|
|
773
|
+
`@${server.remoteService}/mcp-server-${server.remoteService}`,
|
|
774
|
+
`${server.remoteService}-mcp`,
|
|
775
|
+
`mcp-server-${server.remoteService}`,
|
|
776
|
+
server.remoteService,
|
|
777
|
+
]) {
|
|
778
|
+
try {
|
|
779
|
+
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(tryName)}`, {
|
|
780
|
+
signal: AbortSignal.timeout(3000),
|
|
781
|
+
});
|
|
782
|
+
if (res.ok) {
|
|
783
|
+
const data = await res.json();
|
|
784
|
+
let repoUrl = data.repository?.url;
|
|
785
|
+
if (repoUrl) {
|
|
786
|
+
repoUrl = repoUrl.replace(/^git\+/, '').replace(/\.git$/, '').replace(/^ssh:\/\/git@github\.com/, 'https://github.com');
|
|
787
|
+
if (repoUrl.startsWith('http')) return repoUrl;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
} catch {}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// Last resort: if server has a url, show it as context
|
|
795
|
+
if (server.url) {
|
|
796
|
+
try {
|
|
797
|
+
const parsed = new URL(server.url);
|
|
798
|
+
return `https://github.com/search?q=${encodeURIComponent(parsed.hostname + ' MCP')}&type=repositories`;
|
|
799
|
+
} catch {}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
return null;
|
|
803
|
+
}
|
|
804
|
+
|
|
708
805
|
async function discoverCommand() {
|
|
709
806
|
console.log(` ${c.bold}Discovering local MCP servers...${c.reset}`);
|
|
710
807
|
console.log();
|
|
@@ -728,6 +825,7 @@ async function discoverCommand() {
|
|
|
728
825
|
let checkedServers = 0;
|
|
729
826
|
let auditedServers = 0;
|
|
730
827
|
let unauditedServers = 0;
|
|
828
|
+
const unauditedWithUrls = [];
|
|
731
829
|
|
|
732
830
|
for (const config of configs) {
|
|
733
831
|
const servers = extractServersFromConfig(config.content);
|
|
@@ -769,6 +867,7 @@ async function discoverCommand() {
|
|
|
769
867
|
let sourceLabel = '';
|
|
770
868
|
if (server.npmPackage) sourceLabel = `${c.dim}npm:${server.npmPackage}${c.reset}`;
|
|
771
869
|
else if (server.pyPackage) sourceLabel = `${c.dim}pip:${server.pyPackage}${c.reset}`;
|
|
870
|
+
else if (server.url) sourceLabel = `${c.dim}${server.url.length > 60 ? server.url.slice(0, 57) + '...' : server.url}${c.reset}`;
|
|
772
871
|
else if (server.command) sourceLabel = `${c.dim}${[server.command, ...server.args.slice(0, 2)].join(' ')}${c.reset}`;
|
|
773
872
|
|
|
774
873
|
if (regData) {
|
|
@@ -779,11 +878,18 @@ async function discoverCommand() {
|
|
|
779
878
|
console.log(`${pipe} ${riskBadge(riskScore)} Risk ${riskScore} ${hasOfficial ? `${c.green}✔ official${c.reset} ` : ''}${c.dim}${REGISTRY_URL}/skills/${slug}${c.reset}`);
|
|
780
879
|
} else {
|
|
781
880
|
unauditedServers++;
|
|
881
|
+
// Resolve source URL
|
|
882
|
+
const resolvedUrl = await resolveSourceUrl(server);
|
|
782
883
|
console.log(`${branch} ${c.bold}${server.name}${c.reset} ${sourceLabel}`);
|
|
783
|
-
|
|
884
|
+
if (resolvedUrl) {
|
|
885
|
+
console.log(`${pipe} ${c.yellow}⚠ not audited${c.reset} ${c.dim}Run: ${c.cyan}agentaudit audit ${resolvedUrl}${c.reset}`);
|
|
886
|
+
unauditedWithUrls.push({ name: server.name, sourceUrl: resolvedUrl });
|
|
887
|
+
} else {
|
|
888
|
+
console.log(`${pipe} ${c.yellow}⚠ not audited${c.reset} ${c.dim}Source URL unknown — check the package's GitHub/npm page${c.reset}`);
|
|
889
|
+
}
|
|
784
890
|
}
|
|
785
891
|
|
|
786
|
-
if (server.sourceUrl) {
|
|
892
|
+
if (server.sourceUrl && !server.sourceUrl.includes('npmjs.com')) {
|
|
787
893
|
console.log(`${pipe} ${c.dim}source: ${server.sourceUrl}${c.reset}`);
|
|
788
894
|
}
|
|
789
895
|
}
|
|
@@ -800,8 +906,15 @@ async function discoverCommand() {
|
|
|
800
906
|
console.log();
|
|
801
907
|
|
|
802
908
|
if (unauditedServers > 0) {
|
|
803
|
-
|
|
804
|
-
|
|
909
|
+
if (unauditedWithUrls.length > 0) {
|
|
910
|
+
console.log(` ${c.dim}To audit unaudited servers:${c.reset}`);
|
|
911
|
+
for (const { name, sourceUrl } of unauditedWithUrls) {
|
|
912
|
+
console.log(` ${c.cyan}agentaudit audit ${sourceUrl}${c.reset} ${c.dim}(${name})${c.reset}`);
|
|
913
|
+
}
|
|
914
|
+
} else {
|
|
915
|
+
console.log(` ${c.dim}To audit unaudited servers, run:${c.reset}`);
|
|
916
|
+
console.log(` ${c.cyan}agentaudit audit <source-url>${c.reset}`);
|
|
917
|
+
}
|
|
805
918
|
console.log();
|
|
806
919
|
}
|
|
807
920
|
}
|
|
@@ -857,15 +970,28 @@ async function auditRepo(url) {
|
|
|
857
970
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
858
971
|
|
|
859
972
|
if (!anthropicKey && !openaiKey) {
|
|
860
|
-
// No LLM API key —
|
|
973
|
+
// No LLM API key — clear explanation
|
|
974
|
+
console.log();
|
|
975
|
+
console.log(` ${c.yellow}No LLM API key found.${c.reset} The ${c.bold}audit${c.reset} command needs an LLM to analyze code.`);
|
|
976
|
+
console.log();
|
|
977
|
+
console.log(` ${c.bold}Option 1: Set an API key${c.reset}`);
|
|
978
|
+
console.log(` Supported keys: ${c.cyan}ANTHROPIC_API_KEY${c.reset} or ${c.cyan}OPENAI_API_KEY${c.reset}`);
|
|
979
|
+
console.log();
|
|
980
|
+
console.log(` ${c.dim}# Linux / macOS:${c.reset}`);
|
|
981
|
+
console.log(` ${c.dim}export ANTHROPIC_API_KEY=sk-ant-...${c.reset}`);
|
|
982
|
+
console.log(` ${c.dim}export OPENAI_API_KEY=sk-...${c.reset}`);
|
|
983
|
+
console.log();
|
|
984
|
+
console.log(` ${c.dim}# Windows (PowerShell):${c.reset}`);
|
|
985
|
+
console.log(` ${c.dim}$env:ANTHROPIC_API_KEY = "sk-ant-..."${c.reset}`);
|
|
986
|
+
console.log(` ${c.dim}$env:OPENAI_API_KEY = "sk-..."${c.reset}`);
|
|
861
987
|
console.log();
|
|
862
|
-
console.log(` ${c.
|
|
863
|
-
console.log(`
|
|
864
|
-
console.log(`
|
|
988
|
+
console.log(` ${c.bold}Option 2: Export for manual review${c.reset}`);
|
|
989
|
+
console.log(` ${c.cyan}agentaudit audit ${url} --export${c.reset}`);
|
|
990
|
+
console.log(` ${c.dim}Creates a markdown file you can paste into any LLM (Claude, ChatGPT, etc.)${c.reset}`);
|
|
865
991
|
console.log();
|
|
866
|
-
console.log(` ${c.bold}
|
|
867
|
-
console.log(`
|
|
868
|
-
console.log(`
|
|
992
|
+
console.log(` ${c.bold}Option 3: Use MCP in Claude/Cursor/Windsurf (no API key needed)${c.reset}`);
|
|
993
|
+
console.log(` ${c.dim}Add AgentAudit as MCP server — your editor's agent runs the audit using its own LLM.${c.reset}`);
|
|
994
|
+
console.log(` ${c.dim}Config: { "mcpServers": { "agentaudit": { "command": "npx", "args": ["-y", "agentaudit"] } } }${c.reset}`);
|
|
869
995
|
console.log();
|
|
870
996
|
|
|
871
997
|
// Check if --export flag
|
package/index.mjs
CHANGED
|
@@ -176,12 +176,21 @@ function discoverMcpServers() {
|
|
|
176
176
|
const allArgs = [cfg.command, ...(cfg.args || [])].filter(Boolean).join(' ');
|
|
177
177
|
const npxMatch = allArgs.match(/npx\s+(?:-y\s+)?(@?[a-z0-9][\w./-]*)/i);
|
|
178
178
|
const pyMatch = allArgs.match(/(?:uvx|pip run|python -m)\s+(@?[a-z0-9][\w./-]*)/i);
|
|
179
|
+
let remoteService = null;
|
|
180
|
+
if (cfg.url) {
|
|
181
|
+
try {
|
|
182
|
+
const hostParts = new URL(cfg.url).hostname.split('.');
|
|
183
|
+
remoteService = hostParts.length === 3 ? hostParts[1] : hostParts[0];
|
|
184
|
+
} catch {}
|
|
185
|
+
}
|
|
179
186
|
servers.push({
|
|
180
187
|
name,
|
|
181
188
|
command: cfg.command || null,
|
|
182
189
|
args: cfg.args || [],
|
|
190
|
+
url: cfg.url || null,
|
|
183
191
|
npm_package: npxMatch?.[1] || null,
|
|
184
192
|
pip_package: pyMatch?.[1] || null,
|
|
193
|
+
remote_service: remoteService,
|
|
185
194
|
});
|
|
186
195
|
}
|
|
187
196
|
results.push({ platform: c.platform, config_path: c.path, status: 'found', server_count: servers.length, servers });
|
|
@@ -190,6 +199,62 @@ function discoverMcpServers() {
|
|
|
190
199
|
return results;
|
|
191
200
|
}
|
|
192
201
|
|
|
202
|
+
async function resolveSourceUrl(server) {
|
|
203
|
+
if (server.npm_package) {
|
|
204
|
+
try {
|
|
205
|
+
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(server.npm_package)}`, {
|
|
206
|
+
signal: AbortSignal.timeout(5000),
|
|
207
|
+
});
|
|
208
|
+
if (res.ok) {
|
|
209
|
+
const data = await res.json();
|
|
210
|
+
let repoUrl = data.repository?.url;
|
|
211
|
+
if (repoUrl) {
|
|
212
|
+
repoUrl = repoUrl.replace(/^git\+/, '').replace(/\.git$/, '').replace(/^ssh:\/\/git@github\.com/, 'https://github.com');
|
|
213
|
+
if (repoUrl.startsWith('http')) return repoUrl;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} catch {}
|
|
217
|
+
return `https://www.npmjs.com/package/${server.npm_package}`;
|
|
218
|
+
}
|
|
219
|
+
if (server.pip_package) {
|
|
220
|
+
try {
|
|
221
|
+
const res = await fetch(`https://pypi.org/pypi/${encodeURIComponent(server.pip_package)}/json`, {
|
|
222
|
+
signal: AbortSignal.timeout(5000),
|
|
223
|
+
});
|
|
224
|
+
if (res.ok) {
|
|
225
|
+
const data = await res.json();
|
|
226
|
+
const urls = data.info?.project_urls || {};
|
|
227
|
+
const source = urls.Source || urls.Repository || urls.Homepage || urls['Source Code'] || data.info?.home_page;
|
|
228
|
+
if (source && source.startsWith('http')) return source;
|
|
229
|
+
}
|
|
230
|
+
} catch {}
|
|
231
|
+
return `https://pypi.org/project/${server.pip_package}/`;
|
|
232
|
+
}
|
|
233
|
+
// URL-based remote MCP — try npm with common naming patterns
|
|
234
|
+
if (server.remote_service) {
|
|
235
|
+
for (const tryName of [
|
|
236
|
+
`@${server.remote_service}/mcp-server-${server.remote_service}`,
|
|
237
|
+
`${server.remote_service}-mcp`,
|
|
238
|
+
`mcp-server-${server.remote_service}`,
|
|
239
|
+
]) {
|
|
240
|
+
try {
|
|
241
|
+
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(tryName)}`, {
|
|
242
|
+
signal: AbortSignal.timeout(3000),
|
|
243
|
+
});
|
|
244
|
+
if (res.ok) {
|
|
245
|
+
const data = await res.json();
|
|
246
|
+
let repoUrl = data.repository?.url;
|
|
247
|
+
if (repoUrl) {
|
|
248
|
+
repoUrl = repoUrl.replace(/^git\+/, '').replace(/\.git$/, '');
|
|
249
|
+
if (repoUrl.startsWith('http')) return repoUrl;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
} catch {}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
|
|
193
258
|
async function checkRegistry(slug) {
|
|
194
259
|
try {
|
|
195
260
|
const res = await fetch(`${REGISTRY_URL}/api/skills/${encodeURIComponent(slug)}`, {
|
|
@@ -298,9 +363,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
298
363
|
|| srv.name.toLowerCase().replace(/[^a-z0-9-]/gi, '-');
|
|
299
364
|
|
|
300
365
|
text += `### ${srv.name}\n`;
|
|
301
|
-
|
|
366
|
+
if (srv.url) {
|
|
367
|
+
text += `- URL: \`${srv.url}\`\n`;
|
|
368
|
+
} else {
|
|
369
|
+
text += `- Command: \`${[srv.command, ...srv.args].filter(Boolean).join(' ')}\`\n`;
|
|
370
|
+
}
|
|
302
371
|
if (srv.npm_package) text += `- npm: ${srv.npm_package}\n`;
|
|
303
372
|
if (srv.pip_package) text += `- pip: ${srv.pip_package}\n`;
|
|
373
|
+
if (srv.remote_service) text += `- Service: ${srv.remote_service}\n`;
|
|
304
374
|
|
|
305
375
|
if (doRegistryCheck) {
|
|
306
376
|
const regData = await checkRegistry(slug);
|
|
@@ -310,8 +380,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
310
380
|
text += `- **Registry: ✅ Audited** — Risk ${risk}/100${official}\n`;
|
|
311
381
|
text += `- Report: ${REGISTRY_URL}/skills/${slug}\n`;
|
|
312
382
|
} else {
|
|
383
|
+
const sourceUrl = await resolveSourceUrl(srv);
|
|
313
384
|
text += `- **Registry: ⚠️ Not audited** — no audit report found\n`;
|
|
314
|
-
|
|
385
|
+
if (sourceUrl) {
|
|
386
|
+
text += `- Source: ${sourceUrl}\n`;
|
|
387
|
+
text += `- To audit: call \`audit_package\` with source_url \`${sourceUrl}\`\n`;
|
|
388
|
+
} else {
|
|
389
|
+
text += `- Source URL unknown — check the package's GitHub/npm page\n`;
|
|
390
|
+
text += `- To audit: find the source URL, then call \`audit_package\`\n`;
|
|
391
|
+
}
|
|
315
392
|
}
|
|
316
393
|
}
|
|
317
394
|
text += `\n`;
|