agent-ready-mcp 0.5.4 → 0.6.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.
@@ -23093,6 +23093,55 @@ async function call(config2, opts) {
23093
23093
  }
23094
23094
  return payload;
23095
23095
  }
23096
+ async function postAnonScan(config2, url2) {
23097
+ const path = "/api/scan";
23098
+ let res;
23099
+ try {
23100
+ res = await fetch(`${config2.baseUrl}${path}`, {
23101
+ method: "POST",
23102
+ headers: {
23103
+ "Content-Type": "application/json",
23104
+ Accept: "application/json",
23105
+ // Funnel attribution: /api/scan is shared with the web app, so without
23106
+ // this header keyless npm-MCP scans are recorded as source=`web`. The
23107
+ // server tags them `mcp-npm` (distinct from `mcp`, the hosted keyed
23108
+ // endpoint). Read only on /api/scan; ignored elsewhere.
23109
+ "X-Agent-Ready-Client": "mcp-npm"
23110
+ },
23111
+ body: JSON.stringify({ url: url2 }),
23112
+ signal: AbortSignal.timeout(config2.scanTimeoutMs)
23113
+ });
23114
+ } catch (err) {
23115
+ if (err instanceof Error && err.name === "TimeoutError") {
23116
+ throw new ApiError(
23117
+ "timeout",
23118
+ `Request to ${path} timed out after ${config2.scanTimeoutMs}ms.`
23119
+ );
23120
+ }
23121
+ const message = err instanceof Error ? err.message : String(err);
23122
+ throw new ApiError("network_error", `Network error calling ${path}: ${message}`);
23123
+ }
23124
+ const text = await res.text();
23125
+ let payload = null;
23126
+ if (text.length > 0) {
23127
+ try {
23128
+ payload = JSON.parse(text);
23129
+ } catch {
23130
+ }
23131
+ }
23132
+ if (!res.ok) {
23133
+ const body = payload && typeof payload === "object" ? payload : null;
23134
+ let message = typeof body?.error === "string" && body.error ? body.error : text || `HTTP ${res.status} from ${path}`;
23135
+ if (res.status === 429) {
23136
+ if (typeof body?.resetAt === "string") {
23137
+ message += ` Quota resets ${body.resetAt.slice(0, 10)}.`;
23138
+ }
23139
+ throw new ApiError("quota_exhausted", message, res.status);
23140
+ }
23141
+ throw new ApiError(`http_${res.status}`, message, res.status);
23142
+ }
23143
+ return payload;
23144
+ }
23096
23145
  async function postScan(config2, body) {
23097
23146
  return call(config2, {
23098
23147
  method: "POST",
@@ -31531,7 +31580,31 @@ var POLL_INTERVAL_MS = 2e3;
31531
31580
  async function sleep(ms) {
31532
31581
  return new Promise((resolve) => setTimeout(resolve, ms));
31533
31582
  }
31583
+ var ANON_TIER_NOTE = "Scanned on the anonymous free tier (3 scans per 30 days per IP, 25-page depth). A Pro API key (AGENT_READY_API_KEY) unlocks 50 scans/month, 250-page depth, scan history via get_scan, and weekly monitoring \u2014 https://agent-ready.dev/pricing";
31534
31584
  async function scanSite(config2, input) {
31585
+ if (!config2.apiKey) {
31586
+ let res;
31587
+ try {
31588
+ res = await postAnonScan(config2, input.url);
31589
+ } catch (err) {
31590
+ if (err instanceof ApiError) {
31591
+ const hint = err.status === 429 ? " Set a Pro API key (AGENT_READY_API_KEY, from https://agent-ready.dev/dashboard/api-keys) to remove the anonymous cap." : "";
31592
+ throw new ToolError(err.code, err.message + hint);
31593
+ }
31594
+ throw err;
31595
+ }
31596
+ if (!res || typeof res !== "object" || !res.scan) {
31597
+ throw new ToolError(
31598
+ "invalid_response",
31599
+ "Anonymous scan returned no scan payload from /api/scan."
31600
+ );
31601
+ }
31602
+ const payload = { ...res.scan, message: ANON_TIER_NOTE };
31603
+ return {
31604
+ content: [{ type: "text", text: JSON.stringify(payload) }],
31605
+ structuredContent: payload
31606
+ };
31607
+ }
31535
31608
  let placeholder;
31536
31609
  try {
31537
31610
  placeholder = await postScan(config2, {
@@ -31605,6 +31678,12 @@ async function getScanById(config2, input) {
31605
31678
  if (err.status === 404) {
31606
31679
  throw new ToolError("not_found", `Scan ${input.id} not found.`);
31607
31680
  }
31681
+ if (err.code === "missing_api_key") {
31682
+ throw new ToolError(
31683
+ "missing_api_key",
31684
+ "get_scan needs a Pro API key (scan history is account-scoped). scan_site works without one on the anonymous tier and returns its result inline. Keys: https://agent-ready.dev/dashboard/api-keys"
31685
+ );
31686
+ }
31608
31687
  throw new ToolError(err.code, err.message);
31609
31688
  }
31610
31689
  throw err;
@@ -31754,7 +31833,7 @@ var validateStructuredDataInputShape = {
31754
31833
  // src/server.ts
31755
31834
  var SERVER_INFO = {
31756
31835
  name: "agent-ready",
31757
- version: "0.5.4"
31836
+ version: "0.6.1"
31758
31837
  };
31759
31838
  function createMcpServer(config2) {
31760
31839
  const server = new McpServer(SERVER_INFO);
@@ -31768,7 +31847,7 @@ function createMcpServer(config2) {
31768
31847
  "scan_site",
31769
31848
  {
31770
31849
  title: "Scan a site for AI agent readability",
31771
- 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.",
31850
+ 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. Works without an API key on the anonymous free tier (3 scans/30 days per IP, 25-page depth, synchronous). With a Pro AGENT_READY_API_KEY it scans deeper (up to 250 pages) and 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.",
31772
31851
  inputSchema: scanSiteInputShape,
31773
31852
  outputSchema: scanOutputShape,
31774
31853
  annotations: READ_ONLY_OPEN_WORLD
@@ -31785,7 +31864,7 @@ function createMcpServer(config2) {
31785
31864
  "get_scan",
31786
31865
  {
31787
31866
  title: "Get a previous scan by id",
31788
- description: "Fetches a completed or in-progress scan by its id. Only scans owned by the authenticated API key's user are returned.",
31867
+ description: "Fetches a completed or in-progress scan by its id. Requires a Pro API key \u2014 scan history is account-scoped. (Anonymous scan_site calls return their result inline, so keyless use never needs this tool.)",
31789
31868
  inputSchema: getScanInputShape,
31790
31869
  outputSchema: scanOutputShape,
31791
31870
  annotations: READ_ONLY_OPEN_WORLD
@@ -31853,7 +31932,7 @@ async function main() {
31853
31932
  const config2 = createConfig();
31854
31933
  if (!config2.apiKey) {
31855
31934
  process.stderr.write(
31856
- "agent-ready-mcp started without AGENT_READY_API_KEY \u2014 tools will return a configuration message until a key is set. Issue a Pro API key at https://agent-ready.dev/dashboard/api-keys.\n"
31935
+ "agent-ready-mcp started without AGENT_READY_API_KEY \u2014 scan_site runs on the anonymous free tier (3 scans per 30 days per IP, 25-page depth); get_scan needs a Pro key. Issue one at https://agent-ready.dev/dashboard/api-keys for deeper scans, history, and monitoring.\n"
31857
31936
  );
31858
31937
  }
31859
31938
  const server = createMcpServer(config2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-ready-mcp",
3
- "version": "0.5.4",
3
+ "version": "0.6.1",
4
4
  "mcpName": "io.github.mlava/agent-ready-mcp",
5
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). 69 checks with per-check fix guidance.",
6
6
  "license": "MIT",