Expand description
Task 19 (Commit A) — safe local MCP descriptor cache and dormant deferred-tool integration (spec §7.4).
Before exact selection, MCP capability knowledge comes ONLY from bounded
local state: the user’s mcp.json server config plus this descriptor
cache (mcp-descriptors.json), treated as pre-existing operator-managed
local state. Loading NEVER spawns a process or touches the network, and
descriptors are never invented from server config alone: a server without
a fingerprint-matching cache entry contributes no capabilities. This task
deliberately adds no cache-refresh path from live server responses — a
server-wide legacy connection is not exact-tool authorized and must not
become a cache-poisoning bridge.
Under progressive tool disclosure, cached descriptors become truthful
DORMANT registry entries: live tools-map/catalog entries with a stable
identity, schema, and digest whose implementation is deferred
(DeferredMcpTool). Exact activation therefore never mutates the
catalog generation; execution is separately authorization-gated and
(lease lifecycle commit) acquires a session-scoped runtime lease before
any process starts.
Structs§
- Cached
Server Descriptors - Cached descriptors for one configured server, keyed to the exact config fingerprint they were recorded under.
- Cached
Tool Descriptor - One cached MCP tool descriptor: exactly the fields a live
tools/listwould provide, pinned locally so activation can validate the live listing against operator-known expectations. - Deferred
McpTool - A dormant, descriptor-backed MCP tool. Registered like any live tool (stable identity, schema, digest — so wire resolution, discovery, exact grants, and the execution gate all work unchanged), but its implementation is DEFERRED: executing it requires the session MCP lease capability, which starts only this tool’s server after the execution gate has already authorized the call. Without that capability it fails typed and spawns nothing.
- Discovery
Failure - One server’s failed boot-discovery record: the launch fingerprint it failed under, when it was last tried (unix seconds) and how many consecutive attempts under that fingerprint failed.
- McpDescriptor
Cache - Versioned on-disk descriptor cache model.
Enums§
- Descriptor
Cache Error - Typed descriptor-cache boundary failures. All fail closed: a rejected cache yields ZERO dormant capabilities, never a partial or guessed set.
Constants§
- CACHE_
FORMAT_ VERSION - Supported cache format version. Anything else fails typed.
- CACHE_
MAX_ BYTES - Hard byte bound on the cache file — larger files are rejected unread.
- DESCRIPTOR_
CACHE_ FILE - File name of the descriptor cache, resolved through the profile-aware config path helper so profiles cannot collide on one cache file.
- DISCOVERY_
BACKOFF_ BASE_ SECS - First retry delay after a failed boot discovery.
- DISCOVERY_
BACKOFF_ MAX_ SECS - Retry delay cap after repeated failed boot discoveries.
- DISCOVERY_
FAILURES_ MAX - Maximum retained discovery-failure entries (oldest attempts evicted).
- SERVER_
MAX_ TOOLS - Maximum retained descriptors per server entry.
- TOOL_
DESCRIPTION_ MAX_ BYTES - Byte bound applied to cached descriptions on load.
- TOOL_
NAME_ MAX_ BYTES - Maximum bytes of a cached tool name (also: nonempty, no control chars).
- TOOL_
SCHEMA_ MAX_ BYTES - Maximum serialized bytes of one cached input schema.
Functions§
- cache_
writeback_ enabled SYNAPS_MCP_CACHE_WRITEBACK=0disablesrecord_server_listing.- default_
cache_ path - Profile-aware default cache path. Uses the same profile FALLBACK
semantics as every other config read (
resolve_read_path): the profile copy is used when it exists, otherwise the shared base file. A shared base cache can therefore be visible to multiple profiles; the per-server config fingerprint still gates trust in every entry. - dormant_
tools_ for_ config - Build the dormant deferred tools for the current config from the cache. Deterministic order (sorted server names, cached tool order). Only servers present in the CURRENT config whose cache entry fingerprint matches the current config fingerprint contribute tools; everything else is skipped with a warning. Never spawns, never touches the network.
- load_
cache_ from - Load and sanitize a descriptor cache from an explicit path. Boundary
rules: exactly ONE handle is opened (
O_NOFOLLOW|O_NONBLOCKon Unix, so a symlink or FIFO at the path is refused without following/blocking) and ALL checks run against that opened handle’s metadata — no metadata-then-reopen TOCTOU. The opened file must be regular and withinCACHE_MAX_BYTES; the read itself is capped viaRead::takeand a post-read length check rejects concurrent growth past the bound. The content must be valid JSON of the supported version. Individual hostile descriptor entries (invalid name, non-object schema, oversized schema, duplicates) are dropped deterministically; descriptions are bounded. Never spawns, never touches the network. - load_
default_ cache - Load the descriptor cache from the profile-resolved default location.
- record_
discovery_ failure - Negative-cache sibling of
record_server_listing: record that boot discovery ofserverunderfingerprintfailed atnow_unix. Same locked read-merge-write and sanitisation. Consecutive failures under the same fingerprint incrementattempts(driving the backoff); a different fingerprint restarts at 1. Returns the stored record. - record_
server_ listing - Write-back after a successful, lease-authorized
tools/list(daemon-mode phase 2, C4): mergetoolsinto the cache atpathunderserver, keyed by the launchfingerprint, so the NEXT boot’ssetup_lazy_mcpregisters the same dormant descriptors without spawning anything (stable prompt-cache prefix across daemon restarts). - server_
config_ fingerprint - Deterministic fingerprint of one server’s local launch configuration (command, args, env). Cached descriptors are only trusted while the current config still fingerprints identically; any drift invalidates them. Pure local computation — no process, no network.
- store_
cache_ at - Atomically persist a descriptor cache with private-filesystem policy
(spec §5.4): parent directory ensured
0700, temp file createdcreate_newat mode0600(never create-then-chmod), then renamed over the target; symlinks at the target are refused typed. Used to seed/maintain operator-local state (and by tests); this task adds no production writer fed from live server responses.