mcp-scorecard 0.1.2 → 0.2.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/CHANGELOG.md +23 -0
- package/README.md +44 -0
- package/dist/audit/runner.js +1 -1
- package/dist/audit/runner.js.map +1 -1
- package/dist/audit/web/web-checks.d.ts +7 -0
- package/dist/audit/web/web-checks.js +29 -0
- package/dist/audit/web/web-checks.js.map +1 -0
- package/dist/audit/web/web-probe.d.ts +7 -0
- package/dist/audit/web/web-probe.js +78 -0
- package/dist/audit/web/web-probe.js.map +1 -0
- package/dist/audit/web/web-runner.d.ts +2 -0
- package/dist/audit/web/web-runner.js +23 -0
- package/dist/audit/web/web-runner.js.map +1 -0
- package/dist/cli/commands.js +30 -13
- package/dist/cli/commands.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/types.d.ts +26 -1
- package/package.json +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
- **Web mode**: audit hosted/remote MCP servers + sites by URL (`npx -y mcp-scorecard https://your-server`). Adds 10 security + agent-readiness checks: HTTPS/TLS, security headers, auth posture, OAuth discovery, llms.txt, MCP Server Card (SEP-1649), Agent Skills index, API catalog (RFC 9727), robots + AI/Content-Signal, structured metadata (JSON-LD / OG / Markdown-for-Agents). The stdio path (10 protocol-quality checks) is unchanged.
|
|
6
|
+
- Added `scripts/test-web.mjs` to the test suite (offline, fixture-based).
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## 0.1.3 — 2026-05-29
|
|
10
|
+
|
|
11
|
+
### Docs
|
|
12
|
+
|
|
13
|
+
- **README now shows a real captured run.** Added a `## Demo` section with the
|
|
14
|
+
verbatim output of `npx mcp-scorecard @modelcontextprotocol/server-everything`
|
|
15
|
+
— the official MCP reference server scores 44/100, demonstrating the
|
|
16
|
+
conventions checks against a real, well-known public server (no hand-edited
|
|
17
|
+
output).
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **`SERVER_VERSION` constant synced to the published version.** It had drifted
|
|
22
|
+
to `0.1.1` while package.json shipped `0.1.2`, so the report footer
|
|
23
|
+
(`_Generated by mcp-scorecard vX_`) printed a stale version. All four sources
|
|
24
|
+
(package.json, server.json ×2, `src/constants.ts`) are now `0.1.3`.
|
|
25
|
+
|
|
3
26
|
## 0.1.1 — 2026-05-23
|
|
4
27
|
|
|
5
28
|
### Fixed
|
package/README.md
CHANGED
|
@@ -34,6 +34,50 @@ npx -y mcp-scorecard my-mcp --min-score 80
|
|
|
34
34
|
npx -y mcp-scorecard my-mcp --json
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
## Demo
|
|
38
|
+
|
|
39
|
+
Real captured run auditing the official MCP reference server
|
|
40
|
+
[`@modelcontextprotocol/server-everything`](https://www.npmjs.com/package/@modelcontextprotocol/server-everything)
|
|
41
|
+
— nothing here is hand-edited, this is exactly what the CLI printed:
|
|
42
|
+
|
|
43
|
+
```console
|
|
44
|
+
$ npx -y mcp-scorecard @modelcontextprotocol/server-everything
|
|
45
|
+
|
|
46
|
+
# mcp-scorecard - @modelcontextprotocol/server-everything @2026.1.26
|
|
47
|
+
|
|
48
|
+
**Agent-readiness score:** 44/100
|
|
49
|
+
|
|
50
|
+
- [PASS] Schema validity (13/13 tools have valid input schema)
|
|
51
|
+
- [FAIL] Tool naming convention (12/13 tools violate snake_case)
|
|
52
|
+
- [FAIL] Privacy modes documented (only 1 tool(s) mention privacy modes)
|
|
53
|
+
- [PASS] Mutation gating (no write tools — n/a)
|
|
54
|
+
- [FAIL] Agent manifest (no agent_manifest tool)
|
|
55
|
+
- [FAIL] Smoke test (no smoke script and no test script)
|
|
56
|
+
- [PASS] Resources advertised (7 resources registered)
|
|
57
|
+
- [PASS] Tool descriptions (avg 88 chars across 13 tools)
|
|
58
|
+
- [FAIL] Annotations (0/13 read tools annotated)
|
|
59
|
+
- [FAIL] Manifest discoverability (no discovery tools)
|
|
60
|
+
|
|
61
|
+
## Details
|
|
62
|
+
### Tool naming convention
|
|
63
|
+
- Non-snake_case names: get-annotated-message, get-env, get-resource-links, get-resource-reference, get-structured-content, get-sum, get-tiny-image, gzip-file-as-resource, toggle-simulated-logging, toggle-subscriber-updates
|
|
64
|
+
### Annotations
|
|
65
|
+
- Missing readOnlyHint: echo, get-annotated-message, get-env, get-resource-links, get-resource-reference, get-structured-content, get-sum, get-tiny-image, gzip-file-as-resource, toggle-simulated-logging
|
|
66
|
+
|
|
67
|
+
## Suggested fixes
|
|
68
|
+
- Rename tools to lowercase snake_case (a-z, 0-9, _).
|
|
69
|
+
- Add a `privacy_mode` parameter (summary | structured | raw) on read tools so agents can request only what they need.
|
|
70
|
+
- Expose a `<prefix>_agent_manifest` tool that returns { recommended_first_calls, standard_tools, ... } so agents can self-onboard.
|
|
71
|
+
- Add `scripts/smoke-tools.mjs` that boots the server via StdioClientTransport and asserts the tool list.
|
|
72
|
+
- Add `annotations: { readOnlyHint: true, openWorldHint: false }` to every read tool definition.
|
|
73
|
+
- Expose discovery tools so agents can self-onboard: `*_agent_manifest`, `*_data_inventory`, `*_capabilities`, `*_connection_status`.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The reference server is a feature showcase, not a production integration — a 44 is
|
|
77
|
+
expected and is exactly why the conventions checks exist. Agent-oriented servers
|
|
78
|
+
that adopt snake_case naming, a manifest tool, and read-only annotations land in
|
|
79
|
+
the 80s and 90s.
|
|
80
|
+
|
|
37
81
|
## What it checks
|
|
38
82
|
|
|
39
83
|
Ten quality dimensions, each scored 0-10. Final score is the sum, capped at 100.
|
package/dist/audit/runner.js
CHANGED
package/dist/audit/runner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/audit/runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,4BAA4B,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAI7C,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/audit/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web checks — pure scorers over a WebProbe. Ten dimensions, each 0–10, for
|
|
3
|
+
* hosted/remote MCP servers + sites: security posture + agent-readiness
|
|
4
|
+
* (the discovery signals agents and crawlers actually look for).
|
|
5
|
+
*/
|
|
6
|
+
import type { CheckResult, WebProbe } from '../../types.js';
|
|
7
|
+
export declare function scoreWeb(p: WebProbe): CheckResult[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const SEC_HEADERS = [
|
|
2
|
+
'strict-transport-security',
|
|
3
|
+
'content-security-policy',
|
|
4
|
+
'x-content-type-options',
|
|
5
|
+
'referrer-policy',
|
|
6
|
+
'x-frame-options'
|
|
7
|
+
];
|
|
8
|
+
function mk(id, label, score, summary, fixes = [], details = []) {
|
|
9
|
+
const s = Math.max(0, Math.min(10, Math.round(score)));
|
|
10
|
+
return { id, label, score: s, status: s >= 8 ? 'pass' : s >= 4 ? 'warn' : 'fail', summary, details, fixes };
|
|
11
|
+
}
|
|
12
|
+
export function scoreWeb(p) {
|
|
13
|
+
const present = SEC_HEADERS.filter((h) => p.headers[h]);
|
|
14
|
+
const authClear = p.authMd || p.oauthProtectedResource || p.oauthAuthServer;
|
|
15
|
+
const oauth = p.oauthProtectedResource || p.oauthAuthServer;
|
|
16
|
+
return [
|
|
17
|
+
mk('web_https', 'HTTPS / TLS', p.https ? 10 : 0, p.https ? 'served over HTTPS' : 'NOT served over HTTPS', p.https ? [] : ['Serve the MCP server / site over HTTPS only.']),
|
|
18
|
+
mk('web_security_headers', 'Security headers', (present.length / SEC_HEADERS.length) * 10, `${present.length}/${SEC_HEADERS.length} hardening headers present`, present.length >= 4 ? [] : [`Add missing headers: ${SEC_HEADERS.filter((h) => !p.headers[h]).join(', ')}.`]),
|
|
19
|
+
mk('web_auth_posture', 'Auth posture declared', authClear ? 10 : 3, authClear ? 'auth posture is explicit (auth.md / OAuth metadata)' : 'auth posture unclear — agents cannot tell how to authenticate', authClear ? [] : ['Publish /auth.md (even "no auth required") or OAuth discovery metadata so agents know how to connect.']),
|
|
20
|
+
mk('web_oauth_discovery', 'OAuth discovery', oauth ? 10 : p.authMd ? 7 : 0, oauth ? 'OAuth/OIDC discovery metadata present' : p.authMd ? 'no OAuth, but auth.md declares the (no-auth) posture' : 'no OAuth/OIDC discovery metadata', oauth || p.authMd ? [] : ['If the server has protected APIs, publish /.well-known/oauth-protected-resource (+ oauth-authorization-server).']),
|
|
21
|
+
mk('web_llms_txt', 'llms.txt', p.llmsTxt ? 10 : 0, p.llmsTxt ? '/llms.txt present and substantive' : '/llms.txt missing', p.llmsTxt ? [] : ['Add /llms.txt describing the server, install command, tools and links for LLMs.']),
|
|
22
|
+
mk('web_mcp_server_card', 'MCP Server Card', p.serverCardValid ? 10 : p.serverCard ? 5 : 0, p.serverCardValid ? '/.well-known/mcp/server-card.json present and valid' : p.serverCard ? 'server card present but missing serverInfo' : 'MCP Server Card missing', p.serverCardValid ? [] : ['Serve /.well-known/mcp/server-card.json (SEP-1649) with serverInfo, transport and capabilities.']),
|
|
23
|
+
mk('web_agent_skills', 'Agent Skills index', p.agentSkills ? 10 : 0, p.agentSkills ? '/.well-known/agent-skills/index.json present' : 'Agent Skills discovery index missing', p.agentSkills ? [] : ['Publish /.well-known/agent-skills/index.json (Agent Skills Discovery RFC) listing your skills.']),
|
|
24
|
+
mk('web_api_catalog', 'API catalog (RFC 9727)', p.apiCatalog ? 10 : 0, p.apiCatalog ? '/.well-known/api-catalog present' : 'API catalog missing', p.apiCatalog ? [] : ['Publish /.well-known/api-catalog (application/linkset+json) pointing to your OpenAPI / docs / status.']),
|
|
25
|
+
mk('web_robots_signals', 'robots + AI signals', (p.robotsTxt ? 4 : 0) + (p.aiBotRules ? 2 : 0) + (p.contentSignal ? 2 : 0) + (p.sitemap ? 2 : 0), `robots:${p.robotsTxt ? 'y' : 'n'} ai-bots:${p.aiBotRules ? 'y' : 'n'} content-signal:${p.contentSignal ? 'y' : 'n'} sitemap:${p.sitemap ? 'y' : 'n'}`, (p.robotsTxt && p.aiBotRules && p.contentSignal && p.sitemap) ? [] : ['robots.txt with explicit AI-bot rules + a Content-Signal directive + a Sitemap line.']),
|
|
26
|
+
mk('web_structured_data', 'Structured metadata', (p.jsonLd ? 4 : 0) + (p.ogTags ? 3 : 0) + (p.markdownNegotiation ? 3 : 0), `json-ld:${p.jsonLd ? 'y' : 'n'} og:${p.ogTags ? 'y' : 'n'} markdown-for-agents:${p.markdownNegotiation ? 'y' : 'n'}`, (p.jsonLd && p.ogTags && p.markdownNegotiation) ? [] : ['Add JSON-LD (schema.org), OpenGraph tags, and Markdown-for-Agents (Accept: text/markdown).'])
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=web-checks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-checks.js","sourceRoot":"","sources":["../../../src/audit/web/web-checks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web probe — captures discovery + security signals from a hosted/remote MCP
|
|
3
|
+
* server or site over HTTP. Resilient: every fetch failure degrades to a
|
|
4
|
+
* negative signal rather than throwing. Pure scorers (web-checks) read the result.
|
|
5
|
+
*/
|
|
6
|
+
import type { WebProbe } from '../../types.js';
|
|
7
|
+
export declare function fetchWebProbe(rawUrl: string): Promise<WebProbe>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const UA = 'mcp-scorecard/0.2 (+https://mcp-scorecard.vercel.app)';
|
|
2
|
+
async function get(url, opts = {}) {
|
|
3
|
+
const ctrl = new AbortController();
|
|
4
|
+
const t = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 8000);
|
|
5
|
+
try {
|
|
6
|
+
const res = await fetch(url, {
|
|
7
|
+
signal: ctrl.signal,
|
|
8
|
+
redirect: 'follow',
|
|
9
|
+
headers: { 'user-agent': UA, accept: opts.accept ?? '*/*' }
|
|
10
|
+
});
|
|
11
|
+
const headers = {};
|
|
12
|
+
res.headers.forEach((v, k) => { headers[k.toLowerCase()] = v; });
|
|
13
|
+
let text = '';
|
|
14
|
+
try {
|
|
15
|
+
text = await res.text();
|
|
16
|
+
}
|
|
17
|
+
catch { /* binary / empty */ }
|
|
18
|
+
return { status: res.status, text, headers };
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
clearTimeout(t);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const ok = (r) => !!r && r.status >= 200 && r.status < 400;
|
|
28
|
+
export async function fetchWebProbe(rawUrl) {
|
|
29
|
+
const base = rawUrl.replace(/\/+$/, '');
|
|
30
|
+
const u = new URL(base);
|
|
31
|
+
const at = (p) => `${u.origin}${p}`;
|
|
32
|
+
const [root, llms, robots, card, skills, apiCat, sitemap, oauthPR, oauthAS, authMd, mdNeg] = await Promise.all([
|
|
33
|
+
get(base + '/'),
|
|
34
|
+
get(at('/llms.txt')),
|
|
35
|
+
get(at('/robots.txt')),
|
|
36
|
+
get(at('/.well-known/mcp/server-card.json')),
|
|
37
|
+
get(at('/.well-known/agent-skills/index.json')),
|
|
38
|
+
get(at('/.well-known/api-catalog')),
|
|
39
|
+
get(at('/sitemap.xml')),
|
|
40
|
+
get(at('/.well-known/oauth-protected-resource')),
|
|
41
|
+
get(at('/.well-known/oauth-authorization-server')),
|
|
42
|
+
get(at('/auth.md')),
|
|
43
|
+
get(base + '/', { accept: 'text/markdown' })
|
|
44
|
+
]);
|
|
45
|
+
const robotsText = ok(robots) ? robots.text.toLowerCase() : '';
|
|
46
|
+
const rootHtml = ok(root) ? root.text : '';
|
|
47
|
+
let serverCardValid = false;
|
|
48
|
+
if (ok(card)) {
|
|
49
|
+
try {
|
|
50
|
+
serverCardValid = !!JSON.parse(card.text)?.serverInfo;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
serverCardValid = false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
url: base,
|
|
58
|
+
https: u.protocol === 'https:',
|
|
59
|
+
rootStatus: root?.status ?? 0,
|
|
60
|
+
headers: root?.headers ?? {},
|
|
61
|
+
llmsTxt: ok(llms) && llms.text.trim().length > 20,
|
|
62
|
+
serverCard: ok(card),
|
|
63
|
+
serverCardValid,
|
|
64
|
+
agentSkills: ok(skills),
|
|
65
|
+
apiCatalog: ok(apiCat),
|
|
66
|
+
robotsTxt: ok(robots),
|
|
67
|
+
aiBotRules: /gptbot|claudebot|claude-web|perplexitybot|google-extended|anthropic-ai/.test(robotsText),
|
|
68
|
+
contentSignal: /content-signal/.test(robotsText),
|
|
69
|
+
sitemap: ok(sitemap),
|
|
70
|
+
oauthProtectedResource: ok(oauthPR),
|
|
71
|
+
oauthAuthServer: ok(oauthAS),
|
|
72
|
+
authMd: ok(authMd) && /auth\.md/i.test(authMd.text),
|
|
73
|
+
jsonLd: /application\/ld\+json/i.test(rootHtml),
|
|
74
|
+
ogTags: /property=["']og:/i.test(rootHtml),
|
|
75
|
+
markdownNegotiation: !!mdNeg && (mdNeg.headers['content-type'] || '').includes('text/markdown')
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=web-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-probe.js","sourceRoot":"","sources":["../../../src/audit/web/web-probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web runner — orchestrates the hosted/remote audit: fetch a WebProbe, run the
|
|
3
|
+
* pure web checks, aggregate into the same AuditReport shape as the stdio path.
|
|
4
|
+
*/
|
|
5
|
+
import { SERVER_VERSION } from '../../constants.js';
|
|
6
|
+
import { aggregateScore } from '../scorer.js';
|
|
7
|
+
import { fetchWebProbe } from './web-probe.js';
|
|
8
|
+
import { scoreWeb } from './web-checks.js';
|
|
9
|
+
export async function runWebAudit(url) {
|
|
10
|
+
const probe = await fetchWebProbe(url);
|
|
11
|
+
const checks = scoreWeb(probe);
|
|
12
|
+
return {
|
|
13
|
+
target: {
|
|
14
|
+
displayName: probe.url,
|
|
15
|
+
serverName: probe.url
|
|
16
|
+
},
|
|
17
|
+
totalScore: aggregateScore(checks),
|
|
18
|
+
checks,
|
|
19
|
+
generatedAt: new Date().toISOString(),
|
|
20
|
+
scorecardVersion: SERVER_VERSION
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=web-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-runner.js","sourceRoot":"","sources":["../../../src/audit/web/web-runner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAW;IAC3C,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO;QACL,MAAM,EAAE;YACN,WAAW,EAAE,KAAK,CAAC,GAAG;YACtB,UAAU,EAAE,KAAK,CAAC,GAAG;SACtB;QACD,UAAU,EAAE,cAAc,CAAC,MAAM,CAAC;QAClC,MAAM;QACN,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,gBAAgB,EAAE,cAAc;KACjC,CAAC;AACJ,CAAC"}
|
package/dist/cli/commands.js
CHANGED
|
@@ -14,6 +14,11 @@ import { runAudit } from '../audit/runner.js';
|
|
|
14
14
|
import { resolveGithubRepo } from '../resolvers/github-resolver.js';
|
|
15
15
|
import { resolveLocal } from '../resolvers/local-resolver.js';
|
|
16
16
|
import { resolveNpmPackage } from '../resolvers/npm-resolver.js';
|
|
17
|
+
import { runWebAudit } from '../audit/web/web-runner.js';
|
|
18
|
+
/** A hosted/remote target (http[s] URL that isn't a GitHub repo) → web audit path. */
|
|
19
|
+
function isWebTarget(subject) {
|
|
20
|
+
return /^https?:\/\//.test(subject) && !/github\.com/.test(subject);
|
|
21
|
+
}
|
|
17
22
|
import { renderJson, renderMarkdown } from './output.js';
|
|
18
23
|
const USAGE = `mcp-scorecard v${SERVER_VERSION}
|
|
19
24
|
|
|
@@ -96,21 +101,33 @@ export async function run(argv) {
|
|
|
96
101
|
process.stderr.write(`Missing subject.\n\n${USAGE}\n`);
|
|
97
102
|
return 2;
|
|
98
103
|
}
|
|
99
|
-
let target;
|
|
100
|
-
try {
|
|
101
|
-
target = await resolveSubject(parsed.subject);
|
|
102
|
-
}
|
|
103
|
-
catch (err) {
|
|
104
|
-
process.stderr.write(`Resolver error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
105
|
-
return 1;
|
|
106
|
-
}
|
|
107
104
|
let report;
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
if (isWebTarget(parsed.subject)) {
|
|
106
|
+
// Hosted/remote MCP server or site → web security + agent-readiness audit.
|
|
107
|
+
try {
|
|
108
|
+
report = await runWebAudit(parsed.subject);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
process.stderr.write(`Web audit error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
110
114
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
else {
|
|
116
|
+
let target;
|
|
117
|
+
try {
|
|
118
|
+
target = await resolveSubject(parsed.subject);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
process.stderr.write(`Resolver error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
report = await runAudit(target);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
process.stderr.write(`Audit error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
129
|
+
return 1;
|
|
130
|
+
}
|
|
114
131
|
}
|
|
115
132
|
process.stdout.write(parsed.json ? renderJson(report) : renderMarkdown(report));
|
|
116
133
|
process.stdout.write('\n');
|
package/dist/cli/commands.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
|
package/dist/constants.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* same string. Bumping this without updating package.json is intentional during
|
|
4
4
|
* dev (keep src as source of truth); a release script can sync them.
|
|
5
5
|
*/
|
|
6
|
-
export declare const SERVER_VERSION = "0.1.
|
|
6
|
+
export declare const SERVER_VERSION = "0.1.3";
|
|
7
7
|
/** Identifier used when the probe connects to a target MCP server. */
|
|
8
8
|
export declare const PROBE_CLIENT_NAME = "mcp-scorecard";
|
|
9
9
|
/** Env var set on the spawned target so MCP authors can detect we are probing
|
package/dist/constants.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* same string. Bumping this without updating package.json is intentional during
|
|
4
4
|
* dev (keep src as source of truth); a release script can sync them.
|
|
5
5
|
*/
|
|
6
|
-
export const SERVER_VERSION = '0.1.
|
|
6
|
+
export const SERVER_VERSION = '0.1.3';
|
|
7
7
|
/** Identifier used when the probe connects to a target MCP server. */
|
|
8
8
|
export const PROBE_CLIENT_NAME = 'mcp-scorecard';
|
|
9
9
|
/** Env var set on the spawned target so MCP authors can detect we are probing
|
package/dist/types.d.ts
CHANGED
|
@@ -57,7 +57,32 @@ export interface ResolvedTarget {
|
|
|
57
57
|
/** package.json contents, if any. */
|
|
58
58
|
packageJson?: Record<string, unknown>;
|
|
59
59
|
}
|
|
60
|
-
export type CheckId = 'schema_validity' | 'tool_naming' | 'privacy_modes' | 'mutation_gating' | 'agent_manifest' | 'smoke_test' | 'resources' | 'tool_descriptions' | 'annotations' | 'manifest_discoverability';
|
|
60
|
+
export type CheckId = 'schema_validity' | 'tool_naming' | 'privacy_modes' | 'mutation_gating' | 'agent_manifest' | 'smoke_test' | 'resources' | 'tool_descriptions' | 'annotations' | 'manifest_discoverability' | 'web_https' | 'web_security_headers' | 'web_auth_posture' | 'web_oauth_discovery' | 'web_llms_txt' | 'web_mcp_server_card' | 'web_agent_skills' | 'web_api_catalog' | 'web_robots_signals' | 'web_structured_data';
|
|
61
|
+
/**
|
|
62
|
+
* Snapshot captured from a hosted/remote target over HTTP — the parallel of
|
|
63
|
+
* ProbeSnapshot for the web/security audit path. Pure scorers read this.
|
|
64
|
+
*/
|
|
65
|
+
export interface WebProbe {
|
|
66
|
+
url: string;
|
|
67
|
+
https: boolean;
|
|
68
|
+
rootStatus: number;
|
|
69
|
+
headers: Record<string, string>;
|
|
70
|
+
llmsTxt: boolean;
|
|
71
|
+
serverCard: boolean;
|
|
72
|
+
serverCardValid: boolean;
|
|
73
|
+
agentSkills: boolean;
|
|
74
|
+
apiCatalog: boolean;
|
|
75
|
+
robotsTxt: boolean;
|
|
76
|
+
aiBotRules: boolean;
|
|
77
|
+
contentSignal: boolean;
|
|
78
|
+
sitemap: boolean;
|
|
79
|
+
oauthProtectedResource: boolean;
|
|
80
|
+
oauthAuthServer: boolean;
|
|
81
|
+
authMd: boolean;
|
|
82
|
+
jsonLd: boolean;
|
|
83
|
+
ogTags: boolean;
|
|
84
|
+
markdownNegotiation: boolean;
|
|
85
|
+
}
|
|
61
86
|
export interface CheckResult {
|
|
62
87
|
id: CheckId;
|
|
63
88
|
label: string;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-scorecard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"mcpName": "io.github.davidmosiah/mcp-scorecard",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Grade any MCP server's agent-readiness in one command \u2014 stdio (10 protocol checks) AND hosted/remote servers (10 security + web agent-readiness checks). Open source, local-first, no credentials.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"mcp-scorecard": "dist/index.js"
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
"dev": "tsx src/index.ts",
|
|
23
23
|
"test:checks": "node scripts/test-checks.mjs",
|
|
24
24
|
"test:self": "node scripts/smoke-self.mjs",
|
|
25
|
-
"test": "npm run typecheck && npm run build && npm run test:checks && npm run test:self",
|
|
26
|
-
"prepublishOnly": "npm test"
|
|
25
|
+
"test": "npm run typecheck && npm run build && npm run test:checks && npm run test:self && npm run test:web",
|
|
26
|
+
"prepublishOnly": "npm test",
|
|
27
|
+
"test:web": "node scripts/test-web.mjs"
|
|
27
28
|
},
|
|
28
29
|
"keywords": [
|
|
29
30
|
"mcp",
|
|
@@ -61,4 +62,4 @@
|
|
|
61
62
|
"access": "public"
|
|
62
63
|
},
|
|
63
64
|
"main": "dist/index.js"
|
|
64
|
-
}
|
|
65
|
+
}
|