hvtracker-mcp 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 +22 -0
- package/README.md +102 -0
- package/bin/hvtracker-mcp.js +210 -0
- package/package.json +44 -0
- package/server.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 YugantM
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# HVTracker MCP
|
|
2
|
+
|
|
3
|
+
MCP server for checking supply-chain trust before connecting to AI agents,
|
|
4
|
+
frameworks, or MCP servers.
|
|
5
|
+
|
|
6
|
+
The hosted remote server is:
|
|
7
|
+
|
|
8
|
+
```json
|
|
9
|
+
{
|
|
10
|
+
"mcpServers": {
|
|
11
|
+
"hvtracker": {
|
|
12
|
+
"url": "https://hvtracker.net/mcp"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This repository also provides a local stdio package for clients that prefer
|
|
19
|
+
package-based installation.
|
|
20
|
+
|
|
21
|
+
<!-- mcp-name: io.github.yugantm/hvtracker-mcp -->
|
|
22
|
+
|
|
23
|
+
## Tools
|
|
24
|
+
|
|
25
|
+
- `verify_mcp_server`: pre-connect trust verdict for an MCP server, package, GitHub repo, or agent name.
|
|
26
|
+
- `check_agent_trust`: compact trust profile for a tracked AI agent or framework.
|
|
27
|
+
- `search_agents`: search the HVTracker registry by name, repo, description, or category.
|
|
28
|
+
|
|
29
|
+
## Local Install
|
|
30
|
+
|
|
31
|
+
With npm:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install -g hvtracker-mcp
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
With PyPI:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python3 -m pip install hvtracker-mcp
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Example MCP client config:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"hvtracker": {
|
|
49
|
+
"command": "hvtracker-mcp"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Development
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python3 -m pip install -e ".[dev]"
|
|
59
|
+
python3 -m pytest
|
|
60
|
+
hvtracker-mcp
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Use a different HVTracker base URL while testing:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
HVTRACKER_BASE_URL=http://localhost:8080 hvtracker-mcp
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Registry Publishing
|
|
70
|
+
|
|
71
|
+
The official MCP Registry manifest is `server.json`.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
mcp-publisher login github
|
|
75
|
+
mcp-publisher publish
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
In GitHub Actions, run the "Publish MCP Registry" workflow after the npm,
|
|
79
|
+
PyPI, and GHCR packages for the same version are live.
|
|
80
|
+
|
|
81
|
+
The server name is:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
io.github.yugantm/hvtracker-mcp
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Claude Desktop Extension
|
|
88
|
+
|
|
89
|
+
Tagged releases build an `.mcpb` bundle for Claude Desktop from `manifest.json`.
|
|
90
|
+
To build it locally:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm ci --omit=dev
|
|
94
|
+
npx @anthropic-ai/mcpb@2.1.2 pack
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Privacy
|
|
98
|
+
|
|
99
|
+
HVTracker MCP sends the user-supplied search string or server identifier to
|
|
100
|
+
`https://hvtracker.net` to fetch public trust data. It does not require an API
|
|
101
|
+
key and does not write to user systems. See the HVTracker site for current data
|
|
102
|
+
and methodology, and see `PRIVACY.md` for the repository privacy note.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
|
|
9
|
+
const VERSION = "0.1.0";
|
|
10
|
+
const DEFAULT_BASE_URL = "https://hvtracker.net";
|
|
11
|
+
const BASE_URL = (process.env.HVTRACKER_BASE_URL || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
12
|
+
const TIMEOUT_MS = Number(process.env.HVTRACKER_TIMEOUT_SECONDS || 20) * 1000;
|
|
13
|
+
const READ_ONLY = {
|
|
14
|
+
readOnlyHint: true,
|
|
15
|
+
destructiveHint: false,
|
|
16
|
+
idempotentHint: true,
|
|
17
|
+
openWorldHint: true
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export async function apiGet(path, params = {}) {
|
|
21
|
+
const url = new URL(path, BASE_URL);
|
|
22
|
+
for (const [key, value] of Object.entries(params)) {
|
|
23
|
+
if (value !== undefined && value !== null && String(value) !== "") {
|
|
24
|
+
url.searchParams.set(key, String(value));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const response = await fetch(url, {
|
|
29
|
+
headers: { "user-agent": `hvtracker-mcp/${VERSION}` },
|
|
30
|
+
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
31
|
+
});
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error(`HVTracker API returned ${response.status} for ${url.toString()}`);
|
|
34
|
+
}
|
|
35
|
+
const payload = await response.json();
|
|
36
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
37
|
+
throw new Error(`Unexpected response shape from ${url.toString()}`);
|
|
38
|
+
}
|
|
39
|
+
return payload;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function asToolResult(value) {
|
|
43
|
+
return {
|
|
44
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
45
|
+
structuredContent: value
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function agentProfile(agent) {
|
|
50
|
+
const slug = agent.slug;
|
|
51
|
+
return {
|
|
52
|
+
tracked: true,
|
|
53
|
+
name: agent.name,
|
|
54
|
+
repo: agent.repo,
|
|
55
|
+
trust_score: agent.trust_score,
|
|
56
|
+
evidence_grade: agent.evidence_grade,
|
|
57
|
+
rank: agent.rank,
|
|
58
|
+
category: agent.category,
|
|
59
|
+
has_provenance: agent.has_provenance,
|
|
60
|
+
scorecard_score: agent.scorecard_score,
|
|
61
|
+
mcp_server_support: agent.mcp_server_support?.status,
|
|
62
|
+
profile_url: slug ? `${BASE_URL}/agents/${slug}/` : null
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function verifyMcpServer(server) {
|
|
67
|
+
try {
|
|
68
|
+
return await apiGet("/api/v1/mcp/verify", { server });
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
server,
|
|
72
|
+
tracked: false,
|
|
73
|
+
trusted: false,
|
|
74
|
+
error: error instanceof Error ? error.message : String(error),
|
|
75
|
+
reasons: ["HVTracker could not fetch a verdict right now."]
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function checkAgentTrust(nameOrRepo) {
|
|
81
|
+
const verdict = await verifyMcpServer(nameOrRepo);
|
|
82
|
+
if (!verdict.tracked) {
|
|
83
|
+
return {
|
|
84
|
+
query: nameOrRepo,
|
|
85
|
+
tracked: false,
|
|
86
|
+
trusted: false,
|
|
87
|
+
message: "Not in the HVTracker registry; treat as unverified.",
|
|
88
|
+
submit_url: `${BASE_URL}/submit`,
|
|
89
|
+
verdict
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const slug = verdict.slug;
|
|
94
|
+
return {
|
|
95
|
+
query: nameOrRepo,
|
|
96
|
+
tracked: true,
|
|
97
|
+
trusted: verdict.trusted,
|
|
98
|
+
repo: verdict.resolved,
|
|
99
|
+
slug,
|
|
100
|
+
trust_score: verdict.trust_score,
|
|
101
|
+
evidence_grade: verdict.grade,
|
|
102
|
+
confidence: verdict.confidence,
|
|
103
|
+
mcp_server_support: verdict.mcp_server_support,
|
|
104
|
+
tool_permissions: verdict.tool_permissions || [],
|
|
105
|
+
profile_url: slug ? `${BASE_URL}/agents/${slug}/` : null,
|
|
106
|
+
reasons: verdict.reasons || []
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function searchAgents(query = "", category = "", limit = 10) {
|
|
111
|
+
try {
|
|
112
|
+
const data = await apiGet("/api/v1/agents");
|
|
113
|
+
const ql = String(query || "").trim().toLowerCase();
|
|
114
|
+
const cl = String(category || "").trim().toLowerCase();
|
|
115
|
+
const matches = [];
|
|
116
|
+
|
|
117
|
+
for (const agent of data.agents || []) {
|
|
118
|
+
const haystack = ["name", "repo", "description", "slug"]
|
|
119
|
+
.map((key) => String(agent[key] || ""))
|
|
120
|
+
.join(" ")
|
|
121
|
+
.toLowerCase();
|
|
122
|
+
if (ql && !haystack.includes(ql)) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (cl && String(agent.category || "").toLowerCase() !== cl) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
matches.push(agentProfile(agent));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
matches.sort((a, b) => {
|
|
132
|
+
if (a.trust_score == null && b.trust_score != null) return 1;
|
|
133
|
+
if (a.trust_score != null && b.trust_score == null) return -1;
|
|
134
|
+
return (b.trust_score || 0) - (a.trust_score || 0);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const safeLimit = Math.max(1, Math.min(Number.parseInt(limit, 10) || 10, 50));
|
|
138
|
+
return { count: matches.length, results: matches.slice(0, safeLimit) };
|
|
139
|
+
} catch (error) {
|
|
140
|
+
return {
|
|
141
|
+
count: 0,
|
|
142
|
+
results: [],
|
|
143
|
+
error: error instanceof Error ? error.message : String(error)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function createServer() {
|
|
149
|
+
const server = new McpServer({
|
|
150
|
+
name: "hvtracker",
|
|
151
|
+
version: VERSION,
|
|
152
|
+
instructions: "Check trust signals for AI agents and MCP servers."
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
server.registerTool(
|
|
156
|
+
"verify_mcp_server",
|
|
157
|
+
{
|
|
158
|
+
title: "Verify MCP Server",
|
|
159
|
+
description: "Pre-connect trust verdict for an MCP server or AI agent.",
|
|
160
|
+
inputSchema: {
|
|
161
|
+
server: z.string().describe("GitHub repo, package, display name, slug, or MCP URL.")
|
|
162
|
+
},
|
|
163
|
+
annotations: READ_ONLY
|
|
164
|
+
},
|
|
165
|
+
async ({ server }) => asToolResult(await verifyMcpServer(server))
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
server.registerTool(
|
|
169
|
+
"check_agent_trust",
|
|
170
|
+
{
|
|
171
|
+
title: "Check Agent Trust",
|
|
172
|
+
description: "Get the HVTracker trust profile for a tracked AI agent or framework.",
|
|
173
|
+
inputSchema: {
|
|
174
|
+
name_or_repo: z.string().describe("Agent name, slug, GitHub repo, package, or MCP URL.")
|
|
175
|
+
},
|
|
176
|
+
annotations: READ_ONLY
|
|
177
|
+
},
|
|
178
|
+
async ({ name_or_repo }) => asToolResult(await checkAgentTrust(name_or_repo))
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
server.registerTool(
|
|
182
|
+
"search_agents",
|
|
183
|
+
{
|
|
184
|
+
title: "Search Agents",
|
|
185
|
+
description: "Search tracked AI agents and frameworks by name, repo, or description.",
|
|
186
|
+
inputSchema: {
|
|
187
|
+
query: z.string().optional().default(""),
|
|
188
|
+
category: z.string().optional().default(""),
|
|
189
|
+
limit: z.number().int().min(1).max(50).optional().default(10)
|
|
190
|
+
},
|
|
191
|
+
annotations: READ_ONLY
|
|
192
|
+
},
|
|
193
|
+
async ({ query = "", category = "", limit = 10 }) =>
|
|
194
|
+
asToolResult(await searchAgents(query, category, limit))
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
return server;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export async function runStdio() {
|
|
201
|
+
const server = createServer();
|
|
202
|
+
await server.connect(new StdioServerTransport());
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
206
|
+
runStdio().catch((error) => {
|
|
207
|
+
console.error(error);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
});
|
|
210
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hvtracker-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for HVTracker trust checks.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"mcpName": "io.github.yugantm/hvtracker-mcp",
|
|
8
|
+
"bin": {
|
|
9
|
+
"hvtracker-mcp": "./bin/hvtracker-mcp.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE",
|
|
15
|
+
"server.json"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "node --test",
|
|
19
|
+
"pack:check": "npm pack --dry-run"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"model-context-protocol",
|
|
24
|
+
"ai-agents",
|
|
25
|
+
"trust",
|
|
26
|
+
"supply-chain"
|
|
27
|
+
],
|
|
28
|
+
"author": "YugantM",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/YugantM/hvtracker-mcp.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/YugantM/hvtracker-mcp/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://hvtracker.net",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=20"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
42
|
+
"zod": "^4.3.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.yugantm/hvtracker-mcp",
|
|
4
|
+
"title": "HVTracker MCP",
|
|
5
|
+
"description": "Pre-connect trust checks for AI agents and MCP servers using HVTracker's public trust registry.",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"url": "https://github.com/YugantM/hvtracker-mcp",
|
|
9
|
+
"source": "github"
|
|
10
|
+
},
|
|
11
|
+
"remotes": [
|
|
12
|
+
{
|
|
13
|
+
"type": "streamable-http",
|
|
14
|
+
"url": "https://hvtracker.net/mcp"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"packages": [
|
|
18
|
+
{
|
|
19
|
+
"registryType": "npm",
|
|
20
|
+
"identifier": "hvtracker-mcp",
|
|
21
|
+
"version": "0.1.0",
|
|
22
|
+
"transport": {
|
|
23
|
+
"type": "stdio"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"registryType": "pypi",
|
|
28
|
+
"identifier": "hvtracker-mcp",
|
|
29
|
+
"version": "0.1.0",
|
|
30
|
+
"transport": {
|
|
31
|
+
"type": "stdio"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"registryType": "oci",
|
|
36
|
+
"identifier": "ghcr.io/yugantm/hvtracker-mcp:0.1.0",
|
|
37
|
+
"transport": {
|
|
38
|
+
"type": "stdio"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"tags": ["security", "trust", "ai-agents", "mcp", "supply-chain"]
|
|
43
|
+
}
|