0028, Runtime dataset activation over MCP — connect_dataset + a dynamically-registered tool¶
- Status: Accepted (2026-06-07)
- Deciders: Ariadne maintainers
- Refines: ADR-0020 (axis A3, the dynamic MCP surface) · builds on ADR-0025 (ratified mappings) + ADR-0009 (Ariadne as an MCP server)
Context¶
A3 is the dynamic MCP surface: per-source tool families that appear at runtime as
datasets connect, not just whatever was wired at server startup. The enumeration
half shipped (list_datasets). This is the activation half: a host agent should be
able to onboard a user's store mid-session — point Ariadne at a newly-ratified
mapping and immediately work it up — without restarting the MCP server. MCP's
mechanism is the tools listChanged capability plus a
notifications/tools/list_changed message that tells the client to re-fetch
tools/list. The contestable questions: what may be activated at runtime (a
governance question), and how the notification actually fires on our stack. Hence
this ADR.
Decision drivers¶
- Runtime onboarding without restart is the validated 2026 pattern. Database-MCP practice converged on "dynamic onboarding of new data sources without restart or redeployment" — the agent discovers and uses a source mid-session.
- The propose → ratify → freeze governance spine must hold. ADR-0020's hard boundary: the agent operates only on ratified artifacts; it never onboards data a human hasn't vetted. So runtime activation of an already-ratified mapping is in bounds; runtime introspect-map-apply of a raw DSN is not.
- The official MCP SDK does not auto-notify. Ariadne uses the official SDK's bundled
mcp.server.fastmcp.FastMCP, whoseadd_toolonly mutates the tool registry — it does not emitlist_changed(that auto-notify behaviour belongs to the separatejlowin/fastmcpv2 package). The notification must be sent manually, via the request's session, and the SDK only delivers it from within an active request context (registration at server-init fires nothing). - A
list_changedwith nothing to list is noise. The notification signals a tool set change, so activation has to register an actual tool, not merely an adapter. - Hermetically verifiable. The SDK ships an in-memory client↔server harness, so the
real protocol path (activate → the new tool is visible via
tools/list) is testable without a network or a second process.
Considered options¶
connect_dataset(dsn=...)that introspects, maps, and activates a raw store at runtime. Rejected. It lets an agent self-onboard unvetted data and skip human ratification — a direct breach of ADR-0020's hard boundary. The mapping must be human-ratified first; the agent only activates it.- Register every dataset's tool family statically at server startup. Rejected. No
runtime onboarding (the whole point of A3), and a startup-registered
workup_<name>is pure redundancy with the existingworkup(dataset=…)parameter. - Activate the adapter but fire
list_changedwithout adding a tool. Rejected.list_changedis a tools signal; with no tool added there is nothing for a client to re-discover, and the agent gains no new, selectable capability. connect_dataset(name)activates an already-ratified mapping at runtime: register the adapter, expose an intent-namedworkup_<name>tool, and manually sendsend_tool_list_changed. Chosen.
Decision¶
Adopt option 4, in mcp_server.py.
connect_dataset(name, ctx)resolvesnameto a ratified*.tomlunder$ARIADNE_MAPPINGS(the same store ADR-0025'sdiscover_and_registerreads). Not found ⇒ a clear error naming the available mappings. Found ⇒ register itsMappingDrivenAdapter(soworkupcan run it) and register a per-dataset tool.- The per-dataset tool is
workup_<name>(entity, …)— a thin wrapper overrun_workup_tool(entity, dataset=name, …). It is intent-named on purpose: 2026 tool-design guidance is that names tied to a concrete intent improve an agent's selection accuracy, and it is the artifact whose appearancelist_changedannounces. We accept that it overlapsworkup(dataset=<name>); the value is the runtime appearance and the named affordance, not a new capability. - The notification is sent manually,
await ctx.session.send_tool_list_changed(), afterctx.fastmcp.add_tool(...), inside theconnect_datasetrequest context — the only place the official SDK will actually deliver it. Thetools.listChangedcapability is advertised so conforming clients listen. - Testable core + DI. A pure
activate_dataset(name, env, *, register, add_tool)holds the resolve-register-expose logic over injected seams (hermetic unit test); the@mcp.tool()wires the real FastMCPadd_tool+ the session notification. An in-memory-client integration test drives the real protocol: list tools (noworkup_<name>), callconnect_dataset, list tools again (workup_<name>present).
Consequences¶
- A host agent can onboard a ratified user store mid-session —
connect_datasetthenworkup_<name>(orworkup(dataset=<name>)) — with no server restart, and conforming clients re-list automatically. This is the runtime face of the A1/A2 mapping pipeline. - Governance holds: only human-ratified mappings under
$ARIADNE_MAPPINGScan be activated; the agent never introspects/applies a raw DSN. Runtime activation is the in-bounds slice of "adaptive." - The official-SDK manual-notification path (and its request-context-only constraint) is
captured here, so the next person doesn't reach for
jlowin/fastmcp's auto-notify and find it absent. - One honest redundancy:
workup_<name>overlaps thedataset=parameter. Justified by selection ergonomics + the need for alist_changed-bearing artifact; revisited if the per-dataset family grows (e.g. a dataset-scoped search tool). - Tool-count growth is bounded — 2026 guidance is that clients handle hundreds of tools;
if a deployment activates very many datasets we revisit (e.g. a single
workup+ resource-list), but that is not today's scale (YAGNI).
Sources: MCP tools + listChanged — MCP spec, tools;
runtime onboarding of new data sources without restart —
Data API builder MCP,
Google MCP Toolbox for Databases;
intent-named tools aid selection (not tool-explosion) —
MCP server anti-patterns, 2026;
manual notification is required on the official SDK (auto-notify is the separate package) —
FastMCP tools.