Skip to content

3rd Party MCP - PAT Enforcement + Tool Annotation Cache #7559

@cstns

Description

@cstns

Summary

Validate PAT restrictions (readOnly, team scope) at the MCP HTTP layer before executing tool calls across both tool sources (backend platform tools and first-party agent tools). Cache tool annotations so the endpoint knows whether a tool is read or write without re-fetching on every call.

Why

PAT enforcement must happen at the Forge backend regardless of which tool source handles execution:

  • Backend tools go through app.inject() which has full PAT enforcement via @fastify/request-context, but a pre-execution check against tool annotations provides a fast reject before any route processing.
  • First-party agent tools are delegated to the expert agent for execution. PAT enforcement needs to happen before delegation since the agent has no PAT context.

Platform tool annotations are static (defined in code). First-party agent tools (which include NR instance tools, frontend tools like context/navigation/routes) are discovered dynamically from the expert agent's MCP tool list. The MCP endpoint needs to know a tool's read/write nature at execution time, not just at discovery time. The annotation cache stores tool metadata from the expert agent's tool list.

The cache uses lru-cache directly (per-process state, can't go through the global app.caches Redis/Valkey abstraction). On cache miss, the endpoint re-fetches the expert agent's tool list via MQTT. Re-fetching is rate-limited per PAT/instance to prevent hammering the expert agent. If the tool is still not found after re-fetching, the request is rejected.

What to do

  • Before executing any tool call, validate the PAT's readOnly flag against the tool's annotations and the target entity's team against the PAT's teamScopes
  • Cache tool annotations in an in-process LRU cache:
    • Backend tool annotations: effectively static, long TTL
    • Agent tool annotations: per PAT/instance, fetched from the expert agent's tool list (includes frontend and flow/NR tools)
  • On cache miss for agent tools: re-fetch the expert agent's tool list via MQTT, populate cache, then validate
  • Rate-limit re-fetching per PAT/instance combination
  • If tool not found after re-fetch, reject with a structured error
  • Cache should be invalidated when a PAT is updated or deleted (hooks from Scoped PATs - API Routes and Controllers #7515 and Scoped PATs - Token Lifecycle: Cleanup on Team Membership Removal #7481)

Tests

  • Read-only PAT calling a read-annotated tool: allowed
  • Read-only PAT calling a write-annotated tool: rejected at HTTP layer before execution
  • Team-scoped PAT calling a tool on an entity in a non-scoped team: rejected
  • Cache hit: no re-fetch triggered
  • Cache miss for agent tool: expert agent's tool list is fetched, cache is populated, subsequent calls hit cache
  • Rate limiting: rapid repeated calls for a non-existent tool don't hammer the expert agent
  • PAT update/deletion invalidates the cache for that PAT

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions