@agentscore-xyz/mcp-server 2.0.2 → 2.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @agentscore-xyz/mcp-server
2
2
 
3
- MCP security trust layer. Scan packages, get trust verdicts, check incident exposure, query the abuse database. Five tools for MCP security decisions. No API key, zero config.
3
+ MCP security trust layer. Scan packages, get trust verdicts, inspect repo-wide MCP dependencies, generate Policy Gate setup, check incident exposure, and query the abuse database. Seven tools for MCP security decisions. No API key, zero config.
4
4
 
5
5
  [![KYA Scan](https://agentscores.xyz/api/scan/badge?npm=@agentscore-xyz/mcp-server)](https://agentscores.xyz/scan?npm=@agentscore-xyz/mcp-server)
6
6
 
@@ -45,16 +45,36 @@ Your AI can now make security decisions about MCP packages:
45
45
  >
46
46
  > **Claude:** *calls scan_package* "Score 75/100, MODERATE risk. Found: preinstall script modifying npm registry config. No provenance attestations."
47
47
 
48
+ > **You:** "Check this repo for MCP dependencies"
49
+ >
50
+ > **Claude:** *calls check_my_repo* "MCP dependencies found: 5. Two are warnings. Run generate_policy_gate_setup to turn these checks into a CI gate."
51
+
52
+ > **You:** "Set up AgentScore Policy Gate for this repo"
53
+ >
54
+ > **Claude:** *calls generate_policy_gate_setup* "Here is the exact GitHub Actions workflow, the AGENTSCORE_KEY secret name, and the pilot link for your repo."
55
+
48
56
  ## Available Tools
49
57
 
50
58
  | Tool | What it does |
51
59
  |------|-------------|
52
60
  | `scan_package` | Full security scan: install scripts, prompt injection, source code patterns, provenance posture, MCP tool extraction |
53
61
  | `get_verdict` | Trust decision: allow, warn, or block based on scan findings. Also reports monitoring status and publisher posture. |
62
+ | `check_my_repo` | Inspect the current repo for MCP dependencies and summarise verdicts for every package detected locally. |
63
+ | `generate_policy_gate_setup` | Generate the exact GitHub Actions workflow and pilot handoff link needed to enforce Policy Gate in CI. |
54
64
  | `check_exposure` | Incident response: which monitored MCP servers depend on a given package? |
55
65
  | `check_abuse` | Query the KYA abuse database for reported packages or agents |
56
66
  | `monitor_status` | Check if a package is under continuous monitoring and get scan history |
57
67
 
68
+ ## From Ad-Hoc Scans To CI Enforcement
69
+
70
+ The MCP server now bridges one-off package checks into the sticky product:
71
+
72
+ 1. Run `check_my_repo` to see every MCP package used in a repo.
73
+ 2. Run `generate_policy_gate_setup` to get the exact GitHub Actions workflow.
74
+ 3. Start the free pilot to receive a repo-scoped `AGENTSCORE_KEY`.
75
+
76
+ That turns "is this package safe?" into "this repo now enforces MCP dependency policy on every PR."
77
+
58
78
  ## Risk Levels
59
79
 
60
80
  | Score | Risk | Meaning |
@@ -77,7 +97,7 @@ Your AI can now make security decisions about MCP packages:
77
97
 
78
98
  ## Monitoring
79
99
 
80
- AgentScore continuously monitors 60+ MCP packages. The `check_exposure` and `monitor_status` tools use this live dataset. When a package like axios gets compromised, you can instantly find which MCP servers are affected.
100
+ AgentScore continuously monitors hundreds of MCP packages. The `check_exposure` and `monitor_status` tools use this live dataset. When a package like axios gets compromised, you can instantly find which MCP servers are affected.
81
101
 
82
102
  ## Links
83
103
 
package/dist/api.js CHANGED
@@ -1,4 +1,4 @@
1
- const BASE_URL = "https://agentscores.xyz";
1
+ const BASE_URL = "https://www.agentscores.xyz";
2
2
  const TIMEOUT_MS = 8000;
3
3
  export class ApiError extends Error {
4
4
  status;
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { z } from "zod";
5
5
  import { handleTool } from "./tools.js";
6
6
  const server = new McpServer({
7
7
  name: "agentscore",
8
- version: "2.0.2",
8
+ version: "2.1.0",
9
9
  });
10
10
  // --- Tools ---
11
11
  server.tool("scan_package", "Scan an npm package for MCP security issues. Checks install scripts, prompt injection patterns, suspicious URLs, source code patterns, dependency count, metadata completeness, and publisher provenance. Returns score (0-100), risk level, and detailed findings.", {
@@ -23,11 +23,18 @@ server.tool("check_abuse", "Check if a package or agent has been reported to the
23
23
  server.tool("monitor_status", "Check if an MCP package is under continuous monitoring and get its scan history. Shows current score, risk level, and recent changes.", {
24
24
  npm: z.string().describe("npm package name"),
25
25
  }, async (args) => handleTool("monitor_status", args));
26
+ server.tool("check_my_repo", "Inspect the current repo for MCP dependencies, look up AgentScore verdicts for each package, and summarise what should be gated in CI. Use this when a developer wants to understand all MCP packages in a repo instead of scanning one package at a time.", {
27
+ path: z.string().optional().describe("Optional path to the repo root. Defaults to the current working directory."),
28
+ }, async (args) => handleTool("check_my_repo", args));
29
+ server.tool("generate_policy_gate_setup", "Generate the exact GitHub Actions workflow needed to enforce AgentScore Policy Gate for a repo. Detects MCP dependencies locally and returns the YAML, secret name, and pilot link needed for setup.", {
30
+ path: z.string().optional().describe("Optional path to the repo root. Defaults to the current working directory."),
31
+ repo_url: z.string().optional().describe("Optional repository URL override for the pilot handoff link."),
32
+ }, async (args) => handleTool("generate_policy_gate_setup", args));
26
33
  // --- Start ---
27
34
  async function main() {
28
35
  const transport = new StdioServerTransport();
29
36
  await server.connect(transport);
30
- console.error("AgentScore MCP server v2.0 running on stdio");
37
+ console.error("AgentScore MCP server v2.1 running on stdio");
31
38
  }
32
39
  main().catch((err) => {
33
40
  console.error("Fatal error:", err);
package/dist/repo.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export type RepoDependency = {
2
+ name: string;
3
+ requestedVersion: string | null;
4
+ version: string | null;
5
+ source: string;
6
+ };
7
+ type RepositoryContext = {
8
+ repoPath: string;
9
+ repoUrl: string | null;
10
+ dependencies: RepoDependency[];
11
+ checkedFiles: string[];
12
+ };
13
+ export declare function inspectRepoForMcpDependencies(inputPath?: string): RepositoryContext;
14
+ export {};
package/dist/repo.js ADDED
@@ -0,0 +1,172 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const MCP_NAME_PATTERNS = [/mcp/i, /@modelcontextprotocol/i, /model-context-protocol/i];
4
+ const CANDIDATE_CONFIG_FILES = [
5
+ ".mcp.json",
6
+ "mcp.json",
7
+ ".cursor/mcp.json",
8
+ ".vscode/mcp.json",
9
+ "claude_desktop_config.json",
10
+ ];
11
+ function isMcpPackageName(value) {
12
+ return MCP_NAME_PATTERNS.some((pattern) => pattern.test(value));
13
+ }
14
+ function normaliseRequestedVersion(spec) {
15
+ if (!spec)
16
+ return null;
17
+ const trimmed = spec.trim();
18
+ if (!trimmed)
19
+ return null;
20
+ const cleaned = trimmed.replace(/^[~^]/, "");
21
+ return cleaned || null;
22
+ }
23
+ function readJsonIfExists(filePath) {
24
+ if (!fs.existsSync(filePath))
25
+ return null;
26
+ try {
27
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ function resolveLockedVersion(lockfile, packageName) {
34
+ if (!lockfile)
35
+ return null;
36
+ const packageEntry = lockfile.packages?.[`node_modules/${packageName}`];
37
+ if (packageEntry?.version)
38
+ return packageEntry.version;
39
+ const dependencyEntry = lockfile.dependencies?.[packageName];
40
+ if (dependencyEntry?.version)
41
+ return dependencyEntry.version;
42
+ return null;
43
+ }
44
+ function extractRepositoryUrl(pkg, repoPath) {
45
+ const pkgRepository = typeof pkg?.repository === "string"
46
+ ? pkg.repository
47
+ : pkg?.repository?.url;
48
+ if (pkgRepository)
49
+ return pkgRepository;
50
+ const gitConfigPath = path.join(repoPath, ".git", "config");
51
+ if (!fs.existsSync(gitConfigPath))
52
+ return null;
53
+ try {
54
+ const gitConfig = fs.readFileSync(gitConfigPath, "utf8");
55
+ const match = gitConfig.match(/\[remote "origin"\][\s\S]*?url = (.+)/);
56
+ return match?.[1]?.trim() || null;
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ function addDependency(dependencyMap, candidate) {
63
+ const existing = dependencyMap.get(candidate.name);
64
+ if (!existing) {
65
+ dependencyMap.set(candidate.name, candidate);
66
+ return;
67
+ }
68
+ dependencyMap.set(candidate.name, {
69
+ ...existing,
70
+ requestedVersion: existing.requestedVersion || candidate.requestedVersion,
71
+ version: existing.version || candidate.version,
72
+ source: existing.source,
73
+ });
74
+ }
75
+ function collectFromPackageJson(repoPath, dependencyMap, checkedFiles) {
76
+ const packageJsonPath = path.join(repoPath, "package.json");
77
+ const lockfilePath = path.join(repoPath, "package-lock.json");
78
+ const pkg = readJsonIfExists(packageJsonPath);
79
+ if (!pkg)
80
+ return null;
81
+ checkedFiles.push("package.json");
82
+ const lockfile = readJsonIfExists(lockfilePath);
83
+ if (lockfile)
84
+ checkedFiles.push("package-lock.json");
85
+ const dependencies = {
86
+ ...(pkg.dependencies || {}),
87
+ ...(pkg.devDependencies || {}),
88
+ };
89
+ for (const [name, requestedVersion] of Object.entries(dependencies)) {
90
+ if (!isMcpPackageName(name))
91
+ continue;
92
+ addDependency(dependencyMap, {
93
+ name,
94
+ requestedVersion: typeof requestedVersion === "string"
95
+ ? normaliseRequestedVersion(requestedVersion) || requestedVersion
96
+ : null,
97
+ version: resolveLockedVersion(lockfile, name),
98
+ source: "package.json",
99
+ });
100
+ }
101
+ return extractRepositoryUrl(pkg, repoPath);
102
+ }
103
+ function extractPackagesFromCommandObject(value) {
104
+ if (!value || typeof value !== "object")
105
+ return [];
106
+ const command = typeof value.command === "string" ? value.command : "";
107
+ const args = Array.isArray(value.args)
108
+ ? value.args.filter((item) => typeof item === "string")
109
+ : [];
110
+ if (!command || args.length === 0)
111
+ return [];
112
+ const commandUsesPackages = ["npx", "pnpm", "pnpx", "bunx", "yarn", "npm"];
113
+ if (!commandUsesPackages.includes(command))
114
+ return [];
115
+ return args.filter((arg) => {
116
+ if (!arg || arg.startsWith("-"))
117
+ return false;
118
+ if (["exec", "dlx", "create"].includes(arg))
119
+ return false;
120
+ return isMcpPackageName(arg);
121
+ });
122
+ }
123
+ function walkForConfigPackages(value, source, dependencyMap) {
124
+ if (!value)
125
+ return;
126
+ if (Array.isArray(value)) {
127
+ for (const item of value) {
128
+ walkForConfigPackages(item, source, dependencyMap);
129
+ }
130
+ return;
131
+ }
132
+ if (typeof value !== "object")
133
+ return;
134
+ for (const packageName of extractPackagesFromCommandObject(value)) {
135
+ addDependency(dependencyMap, {
136
+ name: packageName,
137
+ requestedVersion: null,
138
+ version: null,
139
+ source,
140
+ });
141
+ }
142
+ for (const child of Object.values(value)) {
143
+ walkForConfigPackages(child, source, dependencyMap);
144
+ }
145
+ }
146
+ function collectFromConfigFiles(repoPath, dependencyMap, checkedFiles) {
147
+ for (const relativePath of CANDIDATE_CONFIG_FILES) {
148
+ const absolutePath = path.join(repoPath, relativePath);
149
+ const config = readJsonIfExists(absolutePath);
150
+ if (!config)
151
+ continue;
152
+ checkedFiles.push(relativePath);
153
+ walkForConfigPackages(config, relativePath, dependencyMap);
154
+ }
155
+ }
156
+ export function inspectRepoForMcpDependencies(inputPath) {
157
+ const repoPath = path.resolve(inputPath || ".");
158
+ if (!fs.existsSync(repoPath)) {
159
+ throw new Error(`Path does not exist: ${repoPath}`);
160
+ }
161
+ const dependencyMap = new Map();
162
+ const checkedFiles = [];
163
+ const repoUrl = collectFromPackageJson(repoPath, dependencyMap, checkedFiles);
164
+ collectFromConfigFiles(repoPath, dependencyMap, checkedFiles);
165
+ const dependencies = [...dependencyMap.values()].sort((a, b) => a.name.localeCompare(b.name));
166
+ return {
167
+ repoPath,
168
+ repoUrl,
169
+ dependencies,
170
+ checkedFiles,
171
+ };
172
+ }
package/dist/tools.js CHANGED
@@ -1,7 +1,53 @@
1
1
  import { fetchApi, ApiError } from "./api.js";
2
+ import { inspectRepoForMcpDependencies, } from "./repo.js";
3
+ const WEBSITE_BASE = "https://www.agentscores.xyz";
2
4
  function text(s, isError = false) {
3
5
  return { content: [{ type: "text", text: s }], isError };
4
6
  }
7
+ function buildIntentUrl(intent, value) {
8
+ const url = new URL("/contact", WEBSITE_BASE);
9
+ url.searchParams.set("intent", intent);
10
+ if (intent === "pilot" && value) {
11
+ url.searchParams.set("repo", value);
12
+ }
13
+ if (intent === "review" && value) {
14
+ url.searchParams.set("package", value);
15
+ }
16
+ return url.toString();
17
+ }
18
+ function normaliseRepositoryUrl(value) {
19
+ if (!value)
20
+ return null;
21
+ const trimmed = value.trim();
22
+ if (!trimmed)
23
+ return null;
24
+ if (trimmed.startsWith("git@github.com:")) {
25
+ return `https://github.com/${trimmed.slice("git@github.com:".length).replace(/\.git$/, "")}`;
26
+ }
27
+ if (trimmed.startsWith("https://github.com/")) {
28
+ return trimmed.replace(/\.git$/, "");
29
+ }
30
+ return trimmed;
31
+ }
32
+ function policyGateHint(options) {
33
+ const lines = [
34
+ "",
35
+ "Policy Gate:",
36
+ " Run check_my_repo to inspect every MCP dependency used by this repo.",
37
+ " Run generate_policy_gate_setup to turn the same checks into a CI gate.",
38
+ ];
39
+ if (options.repoUrl) {
40
+ lines.push(` Start a free pilot: ${buildIntentUrl("pilot", options.repoUrl)}`);
41
+ }
42
+ else if (options.packageName) {
43
+ lines.push(` Start a free pilot: ${buildIntentUrl("pilot")}`);
44
+ lines.push(` Need a written review for this package? ${buildIntentUrl("review", options.packageName)}`);
45
+ }
46
+ else {
47
+ lines.push(` Start a free pilot: ${buildIntentUrl("pilot")}`);
48
+ }
49
+ return lines.join("\n");
50
+ }
5
51
  function handleApiError(err) {
6
52
  if (err instanceof ApiError) {
7
53
  if (err.status === 429) {
@@ -9,7 +55,10 @@ function handleApiError(err) {
9
55
  }
10
56
  return text(`AgentScore API error: ${err.message}`, true);
11
57
  }
12
- return text("AgentScore API is temporarily unavailable.", true);
58
+ if (err instanceof Error) {
59
+ return text(err.message, true);
60
+ }
61
+ return text("AgentScore is temporarily unavailable.", true);
13
62
  }
14
63
  function formatScan(data) {
15
64
  const lines = [
@@ -40,6 +89,7 @@ function formatScan(data) {
40
89
  lines.push(` ${t.name}: ${t.description?.slice(0, 100) || "no description"}`);
41
90
  }
42
91
  }
92
+ lines.push(policyGateHint({ packageName: data.package }));
43
93
  return lines.join("\n");
44
94
  }
45
95
  function formatVerdict(data) {
@@ -61,6 +111,7 @@ function formatVerdict(data) {
61
111
  if (data.tool_count > 0) {
62
112
  lines.push("", `Tools exposed: ${data.tool_count}`);
63
113
  }
114
+ lines.push(policyGateHint({ packageName: data.package }));
64
115
  return lines.join("\n");
65
116
  }
66
117
  function formatExposure(data) {
@@ -114,6 +165,113 @@ function formatMonitor(data) {
114
165
  }
115
166
  return lines.join("\n");
116
167
  }
168
+ async function fetchVerdictForDependency(dep) {
169
+ try {
170
+ const data = await fetchApi("/api/verdict", { npm: dep.name });
171
+ return {
172
+ name: dep.name,
173
+ requestedVersion: dep.requestedVersion,
174
+ version: dep.version || data.version || dep.requestedVersion,
175
+ source: dep.source,
176
+ verdict: data.verdict || "unknown",
177
+ score: typeof data.score === "number" ? data.score : null,
178
+ risk: data.risk || null,
179
+ monitored: Boolean(data.monitored),
180
+ reasons: Array.isArray(data.reasons) ? data.reasons : [],
181
+ };
182
+ }
183
+ catch (err) {
184
+ if (err instanceof ApiError) {
185
+ return {
186
+ name: dep.name,
187
+ requestedVersion: dep.requestedVersion,
188
+ version: dep.version || dep.requestedVersion,
189
+ source: dep.source,
190
+ verdict: "unknown",
191
+ score: null,
192
+ risk: null,
193
+ reasons: [err.code || "lookup_failed"],
194
+ };
195
+ }
196
+ throw err;
197
+ }
198
+ }
199
+ function formatRepoSummary(repoPath, repoUrl, checkedFiles, results) {
200
+ const lines = [
201
+ `Repo path: ${repoPath}`,
202
+ `Repository: ${repoUrl || "not detected"}`,
203
+ `Checked files: ${checkedFiles.length > 0 ? checkedFiles.join(", ") : "none"}`,
204
+ ];
205
+ if (results.length === 0) {
206
+ lines.push("", "No MCP dependencies found in the checked files.");
207
+ lines.push("If this repo uses MCP packages indirectly, point me at the repo root or add them explicitly.");
208
+ return lines.join("\n");
209
+ }
210
+ const counts = {
211
+ allow: results.filter((item) => item.verdict === "allow").length,
212
+ warn: results.filter((item) => item.verdict === "warn").length,
213
+ block: results.filter((item) => item.verdict === "block").length,
214
+ unknown: results.filter((item) => item.verdict === "unknown").length,
215
+ };
216
+ lines.push("", `MCP dependencies found: ${results.length}`, `Allow: ${counts.allow} | Warn: ${counts.warn} | Block: ${counts.block} | Unknown: ${counts.unknown}`, "", "Dependency verdicts:");
217
+ for (const item of results) {
218
+ const version = item.version || item.requestedVersion || "version unknown";
219
+ const score = item.score ?? "?";
220
+ const risk = item.risk || "unknown";
221
+ const reasons = item.reasons.length > 0 ? ` | reasons: ${item.reasons.join(", ")}` : "";
222
+ lines.push(` ${item.name}@${version} - ${item.verdict.toUpperCase()} (${score}/100, ${risk}) [source: ${item.source}]${reasons}`);
223
+ }
224
+ lines.push("", "Next step:", " Run generate_policy_gate_setup to get the exact GitHub Actions workflow for this repo.", ` Start a free pilot: ${buildIntentUrl("pilot", repoUrl)}`);
225
+ return lines.join("\n");
226
+ }
227
+ function buildWorkflowYaml(packages) {
228
+ const lines = [
229
+ "name: AgentScore Policy Gate",
230
+ "",
231
+ "on:",
232
+ " pull_request:",
233
+ " push:",
234
+ " branches: [main]",
235
+ "",
236
+ "jobs:",
237
+ " mcp-policy:",
238
+ " runs-on: ubuntu-latest",
239
+ " steps:",
240
+ " - uses: actions/checkout@v4",
241
+ " - uses: Thezenmonster/mcp-verdict-action@v1",
242
+ " with:",
243
+ " api-key: ${{ secrets.AGENTSCORE_KEY }}",
244
+ ' fail-open: "false"',
245
+ ];
246
+ if (packages.length > 0) {
247
+ lines.push(` packages: "${packages.join(",")}"`);
248
+ }
249
+ return lines.join("\n");
250
+ }
251
+ function formatPolicyGateSetup(repoPath, repoUrl, dependencies, checkedFiles) {
252
+ const explicitPackages = dependencies.map((dep) => dep.name);
253
+ const pilotUrl = buildIntentUrl("pilot", repoUrl);
254
+ const lines = [
255
+ `Repo path: ${repoPath}`,
256
+ `Repository: ${repoUrl || "not detected"}`,
257
+ `Checked files: ${checkedFiles.length > 0 ? checkedFiles.join(", ") : "none"}`,
258
+ `Detected MCP dependencies: ${explicitPackages.length > 0 ? explicitPackages.join(", ") : "none"}`,
259
+ "",
260
+ "Next steps:",
261
+ `1. Start a free Policy Gate pilot to get a repo-scoped key: ${pilotUrl}`,
262
+ "2. Add a GitHub Actions secret named AGENTSCORE_KEY to the repo.",
263
+ "3. Save this workflow as .github/workflows/agentscore-policy-gate.yml:",
264
+ "",
265
+ buildWorkflowYaml(explicitPackages),
266
+ ];
267
+ if (explicitPackages.length === 0) {
268
+ lines.push("", "Note:", " No MCP packages were detected locally. The Action can still auto-detect root package.json dependencies,", " but if this repo uses MCP packages via config files only, add them explicitly to the packages input.");
269
+ }
270
+ else {
271
+ lines.push("", "Why this workflow uses an explicit package list:", " It keeps CI aligned with the MCP dependencies detected in this repo, even when they come from config files", " and not just the root package.json.");
272
+ }
273
+ return lines.join("\n");
274
+ }
117
275
  export async function handleTool(name, args) {
118
276
  try {
119
277
  switch (name) {
@@ -154,6 +312,20 @@ export async function handleTool(name, args) {
154
312
  const data = await fetchApi("/api/monitor", { npm });
155
313
  return text(formatMonitor(data));
156
314
  }
315
+ case "check_my_repo": {
316
+ const inputPath = typeof args.path === "string" ? args.path : undefined;
317
+ const repo = inspectRepoForMcpDependencies(inputPath);
318
+ const repoUrl = normaliseRepositoryUrl(repo.repoUrl);
319
+ const results = await Promise.all(repo.dependencies.map(fetchVerdictForDependency));
320
+ return text(formatRepoSummary(repo.repoPath, repoUrl, repo.checkedFiles, results));
321
+ }
322
+ case "generate_policy_gate_setup": {
323
+ const inputPath = typeof args.path === "string" ? args.path : undefined;
324
+ const repoUrlArg = typeof args.repo_url === "string" ? args.repo_url : undefined;
325
+ const repo = inspectRepoForMcpDependencies(inputPath);
326
+ const repoUrl = normaliseRepositoryUrl(repoUrlArg || repo.repoUrl);
327
+ return text(formatPolicyGateSetup(repo.repoPath, repoUrl, repo.dependencies, repo.checkedFiles));
328
+ }
157
329
  default:
158
330
  return text(`Unknown tool: ${name}`, true);
159
331
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@agentscore-xyz/mcp-server",
3
- "version": "2.0.2",
3
+ "version": "2.1.0",
4
4
  "mcpName": "io.github.Thezenmonster/agentscore",
5
- "description": "MCP security trust layer. Scan packages, get trust verdicts, check incident exposure, query abuse database. Five tools for MCP security decisions.",
5
+ "description": "MCP security trust layer. Scan packages, inspect repo MCP dependencies, generate Policy Gate setup, check exposure, and query abuse data.",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "agentscore-mcp": "./dist/index.js"