fix(19.7): RA-IN-03 document that the Redis session store needs a standalone Redis
reserveScript reads the record keys of the binding set's members, which it cannot declare in KEYS, and its declared keys hash to different slots, so Redis Cluster refuses it and a key-pattern ACL must allow the whole <prefix>mcp: range. The script's comment, the pier README's mcp.sessions.store row (which also no longer says the store opens on the first request) and docs/services/mcp.md say so.
This commit is contained in:
@@ -121,7 +121,7 @@ Configuration keys are exported as constants: `pier.EnabledKey`, `pier.PathKey`,
|
||||
| `mcp.enabled` | `false` | Mounts the endpoint. `true` in a binary built without the `summercms_mcp` tag fails boot naming the tag. |
|
||||
| `mcp.path` | `/mcp` | The endpoint path: an absolute URL path without scheme, host, query, wildcard or dot segments. A route a plugin registered there fails boot naming the plugin. |
|
||||
| `mcp.allow_personal_tokens` | `false` | Lets personal API tokens open sessions: an admin `g15_` token yields backend tools only, a frontend token (through the published resolver) frontend tools only, each capped by the token's own scope. OAuth tokens are always accepted. |
|
||||
| `mcp.sessions.store` | `memory` | `memory` (one instance; sessions end with the process and events reach only its own sessions) or `redis` (sessions and events shared by every instance through `redis.url`, opened on the first request). |
|
||||
| `mcp.sessions.store` | `memory` | `memory` (one instance; sessions end with the process and events reach only its own sessions) or `redis` (sessions and events shared by every instance through `redis.url`, opened at mount by a background warm-up). The Redis store needs a standalone Redis, not Redis Cluster: its atomic reservation script reads keys of several hash slots, some not declared in `KEYS`, so an ACL restricted by key pattern must allow the whole `<prefix>mcp:` range. |
|
||||
| `mcp.sessions.idle_ttl` | `30m` | Sliding idle TTL of a session record. |
|
||||
| `mcp.sessions.max_per_binding` | `20` | Open sessions one binding (client and user, or personal token) may hold; the next `initialize` answers 429. |
|
||||
| `mcp.resource` | `app.url` + `mcp.path` | The resource indicator the endpoint identifies itself by; must equal the OAuth server's MCP resource. Required (or `app.url`) when enabled. |
|
||||
|
||||
@@ -141,6 +141,11 @@ func (s *redisStore) Count(ctx context.Context, binding string) (int, error) {
|
||||
// writes the record with its PX TTL, adds the id to the set and slides the
|
||||
// set's TTL (1). KEYS: the binding set, the record. ARGV: the record key
|
||||
// prefix, max, the record JSON, the TTL in milliseconds, the session id.
|
||||
// The script also reads the record keys of the set's members, which it
|
||||
// cannot declare in KEYS, and the binding set and the record do not share a
|
||||
// hash slot: the store needs a standalone Redis (or a primary with
|
||||
// replicas). Redis Cluster refuses the script as cross-slot, and an ACL
|
||||
// that restricts keys by pattern must allow the whole <prefix>mcp: range.
|
||||
var reserveScript = redis.NewScript(`
|
||||
local members = redis.call('SMEMBERS', KEYS[1])
|
||||
local n = 0
|
||||
|
||||
Reference in New Issue
Block a user