Skip to content

fix(mcp): serve resources/list on the anonymous discovery path (orank mcp-resource-listing)#4719

Merged
koala73 merged 1 commit into
mainfrom
worktree-whimsical-tickling-comet
Jul 4, 2026
Merged

fix(mcp): serve resources/list on the anonymous discovery path (orank mcp-resource-listing)#4719
koala73 merged 1 commit into
mainfrom
worktree-whimsical-tickling-comet

Conversation

@koala73

@koala73 koala73 commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Problem

orank (ora.ai agent-readiness scanner) scores worldmonitor.app 66/100 (C). On the Access layer, mcp-resource-listing is a FAIL (0/3): "Server advertises resources but resources/list returned none", which also cascades mcp-resource-quality to NA (0/3).

Root cause: orank scans the MCP server anonymously. Since #4698, initialize is a public discovery method and its handshake advertises the resources capability — but resources/list was not in PUBLIC_MCP_METHODS, so the anonymous probe got 401'd. orank read the advertised resources capability as advertised-but-empty.

Fix

resources/list is a catalog-enumeration method returning only metadata (URIs / names / descriptions / mimeType — no data, no quota, uses no auth context), exactly like the already-public tools/list. Add it to PUBLIC_MCP_METHODS so an anonymous scanner that reads the advertised resources capability can enumerate it.

Changes

File Change
api/mcp/handler.ts Add 'resources/list' to PUBLIC_MCP_METHODS + update the two governing comments
docs/mcp-server.mdx List resources/list under public discovery (leave prompts/list, logging/setLevel gated)
tests/mcp.test.mjs New: anonymous resources/list → 200 with ≥1 resource (mirrors orank's check)
tests/live-api-cache-auth-regression.test.mjs Deploy-gated live probe for anonymous resources/list

Quota-exemption docs (server-card notes, error catalog, per-minute wording) are unchangedresources/list was and remains quota-exempt.

Tests

174 pass across mcp.test.mjs, mcp-resources.test.mjs, mcp-capability-parity.test.mjs, mcp-protocol-conformance.test.mjs, mcp-transport-conformance.test.mjs, mcp-prompts.test.mjs. The capability-parity and resources tests call resources/list with a valid key, so they still pass (public methods still validate a presented credential).

Expected orank delta (post-deploy)

mcp-resource-listing 0/3 → 3/3; mcp-resource-quality unlocked (up to +6 on Access).

Verify after deploy: POST https://ora.ai/api/scan {"url":"worldmonitor.app"} / GET https://ora.ai/api/score/worldmonitor.app.

Sibling orank Access work: #4698 (public discovery), #4711 (bare-GET → 405 handshake).

https://claude.ai/code/session_01Cj9KjCX7H3anaRhKVw1RfE

… mcp-resource-listing)

orank scans the MCP server anonymously. Since #4698 `initialize` is public
and its handshake advertises the `resources` capability, but `resources/list`
was gated behind auth — so the anonymous probe 401'd and orank scored
`mcp-resource-listing` FAIL 0/3 ("advertises resources capability but
resources/list returned none"), cascading `mcp-resource-quality` to NA 0/3.

`resources/list` is a catalog-enumeration method returning only metadata
(URIs/names/descriptions — no data, no quota), exactly like the already-public
`tools/list`. Add it to PUBLIC_MCP_METHODS so an anonymous scanner that reads
the advertised `resources` capability can enumerate it. `resources/read` stays
gated (spends the Pro daily quota symmetrically with tools/call) — the
data-leak protection is untouched.

There is no orank prompts-exposed check, so `prompts/list` stays gated per the
deliberate minimal-discovery choice from #4698.

- api/mcp/handler.ts: add 'resources/list' to PUBLIC_MCP_METHODS + comments
- docs/mcp-server.mdx: list resources/list under public discovery
- tests/mcp.test.mjs: anonymous resources/list → 200 with >=1 resource
- tests/live-api-cache-auth-regression.test.mjs: deploy-gated live probe

Expected: mcp-resource-listing 0/3 -> 3/3, mcp-resource-quality unlocked;
Access layer +3 (up to +6). Post-deploy: POST https://ora.ai/api/scan {"url":"worldmonitor.app"}.

Claude-Session: https://claude.ai/code/session_01Cj9KjCX7H3anaRhKVw1RfE
@mintlify

mintlify Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
WorldMonitor 🟢 Ready View Preview Jul 4, 2026, 5:46 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@vercel

vercel Bot commented Jul 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldmonitor Ready Ready Preview, Comment Jul 4, 2026 5:48am

Request Review

@greptile-apps

greptile-apps Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a gap where resources/list was absent from PUBLIC_MCP_METHODS, causing anonymous scanners (like orank) to receive a 401 despite initialize advertising the resources capability — making the capability appear advertised-but-empty. The one-line fix adds resources/list to the public set, matching the existing posture of tools/list; resources/read remains fully gated and continues to consume the Pro quota.

  • api/mcp/handler.ts: resources/list added to PUBLIC_MCP_METHODS; the existing switch-case branch was already correct and uses no context, so no further changes are needed there.
  • Tests: A unit test in mcp.test.mjs and a deploy-gated live probe in live-api-cache-auth-regression.test.mjs both verify anonymous resources/list returns HTTP 200 with a non-empty catalog and Cache-Control: no-store.
  • Docs: mcp-server.mdx updated to reflect resources/list as a public discovery method and removed from the gated-methods list.

Confidence Score: 5/5

Safe to merge — the change is a minimal, well-scoped addition to the public discovery surface with no data exposure risk.

The fix is a single-line addition to a well-tested constant. The resources/list handler branch was already correct (returns only static metadata — URIs, names, descriptions — and never touches context), so promoting it to the public set carries no data-exposure risk. resources/read stays fully gated. Anonymous rate-limiting via applyAnonDiscoveryLimit still applies. Both unit and live-probe tests are added. Documentation is updated to match the code.

No files require special attention.

Important Files Changed

Filename Overview
api/mcp/handler.ts Adds resources/list to PUBLIC_MCP_METHODS; the case branch already existed and correctly serves static RESOURCE_LIST_RESPONSE without using context, so the change is safe and symmetric with tools/list.
tests/mcp.test.mjs New unit test mirrors the orank probe: anonymous resources/list → 200 with ≥1 resource. Pattern is consistent with the existing anonymous tools/list test.
tests/live-api-cache-auth-regression.test.mjs Live-probe test for anonymous resources/list validates status 200, Cache-Control: no-store, and a non-empty resource catalog.
docs/mcp-server.mdx Documentation updated to include resources/list among public discovery methods and correctly removes it from the gated-methods list.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Scanner as orank / anon scanner
    participant Handler as mcpHandler
    participant Auth as resolveAuthContext
    participant RateLimit as applyAnonDiscoveryLimit

    Scanner->>Handler: "POST /mcp {method: initialize}"
    Handler->>RateLimit: apply anon rate limit
    RateLimit-->>Handler: ok
    Handler-->>Scanner: "200 {capabilities: {resources: {}, tools: {}}}"

    Scanner->>Handler: "POST /mcp {method: resources/list}"
    Handler->>Handler: "PUBLIC_MCP_METHODS.has(resources/list) = true"
    Handler->>Handler: "hasCredentials(req) = false"
    Handler->>RateLimit: applyAnonDiscoveryLimit
    RateLimit-->>Handler: ok
    Handler-->>Scanner: "200 {resources: [Country Risk, Chokepoint Status, ...]}"

    Scanner->>Handler: "POST /mcp {method: resources/read}"
    Handler->>Auth: resolveAuthContext (requires credentials)
    Auth-->>Handler: 401
    Handler-->>Scanner: 401 — resources/read stays gated
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Scanner as orank / anon scanner
    participant Handler as mcpHandler
    participant Auth as resolveAuthContext
    participant RateLimit as applyAnonDiscoveryLimit

    Scanner->>Handler: "POST /mcp {method: initialize}"
    Handler->>RateLimit: apply anon rate limit
    RateLimit-->>Handler: ok
    Handler-->>Scanner: "200 {capabilities: {resources: {}, tools: {}}}"

    Scanner->>Handler: "POST /mcp {method: resources/list}"
    Handler->>Handler: "PUBLIC_MCP_METHODS.has(resources/list) = true"
    Handler->>Handler: "hasCredentials(req) = false"
    Handler->>RateLimit: applyAnonDiscoveryLimit
    RateLimit-->>Handler: ok
    Handler-->>Scanner: "200 {resources: [Country Risk, Chokepoint Status, ...]}"

    Scanner->>Handler: "POST /mcp {method: resources/read}"
    Handler->>Auth: resolveAuthContext (requires credentials)
    Auth-->>Handler: 401
    Handler-->>Scanner: 401 — resources/read stays gated
Loading

Reviews (1): Last reviewed commit: "fix(mcp): serve resources/list on the an..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant