-
Notifications
You must be signed in to change notification settings - Fork 7
Description
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 likeread_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
openWorldHintclassification - 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
- New identity key
- If a token resolves to a new effective identity, create a new cache entry.
- 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.
- TTL expiry
- Cached entries should expire after a configurable TTL so schema/grants changes are eventually observed without restart.
- 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.
- 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
- Compute openWorldHint from effective ClickHouse grants instead of hard-coding it #58 Compute openWorldHint from effective ClickHouse grants instead of hard-coding it
- Mark dynamic tools as read-only / safe #48 Mark dynamic tools as read-only / safe
- Split execute_query into Read and Write tools #35 Split execute_query into Read and Write tools