Problem
The #2041 create-time picker pipeline carries operator input values as a flat inputs: {key: value} map end to end — the ${key} placeholder contract from config/mcp-catalog.json/#2011, shipped on POST /api/fleet in #2125 and consumed by the picker in #2127. Consequence (QA panel finding on #2127, confirmed; adjudicated merge-then-followup): two MCP servers in one bundle that declare the same input key (e.g. both need a token) collide — the picker's fieldId is origin:key (no server), and even a namespaced UI collapses back to flat at submit, so one value fills both servers' templates. No bundle we ship today declares two servers sharing an input key, but nothing prevents one.
Proposed direction
- Accept
inputs keyed "<server>:<key>" with bare-key fallback on POST /api/fleet (backward compatible).
resolve_bundle_mcp_item gains per-item override resolution: prefer values["<item.name>:<key>"], then values[key], then env/default.
- Picker:
fieldId becomes origin:server:key; splitConfigValues emits namespaced keys only when a collision exists (keeps the common case clean).
Acceptance
Refs
#2041, #2125 (flat contract), #2127 (the finding), config/mcp-catalog.json input contract.
Problem
The #2041 create-time picker pipeline carries operator input values as a flat
inputs: {key: value}map end to end — the${key}placeholder contract fromconfig/mcp-catalog.json/#2011, shipped onPOST /api/fleetin #2125 and consumed by the picker in #2127. Consequence (QA panel finding on #2127, confirmed; adjudicated merge-then-followup): two MCP servers in one bundle that declare the same input key (e.g. both need atoken) collide — the picker'sfieldIdisorigin:key(no server), and even a namespaced UI collapses back to flat at submit, so one value fills both servers' templates. No bundle we ship today declares two servers sharing an input key, but nothing prevents one.Proposed direction
inputskeyed"<server>:<key>"with bare-keyfallback onPOST /api/fleet(backward compatible).resolve_bundle_mcp_itemgains per-item override resolution: prefervalues["<item.name>:<key>"], thenvalues[key], then env/default.fieldIdbecomesorigin:server:key;splitConfigValuesemits namespaced keys only when a collision exists (keeps the common case clean).Acceptance
tokenresolves distinct operator-supplied values per server, end to end (picker → route → resolver).Refs
#2041, #2125 (flat contract), #2127 (the finding), config/mcp-catalog.json input contract.