Skip to content

Pitfall Anonymous vs Authenticated Server

Jm Rohmer edited this page Jul 5, 2026 · 3 revisions

Pitfall: Anonymous server vs authenticated server tool exposure

Affects: Scope boundary, tool visibility
Category: Architecture, Security


The invariant

The server runs four distinct MCP server instances, one per scope level:

Instance Scope Tools registered
anonServer "" (none) Anonymous tools only
readServer content.read Anonymous + read tools
writeServer content.write Anonymous + read + write tools
siteAdminServer site.admin All tools

Tools that require content.read (e.g. validate_site, validate_front_matter, build_agent_context) must NEVER be registered on anonServer.


Why it matters

When OAuth is enabled, all /mcp requests without a bearer token receive a 401 challenge — so anonServer is effectively unreachable from the internet. However, when OAuth is disabled (e.g. for a private deployment without auth), anonServer receives all requests directly. If authenticated tools leak onto anonServer, they become publicly accessible.

The scope ACL layer (scopePolicy.AllowRequest) provides a second line of defence, but the correct first defence is correct server registration.


The test

TestAnonymousServerDoesNotExposeAuthenticatedTools in internal/server/server_test.go enforces this invariant automatically. It spins up a no-OAuth server and asserts that:

  • validate_site, validate_front_matter, build_agent_context, export_agent_context, get_broken_links, get_site_health, diff_page, search_content are absent
  • list_pages, get_page, list_tags are present

Claude.ai UI label is not the same as OAuth scope

Claude.ai labels tools as "Outils en lecture seule" (read-only) based on the tool's ReadOnlyHint annotation — whether the tool modifies data. This is Claude.ai's own approval model and has nothing to do with OAuth scope.

A tool can be:

  • Read-only (no side effects) AND require OAuth (content.read)
  • Read-only AND be available anonymously

validate_site and validate_front_matter are read-only (no writes) but require content.read OAuth. They appear under "Outils en lecture seule" in Claude.ai because of their ReadOnlyHint, not because they are public.


Diagnosis

To verify the anonymous tool set from the command line:

# No Authorization header → should get 401 when OAuth is enabled
curl -s https://mcp.arleo.eu/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# Expected: 401 Unauthorized

# With a content.read token → should see validate_site but NOT build_site
curl -s https://mcp.arleo.eu/mcp \
  -H "Authorization: Bearer $READ_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '[.result.tools[].name]'

Clone this wiki locally