Skip to content

Scope dynamic tools and cached state per JWE / effective ClickHouse identity, with explicit refresh policy #60

@BorisTyshkevich

Description

@BorisTyshkevich

Summary

When JWE auth is enabled, altinity-mcp currently treats dynamic-tool discovery as effectively global process state.

That is incorrect for JWE mode, because each token may resolve to a different ClickHouse host, database, user, grants set, and visible schema. The first successful token/config that initializes dynamic tools can leak the wrong tool surface to later tokens.

We need to introduce per-identity state and a clear refresh/invalidation policy.

Current problem

Today:

  • the incoming JWE token is stored only in request context
  • claims are parsed on demand per request
  • a ClickHouse client is created per request and closed after use
  • buildConfigFromClaims() starts from the base server config, so base flags like read_only, limits, and default TLS settings are inherited
  • dynamic tools are cached globally behind dynamicTools + dynamicToolsInit

In JWE mode this means:

  • dynamic tools are not scoped per token / per effective ClickHouse identity
  • the first token that initializes tools wins
  • later tokens can see the wrong dynamic tool list
  • future per-identity caches such as grants-derived safety metadata would have the same problem if stored globally

Desired behavior

Introduce a per-identity state model.

At minimum, state should be keyed by the effective ClickHouse identity or resolved connection fingerprint, not globally. In JWE mode, different tokens must not share dynamic tools or derived safety/cache state unless they resolve to the same effective identity.

The effective state should include at least:

  • resolved ClickHouse config derived from base config + JWE claims
  • dynamic tool registry for that identity
  • read-only-effective metadata
  • derived safety metadata such as grants-based openWorldHint classification
  • optionally schema/resource caches if we choose to cache them

Refresh / invalidation policy

We need explicit rules for refreshing cached per-identity state.

Required triggers

  1. New identity key
  • If a token resolves to a new effective identity, create a new cache entry.
  1. Token/config change for same logical user
  • If any state-affecting field changes, treat it as a different cache key or invalidate the old entry.
  • This includes at least host, port, database, username, protocol, TLS options, read-only mode, and other fields that affect visible schema or permissions.
  1. TTL expiry
  • Cached entries should expire after a configurable TTL so schema/grants changes are eventually observed without restart.
  1. Explicit refresh on failure/staleness
  • If a dynamic tool call fails because the underlying view/table no longer exists, refresh that identity's dynamic-tool cache and retry once.
  • If grants inspection becomes invalid or inconsistent, refresh that identity's safety profile.
  1. Config reload / server config change
  • When base server config is reloaded, invalidate all cached per-identity entries derived from it.

Recommended behavior

  • Use lazy refresh instead of eagerly refreshing every token on every request.
  • Keep per-identity caches bounded with eviction to avoid unbounded growth.
  • Expose metrics/logs for cache hits, misses, refreshes, invalidations, and evictions.

Keying guidance

Do not key only by raw token string.

Instead, key by a stable effective-identity fingerprint derived from the resolved ClickHouse config fields that materially affect schema visibility, permissions, and tool behavior.

For example, the cache key should include enough of:

  • host
  • port
  • database
  • username
  • protocol
  • TLS-relevant options
  • read_only
  • base-config generation / reload version

If secrets are involved, use a normalized hash/fingerprint rather than storing raw secrets in memory keys or logs.

Acceptance criteria

  • Dynamic tools are no longer global in JWE mode.
  • Two JWEs that resolve to different effective ClickHouse identities do not share dynamic tools or derived cache state.
  • Cached per-identity state has a documented refresh policy.
  • Config reload invalidates affected caches.
  • Stale dynamic tool metadata can be refreshed automatically after schema drift.
  • Cache size is bounded.
  • Tests cover:
    • different JWE identities do not share dynamic tools
    • same effective identity reuses cached state
    • TTL expiry triggers refresh
    • config reload invalidates caches
    • dropped/renamed view causes refresh and updated dynamic tools
    • grants/safety cache is scoped per identity

Related issues

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