mcp-server-mcpindex 0.3.9 → 0.3.11
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/CHANGELOG.md +15 -0
- package/README.md +19 -2
- package/manifest.json +5 -3
- package/package.json +3 -3
- package/src/index.mjs +18 -4
- package/src/tools-meta.json +1 -1
- package/src/trust.mjs +18 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ All notable changes to `mcp-server-mcpindex` are recorded here.
|
|
|
4
4
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.3.11] - 2026-07-18
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Verdict `status` no longer overstates screening completeness.** The live API emits `status: "PARTIAL"` for a description-level screen, but the client's `VALID_STATUSES` omitted `PARTIAL` and coerced it *up* to `EVALUATED` (a full screen) — dishonest for a trust tool. `PARTIAL` now passes through verbatim, an unknown status is coerced *down* to `STALE` (never up to `EVALUATED`), and the `granularity` field (e.g. `"description-level"`) is now preserved instead of dropped.
|
|
12
|
+
- **README:** documents the `PARTIAL` status + `granularity`, and clarifies that `status: "ERROR"` also covers "no verdict on file yet" (not only endpoint failures).
|
|
13
|
+
|
|
14
|
+
## [0.3.10] - 2026-07-18
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **`get_install_command` no longer advertises or mislabels a `zed` client.** `zed` was in the client enum but the install builder never produced a zed config, so a zed request silently returned a different client's config under a `zed` header. Removed `zed` from the enum, and `formatInstall` now labels any fallback honestly (never presents one client's config under another client's name).
|
|
19
|
+
- **`check_tool_trust` schema example uses a real registry slug.** The `server_id` example was `"github"`, a short name that never resolves (always ERROR/UNVERIFIED); it now shows a real long-form slug (e.g. `io-github-microsoft-playwright-mcp`) and states it is not a short name.
|
|
20
|
+
- **`compare_servers` description no longer promises "transport types"** that the output table never contained.
|
|
21
|
+
|
|
7
22
|
## [0.3.9] - 2026-07-15
|
|
8
23
|
|
|
9
24
|
### Changed
|
package/README.md
CHANGED
|
@@ -97,12 +97,15 @@ npx -y mcp-server-mcpindex@latest
|
|
|
97
97
|
|
|
98
98
|
`check_tool_trust` is the **directory client** integration surface (not the in-path `mcpindex-gate`). It lets agent frameworks (Composio, Mastra, LangChain, DSPy, raw LLM-tool-call loops) ask for an advisory screen verdict before dispatching a call. At v1 you will see REVIEW or UNVERIFIED — not a safety clearance.
|
|
99
99
|
|
|
100
|
+
> **Using Mastra?** The sibling package `@mcp-index/mastra` ships this exact screen as a ready-made `beforeToolCall` hook - `npm i @mcp-index/mastra`, no wiring required.
|
|
101
|
+
|
|
100
102
|
### Verdict contract (v1)
|
|
101
103
|
|
|
102
104
|
```jsonc
|
|
103
105
|
{
|
|
104
106
|
"directive": "ALLOW" | "DENY" | "REVIEW" | "UNVERIFIED",
|
|
105
|
-
"status": "EVALUATED" | "STALE" | "ERROR",
|
|
107
|
+
"status": "EVALUATED" | "PARTIAL" | "STALE" | "ERROR",
|
|
108
|
+
"granularity": "description-level" | null, // scope of a PARTIAL screen
|
|
106
109
|
"dimensions": [
|
|
107
110
|
{ "id": "tool_safety", "verdict": "PASS", "severity": "INFO" }
|
|
108
111
|
],
|
|
@@ -190,7 +193,9 @@ async function gateToolCall({ serverId, toolName, invoke, askHuman }) {
|
|
|
190
193
|
|
|
191
194
|
### The load-bearing rule: never fail-open
|
|
192
195
|
|
|
193
|
-
If the verdict endpoint is unreachable, returns 404, times out,
|
|
196
|
+
If the verdict endpoint is unreachable, returns 404, times out, returns malformed JSON, **or has no verdict on file yet for that server**, `check_tool_trust` returns `directive: "UNVERIFIED"` + `status: "ERROR"`. It never silently coerces to ALLOW. Your gate code SHOULD treat UNVERIFIED as "human review required", never as "looks fine, ship it."
|
|
197
|
+
|
|
198
|
+
`status` is telemetry about screen completeness, distinct from the `directive` trust decision: `EVALUATED` (full screen), `PARTIAL` (only part of the surface, e.g. description-level — see `granularity`), `STALE` (verdict past its freshness window), `ERROR` (unreachable / no verdict on file). A `PARTIAL` screen is never reported as `EVALUATED`.
|
|
194
199
|
|
|
195
200
|
This is tested. See `test/trust.test.mjs`.
|
|
196
201
|
|
|
@@ -217,6 +222,18 @@ By default, calls go to `https://mcpindex.ai`. Override with `MCPINDEX_API_BASE=
|
|
|
217
222
|
|
|
218
223
|
The free tier is rate-limited to 60 req/min/IP. Paid keys are coming for higher throughput and the full evidence-bearing verdict (evidence quotes, LLM rationale, chain history).
|
|
219
224
|
|
|
225
|
+
## Related packages
|
|
226
|
+
|
|
227
|
+
Three ways to bring mcpindex into an agent, for different surfaces:
|
|
228
|
+
|
|
229
|
+
| Package | Install | What it does |
|
|
230
|
+
| --- | --- | --- |
|
|
231
|
+
| **`mcp-server-mcpindex`** *(this package)* | `npm i -g mcp-server-mcpindex` | Directory + advisory screen as an MCP server: find servers by task, and `check_tool_trust` before a call. |
|
|
232
|
+
| **`@mcp-index/mastra`** | `npm i @mcp-index/mastra` | The same advisory screen wired into Mastra as a `beforeToolCall` hook (warn / enforce). |
|
|
233
|
+
| **`@mcp-index/sdk`** | `npm i @mcp-index/sdk` | In-path drift gate: `wrap()` an MCP session and HOLD a call when a tool's contract drifts from your pin. |
|
|
234
|
+
|
|
235
|
+
**Advisory screen vs drift gate:** this package and `@mcp-index/mastra` ask mcpindex "has this tool been vetted?" (a network verdict). `@mcp-index/sdk` asks a different question locally: "did this tool's contract change since I pinned it?" Complementary, and none depends on another.
|
|
236
|
+
|
|
220
237
|
## License
|
|
221
238
|
|
|
222
239
|
MIT.
|
package/manifest.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"manifest_version": "0.2",
|
|
3
3
|
"name": "mcp-server-mcpindex",
|
|
4
4
|
"display_name": "mcpindex",
|
|
5
|
-
"version": "0.3.
|
|
5
|
+
"version": "0.3.11",
|
|
6
6
|
"description": "The MCP directory that vets servers, not just lists them: search by task, then get an advisory screen on a server before an agent calls its tools. Advisory, conformance monitored not enforced, not a safety verdict.",
|
|
7
7
|
"long_description": "mcpindex is the MCP directory that vets servers, not just lists them. Search by task to find the right server (ranked picks with install commands and quality scores), then get an advisory screen on a specific tool or a whole server before your agent calls it: whether each tool does what it claims. check_tool_trust and assess_server return an advisory verdict (REVIEW / UNVERIFIED at v1), backed by conformance monitoring and a live drift ledger, and fail closed to human review. Zero-config: queries the public mcpindex.ai API, no credentials. Monitored, not enforced; not a safety verdict and not an in-path gate.",
|
|
8
8
|
"author": {
|
|
9
|
-
"name": "
|
|
9
|
+
"name": "mcpindex.ai",
|
|
10
10
|
"email": "hello@mcpindex.ai",
|
|
11
11
|
"url": "https://mcpindex.ai"
|
|
12
12
|
},
|
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
"entry_point": "src/index.mjs",
|
|
32
32
|
"mcp_config": {
|
|
33
33
|
"command": "node",
|
|
34
|
-
"args": [
|
|
34
|
+
"args": [
|
|
35
|
+
"${__dirname}/src/index.mjs"
|
|
36
|
+
],
|
|
35
37
|
"env": {
|
|
36
38
|
"MCPINDEX_API_BASE": "${user_config.api_base}"
|
|
37
39
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-server-mcpindex",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"mcpName": "io.github.gautamgb/mcp-server-mcpindex",
|
|
5
|
-
"description": "The MCP directory that vets servers, not just lists them. Search by task, then get an advisory screen on a server before your agent calls its tools: whether each tool does what it claims, backed by conformance monitoring and a public drift ledger. Advisory (REVIEW / UNVERIFIED), monitored not enforced, not a safety verdict. Drop-in for Claude Desktop, Claude Code, Cursor, Gemini CLI, Cline
|
|
5
|
+
"description": "The MCP directory that vets servers, not just lists them. Search by task, then get an advisory screen on a server before your agent calls its tools: whether each tool does what it claims, backed by conformance monitoring and a public drift ledger. Advisory (REVIEW / UNVERIFIED), monitored not enforced, not a safety verdict. Drop-in for Claude Desktop, Claude Code, Cursor, Gemini CLI, Cline.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mcp",
|
|
8
8
|
"model-context-protocol",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"bugs": {
|
|
25
25
|
"url": "https://github.com/mcpindex-ai/mcpindex-web/issues"
|
|
26
26
|
},
|
|
27
|
-
"author": "
|
|
27
|
+
"author": "mcpindex.ai <hello@mcpindex.ai>",
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"type": "module",
|
|
30
30
|
"main": "src/index.mjs",
|
package/src/index.mjs
CHANGED
|
@@ -85,7 +85,11 @@ const TOOLS = [
|
|
|
85
85
|
},
|
|
86
86
|
client: {
|
|
87
87
|
type: 'string',
|
|
88
|
-
|
|
88
|
+
// Only clients the API actually generates a config for. 'zed' was advertised
|
|
89
|
+
// but never produced (buildInstalls has no zed branch), so a zed request silently
|
|
90
|
+
// fell back to another client's config; dropped until a real zed generator exists.
|
|
91
|
+
// formatInstall also labels any fallback honestly as defense-in-depth.
|
|
92
|
+
enum: ['claude-desktop', 'claude-code', 'cursor', 'gemini-cli', 'cline'],
|
|
89
93
|
description: 'Target client.',
|
|
90
94
|
},
|
|
91
95
|
},
|
|
@@ -119,7 +123,8 @@ const TOOLS = [
|
|
|
119
123
|
properties: {
|
|
120
124
|
server_id: {
|
|
121
125
|
type: 'string',
|
|
122
|
-
description:
|
|
126
|
+
description:
|
|
127
|
+
'Registry slug from search_mcp_servers / recommend results, e.g. "io-github-microsoft-playwright-mcp" (NOT a short name like "github").',
|
|
123
128
|
},
|
|
124
129
|
tool_name: {
|
|
125
130
|
type: 'string',
|
|
@@ -266,15 +271,24 @@ function formatSearch(data) {
|
|
|
266
271
|
function formatInstall(server, client) {
|
|
267
272
|
// server here is the per-server JSON we expose at /api/v1/server/<slug>.
|
|
268
273
|
// Returns a code block with the appropriate install snippet.
|
|
269
|
-
const
|
|
274
|
+
const exact = (server.installs ?? []).find((i) => i.client === client);
|
|
275
|
+
const target = exact ?? server.installs?.[0];
|
|
270
276
|
if (!target) return `No install path available for ${server.name}`;
|
|
277
|
+
// Never mislabel a fallback as the requested client: if this server publishes no
|
|
278
|
+
// config for `client`, show the header for the config we're ACTUALLY returning and
|
|
279
|
+
// say so, so an agent doesn't paste (e.g.) a claude-desktop block into another client.
|
|
280
|
+
const shownClient = exact ? client : (target.client ?? client);
|
|
281
|
+
const fallbackNote = exact
|
|
282
|
+
? ''
|
|
283
|
+
: `\n(No ${client}-specific config published for this server; showing the ${shownClient} config.)`;
|
|
271
284
|
return [
|
|
272
|
-
`${server.title} (${server.name}) - ${
|
|
285
|
+
`${server.title} (${server.name}) - ${shownClient}`,
|
|
273
286
|
'',
|
|
274
287
|
target.json
|
|
275
288
|
? '```json\n' + target.json + '\n```'
|
|
276
289
|
: '```bash\n' + target.command + '\n```',
|
|
277
290
|
target.notes ? '\n' + target.notes : '',
|
|
291
|
+
fallbackNote,
|
|
278
292
|
`\n${server.url ?? `https://mcpindex.ai/server/${server.slug}`}`,
|
|
279
293
|
].join('\n');
|
|
280
294
|
}
|
package/src/tools-meta.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
{
|
|
18
18
|
"name": "compare_servers",
|
|
19
19
|
"title": "Compare MCP servers",
|
|
20
|
-
"description": "Side-by-side comparison of 2-5 MCP servers - quality scores, install paths,
|
|
20
|
+
"description": "Side-by-side comparison of 2-5 MCP servers - quality scores, install paths, env vars."
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"name": "check_tool_trust",
|
package/src/trust.mjs
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// Contract (v1 advisory, free tier):
|
|
4
4
|
// {
|
|
5
5
|
// directive: "ALLOW" | "DENY" | "REVIEW" | "UNVERIFIED",
|
|
6
|
-
// status: "EVALUATED" | "STALE" | "ERROR",
|
|
6
|
+
// status: "EVALUATED" | "PARTIAL" | "STALE" | "ERROR",
|
|
7
|
+
// granularity: string | null, // e.g. "description-level" for a PARTIAL screen
|
|
7
8
|
// dimensions: [{ id, verdict, severity }],
|
|
8
9
|
// expires_at: string | null,
|
|
9
10
|
// honest_limits: string[],
|
|
@@ -14,6 +15,12 @@
|
|
|
14
15
|
// Fail-CLOSED: if the upstream endpoint is unreachable, returns 404, or
|
|
15
16
|
// returns malformed data, we return directive=UNVERIFIED, status=ERROR.
|
|
16
17
|
// We NEVER fake an ALLOW. This is the load-bearing safety invariant.
|
|
18
|
+
//
|
|
19
|
+
// status is TELEMETRY about screen completeness (not the trust decision — that is
|
|
20
|
+
// `directive`). PARTIAL means the screen only covered part of the surface (e.g.
|
|
21
|
+
// description-level, not the live tool); it must NEVER be reported as EVALUATED. An
|
|
22
|
+
// unexpected/unknown status is coerced DOWN to STALE (conservative), never up to
|
|
23
|
+
// EVALUATED — overstating completeness is the one direction a trust tool must not drift.
|
|
17
24
|
|
|
18
25
|
export const VERDICT_CONTRACT_VERSION = '1.0.0';
|
|
19
26
|
|
|
@@ -24,7 +31,7 @@ export const V1_HONEST_LIMITS = Object.freeze([
|
|
|
24
31
|
]);
|
|
25
32
|
|
|
26
33
|
const VALID_DIRECTIVES = new Set(['ALLOW', 'DENY', 'REVIEW', 'UNVERIFIED']);
|
|
27
|
-
const VALID_STATUSES = new Set(['EVALUATED', 'STALE', 'ERROR']);
|
|
34
|
+
const VALID_STATUSES = new Set(['EVALUATED', 'PARTIAL', 'STALE', 'ERROR']);
|
|
28
35
|
const VALID_VERDICTS = new Set(['PASS', 'FAIL', 'UNVERIFIED']);
|
|
29
36
|
const VALID_SEVERITIES = new Set(['INFO', 'LOW', 'MEDIUM', 'HIGH', 'CRITICAL']);
|
|
30
37
|
|
|
@@ -34,6 +41,7 @@ function unverifiedResponse({ serverId, toolName, sourceUrl, reason }) {
|
|
|
34
41
|
return {
|
|
35
42
|
directive: 'UNVERIFIED',
|
|
36
43
|
status: 'ERROR',
|
|
44
|
+
granularity: null,
|
|
37
45
|
dimensions: [],
|
|
38
46
|
expires_at: null,
|
|
39
47
|
honest_limits: [...V1_HONEST_LIMITS, `unverified_reason:${reason}`],
|
|
@@ -66,11 +74,17 @@ function shapeVerdict({ raw, serverId, toolName, sourceUrl }) {
|
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
const directive = VALID_DIRECTIVES.has(raw.directive) ? raw.directive : 'UNVERIFIED';
|
|
77
|
+
// Known status passes through verbatim (incl. PARTIAL). An UNKNOWN status is coerced
|
|
78
|
+
// conservatively: ERROR when there's no directive, else STALE — NEVER 'EVALUATED',
|
|
79
|
+
// which would falsely claim a complete screen.
|
|
69
80
|
const status = VALID_STATUSES.has(raw.status)
|
|
70
81
|
? raw.status
|
|
71
82
|
: directive === 'UNVERIFIED'
|
|
72
83
|
? 'ERROR'
|
|
73
|
-
: '
|
|
84
|
+
: 'STALE';
|
|
85
|
+
// Screen granularity (e.g. "description-level"); pass through so callers can see a
|
|
86
|
+
// PARTIAL screen's scope instead of silently losing it.
|
|
87
|
+
const granularity = typeof raw.granularity === 'string' ? raw.granularity : null;
|
|
74
88
|
const dimensions = normalizeDimensions(raw.dimensions);
|
|
75
89
|
const expires_at = typeof raw.expires_at === 'string' ? raw.expires_at : null;
|
|
76
90
|
|
|
@@ -84,6 +98,7 @@ function shapeVerdict({ raw, serverId, toolName, sourceUrl }) {
|
|
|
84
98
|
return {
|
|
85
99
|
directive,
|
|
86
100
|
status,
|
|
101
|
+
granularity,
|
|
87
102
|
dimensions,
|
|
88
103
|
expires_at,
|
|
89
104
|
honest_limits,
|