agentaudit 3.4.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.
Files changed (3) hide show
  1. package/cli.mjs +47 -0
  2. package/index.mjs +37 -1
  3. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -702,6 +702,19 @@ function extractServersFromConfig(config) {
702
702
  const pyMatch = allArgs.match(/(?:uvx|pip run|python -m)\s+(@?[a-z0-9][\w./-]*)/i);
703
703
  if (pyMatch) info.pyPackage = pyMatch[1];
704
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
+
705
718
  result.push(info);
706
719
  }
707
720
  return result;
@@ -753,6 +766,39 @@ async function resolveSourceUrl(server) {
753
766
  return `https://pypi.org/project/${server.pyPackage}/`;
754
767
  }
755
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
+
756
802
  return null;
757
803
  }
758
804
 
@@ -821,6 +867,7 @@ async function discoverCommand() {
821
867
  let sourceLabel = '';
822
868
  if (server.npmPackage) sourceLabel = `${c.dim}npm:${server.npmPackage}${c.reset}`;
823
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}`;
824
871
  else if (server.command) sourceLabel = `${c.dim}${[server.command, ...server.args.slice(0, 2)].join(' ')}${c.reset}`;
825
872
 
826
873
  if (regData) {
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 });
@@ -221,6 +230,28 @@ async function resolveSourceUrl(server) {
221
230
  } catch {}
222
231
  return `https://pypi.org/project/${server.pip_package}/`;
223
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
+ }
224
255
  return null;
225
256
  }
226
257
 
@@ -332,9 +363,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
332
363
  || srv.name.toLowerCase().replace(/[^a-z0-9-]/gi, '-');
333
364
 
334
365
  text += `### ${srv.name}\n`;
335
- text += `- Command: \`${[srv.command, ...srv.args].join(' ')}\`\n`;
366
+ if (srv.url) {
367
+ text += `- URL: \`${srv.url}\`\n`;
368
+ } else {
369
+ text += `- Command: \`${[srv.command, ...srv.args].filter(Boolean).join(' ')}\`\n`;
370
+ }
336
371
  if (srv.npm_package) text += `- npm: ${srv.npm_package}\n`;
337
372
  if (srv.pip_package) text += `- pip: ${srv.pip_package}\n`;
373
+ if (srv.remote_service) text += `- Service: ${srv.remote_service}\n`;
338
374
 
339
375
  if (doRegistryCheck) {
340
376
  const regData = await checkRegistry(slug);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentaudit",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "description": "Security scanner for AI packages — MCP server + CLI",
5
5
  "type": "module",
6
6
  "bin": {