@canonry/canonry 7.6.0 → 7.8.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.
Files changed (35) hide show
  1. package/assets/agent-workspace/skills/aero/SKILL.md +70 -6
  2. package/assets/agent-workspace/skills/aero/references/agent-operations.md +4 -1
  3. package/assets/agent-workspace/skills/aero/references/portfolio-analysis.md +15 -7
  4. package/assets/agent-workspace/skills/canonry/SKILL.md +4 -1
  5. package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +2 -2
  6. package/assets/assets/{AuditHistoryPanel-DZbPVuDn.js → AuditHistoryPanel-AJlkzSmq.js} +1 -1
  7. package/assets/assets/{BacklinksPage-6iPxcQdF.js → BacklinksPage-IhPtSh39.js} +1 -1
  8. package/assets/assets/{HistoryPage-DQ4gqs5s.js → HistoryPage-B-vNinvd.js} +1 -1
  9. package/assets/assets/{MeasurementPropertyPage-X4EGa3a5.js → MeasurementPropertyPage-s1kKo2TD.js} +1 -1
  10. package/assets/assets/{ProjectPage-qvbLROZz.js → ProjectPage-iH_9g_lU.js} +1 -1
  11. package/assets/assets/{RunsPage-DBfy79uf.js → RunsPage-BSbEUlNZ.js} +1 -1
  12. package/assets/assets/{SettingsPage-BlNafKGd.js → SettingsPage-zHyljW2M.js} +1 -1
  13. package/assets/assets/SiteHealthSection-AYGPjoka.js +4 -0
  14. package/assets/assets/{TrafficPage-CPZWDxjp.js → TrafficPage-CY_qggoW.js} +1 -1
  15. package/assets/assets/{TrafficSourceDetailPage-BIhpWiRg.js → TrafficSourceDetailPage-aJ2Vk6Pd.js} +1 -1
  16. package/assets/assets/{extract-error-message-C9XX_kMH.js → extract-error-message-CHEe8sOP.js} +1 -1
  17. package/assets/assets/index-DfamSNSD.js +86 -0
  18. package/assets/assets/{react-sigma_core.esm.min-FTRP6jVQ.js → react-sigma_core.esm.min-2sZRqoN4.js} +1 -1
  19. package/assets/assets/{v2-overview-adapter-DEuH0zoM.js → v2-overview-adapter-ZNgNU_Yp.js} +1 -1
  20. package/assets/index.html +1 -1
  21. package/dist/{chunk-HXAGDOYV.js → chunk-AAMKTL6B.js} +3 -3
  22. package/dist/{chunk-WO7XJJHH.js → chunk-DO7XPDG5.js} +5977 -5742
  23. package/dist/{chunk-FRSGZYQZ.js → chunk-KRFTA4I7.js} +1129 -477
  24. package/dist/{chunk-BLHPFAR6.js → chunk-L35TGPAV.js} +458 -161
  25. package/dist/{chunk-7FJZCG7A.js → chunk-LO56QXPO.js} +5 -4
  26. package/dist/{chunk-P72CWNTU.js → chunk-T2V2PESH.js} +4 -3
  27. package/dist/{chunk-B7IT47FE.js → chunk-ZLB7WX5D.js} +26 -33
  28. package/dist/cli.js +67 -28
  29. package/dist/{demo-server-Z7NM6DJT.js → demo-server-KDMG3APY.js} +4 -4
  30. package/dist/index.js +6 -6
  31. package/dist/{intelligence-service-P7AOOXLZ.js → intelligence-service-LAXJFE3U.js} +2 -2
  32. package/dist/mcp.js +4 -4
  33. package/package.json +8 -8
  34. package/assets/assets/SiteHealthSection-79l2PnfR.js +0 -4
  35. package/assets/assets/index-CUQtRdIf.js +0 -86
@@ -5,7 +5,7 @@ import {
5
5
  PACKAGE_VERSION,
6
6
  canonryMcpTools,
7
7
  isMachineFormat
8
- } from "./chunk-B7IT47FE.js";
8
+ } from "./chunk-ZLB7WX5D.js";
9
9
  import {
10
10
  AGENT_PROVIDER_IDS,
11
11
  CANONRY_NPM_PACKAGE_URL,
@@ -50,7 +50,7 @@ import {
50
50
  skillsClientSchema,
51
51
  upgradeCaveatFor,
52
52
  upgradeCommandFor
53
- } from "./chunk-WO7XJJHH.js";
53
+ } from "./chunk-DO7XPDG5.js";
54
54
 
55
55
  // src/config.ts
56
56
  import fs from "fs";
@@ -8877,7 +8877,7 @@ var ApiClient = class {
8877
8877
  () => getApiV1ProjectsByNameTechnicalAeoCrawl({
8878
8878
  client: this.heyClient,
8879
8879
  path: { name: project },
8880
- query: { runId: opts?.runId }
8880
+ query: { runId: opts?.runId, date: opts?.date }
8881
8881
  })
8882
8882
  );
8883
8883
  }
@@ -8940,6 +8940,7 @@ var ApiClient = class {
8940
8940
  path: { name: project },
8941
8941
  query: {
8942
8942
  runId: opts?.runId,
8943
+ date: opts?.date,
8943
8944
  inventoryEligible: opts?.inventoryEligible,
8944
8945
  fetchState: opts?.fetchState,
8945
8946
  indexabilityState: opts?.indexabilityState,
@@ -9884,7 +9885,7 @@ var OPERATIONS_GUIDE = {
9884
9885
  "guidance": "Build the requested summary from stored project overview, organic evidence, and the relevant visibility, measurement, integration, or traffic reads. Retain evidence dates, scope, sample sizes, missing data, and separate mention/citation signals; do not generate fresh runs implicitly."
9885
9886
  }
9886
9887
  },
9887
- "markdown": "# Canonry Operations Guide v1\n\nCanonry is an agent-first AI visibility platform. MCP is the universal entry\npoint for connected agents. Host-native skills are optional upgrades, not a\nprerequisite or a permission mechanism.\n\n## Connect and choose a route\n\nRead the initialization guidance, then call `canonry_help` with an `intent`:\n`status`, `diagnose`, `operations`, `prospecting`, `measurement`, `integrations`,\n`reports`, or a short task\ndescription. Select an accessible project with `canonry_projects_list` before\nusing its exact name in project tools. Inspect each listed tool's input schema;\nhelp suggests tool names, not invented arguments or authorization.\n\nHelp returns a versioned, compact route: connection `mode`, available `next`\ntools, workflow guidance, approval boundaries, and this guide's URL. It performs\nno provider calls, reads no project data, and changes no permissions.\n`next` lists stored reads. Optional `actions` lists loaded tools for work that\nrequires approval; listing an action does not authorize or execute it.\n`includeCatalog: true` additionally returns toolkit details when needed.\n\nHosted connections use a fixed catalog. Help only suggests tools offered by that\nconnection; it never tells a hosted agent to dynamically load a toolkit. A\nprogressive local stdio connection may return `loadToolkits`: call\n`canonry_load_toolkit` with one returned name, await it, then call help again.\nLoading only changes local tool discovery, never server authority.\n\nThe optional `canonry://agent-operations/v1` MCP resource contains this same\nguide. If the host cannot read resources or open links, continue through help.\nDo not install a plugin, local runtime, or skill merely to use connected MCP.\nAn installed Codex or Claude Canonry skill contains a generated copy of this\nguide plus links to host-native references. It does not replace runtime help.\n\n## Vocabulary and evidence\n\n- **Mentioned** means the brand appears in answer text. **Cited** means its\n domain appears in source links. Either, both, or neither can occur; never\n compute one signal from the other.\n- `answerMentioned: null` means not checked, not false. Missing runs and empty\n populations mean no measurement, not zero visibility.\n- Preserve project, time window, provider, requested/served model, location,\n sample size, and query class when comparing evidence. Use server-returned\n metrics; do not invent a score from incompatible populations.\n- Simple projects and Advanced portfolios share the workflow. For Advanced\n results, preserve Property, Target, market, plan revision, and class scope.\n Groups organize navigation; do not infer an unrequested fan-out.\n- Research is isolated evidence, not tracked measurement. A probe still spends\n quota and persists evidence but is excluded from normal tracking metrics.\n\n## Workflows\n\n**Status:** read the stored overview and freshness first. Say when evidence is\nmissing instead of silently creating it.\n\n**Diagnose:** inspect stored history and comparable evidence. Explain what\nchanged separately from why it might have changed. A hypothesis is not a\nmeasured cause. Propose bounded verification if stored evidence is insufficient.\n\n**Prospecting:** generate a one-shot company snapshot without creating a project.\nInspect stored provider settings first. Agree on the company, domain, selected\nproviders, and queries before starting the quota-spending snapshot action.\nBrowser-only selection requires manual queries. Progressive stdio help offers\nthe discovery toolkit when this connection permits snapshots; load it, then\ncall help again. Fixed catalogs offer only already available actions. Read-only\nand restricted connections must not bypass missing snapshot access.\n\n**Measurement:** inspect the existing setup and results before proposing edits.\nKeep research, query tracking, plan publication, and sweep execution separate.\nFor direct research, submit the final editable query text in one context. For a\nreviewed batch, submit each explicit destination with its final text and one\nidempotency key. Pattern substitution happens in the client before either\nrequest; choosing a market or Property records a destination only and never\nrewrites a query or creates an automatic fan-out. `research.run` does not\nauthorize saving patterns, changing tracking, publishing plans, or settings.\nUse a supported preview where available, inspect its exact destination and\nrevision, then seek approval for the actual change. A preview may itself require\nwrite permission; never treat a dry-run flag as a universal safety guarantee.\n\n**Integrations:** inspect stored connection state and snapshot freshness first.\nProvider configuration evidence does not prove a browser event fired or a\nconversion was recorded. Connection, resource selection, refresh/sync, and live\nreads are separate actions. Credentials belong in the operator's secure setup\nflow, never in chat, tool arguments, reports, or public guidance.\n\n**Reports:** use saved evidence for the requested period and scope. Keep mention\nand citation signals separate, include dates and sample sizes, and state missing\nor stale inputs. For Advanced Property mention rankings, use\n`canonry_measurement_portfolio_summary` and its `mentionRanking.strongest`,\n`.weakest`, and `.excluded` lists. It defaults to non-brand questions; state the\nreturned class and report branded results separately. An unavailable portfolio\naggregate does not invalidate available Property mention rates. Flag excluded\nProperties individually; do not silently replace mention ranking with citation\nranking. Keep sample sizes and ties visible. Preparing a report does not\nauthorize new measurement.\n\n## Authority and approval\n\n### Agent operations\n\nUse `canonry_key_self` (CLI `canonry key whoami --format json`) to inspect the\ncurrent credential's scopes, project boundary, and host-derived `operator`\nauthority without exposing its token. Missing `operator` means unapproved.\n`canonry_settings_get` and\n`canonry_telemetry_get` describe the connected server, not the agent's local\nmachine. Telemetry reports configured preference, effective state, and any\nenvironment override; inspecting status never creates an anonymous identifier.\nAfter approval, `canonry_telemetry_update` changes that preference and\n`canonry_provider_settings_update` changes an already-configured provider's\nmodel/quota. Both require `settings.write`; neither accepts credentials.\nServer telemetry reads and updates additionally require operator authority.\nOrdinary audit-history reads omit internal telemetry events and their state.\n\nOperator authority is deny-by-default and separate from customer admin roles.\nThe deployment owner must approve a dedicated, instance-wide API key's ID in\nthe server environment variable `CANONRY_OPERATOR_KEY_IDS` (comma-separated IDs),\nthen restart the server. Empty/unset approves nobody; wildcards are invalid.\nKeep the bearer private to internal operators; never approve a customer-held or\nshared proxy/bootstrap key. Use `logs.read` for read-only diagnostics, adding\n`settings.write` only when telemetry control is required. Ordinary key creation,\naccount roles, OAuth consent, and caller headers cannot grant operator status.\nRevoking an approved key invalidates it immediately. Host enrollment is a trust\nbootstrap step, intentionally unavailable through customer-facing APIs.\nAPI, CLI, and MCP enforce the same boundary; MCP hides internal tools unless\nthe server confirms operator authority, including in explicit read-only mode.\nProject analytics, research, and normal project permissions are unchanged.\n\n`canonry_logs_list` reads bounded, redacted runtime events from both the\napplication logger and Fastify request/error logging. It requires an\ninstance-wide `logs.read` grant (or wildcard) and a host-approved direct bearer.\nBrowser sessions, OAuth/delegated credentials, customer admins, and project-scoped\nkeys cannot use it, even with a project filter or a matching allowlist ID.\nA `logs.read`-only key is read-only automatically, without a\nsecond `read` marker. Named `*.read` scopes cannot grant mutations; an explicit\nwrite grant is needed and remains subject to its route gates. Returned messages\nare sanitized and bounded; raw request or response bodies, headers, cookies,\nprovider payloads, and stacks are not\npart of the queryable surface. The same secret-redaction policy runs before\nconsole output and storage. Do not deliberately log secrets: redaction is a\ndefense in depth, not permission to put credentials into diagnostic strings.\nOpaque escaped payloads containing secret assignments are omitted when safe\npartial masking cannot be guaranteed; correlate their retained error codes and IDs.\n\nFile-backed hosts retain runtime logs in SQLite across restarts, bounded to\n10,000 events and seven days. In-memory hosts report `retention: \"process\"`.\nFilter by `actor`, `requestId`, `runId`, `projectId`, `module`, `level`, or an\ninclusive `since`/`until` interval. Keep filters unchanged when resuming an\nopaque cursor; retention eviction can invalidate it. Inspect `retentionPolicy`,\n`captureErrors`, `dropped`, `truncated`, and `retention` before drawing\nconclusions. Missing logs are not proof that an action did not happen. Use\n`canonry_project_history` or `canonry_history_global` for persistent audit events;\noffset pages have deterministic ordering but are not snapshots of concurrent writes.\n\nAudit `actor` comes from authenticated identity (`user:<id>` or `api-key:<id>`),\nnot a caller-supplied header. A delegated MCP credential records its originating\nuser as actor and the actual credential in `credentialId`. `requestId` correlates\nHTTP events; `userAgent` and `actorSession` are bounded, untrusted client hints,\nnever identity or permission grants. Older audit rows are not backfilled with\nidentities the server cannot prove.\n\nBoth shipped HTTP hosts issue restart-safe UUID request IDs and return them in\n`x-request-id`. Use that value to correlate a CLI/API failure with log entries;\nHTTP diagnostics retain the method and route template, not raw URL parameters.\nRequest-bound loggers retain completion attribution, while generic background\ncontinuations stop inheriting caller identity after the response completes.\nCapture covers the owning server process after initialization, not arbitrary\nconsole output, other worker processes, or host/container logs. Run one server\ninstance per process and database, as required by the single-tenant deployment\nmodel; this is not a cross-tenant or distributed log collector.\n\nFor CLI use, settings reads are remote. Google setup and telemetry retain their\nlocal defaults: pass `--target server` explicitly to configure the connected\nserver. `schedule list <project>` lists all schedule kinds, and\n`notify events --target server` discovers the server's event catalog.\n\nMCP returns legacy text JSON plus structured results. Objects keep their shape;\narrays use `{items: [...]}` in `structuredContent`, and scalars use `{value: ...}`.\nErrors preserve the existing envelope and CLI exit codes: HTTP 4xx (including\n429 policy limits) use exit 1; HTTP 5xx use exit 2. Server-provided `Retry-After`\nand request IDs are exposed as `retryAfterMs` and `requestId` when available;\nclients do not infer retryability from HTTP 429 or retry automatically.\nFor a write with an ambiguous outcome, inspect saved state or its receipt before\nretrying; a retry hint is not proof that repeating a write is safe.\n\n### Action boundaries\n\nStart with stored evidence. Before a live provider read, sweep, probe, research\nrun, sync, write, or externally visible action, obtain approval covering its\nexact target, action, and bounded work. Approval already given for that exact\noperation need not be asked for again, but does not extend to more projects,\nlarger batches, retries with new identities, or recurring work.\n\nHTTP GET and MCP `readOnlyHint` describe aspects of an operation, not its cost\nor permission. Provider discovery, account reads, and live diagnostics may\nconsume quota even when labeled read-only. If the tool's effect is unclear,\ninspect its description and request direction before calling it.\n\nAuthentication, role/scope checks, project restrictions, quotas, and guarded\napproval receipts are enforced by the server. Help, skills, resources, and tool\nvisibility cannot grant authority. Never change credentials, endpoints, or\nproject identifiers to work around a missing tool or a `403` response.\n\nFor guarded ads writes, inspect unresolved operation receipts before retrying.\nUse the receipt's supported recovery action; do not replay a mutation under a\nnew identity. An executor cannot create or widen its own human approval grant.\nOn ambiguous results, exhausted bounds, or refusal, stop and report what is\nknown and what permission or operator action is needed.\n\n## Version and source\n\nThis public, versioned document is the source for initialization guidance,\nintent routes, the optional resource, and generated Canonry `SKILL.md` files.\nGuide v1 may receive compatible clarifications; incompatible routing contracts\nrequire a new guide version. The running server's help describes its actual\ncatalog and remains usable without fetching this document.\n"
9888
+ "markdown": "# Canonry Operations Guide v1\n\nCanonry is an agent-first AI visibility platform. MCP is the universal entry\npoint for connected agents. Host-native skills are optional upgrades, not a\nprerequisite or a permission mechanism.\n\n## Connect and choose a route\n\nRead the initialization guidance, then call `canonry_help` with an `intent`:\n`status`, `diagnose`, `operations`, `prospecting`, `measurement`, `integrations`,\n`reports`, or a short task\ndescription. Select an accessible project with `canonry_projects_list` before\nusing its exact name in project tools. Inspect each listed tool's input schema;\nhelp suggests tool names, not invented arguments or authorization.\n\nHelp returns a versioned, compact route: connection `mode`, available `next`\ntools, workflow guidance, approval boundaries, and this guide's URL. It performs\nno provider calls, reads no project data, and changes no permissions.\n`next` lists stored reads. Optional `actions` lists loaded tools for work that\nrequires approval; listing an action does not authorize or execute it.\n`includeCatalog: true` additionally returns toolkit details when needed.\n\nHosted connections use a fixed catalog. Help only suggests tools offered by that\nconnection; it never tells a hosted agent to dynamically load a toolkit. A\nprogressive local stdio connection may return `loadToolkits`: call\n`canonry_load_toolkit` with one returned name, await it, then call help again.\nLoading only changes local tool discovery, never server authority.\n\nThe optional `canonry://agent-operations/v1` MCP resource contains this same\nguide. If the host cannot read resources or open links, continue through help.\nDo not install a plugin, local runtime, or skill merely to use connected MCP.\nAn installed Codex or Claude Canonry skill contains a generated copy of this\nguide plus links to host-native references. It does not replace runtime help.\n\n## Vocabulary and evidence\n\n- **Mentioned** means the brand appears in answer text. **Cited** means its\n domain appears in source links. Either, both, or neither can occur; never\n compute one signal from the other.\n- `answerMentioned: null` means not checked, not false. Missing runs and empty\n populations mean no measurement, not zero visibility.\n- Preserve project, time window, provider, requested/served model, location,\n sample size, and query class when comparing evidence. Use server-returned\n metrics; do not invent a score from incompatible populations.\n- Simple projects and Advanced portfolios share the workflow. For Advanced\n results, preserve Property, Target, market, plan revision, and class scope.\n Groups organize navigation; do not infer an unrequested fan-out.\n- Research is isolated evidence, not tracked measurement. A probe still spends\n quota and persists evidence but is excluded from normal tracking metrics.\n\n## Workflows\n\n**Status:** read the stored overview and freshness first. Say when evidence is\nmissing instead of silently creating it.\n\n**Diagnose:** inspect stored history and comparable evidence. Explain what\nchanged separately from why it might have changed. A hypothesis is not a\nmeasured cause. Propose bounded verification if stored evidence is insufficient.\n\n**Prospecting:** generate a one-shot company snapshot without creating a project.\nInspect stored provider settings first. Agree on the company, domain, selected\nproviders, and queries before starting the quota-spending snapshot action.\nBrowser-only selection requires manual queries. Progressive stdio help offers\nthe discovery toolkit when this connection permits snapshots; load it, then\ncall help again. Fixed catalogs offer only already available actions. Read-only\nand restricted connections must not bypass missing snapshot access.\n\n**Measurement:** inspect the existing setup and results before proposing edits.\nKeep research, query tracking, plan publication, and sweep execution separate.\nFor direct research, submit the final editable query text in one context. For a\nreviewed batch, submit each explicit destination with its final text and one\nidempotency key. Pattern substitution happens in the client before either\nrequest; choosing a market or Property records a destination only and never\nrewrites a query or creates an automatic fan-out. `research.run` does not\nauthorize saving patterns, changing tracking, publishing plans, or settings.\nUse a supported preview where available, inspect its exact destination and\nrevision, then seek approval for the actual change. A preview may itself require\nwrite permission; never treat a dry-run flag as a universal safety guarantee.\n\n**Integrations:** inspect stored connection state and snapshot freshness first.\nProvider configuration evidence does not prove a browser event fired or a\nconversion was recorded. Connection, resource selection, refresh/sync, and live\nreads are separate actions. Credentials belong in the operator's secure setup\nflow, never in chat, tool arguments, reports, or public guidance.\n\n**Reports:** use saved evidence for the requested period and scope. Keep mention\nand citation signals separate, include dates and sample sizes, and state missing\nor stale inputs. For Advanced Property mention rankings, use\n`canonry_measurement_portfolio_summary` and its `mentionRanking.strongest`,\n`.weakest`, and `.excluded` lists. It defaults to non-brand questions; state the\nreturned class and report branded results separately. An unavailable portfolio\naggregate does not invalidate available Property mention rates. Flag excluded\nProperties individually; do not silently replace mention ranking with citation\nranking. Compact `nextCursor` completes only `pageList`; use `list` to enumerate\none ranking, markets, observed names or cited domains, preserving filters.\nFirst-page sibling lists are bounded summaries. Keep sample sizes and ties\nvisible. Preparing a report does not\nauthorize new measurement.\n\n## Authority and approval\n\n### Agent operations\n\nUse `canonry_key_self` (CLI `canonry key whoami --format json`) to inspect the\ncurrent credential's scopes, project boundary, and host-derived `operator`\nauthority without exposing its token. Missing `operator` means unapproved.\n`canonry_settings_get` and\n`canonry_telemetry_get` describe the connected server, not the agent's local\nmachine. Telemetry reports configured preference, effective state, and any\nenvironment override; inspecting status never creates an anonymous identifier.\nAfter approval, `canonry_telemetry_update` changes that preference and\n`canonry_provider_settings_update` changes an already-configured provider's\nmodel/quota. Both require `settings.write`; neither accepts credentials.\nServer telemetry reads and updates additionally require operator authority.\nOrdinary audit-history reads omit internal telemetry events and their state.\n\nOperator authority is deny-by-default and separate from customer admin roles.\nThe deployment owner must approve a dedicated, instance-wide API key's ID in\nthe server environment variable `CANONRY_OPERATOR_KEY_IDS` (comma-separated IDs),\nthen restart the server. Empty/unset approves nobody; wildcards are invalid.\nKeep the bearer private to internal operators; never approve a customer-held or\nshared proxy/bootstrap key. Use `logs.read` for read-only diagnostics, adding\n`settings.write` only when telemetry control is required. Ordinary key creation,\naccount roles, OAuth consent, and caller headers cannot grant operator status.\nRevoking an approved key invalidates it immediately. Host enrollment is a trust\nbootstrap step, intentionally unavailable through customer-facing APIs.\nAPI, CLI, and MCP enforce the same boundary; MCP hides internal tools unless\nthe server confirms operator authority, including in explicit read-only mode.\nProject analytics, research, and normal project permissions are unchanged.\n\n`canonry_logs_list` reads bounded, redacted runtime events from both the\napplication logger and Fastify request/error logging. It requires an\ninstance-wide `logs.read` grant (or wildcard) and a host-approved direct bearer.\nBrowser sessions, OAuth/delegated credentials, customer admins, and project-scoped\nkeys cannot use it, even with a project filter or a matching allowlist ID.\nA `logs.read`-only key is read-only automatically, without a\nsecond `read` marker. Named `*.read` scopes cannot grant mutations; an explicit\nwrite grant is needed and remains subject to its route gates. Returned messages\nare sanitized and bounded; raw request or response bodies, headers, cookies,\nprovider payloads, and stacks are not\npart of the queryable surface. The same secret-redaction policy runs before\nconsole output and storage. Do not deliberately log secrets: redaction is a\ndefense in depth, not permission to put credentials into diagnostic strings.\nOpaque escaped payloads containing secret assignments are omitted when safe\npartial masking cannot be guaranteed; correlate their retained error codes and IDs.\n\nFile-backed hosts retain runtime logs in SQLite across restarts, bounded to\n10,000 events and seven days. In-memory hosts report `retention: \"process\"`.\nFilter by `actor`, `requestId`, `runId`, `projectId`, `module`, `level`, or an\ninclusive `since`/`until` interval. Keep filters unchanged when resuming an\nopaque cursor; retention eviction can invalidate it. Inspect `retentionPolicy`,\n`captureErrors`, `dropped`, `truncated`, and `retention` before drawing\nconclusions. Missing logs are not proof that an action did not happen. Use\n`canonry_project_history` or `canonry_history_global` for persistent audit events;\noffset pages have deterministic ordering but are not snapshots of concurrent writes.\n\nAudit `actor` comes from authenticated identity (`user:<id>` or `api-key:<id>`),\nnot a caller-supplied header. A delegated MCP credential records its originating\nuser as actor and the actual credential in `credentialId`. `requestId` correlates\nHTTP events; `userAgent` and `actorSession` are bounded, untrusted client hints,\nnever identity or permission grants. Older audit rows are not backfilled with\nidentities the server cannot prove.\n\nBoth shipped HTTP hosts issue restart-safe UUID request IDs and return them in\n`x-request-id`. Use that value to correlate a CLI/API failure with log entries;\nHTTP diagnostics retain the method and route template, not raw URL parameters.\nRequest-bound loggers retain completion attribution, while generic background\ncontinuations stop inheriting caller identity after the response completes.\nCapture covers the owning server process after initialization, not arbitrary\nconsole output, other worker processes, or host/container logs. Run one server\ninstance per process and database, as required by the single-tenant deployment\nmodel; this is not a cross-tenant or distributed log collector.\n\nFor CLI use, settings reads are remote. Google setup and telemetry retain their\nlocal defaults: pass `--target server` explicitly to configure the connected\nserver. `schedule list <project>` lists all schedule kinds, and\n`notify events --target server` discovers the server's event catalog.\n\nMCP returns legacy text JSON plus structured results. Objects keep their shape;\narrays use `{items: [...]}` in `structuredContent`, and scalars use `{value: ...}`.\nErrors preserve the existing envelope and CLI exit codes: HTTP 4xx (including\n429 policy limits) use exit 1; HTTP 5xx use exit 2. Server-provided `Retry-After`\nand request IDs are exposed as `retryAfterMs` and `requestId` when available;\nclients do not infer retryability from HTTP 429 or retry automatically.\nFor a write with an ambiguous outcome, inspect saved state or its receipt before\nretrying; a retry hint is not proof that repeating a write is safe.\n\n### Action boundaries\n\nStart with stored evidence. Before a live provider read, sweep, probe, research\nrun, sync, write, or externally visible action, obtain approval covering its\nexact target, action, and bounded work. Approval already given for that exact\noperation need not be asked for again, but does not extend to more projects,\nlarger batches, retries with new identities, or recurring work.\n\nHTTP GET and MCP `readOnlyHint` describe aspects of an operation, not its cost\nor permission. Provider discovery, account reads, and live diagnostics may\nconsume quota even when labeled read-only. If the tool's effect is unclear,\ninspect its description and request direction before calling it.\n\nAuthentication, role/scope checks, project restrictions, quotas, and guarded\napproval receipts are enforced by the server. Help, skills, resources, and tool\nvisibility cannot grant authority. Never change credentials, endpoints, or\nproject identifiers to work around a missing tool or a `403` response.\n\nFor guarded ads writes, inspect unresolved operation receipts before retrying.\nUse the receipt's supported recovery action; do not replay a mutation under a\nnew identity. An executor cannot create or widen its own human approval grant.\nOn ambiguous results, exhausted bounds, or refusal, stop and report what is\nknown and what permission or operator action is needed.\n\n## Version and source\n\nThis public, versioned document is the source for initialization guidance,\nintent routes, the optional resource, and generated Canonry `SKILL.md` files.\nGuide v1 may receive compatible clarifications; incompatible routing contracts\nrequire a new guide version. The running server's help describes its actual\ncatalog and remains usable without fetching this document.\n"
9888
9889
  };
9889
9890
 
9890
9891
  // src/mcp/operations-guide.ts
@@ -5,15 +5,16 @@ import {
5
5
  siteCrawlGraphNodes,
6
6
  siteCrawlPages,
7
7
  siteCrawlSnapshots
8
- } from "./chunk-FRSGZYQZ.js";
8
+ } from "./chunk-KRFTA4I7.js";
9
9
  import {
10
10
  SITE_CRAWL_GRAPH_MAX_EDGES,
11
11
  SITE_CRAWL_GRAPH_MAX_NODES,
12
12
  SiteAuditFactorStatuses,
13
13
  TEMPLATE_LINK_RATIO_THRESHOLD,
14
+ factorStatusFromDistribution,
14
15
  factorStatusFromScore,
15
16
  roundPreservingTotal
16
- } from "./chunk-WO7XJJHH.js";
17
+ } from "./chunk-DO7XPDG5.js";
17
18
 
18
19
  // src/site-audit-factors.ts
19
20
  function recordedSharePct(factor) {
@@ -56,7 +57,7 @@ function computeFactorAverages(pages) {
56
57
  weight: rollup.weight,
57
58
  sharePct: shares?.[index] ?? null,
58
59
  avgScore,
59
- status: factorStatusFromScore(avgScore),
60
+ status: factorStatusFromDistribution({ pagesFailing: rollup.fail, pagesPartial: rollup.partial }),
60
61
  pagesPassing: rollup.pass,
61
62
  pagesPartial: rollup.partial,
62
63
  pagesFailing: rollup.fail
@@ -26,6 +26,7 @@ import {
26
26
  agentConversationCreateSchema,
27
27
  apiKeyDtoSchema,
28
28
  backlinkSourceSchema,
29
+ calendarDateSchema,
29
30
  canonicalizeGtmAccountId,
30
31
  canonicalizeGtmResourceSelection,
31
32
  competitorAliasesRequestSchema,
@@ -137,7 +138,7 @@ import {
137
138
  trafficSeriesGranularitySchema,
138
139
  visibilityCompareSelectionSchema,
139
140
  visibilityReportRequestSchema
140
- } from "./chunk-WO7XJJHH.js";
141
+ } from "./chunk-DO7XPDG5.js";
141
142
 
142
143
  // src/cli-error.ts
143
144
  function isMachineFormat(format) {
@@ -407,7 +408,8 @@ var measurementOverviewInputSchema = z3.object({
407
408
  search: measurementOverviewQuerySchema.shape.search,
408
409
  sort: measurementOverviewQuerySchema.shape.sort,
409
410
  cursor: measurementOverviewQuerySchema.shape.cursor,
410
- limit: measurementOverviewQuerySchema.shape.limit
411
+ limit: measurementOverviewQuerySchema.shape.limit,
412
+ compact: z3.boolean().default(true).describe("Defaults to true: bounded whole rows omit repeated per-Property engine detail with explicit detailsOmitted. Use compact=false for that detail; preserve all filters when following nextCursor.")
411
413
  }).strict().superRefine((input, context) => {
412
414
  if (input.scope === "group" && !input.groupKey) {
413
415
  context.addIssue({ code: "custom", path: ["groupKey"], message: "Group scope requires groupKey." });
@@ -435,21 +437,13 @@ var measurementPropertyEvidenceInputSchema = measurementPropertyEvidenceQuerySch
435
437
  "What one row is. Omit for sources (one row per cited URL). answers gives one row per measured answer with its cited URLs nested, including the answers that cited nothing."
436
438
  )
437
439
  }).strict();
438
- var PORTFOLIO_SUMMARY_AGENT_ROWS = 4;
439
- var PORTFOLIO_SUMMARY_AGENT_LIMIT_MAX = 10;
440
- function portfolioSummaryForAgents(summary, limitNote) {
441
- const rows = summary.weakestProperties;
442
- return {
443
- ...limitNote === void 0 ? {} : { limitNote },
444
- ...summary,
445
- ...Array.isArray(rows) ? { weakestProperties: summary.weakestProperties.map(({ recommendedInstead: _names, recommendedInsteadTotal: _total, recommendedInsteadTruncated: _cut, ...row }) => row) } : {}
446
- };
447
- }
448
440
  var measurementPortfolioSummaryInputSchema = measurementPortfolioSummaryQuerySchema.extend({
449
441
  project: projectNameSchema,
450
442
  groupKey: measurementPortfolioSummaryQuerySchema.shape.groupKey.describe("A market stable key (a row's metro.groupKey or a markets row's groupKey). Scopes rows to that market and lists its direct submarkets."),
451
- limit: z3.number().int().positive().max(PORTFOLIO_SUMMARY_AGENT_LIMIT_MAX).optional().describe(`Rows per Property list. Default ${PORTFOLIO_SUMMARY_AGENT_ROWS}, which is also the most returned (a larger value is lowered and limitNote says so). For other rows pass groupKey (one metro's weakest), or page canonry_measurement_overview.`),
452
- includeNestedMarkets: measurementPortfolioSummaryQuerySchema.shape.includeNestedMarkets.describe("Every market at every level, uncapped. Off by default and rarely needed: markets already lists every metro, and groupKey lists one metro's submarkets. Large portfolios exceed the tool-result cap with it on.")
443
+ list: measurementPortfolioSummaryQuerySchema.shape.list.describe("Select one complete compact list. Default cursor walks weakest-properties; other first-page lists are bounded summaries. observed-names and cited-domains require answers=not-mentioned. Keep list unchanged across cursors."),
444
+ limit: measurementPortfolioSummaryQuerySchema.shape.limit.describe("Maximum selected-list rows; omitted auto-sizes up to 50 under the byte budget. nextCursor advances only pageList; use list to enumerate another ranking, market or evidence list."),
445
+ compact: z3.boolean().default(true).describe("Defaults to true. Omits detailed evidence with explicit detailsOmitted and returns bounded cursor pages."),
446
+ includeNestedMarkets: measurementPortfolioSummaryQuerySchema.shape.includeNestedMarkets.describe("Include every market level; compact pages preserve market totals and page the rows.")
453
447
  }).strict();
454
448
  var measurementPropertyQuestionsInputSchema = measurementPropertyQuestionsQuerySchema.extend({
455
449
  project: projectNameSchema
@@ -1105,7 +1099,8 @@ var crawlRunIdSchema = runIdSchema.optional().describe(
1105
1099
  );
1106
1100
  var technicalAeoCrawlInputSchema = z3.object({
1107
1101
  project: projectNameSchema,
1108
- runId: crawlRunIdSchema
1102
+ runId: crawlRunIdSchema,
1103
+ date: calendarDateSchema.optional().describe("Exact UTC scan date YYYY-MM-DD, mutually exclusive with runId. A dated no-data result may include availableScanDates. When the user omitted the year, resolve it from matchingMonthDayDates and request that exact returned date before concluding the scan is missing. Never silently select another date.")
1109
1104
  });
1110
1105
  var siteHealthPageAuditInputSchema = z3.object({
1111
1106
  project: projectNameSchema,
@@ -1163,6 +1158,7 @@ var siteHealthChangesInputSchema = z3.object({
1163
1158
  var technicalAeoCrawlPagesInputSchema = z3.object({
1164
1159
  project: projectNameSchema,
1165
1160
  runId: runIdSchema.optional(),
1161
+ date: calendarDateSchema.optional().describe("Exact UTC scan date YYYY-MM-DD, mutually exclusive with runId. A dated no-data result may include availableScanDates. When the user omitted the year, resolve it from matchingMonthDayDates and request that exact returned date before concluding the scan is missing. Missing data never falls back to another date."),
1166
1162
  inventoryEligible: z3.boolean().optional().describe("Filter Canonry technical-inventory eligibility. This is not actual Google index coverage."),
1167
1163
  fetchState: z3.string().min(1).optional().describe("Filter crawler fetch state, for example html, redirect, non-html, or fetch-error."),
1168
1164
  indexabilityState: z3.string().min(1).optional().describe("Filter crawler-derived indexability state. This is not Google index coverage."),
@@ -1286,7 +1282,7 @@ var canonryMcpTools = [
1286
1282
  defineTool({
1287
1283
  name: "canonry_sentiment",
1288
1284
  title: "Read sentiment and per-query scores",
1289
- description: 'Read stored model-classified language about frozen Simple identities or Advanced Properties. Returns headline and per-query favorable/mixed/unfavorable rates, judged and selected assessment counts, rated and eligible answers with their share (ratedAnswerRate: answers with a favorable, mixed or unfavorable rating over every eligible answer, admitted or not; each answer counts once), exclusions, intervals and method limitations. Favorable % is favorable / (favorable + mixed + unfavorable); factual and unjudged answers do not enter that denominator. Select branded or non-brand explicitly and keep their metrics separate; the default is branded. Optional queryId selects one frozen query. Query rows are compact and paged: queryLimit (default 25) rows per page, then pass queryPage.nextCursor as queryCursor. Per-engine assessment verdicts and per-location scores are opt-in with include=["assessments","locations"], or come by default when queryId names one query. Advanced rows are per executionNodeKey; pass it to narrow evidence to that node. Optional runIds selects the exact grouped location runs instead of runId. A known subject absent from a non-brand answer is not unfavorable. Branded reads carry criticizedProperties: total Properties with a mixed or unfavorable rating, and keys, the first five property breakdown keys, most criticized first ({total: 0, keys: []} with fewer than two Properties in view; absent for non-brand, disabled or unavailable reads); read their answers on canonry_sentiment_evidence with scope property, that scopeKey and outcome ["mixed","unfavorable"]. Partial values are provisional. Never starts a classifier. Preserve the resolved evaluationDefinitionId and complete selection for later evidence reads.',
1285
+ description: 'Read stored model-classified language about frozen Simple identities or Advanced Properties. Returns headline and per-query favorable/mixed/unfavorable rates, judged and selected assessment counts, rated and eligible answers with their share (ratedAnswerRate: answers with a favorable, mixed or unfavorable rating over every eligible answer, admitted or not; each answer counts once), exclusions, intervals and method limitations. Favorable % is favorable / (favorable + mixed + unfavorable); factual and unjudged answers do not enter that denominator. Select branded or non-brand explicitly and keep their metrics separate; the default is branded. Optional queryId selects one frozen query. Query rows are compact and paged: queryLimit (default 25) rows per page, then pass queryPage.nextCursor as queryCursor. Per-engine assessment verdicts and per-location scores are opt-in with include=["assessments","locations"], or come by default when queryId names one query. Advanced rows are per executionNodeKey; pass it to narrow evidence to that node. Optional runIds selects the exact grouped location runs instead of runId. coverage.subjectNotMentioned counts distinct answers where every assessed subject is absent; this is expected for non-brand queries and is not missing data. A known subject absent from a non-brand answer is not unfavorable. Branded reads carry criticizedProperties: total Properties with a mixed or unfavorable rating, and keys, the first five property breakdown keys, most criticized first ({total: 0, keys: []} with fewer than two Properties in view; absent for non-brand, disabled or unavailable reads); read their answers on canonry_sentiment_evidence with scope property, that scopeKey and outcome ["mixed","unfavorable"]. Partial values are provisional. Never starts a classifier. Preserve the resolved evaluationDefinitionId and complete selection for later evidence reads.',
1290
1286
  access: "read",
1291
1287
  tier: "monitoring",
1292
1288
  inputSchema: sentimentInputSchema,
@@ -1316,7 +1312,7 @@ var canonryMcpTools = [
1316
1312
  defineTool({
1317
1313
  name: "canonry_sentiment_compare",
1318
1314
  title: "Compare matched sentiment periods",
1319
- description: "Compare stored periods under compatible frozen scope, answer-engine identity and evaluator, with complete classification coverage. Returns matched/excluded units, each denominator, all refusal reasons, a nullable direction, and both period summaries with compact query rows (per-engine detail is on canonry_sentiment). Overlapping Wilson intervals mean no clear change detected; they do not establish equivalence. No provider calls.",
1315
+ description: "fromRunId accepts previous-rated to select the preceding rated run in the same scope within 50 rated candidates; identical run IDs are refused. Population changes, incomplete targets and search-limit refusals are distinct; search-limit asks for an explicit older run. Compare stored periods under compatible frozen scope, answer-engine identity and evaluator, with complete classification coverage. Returns matched/excluded units, each denominator, all refusal reasons, a nullable direction, and both period summaries with compact query rows (per-engine detail is on canonry_sentiment). Overlapping Wilson intervals mean no clear change detected; they do not establish equivalence. No provider calls.",
1320
1316
  access: "read",
1321
1317
  tier: "monitoring",
1322
1318
  inputSchema: sentimentCompareInputSchema,
@@ -1478,7 +1474,7 @@ var canonryMcpTools = [
1478
1474
  defineTool({
1479
1475
  name: "canonry_project_overview",
1480
1476
  title: "Get project overview (composite)",
1481
- description: `One-call summary for "how is project X doing?". Returns independent mention and citation coverage, separate query-level movement for each signal, query-basket comparability with added/removed counts, latest run and health, insights, provider/model breakdowns, competitors, attention items, and recent history. Movement excludes queries not shared by both sweeps. Filterable by location and time window. Prefer this over fanning out to separate read tools for the same question. queryClassScope says which figures pool branded and non-brand: the coverage, scores (except scores.mentionShare) and movement count queries across both classes, so never report them as one class or as answers. The latest run's status and health are not a completeness check: for expected, executed and missing answers read canonry_measurement_data_quality or, for a plan run, canonry_run_completeness. canonry_doctor checks configuration and integrations, not sweeps. SCOPE LIMIT: this returns nothing about Advanced Measurement: no plan, Targets, Properties, groups, or segments. Its silence is not evidence that a project lacks them. When the system prompt has no Project shape line, call canonry_measurement_plan_get before stating that a project has no Properties, no Advanced plan, or cannot break out per-Property performance; use canonry_measurement_portfolio_summary to rank Properties.`,
1477
+ description: `Sentiment headline is sentiment.branded; nonBrand is a separate exception population. pooledOverall and deprecated overall are explicitly pooled compatibility figures and must never be reported as a class-specific headline. One-call summary for "how is project X doing?". Returns independent mention and citation coverage, separate query-level movement for each signal, query-basket comparability with added/removed counts, latest run and health, insights, provider/model breakdowns, competitors, attention items, and recent history. Movement excludes queries not shared by both sweeps. Filterable by location and time window. Prefer this over fanning out to separate read tools for the same question. queryClassScope says which figures pool branded and non-brand: the coverage, scores (except scores.mentionShare) and movement count queries across both classes, so never report them as one class or as answers. The latest run's status and health are not a completeness check: for expected, executed and missing answers read canonry_measurement_data_quality or, for a plan run, canonry_run_completeness. canonry_doctor checks configuration and integrations, not sweeps. SCOPE LIMIT: this returns nothing about Advanced Measurement: no plan, Targets, Properties, groups, or segments. Its silence is not evidence that a project lacks them. When the system prompt has no Project shape line, call canonry_measurement_plan_get before stating that a project has no Properties, no Advanced plan, or cannot break out per-Property performance; use canonry_measurement_portfolio_summary to rank Properties.`,
1482
1478
  access: "read",
1483
1479
  tier: "core",
1484
1480
  inputSchema: z3.object({
@@ -1550,7 +1546,7 @@ var canonryMcpTools = [
1550
1546
  defineTool({
1551
1547
  name: "canonry_competitor_landscape",
1552
1548
  title: "Get historical competitor landscape",
1553
- description: "Returns pinned competitors first, then observed direct competitors and other cited sources from stored answer/source evidence only. Mention share is percentage points (0..100) from answer text; citations are independent. The response carries basis (tracked or observed), availability, and reason. Pins exclusively define the comparison set when present. Otherwise at least 3 stored direct competitors must each be mentioned in 3 answers in the selected scope; raw names and platforms never enter that denominator. Without a comparison set the ratio is null, never 100%. The full comparison array is uncapped. Share of voice needs ONE query class: `queryClass=all`, and omitting it, pool branded and non-brand, so every `shareOfVoice` comes back null and only the counts are published. Pass `queryClass=non-brand` (or `branded`) for a ratio, exactly as visibility-stats does. A project with no brand name or alias cannot be split by class, so a class-scoped read on one is refused rather than answered empty. Optional groupBy: model adds provider/requested-model groups with separate served-model evidence and sample counts. Optional model filters one exact requested model ID and requires provider. Unknown historical models remain explicit. Groups are not a matched-query or equal-weight comparison. Advanced reads support one market group or explicit scope: all-markets. On a project with an active v2 measurement plan, queryClass=branded or non-brand with no scope or groupKey defaults to scope=all-markets, so classes come from the plan; pass scope=project to force the text classifier. No provider, discovery, or classifier work runs. Ranked lists are capped at 100 observed/other-source rows per group; pins remain complete. `observedNames` are names written in answer text, not cited sources, capped at the top 50 by answer count; `observedNamesTotal` counts distinct names, never answers (`countUnits` says what each count counts). Model groups are capped at 50 and disclose truncation. For which names answers give instead across a portfolio, read this with queryClass and runId latest: per-Property named-instead lists are samples of weak Properties. With queryClass branded or non-brand and neither runId nor window, this tool reads the latest sweep; pass window to pool sweeps. `runCount` and `runIds` name the runs counted; above 1 the counts pool several runs.",
1549
+ description: "answers=not-mentioned selects only answers where none of their targeted subjects was named, with answerSelection reporting population and selected counts. Returns pinned competitors first, then observed direct competitors and other cited sources from stored answer/source evidence only. Mention share is percentage points (0..100) from answer text; citations are independent. The response carries basis (tracked or observed), availability, and reason. Pins exclusively define the comparison set when present. Otherwise at least 3 stored direct competitors must each be mentioned in 3 answers in the selected scope; raw names and platforms never enter that denominator. Without a comparison set the ratio is null, never 100%. The full comparison array is uncapped. Share of voice needs ONE query class: `queryClass=all`, and omitting it, pool branded and non-brand, so every `shareOfVoice` comes back null and only the counts are published. Pass `queryClass=non-brand` (or `branded`) for a ratio, exactly as visibility-stats does. A project with no brand name or alias cannot be split by class, so a class-scoped read on one is refused rather than answered empty. Optional groupBy: model adds provider/requested-model groups with separate served-model evidence and sample counts. Optional model filters one exact requested model ID and requires provider. Unknown historical models remain explicit. Groups are not a matched-query or equal-weight comparison. Advanced reads support one market group or explicit scope: all-markets. On a project with an active v2 measurement plan, queryClass=branded or non-brand with no scope or groupKey defaults to scope=all-markets, so classes come from the plan; pass scope=project to force the text classifier. No provider, discovery, or classifier work runs. Ranked lists are capped at 100 observed/other-source rows per group; pins remain complete. `observedNames` are names written in answer text, not cited sources, capped at the top 50 by answer count; `observedNamesTotal` counts distinct names, never answers (`countUnits` says what each count counts). Model groups are capped at 50 and disclose truncation. For which names answers give instead across a portfolio, read this with queryClass and runId latest: per-Property named-instead lists are samples of weak Properties. With queryClass branded or non-brand and neither runId nor window, this tool reads the latest sweep; pass window to pool sweeps. answers=not-mentioned publishes names and mention/citation counts only; all competitive shares are null with reason answer-selection because the population is selected by the measured mention outcome. `runCount` and `runIds` name the runs counted; above 1 the counts pool several runs.",
1554
1550
  access: "read",
1555
1551
  tier: "monitoring",
1556
1552
  inputSchema: competitorLandscapeInputSchema,
@@ -2885,7 +2881,7 @@ var canonryMcpTools = [
2885
2881
  defineTool({
2886
2882
  name: "canonry_measurement_overview",
2887
2883
  title: "Get Advanced Measurement overview",
2888
- description: "For best/worst Property mention rankings, prefer canonry_measurement_portfolio_summary and its mentionRanking. An unavailable aggregate does not invalidate available Property metrics. Return stored, revision-pinned Advanced Measurement metrics and a bounded page of Property rows for all Properties, one reporting group, or one Property. Filter by query class, provider, location, date window, run, or Property search; search filters rows without changing metric denominators. On a schema-v2 plan each Property row carries its metro (and otherMetros when it is in several). It ranks one run snapshot only and never infers a trend or compares across revisions. Choose label-asc (default), label-desc, citationCoverage-asc/desc, or mentionCoverage-asc/desc. For a coverage sort, unavailable rows form the first bucket in either direction; available rows then follow the requested numeric direction. The cursor is sort-aware, pins pagination to the active revision, displayed run, evidence snapshot, and filters even if a newer run completes, and must be reused unchanged with the same sort and filters. Legacy label cursors work only when sort is omitted, while any explicit sort needs a new sort-bound cursor. It never starts provider work or incurs provider cost; page size is at most 100, and it refuses invalid scope keys, cursor combinations, appended evidence, or a run pinned to another revision.",
2884
+ description: "Compact pages are byte-bounded whole rows by default; use nextCursor with unchanged filters. For best/worst Property mention rankings, prefer canonry_measurement_portfolio_summary and its mentionRanking. An unavailable aggregate does not invalidate available Property metrics. Return stored, revision-pinned Advanced Measurement metrics and a bounded page of Property rows for all Properties, one reporting group, or one Property. Filter by query class, provider, location, date window, run, or Property search; search filters rows without changing metric denominators. On a schema-v2 plan each Property row carries its metro (and otherMetros when it is in several). It ranks one run snapshot only and never infers a trend or compares across revisions. Choose label-asc (default), label-desc, citationCoverage-asc/desc, or mentionCoverage-asc/desc. For a coverage sort, unavailable rows form the first bucket in either direction; available rows then follow the requested numeric direction. The cursor is sort-aware, pins pagination to the active revision, displayed run, evidence snapshot, and filters even if a newer run completes, and must be reused unchanged with the same sort and filters. Legacy label cursors work only when sort is omitted, while any explicit sort needs a new sort-bound cursor. It never starts provider work or incurs provider cost; page size is at most 100, and it refuses invalid scope keys, cursor combinations, appended evidence, or a run pinned to another revision.",
2889
2885
  access: "read",
2890
2886
  tier: "setup",
2891
2887
  inputSchema: measurementOverviewInputSchema,
@@ -2893,7 +2889,7 @@ var canonryMcpTools = [
2893
2889
  openApiOperations: ["GET /api/v1/projects/{name}/measurement-overview"],
2894
2890
  handler: (client, input) => {
2895
2891
  const { project, ...query } = input;
2896
- return client.getMeasurementOverview(project, query);
2892
+ return client.getMeasurementOverview(project, { ...query, compact: query.compact ?? true });
2897
2893
  }
2898
2894
  }),
2899
2895
  defineTool({
@@ -2913,7 +2909,7 @@ var canonryMcpTools = [
2913
2909
  defineTool({
2914
2910
  name: "canonry_measurement_portfolio_summary",
2915
2911
  title: "Summarize measured Properties",
2916
- description: "Start here for best and worst Properties. One call returns the weakest Properties, the names answers wrote instead of them, and the domains engines cited for them. Defaults to non-brand queries; state the returned queryClass and never pool branded with non-brand. Returns at most 4 Property rows per list, the most that fit one result; a larger limit is lowered and limitNote says so. For other rows pass groupKey (one metro's weakest), or page canonry_measurement_overview. Mention (answer text names the Property) and citation (a source URL on its page) are separate: report each as numerator/denominator, with any unattributed count beside mention and any unchecked count beside citation (saved answers whose source capture was incomplete; they are in neither side). Denominators count answers, one per query per engine (queries x engines = answers, less any unattributed or unchecked). Group Properties only by each row's metro and submarkets, never by label. namedInsteadInAnswerText lists names written in the answer text of answers that neither named nor cited the Property, counted by answer: they are not citations, and namedInsteadInAnswerTextTotal counts distinct names, not answers. citedDomains and weakestAnswerSources count answers citing each domain, every engine included; weakestAnswerSources pools the weakest rows plus every tied Property, each answer once, and its domainTotal counts distinct domains, not citations. weakestAnswerSources.ownDomainAnswers counts those answers that cite any of your own domains. A Property's citationCoverage counts only answers citing that Property's own page, so 0% citation does not mean your domain went uncited; use ownDomainAnswers for that. When tiedAtWeakest is set, tiedAtWeakest.count Properties share the weakest rates and the rows are the first of them by name, not a rank: report the count, tiedAtWeakest.byMetro (tied Properties per metro over the whole tie; a Property in two metros counts in both) and tiedAtWeakest.namedInstead (names written instead across the whole tie, counted by distinct answer; namedInsteadTotal is distinct names), and call the rows examples. mentionRanking.strongest/.weakest rank every Property with an available mention rate; .excluded lists the rest with reasons. markets lists every market at one level, worst-first (every metro, or a groupKey's submarkets), whatever the limit. Markets may share Properties and never sum to portfolio totals. Reads stored data only; never starts provider work.",
2912
+ description: "Read revision-pinned Property rankings and market rollups from stored answers. Start here for portfolio headlines: metrics.propertiesMentioned, propertiesNeverMentioned, mentionCoverage and citationCoverage. Defaults to non-brand; report branded separately. A never-mentioned Property may still be cited. Compact weakestMarkets ranks actual full top-level market rates; use it for weakest metros. tiedAtWeakest requires both zero mentions and zero citations; its byMetro rows describe only that Property cohort, not full metro rates, and its count is not the never-mentioned count. Compact pages are byte-bounded whole rows by default. nextCursor advances only pageList, default weakest-properties. Other lists are bounded first-page summaries, then omitted; totals remain complete. To enumerate another list, set list to strongest-mentions, weakest-mentions, excluded-mentions, markets, observed-names or cited-domains. Selected-list mode omits sibling row bodies; without limit, pages auto-size up to 50 rows. Follow nextCursor as cursor with unchanged list and filters; detailsOmitted names evidence available on drill-down tools. Mention and citation denominators are independent; preserve unattributed and unchecked counts. answers=not-mentioned returns answerEvidence names and registrable cited domains only from answers naming none of their targeted Properties, with populationSize, answerCount and unknownMentionAnswers. Counts for names and domains count distinct answers. Markets can overlap and must not be summed. Group rows by returned metro/submarkets rather than label. No provider calls.",
2917
2913
  access: "read",
2918
2914
  tier: "monitoring",
2919
2915
  inputSchema: measurementPortfolioSummaryInputSchema,
@@ -2921,11 +2917,7 @@ var canonryMcpTools = [
2921
2917
  openApiOperations: ["GET /api/v1/projects/{name}/measurement-portfolio-summary"],
2922
2918
  handler: async (client, input) => {
2923
2919
  const { project, ...query } = input;
2924
- const most = PORTFOLIO_SUMMARY_AGENT_ROWS;
2925
- const lowered = query.limit !== void 0 && query.limit > most;
2926
- const summary = await client.getMeasurementPortfolioSummary(project, lowered ? { ...query, limit: most } : query);
2927
- const more = query.groupKey === void 0 ? "For other rows pass groupKey (one metro's weakest), or page canonry_measurement_overview." : "For more rows page canonry_measurement_overview with scope group.";
2928
- return portfolioSummaryForAgents(summary, lowered ? `limit ${query.limit} was lowered to ${most}, the most rows that fit one result. ${more}` : void 0);
2920
+ return client.getMeasurementPortfolioSummary(project, { ...query, compact: query.compact ?? true });
2929
2921
  }
2930
2922
  }),
2931
2923
  defineTool({
@@ -3601,13 +3593,13 @@ var canonryMcpTools = [
3601
3593
  defineTool({
3602
3594
  name: "canonry_site_health_overview",
3603
3595
  title: "Get Site Health overview",
3604
- description: "Start a Site Health investigation with the latest or selected scan summary: root URL, completeness, crawl and link counts, budgets, versions, termination, and dead-link check state. Follow with a focused Site Health subgraph, shortest path, or scan changes; never request the interactive graph projection through MCP.",
3596
+ description: "For a requested scan date pass date=YYYY-MM-DD. If the year was omitted and that date has no data, use availableScanDates.matchingMonthDayDates to resolve the year and request the exact returned date before concluding the historical scan is missing. recentDates and matchingMonthDayDates are bounded; totalDates and matchingMonthDayTotal report their full counts. No automatic date fallback. inventorySummary provides complete eligibility counts and disjoint excluded reasons, including terminal redirects. Start a Site Health investigation with the latest or selected scan summary: root URL, completeness, crawl and link counts, budgets, versions, termination, and dead-link check state. Follow with a focused Site Health subgraph, shortest path, or scan changes; never request the interactive graph projection through MCP.",
3605
3597
  access: "read",
3606
3598
  tier: "monitoring",
3607
3599
  inputSchema: technicalAeoCrawlInputSchema,
3608
3600
  annotations: readAnnotations(),
3609
3601
  openApiOperations: ["GET /api/v1/projects/{name}/technical-aeo/crawl"],
3610
- handler: (client, input) => client.getTechnicalAeoCrawl(input.project, { runId: input.runId })
3602
+ handler: (client, input) => client.getTechnicalAeoCrawl(input.project, { runId: input.runId, date: input.date })
3611
3603
  }),
3612
3604
  defineTool({
3613
3605
  name: "canonry_site_health_page_audit",
@@ -3681,7 +3673,7 @@ var canonryMcpTools = [
3681
3673
  defineTool({
3682
3674
  name: "canonry_technical_aeo_score",
3683
3675
  title: "Get Technical AEO score",
3684
- description: "Get the Technical AEO scorecard for a project: the latest site-audit aggregate 0\u2013100 score, per-factor site-level averages (with pass/partial/fail distribution), cross-cutting issues, prioritized fixes, and the delta vs the previous audit. A factor's share of the site score is `sharePct` (0\u2013100, the shares add up to 100; null for a scan that did not record it); its `weight` is relative and never a percentage. When `hasData` is false the project has never been audited \u2014 call canonry_technical_aeo_run first.",
3676
+ description: "pagesFailing and pagesPartial are independent counts; their sum is the number of audited pages below pass for that factor. Any failing page makes a factor fail, independently of its average score or affected-page count. These counts use the audited-page population; crawl eligibility is a separate inventory population. affectedPageRanking orders recorded per-factor below-pass counts from largest to smallest; this is affected-page breadth, not a prediction of mention or citation lift. Factor cohorts overlap, so do not add their counts together. runSelection flags same-date ambiguity and prefers a complete run on the latest scan date, otherwise the largest page sample. Get the Technical AEO scorecard for a project: the latest site-audit aggregate 0\u2013100 score, per-factor site-level averages (with pass/partial/fail distribution), cross-cutting issues, prioritized fixes, and the delta vs the previous audit. A factor's share of the site score is `sharePct` (0\u2013100, the shares add up to 100; null for a scan that did not record it); its `weight` is relative and never a percentage. When `hasData` is false, no scored audit is available for the selected scan; a stored crawl may still exist. Inspect the stored crawl before proposing a new audit.",
3685
3677
  access: "read",
3686
3678
  tier: "monitoring",
3687
3679
  inputSchema: technicalAeoScoreInputSchema,
@@ -3720,18 +3712,18 @@ var canonryMcpTools = [
3720
3712
  defineTool({
3721
3713
  name: "canonry_technical_aeo_crawl",
3722
3714
  title: "Get persisted Technical AEO crawl",
3723
- description: "Read persisted full-crawl metadata for the latest or selected Technical AEO site-audit run: root URL, crawl/indexability/link-score versions, effective budgets, completeness, counts, and explicit dead-link check state. This does not fabricate a graph from older scorecard-only audits.",
3715
+ description: "For a historical question pass its exact UTC date (YYYY-MM-DD). If the year was omitted and that date has no data, use availableScanDates.matchingMonthDayDates to resolve the year and request the exact returned date before concluding the historical scan is missing. recentDates and matchingMonthDayDates are bounded; totalDates and matchingMonthDayTotal report their full counts. No automatic date fallback. inventorySummary gives whole-snapshot eligibility and disjoint excluded reasons; unknown is not canonical-away. Read persisted full-crawl metadata for the latest or selected Technical AEO site-audit run: root URL, crawl/indexability/link-score versions, effective budgets, completeness, counts, and explicit dead-link check state. This does not fabricate a graph from older scorecard-only audits.",
3724
3716
  access: "read",
3725
3717
  tier: "monitoring",
3726
3718
  inputSchema: technicalAeoCrawlInputSchema,
3727
3719
  annotations: readAnnotations(),
3728
3720
  openApiOperations: ["GET /api/v1/projects/{name}/technical-aeo/crawl"],
3729
- handler: (client, input) => client.getTechnicalAeoCrawl(input.project, { runId: input.runId })
3721
+ handler: (client, input) => client.getTechnicalAeoCrawl(input.project, { runId: input.runId, date: input.date })
3730
3722
  }),
3731
3723
  defineTool({
3732
3724
  name: "canonry_technical_aeo_crawl_pages",
3733
3725
  title: "List Technical AEO crawl pages",
3734
- description: "Read one bounded, cursor-paged list of canonical crawl nodes. Filter crawler-derived indexability and audit state, then follow nextCursor; this is technical inventory eligibility, not a claim about Google index coverage. `complete: false` means the crawl stopped at the budget named by `termination`, so a missing page or link is not proof it does not exist.",
3726
+ description: "For a requested scan date pass date=YYYY-MM-DD. If the year was omitted and that date has no data, use availableScanDates.matchingMonthDayDates to resolve the year and request the exact returned date before concluding the historical scan is missing. recentDates and matchingMonthDayDates are bounded; totalDates and matchingMonthDayTotal report their full counts. No automatic date fallback. inventorySummary gives whole-snapshot eligible/excluded counts and disjoint excluded reasons; healthReasonCounts covers the filtered list before paging. Use these exact counts, never infer canonical-away from unknown indexability. healthReason distinguishes terminal redirects from canonical-to-other pages. Default runSelection reports same-date ambiguity and selects complete or most-pages evidence on the latest scan date. Read one bounded, cursor-paged list of canonical crawl nodes. Filter crawler-derived indexability and audit state, then follow nextCursor; this is technical inventory eligibility, not a claim about Google index coverage. `complete: false` means the crawl stopped at the budget named by `termination`, so a missing page or link is not proof it does not exist.",
3735
3727
  access: "read",
3736
3728
  tier: "monitoring",
3737
3729
  inputSchema: technicalAeoCrawlPagesInputSchema,
@@ -3739,6 +3731,7 @@ var canonryMcpTools = [
3739
3731
  openApiOperations: ["GET /api/v1/projects/{name}/technical-aeo/crawl/pages"],
3740
3732
  handler: (client, input) => client.getTechnicalAeoCrawlPages(input.project, {
3741
3733
  runId: input.runId,
3734
+ date: input.date,
3742
3735
  inventoryEligible: input.inventoryEligible,
3743
3736
  fetchState: input.fetchState,
3744
3737
  indexabilityState: input.indexabilityState,