agent-ready-mcp 0.2.0 → 0.4.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
  # agent-ready-mcp
2
2
 
3
- MCP server for [Agent Ready](https://agent-ready.dev) — scan any URL for AI agent readability against the [Vercel Agent Readability Spec](https://vercel.com/kb/guide/agent-readability-spec), the [llmstxt.org](https://llmstxt.org) standard, and agent-protocol manifests (MCP server cards, A2A, agents.json, agent-permissions.json, UCP, x402). 59 checks with per-check fix guidance.
3
+ MCP server for [Agent Ready](https://agent-ready.dev) — scan any URL for AI agent readability against the [Vercel Agent Readability Spec](https://vercel.com/kb/guide/agent-readability-spec), the [llmstxt.org](https://llmstxt.org) standard, and agent-protocol manifests (MCP server cards, A2A, agents.json, agent-permissions.json, UCP, x402, NLWeb). 60 checks with per-check fix guidance.
4
4
 
5
5
  Hosted at `https://agent-ready.dev/api/v1/mcp` (Streamable HTTP); this package is a thin stdio wrapper around the same REST endpoints, distributed via npm for local MCP clients (Claude Desktop, Claude Code, Cursor, VS Code, Windsurf).
6
6
 
@@ -8,6 +8,7 @@ Hosted at `https://agent-ready.dev/api/v1/mcp` (Streamable HTTP); this package i
8
8
 
9
9
  - **`scan_site`** — fresh agent-readability scan on any URL. Polls the hosted API up to 60s; returns the full scan or a `running` placeholder.
10
10
  - **`get_scan`** — fetch a previously-run scan by id.
11
+ - **`ask`** — natural-language (NLWeb) search over Agent Ready's own methodology, checks, and specs. Public, no API key required; returns Schema.org-typed results.
11
12
  - **Three discovery prompts** — `scan`, `interpret_scan`, `remediation_plan`. End-to-end workflows from URL → score → fix-it plan.
12
13
  - **`SKILL.md`** — Claude Skill descriptor included under `skills/agent-ready/` for activation routing.
13
14
 
@@ -74,6 +75,7 @@ claude mcp add agent-ready \
74
75
  |---|---|---|
75
76
  | `scan_site` | `url` (string, required), `pageLimit` (number, optional, max 2000 — capped by your plan) | Scan object: Vercel score 0–100, llms.txt sub-score 0–100, per-check findings with `howToFix` strings. Returns `{ id, status: "running" }` placeholder if the scan exceeds the poll deadline. |
76
77
  | `get_scan` | `id` (string, scan id from a prior `scan_site` call) | Same scan object as `scan_site`, or `not_found` if the id is unknown or doesn't belong to the authenticated user. |
78
+ | `ask` | `q` (string, required), `itemType` (optional corpus filter), `mode` (optional, `list` or `summarize`) | NLWeb `/ask` over Agent Ready's methodology, checks, and specs. Public — no API key required. Schema.org-typed result objects. |
77
79
 
78
80
  ## Prompts
79
81
 
@@ -113,7 +115,7 @@ If you'd rather use the hosted MCP server directly (Streamable HTTP transport, n
113
115
 
114
116
  ## Methodology
115
117
 
116
- The 59 checks, their weights, and the score formula are documented at [agent-ready.dev/methodology](https://agent-ready.dev/methodology). Both `manifest.json` and `server.json` in this repo conform to the relevant registry schemas (Glama Marketplace v0.3 and MCP registry 2025-12-11 respectively).
118
+ The 60 checks, their weights, and the score formula are documented at [agent-ready.dev/methodology](https://agent-ready.dev/methodology). Both `manifest.json` and `server.json` in this repo conform to the relevant registry schemas (Glama Marketplace v0.3 and MCP registry 2025-12-11 respectively).
117
119
 
118
120
  ## Development
119
121
 
@@ -9229,13 +9229,13 @@ var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
9229
9229
  canary.aborted = true;
9230
9230
  return canary;
9231
9231
  }
9232
- const checkResult = runChecks(payload, checks, ctx);
9233
- if (checkResult instanceof Promise) {
9232
+ const checkResult2 = runChecks(payload, checks, ctx);
9233
+ if (checkResult2 instanceof Promise) {
9234
9234
  if (ctx.async === false)
9235
9235
  throw new $ZodAsyncError();
9236
- return checkResult.then((checkResult2) => inst._zod.parse(checkResult2, ctx));
9236
+ return checkResult2.then((checkResult3) => inst._zod.parse(checkResult3, ctx));
9237
9237
  }
9238
- return inst._zod.parse(checkResult, ctx);
9238
+ return inst._zod.parse(checkResult2, ctx);
9239
9239
  };
9240
9240
  inst._zod.run = (payload, ctx) => {
9241
9241
  if (ctx.skipChecks) {
@@ -23108,6 +23108,51 @@ async function getScanFromApi(config2, id) {
23108
23108
  timeoutMs: config2.getTimeoutMs
23109
23109
  });
23110
23110
  }
23111
+ async function postAsk(config2, body) {
23112
+ const url2 = `${config2.baseUrl}/api/v1/ask`;
23113
+ const headers = {
23114
+ "Content-Type": "application/json",
23115
+ Accept: "application/json"
23116
+ };
23117
+ if (config2.apiKey) headers.Authorization = `Bearer ${config2.apiKey}`;
23118
+ const payloadBody = {
23119
+ query: { q: body.q, itemType: body.itemType },
23120
+ prefer: body.mode ? { mode: body.mode } : void 0
23121
+ };
23122
+ let res;
23123
+ try {
23124
+ res = await fetch(url2, {
23125
+ method: "POST",
23126
+ headers,
23127
+ body: JSON.stringify(payloadBody),
23128
+ signal: AbortSignal.timeout(config2.getTimeoutMs)
23129
+ });
23130
+ } catch (err) {
23131
+ if (err instanceof Error && err.name === "TimeoutError") {
23132
+ throw new ApiError(
23133
+ "timeout",
23134
+ `Request to /api/v1/ask timed out after ${config2.getTimeoutMs}ms.`
23135
+ );
23136
+ }
23137
+ const message = err instanceof Error ? err.message : String(err);
23138
+ throw new ApiError("network_error", `Network error calling /api/v1/ask: ${message}`);
23139
+ }
23140
+ const text = await res.text();
23141
+ let payload = null;
23142
+ if (text.length > 0) {
23143
+ try {
23144
+ payload = JSON.parse(text);
23145
+ } catch {
23146
+ }
23147
+ }
23148
+ if (payload && typeof payload === "object" && "_meta" in payload) {
23149
+ return payload;
23150
+ }
23151
+ if (!res.ok) {
23152
+ throw new ApiError(`http_${res.status}`, text || `HTTP ${res.status}`, res.status);
23153
+ }
23154
+ return payload;
23155
+ }
23111
23156
 
23112
23157
  // node_modules/zod/v3/helpers/util.js
23113
23158
  var util;
@@ -31136,7 +31181,7 @@ Skip checks that already pass.`
31136
31181
  // src/resource-content.ts
31137
31182
  var METHODOLOGY_MD = `# How Agent Ready scores a site
31138
31183
 
31139
- > 59 checks across four categories, mapped to the Vercel Agent Readability Spec and the llmstxt.org standard. Every check is open and reproducible.
31184
+ > 60 checks across four categories, mapped to the Vercel Agent Readability Spec and the llmstxt.org standard. Every check is open and reproducible.
31140
31185
 
31141
31186
  ## What does Agent Ready measure?
31142
31187
 
@@ -31173,7 +31218,7 @@ Full guide: <https://agent-ready.dev/methodology>
31173
31218
  `;
31174
31219
  var CHECKS_MD = `# Agent Ready check registry
31175
31220
 
31176
- > 59 checks total across four categories. IDs are stable and referenced in every scan result's \`details\` array. Each check is implemented as a single function in \`src/lib/checks/{category}/{id}-{slug}.ts\` in the agent-ready repository.
31221
+ > 60 checks total across four categories. IDs are stable and referenced in every scan result's \`details\` array. Each check is implemented as a single function in \`src/lib/checks/{category}/{id}-{slug}.ts\` in the agent-ready repository.
31177
31222
 
31178
31223
  ## Site checks (15)
31179
31224
 
@@ -31244,7 +31289,7 @@ Run when the site has an \`llms.txt\` file. Validate against the [llmstxt.org](h
31244
31289
  | L9 | Content-Type: text/plain |
31245
31290
  | L10 | llms-full.txt available |
31246
31291
 
31247
- ## Protocol checks (11)
31292
+ ## Protocol checks (12)
31248
31293
 
31249
31294
  Discover-then-validate: when the relevant well-known endpoint returns 404, the check drops rather than failing. A marketing site doesn't score itself against agent manifests it has no reason to ship.
31250
31295
 
@@ -31261,10 +31306,11 @@ Discover-then-validate: when the relevant well-known endpoint returns 404, the c
31261
31306
  | C9 | UCP OAuth Authorization Server metadata |
31262
31307
  | C10 | x402 Payment Required response |
31263
31308
  | C11 | x402 accepts entries |
31309
+ | C12 | NLWeb endpoint |
31264
31310
  `;
31265
31311
  var LLMS_TXT = `# Agent Ready
31266
31312
 
31267
- > Agent Ready is a free tool that scores any website against the Vercel Agent Readability Spec, the llmstxt.org specification, and agent-protocol specs (MCP, A2A, agents.json). It runs 59 checks and provides actionable fix guidance for every failing check.
31313
+ > Agent Ready is a free tool that scores any website against the Vercel Agent Readability Spec, the llmstxt.org specification, and agent-protocol specs (MCP, A2A, agents.json). It runs 60 checks and provides actionable fix guidance for every failing check.
31268
31314
 
31269
31315
  This resource mirrors agent-ready.dev's own /llms.txt so MCP clients can introspect the same surface that ChatGPT, Perplexity, and other AI agents see when discovering Agent Ready as a tool.
31270
31316
 
@@ -31293,7 +31339,7 @@ This resource mirrors agent-ready.dev's own /llms.txt so MCP clients can introsp
31293
31339
  `;
31294
31340
  var SPECS_MD = `# Specs Agent Ready validates against
31295
31341
 
31296
- Agent Ready's 59 checks map to seven specifications. Each entry below links to the canonical document and notes the check IDs that implement it.
31342
+ Agent Ready's 60 checks map to the specifications below. Each entry links to the canonical document and notes the check IDs that implement it.
31297
31343
 
31298
31344
  ## Vercel Agent Readability Spec
31299
31345
 
@@ -31345,6 +31391,12 @@ A composite profile that bundles OAuth authorization server metadata ([RFC 8414]
31345
31391
  Reference: <https://www.x402.org>
31346
31392
 
31347
31393
  Behavioural rather than manifest-based. Agent Ready probes the scanned URL preserving its path; if the response is HTTP 402 with valid \`accepts\` entries, C10 and C11 pass. Otherwise both drop.
31394
+
31395
+ ## NLWeb
31396
+
31397
+ Canonical: <https://nlweb.ai/docs/specification>
31398
+
31399
+ An open natural-language query protocol: a site exposes \`POST /ask\` returning Schema.org-typed JSON, and every NLWeb instance is also an MCP server. NLWeb has no discovery standard, so detection is heuristic \u2014 Agent Ready does a low-aggression GET to the conventional \`/ask\` path and correlates with the MCP server card. Drives C12 (best-effort NLWeb endpoint detection, informational).
31348
31400
  `;
31349
31401
 
31350
31402
  // src/resources.ts
@@ -31354,7 +31406,7 @@ function registerResources(server) {
31354
31406
  "agent-ready://methodology",
31355
31407
  {
31356
31408
  title: "Scoring methodology",
31357
- description: "How Agent Ready computes the 0\u2013100 readability score and the llms.txt sub-score. Covers the 59 checks across four categories, rating bands, weighting, and JS-rendering handling.",
31409
+ description: "How Agent Ready computes the 0\u2013100 readability score and the llms.txt sub-score. Covers the 60 checks across four categories, rating bands, weighting, and JS-rendering handling.",
31358
31410
  mimeType: "text/markdown"
31359
31411
  },
31360
31412
  async () => ({
@@ -31372,7 +31424,7 @@ function registerResources(server) {
31372
31424
  "agent-ready://checks",
31373
31425
  {
31374
31426
  title: "Check registry",
31375
- description: "Reference table of all 59 checks Agent Ready runs, grouped by category (site, page, llms.txt, protocol). Each row pairs the stable check ID (e.g. P11, S15, L9, C3) with its human-readable name. Use this to identify a check by id when interpreting scan results.",
31427
+ description: "Reference table of all 60 checks Agent Ready runs, grouped by category (site, page, llms.txt, protocol). Each row pairs the stable check ID (e.g. P11, S15, L9, C3) with its human-readable name. Use this to identify a check by id when interpreting scan results.",
31376
31428
  mimeType: "text/markdown"
31377
31429
  },
31378
31430
  async () => ({
@@ -31408,7 +31460,7 @@ function registerResources(server) {
31408
31460
  "agent-ready://specs",
31409
31461
  {
31410
31462
  title: "Specifications Agent Ready validates against",
31411
- description: "Canonical URLs and check-ID mappings for the seven specifications Agent Ready implements: Vercel Agent Readability Spec, llmstxt.org, MCP Server Cards (SEP-1649 / RFC 9728), A2A Agent Cards (a2a.proto v1.0.0), Wildcard agents.json, agent-permissions.json, UCP (RFC 8414), and x402 Payment Required.",
31463
+ description: "Canonical URLs and check-ID mappings for the specifications Agent Ready implements: Vercel Agent Readability Spec, llmstxt.org, MCP Server Cards (SEP-1649 / RFC 9728), A2A Agent Cards (a2a.proto v1.0.0), Wildcard agents.json, agent-permissions.json, UCP (RFC 8414), x402 Payment Required, and NLWeb.",
31412
31464
  mimeType: "text/markdown"
31413
31465
  },
31414
31466
  async () => ({
@@ -31477,22 +31529,22 @@ async function scanSite(config2, input) {
31477
31529
  throw err;
31478
31530
  }
31479
31531
  if (last && last.status && last.status !== "running") {
31480
- return { content: [{ type: "text", text: JSON.stringify(last) }] };
31532
+ return {
31533
+ content: [{ type: "text", text: JSON.stringify(last) }],
31534
+ structuredContent: last
31535
+ };
31481
31536
  }
31482
31537
  }
31538
+ const running = {
31539
+ id: placeholder.id,
31540
+ status: "running",
31541
+ url: input.url,
31542
+ pollUrl: placeholder.pollUrl ?? `/api/v1/scans/${placeholder.id}`,
31543
+ message: "Scan still running after the local poll deadline. Call get_scan with this id to fetch the final result."
31544
+ };
31483
31545
  return {
31484
- content: [
31485
- {
31486
- type: "text",
31487
- text: JSON.stringify({
31488
- id: placeholder.id,
31489
- status: "running",
31490
- url: input.url,
31491
- pollUrl: placeholder.pollUrl ?? `/api/v1/scans/${placeholder.id}`,
31492
- message: "Scan still running after the local poll deadline. Call get_scan with this id to fetch the final result."
31493
- })
31494
- }
31495
- ]
31546
+ content: [{ type: "text", text: JSON.stringify(running) }],
31547
+ structuredContent: running
31496
31548
  };
31497
31549
  }
31498
31550
 
@@ -31500,7 +31552,10 @@ async function scanSite(config2, input) {
31500
31552
  async function getScanById(config2, input) {
31501
31553
  try {
31502
31554
  const scan = await getScanFromApi(config2, input.id);
31503
- return { content: [{ type: "text", text: JSON.stringify(scan) }] };
31555
+ return {
31556
+ content: [{ type: "text", text: JSON.stringify(scan) }],
31557
+ structuredContent: scan
31558
+ };
31504
31559
  } catch (err) {
31505
31560
  if (err instanceof ApiError) {
31506
31561
  if (err.status === 404) {
@@ -31512,6 +31567,78 @@ async function getScanById(config2, input) {
31512
31567
  }
31513
31568
  }
31514
31569
 
31570
+ // src/tools/ask.ts
31571
+ async function askQuery(config2, input) {
31572
+ try {
31573
+ const envelope = await postAsk(config2, input);
31574
+ return {
31575
+ content: [{ type: "text", text: JSON.stringify(envelope) }],
31576
+ structuredContent: envelope && typeof envelope === "object" ? envelope : {}
31577
+ };
31578
+ } catch (err) {
31579
+ if (err instanceof ApiError) {
31580
+ throw new ToolError(err.code, err.message);
31581
+ }
31582
+ throw err;
31583
+ }
31584
+ }
31585
+
31586
+ // src/output.ts
31587
+ var checkResult = external_exports.object({
31588
+ checkId: external_exports.string(),
31589
+ name: external_exports.string(),
31590
+ status: external_exports.enum(["pass", "fail", "warn", "error"]),
31591
+ message: external_exports.string(),
31592
+ howToFix: external_exports.string().nullable(),
31593
+ details: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
31594
+ });
31595
+ var pageResult = external_exports.object({
31596
+ url: external_exports.string(),
31597
+ checks: external_exports.array(checkResult)
31598
+ });
31599
+ var scanOutputShape = {
31600
+ id: external_exports.string(),
31601
+ status: external_exports.enum(["running", "completed", "failed"]),
31602
+ rootUrl: external_exports.string().optional(),
31603
+ createdAt: external_exports.string().optional(),
31604
+ completedAt: external_exports.string().nullable().optional(),
31605
+ pagesDiscovered: external_exports.number().optional(),
31606
+ pagesScanned: external_exports.number().optional(),
31607
+ vercelScore: external_exports.number().optional(),
31608
+ vercelRating: external_exports.string().optional(),
31609
+ llmstxtScore: external_exports.number().optional(),
31610
+ siteChecks: external_exports.array(checkResult).optional(),
31611
+ llmstxtChecks: external_exports.array(checkResult).optional(),
31612
+ pageResults: external_exports.array(pageResult).optional(),
31613
+ shareToken: external_exports.string().optional(),
31614
+ // Present only on the running placeholder, not on a full Scan.
31615
+ url: external_exports.string().optional(),
31616
+ pollUrl: external_exports.string().optional(),
31617
+ message: external_exports.string().optional()
31618
+ };
31619
+ var askResult = external_exports.object({
31620
+ url: external_exports.string().optional(),
31621
+ name: external_exports.string().optional(),
31622
+ site: external_exports.string().optional(),
31623
+ score: external_exports.number().optional(),
31624
+ description: external_exports.string().optional(),
31625
+ schema_object: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
31626
+ });
31627
+ var askOutputShape = {
31628
+ _meta: external_exports.object({
31629
+ response_type: external_exports.string(),
31630
+ version: external_exports.string().optional(),
31631
+ mode: external_exports.string().optional()
31632
+ }).optional(),
31633
+ query_id: external_exports.string().optional(),
31634
+ site: external_exports.string().optional(),
31635
+ mode: external_exports.string().optional(),
31636
+ query: external_exports.string().optional(),
31637
+ results: external_exports.array(askResult).optional(),
31638
+ summary: external_exports.string().optional(),
31639
+ error: external_exports.object({ code: external_exports.string(), message: external_exports.string() }).optional()
31640
+ };
31641
+
31515
31642
  // src/types.ts
31516
31643
  var scanSiteInputShape = {
31517
31644
  url: external_exports.string().url().max(2e3).describe("Fully-qualified URL to scan, including scheme (https://...)."),
@@ -31524,11 +31651,18 @@ var getScanInputShape = {
31524
31651
  "Scan id returned by a previous scan_site call (10-character nanoid)."
31525
31652
  )
31526
31653
  };
31654
+ var askInputShape = {
31655
+ q: external_exports.string().min(1).max(2e3).describe(
31656
+ "Natural-language question about Agent Ready's scoring methodology, its check registry, or the specs it validates."
31657
+ ),
31658
+ itemType: external_exports.enum(["methodology", "checks", "specs", "llms-txt", "check", "any"]).optional().describe("Optional filter narrowing the search to one corpus type."),
31659
+ mode: external_exports.enum(["list", "summarize"]).optional().describe("'summarize' adds an extractive summary over the top results.")
31660
+ };
31527
31661
 
31528
31662
  // src/server.ts
31529
31663
  var SERVER_INFO = {
31530
31664
  name: "agent-ready",
31531
- version: "0.2.0"
31665
+ version: "0.4.0"
31532
31666
  };
31533
31667
  function createMcpServer(config2) {
31534
31668
  const server = new McpServer(SERVER_INFO);
@@ -31544,6 +31678,7 @@ function createMcpServer(config2) {
31544
31678
  title: "Scan a site for AI agent readability",
31545
31679
  description: "Runs the agent-ready.dev scanner against a URL and returns structured results: Vercel score, llmstxt.org score, and per-check findings with remediation hints. Scans may take up to ~60s; if the local poll deadline elapses, the tool returns the scan id and asks you to poll with get_scan.",
31546
31680
  inputSchema: scanSiteInputShape,
31681
+ outputSchema: scanOutputShape,
31547
31682
  annotations: READ_ONLY_OPEN_WORLD
31548
31683
  },
31549
31684
  async (args) => {
@@ -31560,6 +31695,7 @@ function createMcpServer(config2) {
31560
31695
  title: "Get a previous scan by id",
31561
31696
  description: "Fetches a completed or in-progress scan by its id. Only scans owned by the authenticated API key's user are returned.",
31562
31697
  inputSchema: getScanInputShape,
31698
+ outputSchema: scanOutputShape,
31563
31699
  annotations: READ_ONLY_OPEN_WORLD
31564
31700
  },
31565
31701
  async (args) => {
@@ -31570,6 +31706,23 @@ function createMcpServer(config2) {
31570
31706
  }
31571
31707
  }
31572
31708
  );
31709
+ server.registerTool(
31710
+ "ask",
31711
+ {
31712
+ title: "Ask Agent Ready in natural language",
31713
+ description: "Natural-language search (NLWeb /ask) over Agent Ready's own content \u2014 scoring methodology, the check registry, and the specs it validates. Public, no API key required. Returns Schema.org-typed result objects; optional itemType narrows to a corpus type and mode 'summarize' adds an extractive summary.",
31714
+ inputSchema: askInputShape,
31715
+ outputSchema: askOutputShape,
31716
+ annotations: READ_ONLY_OPEN_WORLD
31717
+ },
31718
+ async (args) => {
31719
+ try {
31720
+ return await askQuery(config2, args);
31721
+ } catch (err) {
31722
+ return toolErrorToContent(err);
31723
+ }
31724
+ }
31725
+ );
31573
31726
  registerPrompts(server);
31574
31727
  registerResources(server);
31575
31728
  return server;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "agent-ready-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "mcpName": "io.github.mlava/agent-ready-mcp",
5
- "description": "MCP server for Agent Ready — scan any URL for AI-readability against the Vercel Agent Readability Spec, the llmstxt.org standard, and agent-protocol manifests (MCP server cards, A2A, agents.json, agent-permissions.json, UCP, x402). 59 checks with per-check fix guidance.",
5
+ "description": "MCP server for Agent Ready — scan any URL for AI-readability against the Vercel Agent Readability Spec, the llmstxt.org standard, and agent-protocol manifests (MCP server cards, A2A, agents.json, agent-permissions.json, UCP, x402, NLWeb). 60 checks with per-check fix guidance.",
6
6
  "license": "MIT",
7
7
  "author": "Agent Ready",
8
8
  "repository": {
@@ -67,7 +67,7 @@ When grouping or filtering, use the check-id prefix:
67
67
  - **S1–S15** — site-wide checks (run once per scan): llms.txt, robots.txt, sitemap.xml, sitemap.md, AGENTS.md, HTTPS, OpenAPI
68
68
  - **P1–P23** — per-page checks (run on every fetched URL): HTTP semantics, metadata, JSON-LD, markdown mirrors, content negotiation, code-block language, JS-rendering dependency
69
69
  - **L1–L10** — llms.txt content checks (run when llms.txt exists): structure, link format, content-type, llms-full.txt companion
70
- - **C1–C11** — agent-protocol checks (run when the relevant `/.well-known` endpoint exists): MCP server cards, A2A agent cards, agents.json, agent-permissions.json, UCP, x402
70
+ - **C1–C12** — agent-protocol checks (run when the relevant endpoint exists): MCP server cards, A2A agent cards, agents.json, agent-permissions.json, UCP, x402, NLWeb (`/ask`)
71
71
 
72
72
  C-series checks follow a discover-then-validate pattern: missing endpoints drop the check rather than failing it. Don't report a missing C-check as a problem unless the user explicitly wants that protocol.
73
73