@agentscore-xyz/mcp-server 2.0.0 → 2.0.1
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 +2 -2
- package/dist/index.js +1 -1
- package/dist/tools.js +12 -3
- package/package.json +3 -2
- package/dist/format.d.ts +0 -6
- package/dist/format.js +0 -181
- package/dist/resources.d.ts +0 -9
- package/dist/resources.js +0 -82
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Your AI can now make security decisions about MCP packages:
|
|
|
39
39
|
|
|
40
40
|
> **You:** "The axios package was compromised. Which MCP servers are affected?"
|
|
41
41
|
>
|
|
42
|
-
> **Claude:** *calls check_exposure* "
|
|
42
|
+
> **Claude:** *calls check_exposure* "Multiple monitored MCP servers depend on axios, including exa-mcp-server, tavily-mcp, and figma-mcp."
|
|
43
43
|
|
|
44
44
|
> **You:** "Scan @azure-devops/mcp for security issues"
|
|
45
45
|
>
|
|
@@ -50,7 +50,7 @@ Your AI can now make security decisions about MCP packages:
|
|
|
50
50
|
| Tool | What it does |
|
|
51
51
|
|------|-------------|
|
|
52
52
|
| `scan_package` | Full security scan: install scripts, prompt injection, source code patterns, provenance posture, MCP tool extraction |
|
|
53
|
-
| `get_verdict` | Trust decision: allow, warn, or block.
|
|
53
|
+
| `get_verdict` | Trust decision: allow, warn, or block based on scan findings. Also reports monitoring status and publisher posture. |
|
|
54
54
|
| `check_exposure` | Incident response: which monitored MCP servers depend on a given package? |
|
|
55
55
|
| `check_abuse` | Query the KYA abuse database for reported packages or agents |
|
|
56
56
|
| `monitor_status` | Check if a package is under continuous monitoring and get scan history |
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ const server = new McpServer({
|
|
|
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.", {
|
|
12
12
|
npm: z.string().describe("npm package name (e.g. 'exa-mcp-server', '@modelcontextprotocol/server-github')"),
|
|
13
13
|
}, async (args) => handleTool("scan_package", args));
|
|
14
|
-
server.tool("get_verdict", "Get a trust verdict for an MCP package: allow, warn, or block.
|
|
14
|
+
server.tool("get_verdict", "Get a trust verdict for an MCP package: allow, warn, or block. Based on scan findings (score and severity). Also reports monitoring status and publisher posture. Use this before installing or connecting to an MCP server.", {
|
|
15
15
|
npm: z.string().describe("npm package name"),
|
|
16
16
|
}, async (args) => handleTool("get_verdict", args));
|
|
17
17
|
server.tool("check_exposure", "Check which monitored MCP servers depend on a given package. Use this during incident response to find blast radius. Example: 'which MCP servers use axios?'", {
|
package/dist/tools.js
CHANGED
|
@@ -81,10 +81,19 @@ function formatExposure(data) {
|
|
|
81
81
|
return lines.join("\n");
|
|
82
82
|
}
|
|
83
83
|
function formatAbuse(data) {
|
|
84
|
-
if (data.
|
|
85
|
-
|
|
84
|
+
if (data.status === "reported") {
|
|
85
|
+
const lines = [
|
|
86
|
+
`ABUSE REPORTED for ${data.agent}`,
|
|
87
|
+
`Reports: ${data.report_count}`,
|
|
88
|
+
`Severity: ${data.severity}`,
|
|
89
|
+
`Recommendation: ${data.recommendation}`,
|
|
90
|
+
];
|
|
91
|
+
if (data.reasons?.length > 0) {
|
|
92
|
+
lines.push(`Reasons: ${data.reasons.join(", ")}`);
|
|
93
|
+
}
|
|
94
|
+
return lines.join("\n");
|
|
86
95
|
}
|
|
87
|
-
return `No abuse reports found for ${data.agent
|
|
96
|
+
return `No abuse reports found for ${data.agent}. Status: clean.`;
|
|
88
97
|
}
|
|
89
98
|
function formatMonitor(data) {
|
|
90
99
|
if (!data.monitored) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentscore-xyz/mcp-server",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"mcpName": "io.github.Thezenmonster/agentscore",
|
|
5
5
|
"description": "MCP security trust layer. Scan packages, get trust verdicts, check incident exposure, query abuse database. Five tools for MCP security decisions.",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"main": "./dist/index.js",
|
|
11
11
|
"scripts": {
|
|
12
|
-
"
|
|
12
|
+
"clean": "node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true})\"",
|
|
13
|
+
"build": "npm run clean && tsc",
|
|
13
14
|
"dev": "tsc --watch",
|
|
14
15
|
"start": "node dist/index.js",
|
|
15
16
|
"prepublishOnly": "npm run build"
|
package/dist/format.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export declare function formatTrustCheck(data: any): string;
|
|
2
|
-
export declare function formatScore(data: any): string;
|
|
3
|
-
export declare function formatCompare(data: any): string;
|
|
4
|
-
export declare function formatSearch(data: any): string;
|
|
5
|
-
export declare function formatLeaderboard(data: any, category?: string, limit?: number): string;
|
|
6
|
-
export declare function formatHistory(data: any, name: string): string;
|
package/dist/format.js
DELETED
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
const AGENT_URL = "https://agentscores.xyz/agent";
|
|
2
|
-
const UTM = "?utm_source=mcp&utm_medium=tool&utm_campaign=npm";
|
|
3
|
-
export function formatTrustCheck(data) {
|
|
4
|
-
const name = data.name || "Unknown";
|
|
5
|
-
const trusted = data.trusted;
|
|
6
|
-
const score = data.score ?? 0;
|
|
7
|
-
const band = data.band || "UNKNOWN";
|
|
8
|
-
const threshold = data.threshold ?? 20;
|
|
9
|
-
const platforms = data.platforms ?? 0;
|
|
10
|
-
const scoredAt = data.scored_at || "never";
|
|
11
|
-
let verdict;
|
|
12
|
-
if (score === 0 && !data.scored_at) {
|
|
13
|
-
verdict = "UNKNOWN (not yet scored)";
|
|
14
|
-
}
|
|
15
|
-
else if (trusted) {
|
|
16
|
-
verdict = "TRUSTED";
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
verdict = "UNTRUSTED (below threshold)";
|
|
20
|
-
}
|
|
21
|
-
const lines = [
|
|
22
|
-
`Agent: ${name}`,
|
|
23
|
-
`Verdict: ${verdict}`,
|
|
24
|
-
`Score: ${score}/100 (${band})`,
|
|
25
|
-
`Threshold: ${threshold}`,
|
|
26
|
-
`Platforms verified: ${platforms}/4`,
|
|
27
|
-
`Last scored: ${scoredAt}`,
|
|
28
|
-
"",
|
|
29
|
-
`Details: ${AGENT_URL}/${encodeURIComponent(name)}${UTM}`,
|
|
30
|
-
];
|
|
31
|
-
return lines.join("\n");
|
|
32
|
-
}
|
|
33
|
-
export function formatScore(data) {
|
|
34
|
-
const agent = data.agent || {};
|
|
35
|
-
const score = data.score || {};
|
|
36
|
-
const breakdown = score.breakdown || {};
|
|
37
|
-
const name = agent.name || "Unknown";
|
|
38
|
-
const dims = [
|
|
39
|
-
{ key: "identity", label: "Identity" },
|
|
40
|
-
{ key: "activity", label: "Activity" },
|
|
41
|
-
{ key: "reputation", label: "Reputation" },
|
|
42
|
-
{ key: "workHistory", label: "Work History" },
|
|
43
|
-
{ key: "consistency", label: "Consistency" },
|
|
44
|
-
];
|
|
45
|
-
const platformList = Object.entries(agent.platforms || {})
|
|
46
|
-
.filter(([, v]) => v)
|
|
47
|
-
.map(([k]) => k);
|
|
48
|
-
const lines = [
|
|
49
|
-
`Agent: ${name}`,
|
|
50
|
-
`Overall: ${score.effective ?? 0}/100 (${score.band || "UNKNOWN"})`,
|
|
51
|
-
`Raw score: ${score.total ?? 0} | Coverage: ${score.data_coverage || "0%"}`,
|
|
52
|
-
"",
|
|
53
|
-
"Dimensions:",
|
|
54
|
-
];
|
|
55
|
-
for (const dim of dims) {
|
|
56
|
-
const d = breakdown[dim.key] || {};
|
|
57
|
-
const s = d.score ?? 0;
|
|
58
|
-
const max = 20;
|
|
59
|
-
lines.push(` ${dim.label.padEnd(14)} ${String(s).padStart(2)}/${max}`);
|
|
60
|
-
}
|
|
61
|
-
lines.push("");
|
|
62
|
-
lines.push(`Platforms: ${platformList.length > 0 ? platformList.join(", ") : "none"}`);
|
|
63
|
-
if (data.recommendations && data.recommendations.length > 0) {
|
|
64
|
-
lines.push("");
|
|
65
|
-
lines.push("Recommendations:");
|
|
66
|
-
for (const rec of data.recommendations.slice(0, 3)) {
|
|
67
|
-
lines.push(` - ${rec.text || rec}`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
lines.push("");
|
|
71
|
-
lines.push(`Details: ${AGENT_URL}/${encodeURIComponent(name)}${UTM}`);
|
|
72
|
-
return lines.join("\n");
|
|
73
|
-
}
|
|
74
|
-
export function formatCompare(data) {
|
|
75
|
-
const agents = data.agents || [];
|
|
76
|
-
const winner = data.winner;
|
|
77
|
-
const margin = data.margin ?? 0;
|
|
78
|
-
if (agents.length === 0) {
|
|
79
|
-
return "No agents found to compare.";
|
|
80
|
-
}
|
|
81
|
-
const lines = ["Comparison (ranked by trust score):", ""];
|
|
82
|
-
for (let i = 0; i < agents.length; i++) {
|
|
83
|
-
const a = agents[i];
|
|
84
|
-
const tag = i === 0 ? " -- Winner" : "";
|
|
85
|
-
lines.push(`${i + 1}. ${a.name}: ${a.score?.effective ?? 0}/100 (${a.score?.band || "?"})${tag}`);
|
|
86
|
-
}
|
|
87
|
-
lines.push("");
|
|
88
|
-
lines.push(`Margin: ${margin} points`);
|
|
89
|
-
if (data.dimension_winners) {
|
|
90
|
-
lines.push("");
|
|
91
|
-
lines.push("Dimension leaders:");
|
|
92
|
-
for (const [dim, name] of Object.entries(data.dimension_winners)) {
|
|
93
|
-
lines.push(` ${dim}: ${name}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
lines.push("");
|
|
97
|
-
lines.push(`Full comparison: https://agentscores.xyz/compare${UTM}`);
|
|
98
|
-
return lines.join("\n");
|
|
99
|
-
}
|
|
100
|
-
export function formatSearch(data) {
|
|
101
|
-
const results = data.results || [];
|
|
102
|
-
const total = data.total ?? results.length;
|
|
103
|
-
if (results.length === 0) {
|
|
104
|
-
return "No agents found matching that query.";
|
|
105
|
-
}
|
|
106
|
-
const lines = [`Found ${total} agent(s):`, ""];
|
|
107
|
-
for (const r of results) {
|
|
108
|
-
const score = r.score_effective ?? r.score ?? 0;
|
|
109
|
-
const band = r.band || r.score_band || "?";
|
|
110
|
-
lines.push(` ${r.name}: ${score}/100 (${band})`);
|
|
111
|
-
}
|
|
112
|
-
lines.push("");
|
|
113
|
-
lines.push(`Browse all: https://agentscores.xyz/discover${UTM}`);
|
|
114
|
-
return lines.join("\n");
|
|
115
|
-
}
|
|
116
|
-
export function formatLeaderboard(data, category, limit) {
|
|
117
|
-
let agents = data.agents || [];
|
|
118
|
-
if (category) {
|
|
119
|
-
agents = agents.filter((a) => a.category?.toLowerCase() === category.toLowerCase());
|
|
120
|
-
}
|
|
121
|
-
if (limit && limit > 0) {
|
|
122
|
-
agents = agents.slice(0, limit);
|
|
123
|
-
}
|
|
124
|
-
if (agents.length === 0) {
|
|
125
|
-
return category
|
|
126
|
-
? `No agents found in category "${category}".`
|
|
127
|
-
: "No agents on the leaderboard yet.";
|
|
128
|
-
}
|
|
129
|
-
const header = category
|
|
130
|
-
? `Top ${agents.length} agents in "${category}":`
|
|
131
|
-
: `Top ${agents.length} agents by trust score:`;
|
|
132
|
-
const lines = [header, ""];
|
|
133
|
-
for (let i = 0; i < agents.length; i++) {
|
|
134
|
-
const a = agents[i];
|
|
135
|
-
lines.push(`${String(i + 1).padStart(3)}. ${a.name}: ${a.score_effective ?? 0}/100 (${a.band || "?"})`);
|
|
136
|
-
}
|
|
137
|
-
if (data.categories) {
|
|
138
|
-
const cats = Object.entries(data.categories)
|
|
139
|
-
.filter(([, count]) => count > 0)
|
|
140
|
-
.map(([cat, count]) => `${cat} (${count})`)
|
|
141
|
-
.join(", ");
|
|
142
|
-
lines.push("");
|
|
143
|
-
lines.push(`Categories: ${cats}`);
|
|
144
|
-
}
|
|
145
|
-
lines.push("");
|
|
146
|
-
lines.push(`Full leaderboard: https://agentscores.xyz/discover${UTM}`);
|
|
147
|
-
return lines.join("\n");
|
|
148
|
-
}
|
|
149
|
-
export function formatHistory(data, name) {
|
|
150
|
-
const history = data.history || data || [];
|
|
151
|
-
if (!Array.isArray(history) || history.length === 0) {
|
|
152
|
-
return `No score history found for "${name}".`;
|
|
153
|
-
}
|
|
154
|
-
const latest = history[0];
|
|
155
|
-
const oldest = history[history.length - 1];
|
|
156
|
-
const latestScore = latest?.score_total ?? latest?.total ?? 0;
|
|
157
|
-
const oldestScore = oldest?.score_total ?? oldest?.total ?? 0;
|
|
158
|
-
const diff = latestScore - oldestScore;
|
|
159
|
-
let trend;
|
|
160
|
-
if (diff > 3)
|
|
161
|
-
trend = "RISING";
|
|
162
|
-
else if (diff < -3)
|
|
163
|
-
trend = "FALLING";
|
|
164
|
-
else
|
|
165
|
-
trend = "STABLE";
|
|
166
|
-
const lines = [
|
|
167
|
-
`Score history for ${name}:`,
|
|
168
|
-
`Trend: ${trend} (${diff >= 0 ? "+" : ""}${diff} over ${history.length} snapshots)`,
|
|
169
|
-
"",
|
|
170
|
-
"Recent scores:",
|
|
171
|
-
];
|
|
172
|
-
for (const h of history.slice(0, 10)) {
|
|
173
|
-
const score = h.score_total ?? h.total ?? 0;
|
|
174
|
-
const date = h.scored_at || h.created_at || "?";
|
|
175
|
-
const dateStr = date !== "?" ? new Date(date).toISOString().split("T")[0] : "?";
|
|
176
|
-
lines.push(` ${dateStr}: ${score}/100`);
|
|
177
|
-
}
|
|
178
|
-
lines.push("");
|
|
179
|
-
lines.push(`Details: ${AGENT_URL}/${encodeURIComponent(name)}${UTM}`);
|
|
180
|
-
return lines.join("\n");
|
|
181
|
-
}
|
package/dist/resources.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export declare const METHODOLOGY_URI = "agentscore://methodology";
|
|
2
|
-
export declare const SCORE_BANDS_URI = "agentscore://score-bands";
|
|
3
|
-
export declare const resources: {
|
|
4
|
-
uri: string;
|
|
5
|
-
name: string;
|
|
6
|
-
description: string;
|
|
7
|
-
mimeType: string;
|
|
8
|
-
}[];
|
|
9
|
-
export declare function getResourceContent(uri: string): string | null;
|
package/dist/resources.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
export const METHODOLOGY_URI = "agentscore://methodology";
|
|
2
|
-
export const SCORE_BANDS_URI = "agentscore://score-bands";
|
|
3
|
-
export const resources = [
|
|
4
|
-
{
|
|
5
|
-
uri: METHODOLOGY_URI,
|
|
6
|
-
name: "AgentScore Methodology",
|
|
7
|
-
description: "How AgentScore calculates trust scores: the five dimensions, data sources, coverage multipliers, and anti-gaming measures.",
|
|
8
|
-
mimeType: "text/plain",
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
uri: SCORE_BANDS_URI,
|
|
12
|
-
name: "Trust Score Bands",
|
|
13
|
-
description: "Reference table for interpreting AgentScore trust bands and what they mean.",
|
|
14
|
-
mimeType: "text/plain",
|
|
15
|
-
},
|
|
16
|
-
];
|
|
17
|
-
export function getResourceContent(uri) {
|
|
18
|
-
if (uri === METHODOLOGY_URI) {
|
|
19
|
-
return METHODOLOGY_TEXT;
|
|
20
|
-
}
|
|
21
|
-
if (uri === SCORE_BANDS_URI) {
|
|
22
|
-
return SCORE_BANDS_TEXT;
|
|
23
|
-
}
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
const METHODOLOGY_TEXT = `AgentScore Methodology
|
|
27
|
-
======================
|
|
28
|
-
|
|
29
|
-
AgentScore produces a 0-100 trust score for AI agents by aggregating data from four independent platforms.
|
|
30
|
-
|
|
31
|
-
Data Sources:
|
|
32
|
-
1. Moltbook — Social reputation (karma, posts, followers, verification)
|
|
33
|
-
2. ERC-8004 — On-chain identity (Ethereum agent registry)
|
|
34
|
-
3. ClawTasks — Work history (task completions, success rate, earnings)
|
|
35
|
-
4. Molt Market — Freelance gigs (completed gigs, client feedback)
|
|
36
|
-
|
|
37
|
-
Five Scoring Dimensions (20 points each):
|
|
38
|
-
1. Identity — Verified accounts, on-chain registration, profile completeness
|
|
39
|
-
2. Activity — Posting frequency, recency, multi-platform engagement
|
|
40
|
-
3. Reputation — Karma, followers, peer feedback, community standing
|
|
41
|
-
4. Work History — Tasks completed, success rate, earnings, client reviews
|
|
42
|
-
5. Consistency — Cross-platform name matching, profile alignment, posting regularity
|
|
43
|
-
|
|
44
|
-
Coverage Multipliers:
|
|
45
|
-
The effective score is the raw score multiplied by a coverage factor based on how many platforms verify the agent:
|
|
46
|
-
0 platforms: 0.00 (no score)
|
|
47
|
-
1 platform: 0.40 (max effective score: 40)
|
|
48
|
-
2 platforms: 0.65 (max effective score: 65)
|
|
49
|
-
3 platforms: 0.85 (max effective score: 85)
|
|
50
|
-
4 platforms: 1.00 (max effective score: 100)
|
|
51
|
-
|
|
52
|
-
This means an agent on only one platform — even with perfect scores — can never exceed 40/100.
|
|
53
|
-
Multi-source verification is the architecture, not a feature.
|
|
54
|
-
|
|
55
|
-
Inactivity Decay:
|
|
56
|
-
Agents inactive for more than 30 days receive a decay multiplier on Activity and Reputation:
|
|
57
|
-
decay = max(0.50, 1.0 - (inactive_days - 30) * 0.005)
|
|
58
|
-
Floor at 50% — earned trust is never fully erased by inactivity.
|
|
59
|
-
|
|
60
|
-
Anti-Gaming:
|
|
61
|
-
- Single-source penalty (60% reduction for one-platform agents)
|
|
62
|
-
- Cross-platform consistency checks
|
|
63
|
-
- Anomaly detection for sudden score changes
|
|
64
|
-
- Gaming one platform is easy; gaming four simultaneously is exponentially harder
|
|
65
|
-
|
|
66
|
-
Full methodology: https://agentscores.xyz/methodology?utm_source=mcp&utm_medium=resource&utm_campaign=npm
|
|
67
|
-
Whitepaper: https://agentscores.xyz/methodology/whitepaper?utm_source=mcp&utm_medium=resource&utm_campaign=npm`;
|
|
68
|
-
const SCORE_BANDS_TEXT = `AgentScore Trust Bands
|
|
69
|
-
======================
|
|
70
|
-
|
|
71
|
-
Score Band Meaning
|
|
72
|
-
----- ---- -------
|
|
73
|
-
0-19 UNVERIFIED No meaningful trust signal. Agent may exist on one platform but lacks cross-platform verification.
|
|
74
|
-
20-39 LOW TRUST Some presence but limited verification. Typically single-platform agents with moderate activity.
|
|
75
|
-
40-59 MODERATE Established presence with partial cross-platform verification. Building trust.
|
|
76
|
-
60-79 TRUSTED Strong cross-platform presence. Verified on multiple independent platforms.
|
|
77
|
-
80-100 HIGHLY TRUSTED Exceptional multi-source verification. Verified across all platforms with strong scores.
|
|
78
|
-
|
|
79
|
-
Note: The highest effective score in the ecosystem is currently 22/100 — because no agent has verified across all four platforms yet. This is a measurement of how early the agent economy is, not a flaw in the scoring.
|
|
80
|
-
|
|
81
|
-
Effective score = raw score * coverage multiplier
|
|
82
|
-
Coverage multipliers: 1 platform = 0.40, 2 = 0.65, 3 = 0.85, 4 = 1.00`;
|