Security, Operations, And Testing
Security Model
Token storage
Only the SHA256 digest of the raw token is stored. The raw token is emitted once at creation or regeneration time.
Token lifecycle
The model supports:
- creation
- regeneration
- revocation
- last-used tracking
- scoped permissions
Permission filtering happens before discovery
This is important. The client does not merely get blocked from calling forbidden tools. It does not see those tools at all if the token lacks the matching permission key.
Tenant isolation is structural
For the org MCP, the tenant is resolved before the tool executes and stored in request-local state. The tool never receives a tenant id from the caller.
Token in URL trade-off
This design is practical for MCP hosts that only configure URLs, but it means tokens can appear in logs.
Mitigations:
- HTTPS only
- short rotation cycle for sensitive tokens
- regeneration and revoke flows in the UI
- avoid logging full request paths where possible
Operational Behaviors Worth Preserving
Rate limiting
The base Rack app enforces 60 requests per minute per token in memory. That is a good default for a simple Rails deployment.
If the target app runs multiple web processes or many replicas, move this logic to a shared store or API gateway.
Thread-local cleanup
Always clear thread-local MCP state in an ensure path after dispatch. This is not optional in a threaded server.
Transport reuse
This implementation reuses a StreamableHTTPTransport per token and swaps its server object on each request. Keep that pattern if you want to preserve the same runtime shape.
Audit logging
The logging hook sits in the base tool via a prepended module. That is a good pattern because every tool gets logging automatically and the individual tool classes stay clean.
Recommended Tests For A Fresh Export
Model tests
- raw token lookup returns only active tokens
- revoked tokens fail lookup
- permission helpers return expected keys
- regeneration changes the digest and prefix
Rack tests
- missing token returns 401
- invalid token returns 401
- rate limit returns 429 after the configured threshold
- valid token reaches
tools/list
Builder tests
- permitted tool classes are included
- forbidden tool classes are excluded
- org builder and admin builder do not mix tool classes
Tool tests
- each tool only reads or mutates within its scope root
- list tools include IDs in results
- validation failures return a readable response
- successful calls create activity log rows
UI tests if token management is exported
- admins can create tokens
- non-admins cannot create tokens
- regenerate invalidates the old raw token
- revoke removes runtime access immediately
Observability Suggestions
Add metrics or logs for:
- request count by token scope
- 401 and 429 rate
- tool calls by domain and action
- average duration per tool
- top failing tools
If the target project already has structured logging, store at least:
- token id
- scope
- tool name
- domain
- action
- status code
- duration
Safe Rollout Strategy
- ship read-only tools first
- enable write permissions only for a staff-controlled token
- keep create tools drafting or staging by default
- monitor activity logs before broad rollout
- expand the tool surface only after the first domains are stable
What To Avoid In The Target Project
- Unscoped model queries inside tools
- Generic catch-all update tools
- Storing raw tokens in the database
- Using class variables for request context
- Skipping activity logs for mutation tools
- Exposing delete operations before you have a strong operational reason