Unconfirmed observation from the QA run — recorded for triage; the two mechanisms below were located but not independently root-caused, and the natural next step is to decide the single source of truth for skill rows (merge/dedup by name) that both the meta HTTP list and the MCP prompt bridge read from.
Symptom
Two related divergences in how skill metadata is read after a runtime store override:
GET /api/v1/meta/skill returns the same skill twice after a runtime PUT — the store-override row and the package row are both listed, not merged/deduped by name.PUT /meta/skill/<name> {active:true} → 200 does not reach the prompt bridge. Flipping a skill active via the runtime metadata API does not make it appear over MCP prompts.
- Expected: one row per skill name (override merged over package), and a runtime
{active:true} PUT reflected in the prompt bridge.
Root cause (as located, unconfirmed)
The two surfaces read from different sources:
- The MCP prompt bridge's
listSkills calls metadataService.list('skill') — registry/package rows (packages/mcp/src/mcp-server-runtime.ts:753, listSkills: async () => (await metadataService.list('skill')) ?? []). - The HTTP meta list goes through
protocol.getMetaItems — which merges store overrides.
So the store override (the {active:true} PUT) lands on the getMetaItems path but never on the metadataService.list path the bridge consumes; and on the HTTP list path the override and package rows are not deduped by name, producing the double listing. Both mechanisms are present on origin/main (mcp-server-runtime.ts:753; the dedup gap is in the getMetaItems merge in packages/metadata-protocol).
Note: the fix straddles packages/metadata-protocol (dedup/merge of skill rows) and packages/mcp (which source listSkills reads). Routed domain:metadata because the defining symptom — duplicate rows and un-merged overrides — lives in the metadata read/list layer; flag if the maintainer prefers the bridge-side lane.
Reproduction
- Boot a showcase with a packaged
skill. PUT /api/v1/meta/skill/<name> with {active:true} → 200.GET /api/v1/meta/skill → the skill appears twice (override row + package row).- Query the skill over MCP prompts → the
{active:true} flip is not reflected.
Source
Extracted from the QA run #7627 (framework 92f26f7, console 6314e87f).
Unconfirmed observation from the QA run — recorded for triage; the two mechanisms below were located but not independently root-caused, and the natural next step is to decide the single source of truth for skill rows (merge/dedup by name) that both the meta HTTP list and the MCP prompt bridge read from.
Symptom
Two related divergences in how
skillmetadata is read after a runtime store override:GET /api/v1/meta/skillreturns the same skill twice after a runtimePUT— the store-override row and the package row are both listed, not merged/deduped by name.PUT /meta/skill/<name> {active:true}→ 200 does not reach the prompt bridge. Flipping a skill active via the runtime metadata API does not make it appear over MCP prompts.{active:true}PUT reflected in the prompt bridge.Root cause (as located, unconfirmed)
The two surfaces read from different sources:
listSkillscallsmetadataService.list('skill')— registry/package rows (packages/mcp/src/mcp-server-runtime.ts:753,listSkills: async () => (await metadataService.list('skill')) ?? []).protocol.getMetaItems— which merges store overrides.So the store override (the
{active:true}PUT) lands on thegetMetaItemspath but never on themetadataService.listpath the bridge consumes; and on the HTTP list path the override and package rows are not deduped by name, producing the double listing. Both mechanisms are present onorigin/main(mcp-server-runtime.ts:753; the dedup gap is in thegetMetaItemsmerge inpackages/metadata-protocol).Note: the fix straddles
packages/metadata-protocol(dedup/merge of skill rows) andpackages/mcp(which sourcelistSkillsreads). Routeddomain:metadatabecause the defining symptom — duplicate rows and un-merged overrides — lives in the metadata read/list layer; flag if the maintainer prefers the bridge-side lane.Reproduction
skill.PUT /api/v1/meta/skill/<name>with{active:true}→ 200.GET /api/v1/meta/skill→ the skill appears twice (override row + package row).{active:true}flip is not reflected.Source
Extracted from the QA run #7627 (framework 92f26f7, console 6314e87f).