diff --git a/src/http/AGENTS.md b/src/http/AGENTS.md index 01fe3b5..399d17f 100644 --- a/src/http/AGENTS.md +++ b/src/http/AGENTS.md @@ -8,7 +8,7 @@ Provides Fastify-based HTTP transport for the MCP protocol. Handles SSE streams, - **FastifyTransport** (`FastifyTransport.ts`) — Main HTTP transport. Per-client bundles via ClientResourceCache, optional SessionContextResolver for context differentiation. - **Origin validation** (`originValidation.ts`) — `normalizeAllowedOrigins()` + `createOriginGuard()`; each transport applies the guard as an `onRequest` hook on `/mcp` POST/GET/DELETE only, when `allowedOrigins` is set. -- **Custom Endpoints** (`customEndpoints.ts`, `endpointRegistration.ts`) — Type-safe endpoint definitions with Zod schemas. `defineEndpoint()` / `definePermissionAwareEndpoint()` helpers. +- **Custom Endpoints** (`http.utils.ts`, types in `http.types.ts`) — Type-safe endpoint definitions with Zod 4 schemas. `defineEndpoint()` / `definePermissionAwareEndpoint()` helpers build them; `registerCustomEndpoints()` mounts them and enforces the reserved paths. Validation failures answer 400 `VALIDATION_ERROR` with Zod 4 issues in `error.details`. ## Endpoints diff --git a/src/meta/AGENTS.md b/src/meta/AGENTS.md index 93297e0..6677684 100644 --- a/src/meta/AGENTS.md +++ b/src/meta/AGENTS.md @@ -18,13 +18,13 @@ Registers meta-tools on MCP servers for runtime toolset management. Called by Se ## Invariants -1. **`_meta` is a reserved toolset key** — If a catalog uses `"_meta"` as a toolset key, ToolRegistry will detect a name collision at registration time. Never use this key in the catalog. +1. **`_meta` is a reserved toolset key** — `RESERVED_TOOLSET_KEYS` (`src/mode/mode.types.ts`) makes the `ModuleResolver` constructor throw when a catalog has a `"_meta"` key, and `validateToolsetName()` also refuses it ("reserved for internal use"). Never use this key in the catalog. 2. **Meta-tools registered via ToolRegistry** — Same collision detection path as user tools. Meta-tools appear in `toolRegistry.list()` and `toolRegistry.listByToolset()`. 3. **`enable_toolset` / `disable_toolset` annotated with `destructiveHint: false, idempotentHint: true`** — They change which tools are exposed but destroy nothing, and repeating either call has no further effect on state. Visible to LLM clients via MCP tool annotations. Discovery tools (`list_*`, `describe_*`) use `readOnlyHint: true`. ## Anti-patterns -- Using `_meta` as a catalog key (collision with meta-tools) +- Using `_meta` as a catalog key (the resolver throws at construction) - Registering meta-tools outside of `registerMetaTools()` (bypasses ToolRegistry) - Expecting meta-tools in permission-based servers (they are never registered — `registerMetaTools(false)`) diff --git a/src/mode/AGENTS.md b/src/mode/AGENTS.md index 6b3c303..308daed 100644 --- a/src/mode/AGENTS.md +++ b/src/mode/AGENTS.md @@ -7,14 +7,16 @@ Handles toolset resolution, validation, and module loading. Bridges toolset defi ## Key Components - **ModuleResolver** (`ModuleResolver.ts`) — Stores catalog, validates toolset names, resolves tools by collecting direct tools + calling module loaders. +- **moduleLoading.ts** — empty placeholder: no code, nothing imports it. - **ToolsetValidator** (`ModeResolver.ts`) — Validates toolset names against a provided catalog, parses comma-separated toolset strings. Also resolves startup mode from env/args. ## Invariants -1. **Module loaders fail silently** — Errors caught, logged as warnings, skipped. Toolset activates with whatever tools succeeded. This is intentional — one broken loader should not block the rest. -2. **ToolsetValidator is in ModeResolver.ts** — Historical naming quirk. `ToolsetValidator.ts` just re-exports from `ModeResolver.ts`. Import from either file. -3. **ModuleResolver stores catalog; ToolsetValidator takes it as parameter** — Different validation signatures. ModuleResolver validates against its stored catalog; ToolsetValidator is stateless. -4. **Context passed to module loaders** — Enables dynamic tool generation based on runtime context (e.g., per-client tools). +1. **`_meta` is rejected** — `RESERVED_TOOLSET_KEYS` (`mode.types.ts`): the `ModuleResolver` constructor throws if the catalog has such a key, and `validateToolsetName()` refuses it again as defense in depth. +2. **Module loaders fail silently** — Errors caught, logged as warnings, skipped. Toolset activates with whatever tools succeeded. This is intentional — one broken loader should not block the rest. +3. **ToolsetValidator is in ModeResolver.ts** — Historical naming quirk. `ToolsetValidator.ts` just re-exports from `ModeResolver.ts`. Import from either file. +4. **ModuleResolver stores catalog; ToolsetValidator takes it as parameter** — Different validation signatures. ModuleResolver validates against its stored catalog; ToolsetValidator is stateless. +5. **Context passed to module loaders** — Enables dynamic tool generation based on runtime context (e.g., per-client tools). ## Module Loading Flow @@ -25,14 +27,14 @@ resolveToolsForToolsets([toolsetName], context) 2. For each module in definition.modules[]: → Look up loader in moduleLoaders map → Call loader(context) — may be async - → Catch errors → warn and skip (invariant #1) + → Catch errors → warn and skip (invariant #2) → Append returned tools Return flattened McpToolDefinition[] ``` ## Anti-patterns -- Throwing on invalid toolset names (return validation result instead) +- Throwing on invalid toolset names at runtime (return a validation result instead; only a reserved key in the catalog throws, at construction) - Assuming module loaders are synchronous (always await) - Using `ToolsetValidator.ts` directly when `ModeResolver.ts` is clearer diff --git a/src/session/AGENTS.md b/src/session/AGENTS.md index 4dd715e..9c28c95 100644 --- a/src/session/AGENTS.md +++ b/src/session/AGENTS.md @@ -14,7 +14,7 @@ Manages per-session context resolution and client resource caching. Enables mult 1. **Fail-secure parsing** — Invalid base64/JSON encoding returns `{}`, not an error. Malformed client input never crashes the server. 2. **Disallowed keys silently filtered** — No logging to prevent information leakage about what keys exist. -3. **Cache key includes context hash** — Format: `${clientId}:${sha256suffix}`. Uses SHA-256 of sorted config keys — property order does not affect the hash. +3. **Cache key includes context hash** — Format: `${clientId}:${sha256suffix}`, built by the transport's `resolveSessionContext()`. When the resolver returns the suffix `"default"` (`enabled: false`; no usable `?config=`, meaning absent, malformed or empty after filtering; or a throwing `contextResolver`) the key is the plain `clientId`. The suffix is SHA-256 (first 16 hex chars) of the filtered config as JSON, top-level keys sorted — the values are part of the hash, property order is not. 4. **LRU eviction triggers cleanup** — onEvict callback called on removal. Transport uses this to close all sessions in an evicted bundle. 5. **Background pruning interval** — Removes TTL-expired entries periodically. `stop(true)` clears all entries and triggers cleanup.