Skip to content

docs(mcp): custom mcpTools are invocable anonymously — "logged in only" wording is incorrect #566

Description

@kylebernhardy

Summary

reference/mcp/tools-and-resources.mdCustom mcpTools opt-in, ~line 109) says:

Authentication is "is the user logged in" only — finer-grained gating is the method's responsibility.

The "is the user logged in" only part is inaccurate: a custom mcpTool is invocable by a fully anonymous / unauthenticated MCP session. There is no login gate.

Evidence (live, harper@5.1.15, mcp: { application: {} })

Component: a @table @export Widget + a Resource with

static mcpTools = [{ name: 'echo_docs', method: 'echoDocs', inputSchema: { type:'object', properties:{ message:{type:'string'} } } }];
async echoDocs(args, context) { return { echoed: args.message, sawUser: context?.user?.username ?? '(anonymous)' }; }

Driving POST /mcp with no Authorization header:

  • initialize → 200, session issued.
  • tools/list → returns echo_docs (the auto verb tools get_widget/search_widget are RBAC-hidden from the anonymous user).
  • tools/call echo_docs200, {"echoed":"…","sawUser":""} — the method executed with an empty user.

This matches the implementation: dist/components/mcp/tools/application.js:646 registers custom tools with visibleTo: () => true, the route mounts { after: 'authentication' } with no auth requirement (dist/components/mcp/index.js:110), and there is no 401/WWW-Authenticate path anywhere in components/mcp/. The application.d.ts comment ("the MCP layer does not invent new ACLs for these") is the accurate description.

Suggested wording

Custom mcpTools are exposed to any MCP session, including anonymous / unauthenticated ones — the MCP layer performs no authentication or ACL check for them. RBAC is entirely the method's responsibility (the method still runs through the transactional() envelope, so per-record allow* predicates and audit logging apply). To restrict a tool to logged-in users, the method must enforce that itself.

Note: there is a related server-behaviour question — initialize advertises resources.subscribe: true while ~line 123 says "no resources/subscribe in v1" — filed separately as HarperFast/harper#1611.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions