@seed-ship/mcp-ui-solid 5.2.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/components/ElicitationForm.cjs +51 -0
  3. package/dist/components/ElicitationForm.cjs.map +1 -0
  4. package/dist/components/ElicitationForm.d.ts +68 -0
  5. package/dist/components/ElicitationForm.d.ts.map +1 -0
  6. package/dist/components/ElicitationForm.js +51 -0
  7. package/dist/components/ElicitationForm.js.map +1 -0
  8. package/dist/components/index.d.ts +2 -0
  9. package/dist/components/index.d.ts.map +1 -1
  10. package/dist/components.cjs +2 -0
  11. package/dist/components.cjs.map +1 -1
  12. package/dist/components.d.cts +2 -0
  13. package/dist/components.d.ts +2 -0
  14. package/dist/components.js +2 -0
  15. package/dist/components.js.map +1 -1
  16. package/dist/index.cjs +8 -0
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +4 -0
  19. package/dist/index.d.ts +4 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +8 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/stores/server-capabilities-store.cjs +61 -0
  24. package/dist/stores/server-capabilities-store.cjs.map +1 -0
  25. package/dist/stores/server-capabilities-store.d.ts +172 -0
  26. package/dist/stores/server-capabilities-store.d.ts.map +1 -0
  27. package/dist/stores/server-capabilities-store.js +61 -0
  28. package/dist/stores/server-capabilities-store.js.map +1 -0
  29. package/docs/recipes/elicitation-pseudo-spec-adapter.md +171 -0
  30. package/docs/recipes/feedback-inline-wiring.md +142 -0
  31. package/package.json +1 -1
  32. package/src/components/ElicitationForm.test.tsx +197 -0
  33. package/src/components/ElicitationForm.tsx +126 -0
  34. package/src/components/index.ts +4 -0
  35. package/src/index.ts +16 -0
  36. package/src/stores/server-capabilities-store.test.tsx +206 -0
  37. package/src/stores/server-capabilities-store.tsx +215 -0
  38. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Server Capabilities Store — reactive snapshot of the MCP `initialize`
3
+ * response echoed by the server.
4
+ *
5
+ * @experimental
6
+ * @since v5.3.0
7
+ *
8
+ * mcp-ui doesn't speak MCP protocol directly — the consumer's transport
9
+ * layer (stdio child process, HTTP/SSE client, ...) parses the
10
+ * `initialize` JSON-RPC response and pushes the relevant fields into this
11
+ * store via `setServerCapabilities(info)`. Components then read
12
+ * reactively via `useServerCapabilities()` to gate behavior :
13
+ *
14
+ * ```tsx
15
+ * const { capabilities } = useServerCapabilities()
16
+ * <Show when={capabilities()?.tools?.listChanged}>
17
+ * <ToolListSubscriber />
18
+ * </Show>
19
+ * ```
20
+ *
21
+ * ## Two consumption modes (mirrors `scratchpad-store`)
22
+ *
23
+ * 1. **Singleton mode (default)** — `setServerCapabilities(info)` mutates
24
+ * the module-level singleton. `useServerCapabilities()` reads from it.
25
+ * Use for single-MCP-server consumers (the common case).
26
+ *
27
+ * 2. **Multi-instance mode** — wrap a subtree in
28
+ * `<ServerCapabilitiesProvider>` to scope a separate handle. Pass
29
+ * `store={createServerCapabilitiesStore()}` explicitly when you need to
30
+ * drive it from a non-reactive scope (e.g. a transport adapter living
31
+ * at the app root).
32
+ *
33
+ * ## Note on `elicitation`
34
+ *
35
+ * Per MCP spec 2025-06-18, `elicitation` is a **CLIENT** capability, not
36
+ * a server one. Servers do not declare it. If you need to gate
37
+ * `<ElicitationForm>` rendering on whether the connected client *itself*
38
+ * supports elicitation — that's a separate concern (your own state, set
39
+ * by your transport layer based on its own configuration).
40
+ */
41
+ import { type ParentComponent } from 'solid-js';
42
+ /**
43
+ * Server capabilities object as advertised in the MCP `initialize` response.
44
+ * Mirrors the spec 2025-06-18 `ServerCapabilities` shape with permissive
45
+ * `experimental` for forward compatibility.
46
+ */
47
+ export interface ServerCapabilities {
48
+ experimental?: Record<string, unknown>;
49
+ logging?: Record<string, never>;
50
+ tools?: {
51
+ listChanged?: boolean;
52
+ };
53
+ prompts?: {
54
+ listChanged?: boolean;
55
+ };
56
+ resources?: {
57
+ listChanged?: boolean;
58
+ subscribe?: boolean;
59
+ };
60
+ completions?: Record<string, never>;
61
+ }
62
+ /**
63
+ * Subset of the MCP `initialize` response relevant to the UI layer.
64
+ * Consumers may extend this via the `experimental` field.
65
+ */
66
+ export interface ServerInitializeInfo {
67
+ protocolVersion: string;
68
+ serverInfo: {
69
+ name: string;
70
+ version: string;
71
+ title?: string;
72
+ [key: string]: unknown;
73
+ };
74
+ capabilities: ServerCapabilities;
75
+ instructions?: string;
76
+ }
77
+ export interface ServerCapabilitiesStoreHandle {
78
+ /** Push a fresh `initialize` snapshot into the store, or clear with `null`. */
79
+ set: (info: ServerInitializeInfo | null) => void;
80
+ /** Reactive accessor for the full info (null when no initialize received). */
81
+ info: () => ServerInitializeInfo | null;
82
+ /** Reactive accessor for just the `capabilities` field. */
83
+ capabilities: () => ServerCapabilities | null;
84
+ /** Reactive accessor for just the `serverInfo` field. */
85
+ serverInfo: () => ServerInitializeInfo['serverInfo'] | null;
86
+ /** Reactive accessor for the protocol version string. */
87
+ protocolVersion: () => string | null;
88
+ /**
89
+ * Helper : returns true if the server advertised the named capability key
90
+ * with a truthy value (i.e. the key is present, even as an empty object).
91
+ */
92
+ hasCapability: (key: keyof ServerCapabilities) => boolean;
93
+ }
94
+ /**
95
+ * Create an isolated server-capabilities store instance.
96
+ *
97
+ * Use this when you need to track multiple MCP servers in parallel (rare),
98
+ * or to drive the store from a non-reactive transport adapter. Pair with
99
+ * `<ServerCapabilitiesProvider store={...}>` to scope a SolidJS subtree.
100
+ *
101
+ * @experimental
102
+ * @since v5.3.0
103
+ */
104
+ export declare function createServerCapabilitiesStore(): ServerCapabilitiesStoreHandle;
105
+ /**
106
+ * Push the parsed MCP `initialize` response into the module-level singleton
107
+ * store. Pass `null` to clear (e.g. on disconnect / server change).
108
+ *
109
+ * @experimental
110
+ * @since v5.3.0
111
+ *
112
+ * @example
113
+ * // In your transport adapter, after receiving the initialize response :
114
+ * setServerCapabilities({
115
+ * protocolVersion: response.result.protocolVersion,
116
+ * serverInfo: response.result.serverInfo,
117
+ * capabilities: response.result.capabilities,
118
+ * instructions: response.result.instructions,
119
+ * })
120
+ */
121
+ export declare function setServerCapabilities(info: ServerInitializeInfo | null): void;
122
+ /**
123
+ * Context for a scoped server-capabilities store. Populated by
124
+ * `<ServerCapabilitiesProvider>`. Read by `useServerCapabilities()` with
125
+ * automatic fallback to the module-level singleton when absent.
126
+ *
127
+ * @experimental
128
+ * @since v5.3.0
129
+ */
130
+ export declare const ServerCapabilitiesContext: import("solid-js").Context<ServerCapabilitiesStoreHandle | undefined>;
131
+ /**
132
+ * Provide a scoped `ServerCapabilitiesStoreHandle` to a SolidJS subtree.
133
+ * Children calling `useServerCapabilities()` bind to this store instead of
134
+ * the module singleton.
135
+ *
136
+ * If no `store` prop is passed, a fresh store is created for the provider's
137
+ * lifetime. Pass `store` explicitly when you need the handle outside the
138
+ * tree (e.g. in a transport adapter living at the app root).
139
+ *
140
+ * @experimental
141
+ * @since v5.3.0
142
+ */
143
+ export declare const ServerCapabilitiesProvider: ParentComponent<{
144
+ store?: ServerCapabilitiesStoreHandle;
145
+ }>;
146
+ /**
147
+ * Hook for components — reads the server capabilities reactively.
148
+ *
149
+ * If called inside a `<ServerCapabilitiesProvider>`, reads the scoped
150
+ * handle; otherwise falls back to the module singleton.
151
+ *
152
+ * @experimental
153
+ * @since v5.3.0
154
+ *
155
+ * @example
156
+ * const { capabilities, serverInfo, hasCapability } = useServerCapabilities()
157
+ *
158
+ * <Show when={capabilities()}>
159
+ * <p>Connected to {serverInfo()?.name} v{serverInfo()?.version}</p>
160
+ * <Show when={hasCapability('tools')}>
161
+ * <ToolPalette />
162
+ * </Show>
163
+ * </Show>
164
+ */
165
+ export declare function useServerCapabilities(): {
166
+ info: () => ServerInitializeInfo | null;
167
+ capabilities: () => ServerCapabilities | null;
168
+ serverInfo: () => ServerInitializeInfo['serverInfo'] | null;
169
+ protocolVersion: () => string | null;
170
+ hasCapability: (key: keyof ServerCapabilities) => boolean;
171
+ };
172
+ //# sourceMappingURL=server-capabilities-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-capabilities-store.d.ts","sourceRoot":"","sources":["../../src/stores/server-capabilities-store.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EAA6B,KAAK,eAAe,EAAY,MAAM,UAAU,CAAA;AAKpF;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;IAC/B,KAAK,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;IACjC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;IACnC,SAAS,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;IACrF,YAAY,EAAE,kBAAkB,CAAA;IAChC,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAID,MAAM,WAAW,6BAA6B;IAC5C,+EAA+E;IAC/E,GAAG,EAAE,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI,KAAK,IAAI,CAAA;IAChD,8EAA8E;IAC9E,IAAI,EAAE,MAAM,oBAAoB,GAAG,IAAI,CAAA;IACvC,2DAA2D;IAC3D,YAAY,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAA;IAC7C,yDAAyD;IACzD,UAAU,EAAE,MAAM,oBAAoB,CAAC,YAAY,CAAC,GAAG,IAAI,CAAA;IAC3D,yDAAyD;IACzD,eAAe,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;IACpC;;;OAGG;IACH,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,kBAAkB,KAAK,OAAO,CAAA;CAC1D;AAID;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,IAAI,6BAA6B,CAW7E;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI,GAAG,IAAI,CAE7E;AAID;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,uEAErC,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,EAAE,eAAe,CAAC;IACvD,KAAK,CAAC,EAAE,6BAA6B,CAAA;CACtC,CAOA,CAAA;AAID;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,IAAI;IACvC,IAAI,EAAE,MAAM,oBAAoB,GAAG,IAAI,CAAA;IACvC,YAAY,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAA;IAC7C,UAAU,EAAE,MAAM,oBAAoB,CAAC,YAAY,CAAC,GAAG,IAAI,CAAA;IAC3D,eAAe,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;IACpC,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,kBAAkB,KAAK,OAAO,CAAA;CAC1D,CAUA"}
@@ -0,0 +1,61 @@
1
+ import { createComponent } from "solid-js/web";
2
+ import { createContext, useContext } from "solid-js";
3
+ import { createStore } from "solid-js/store";
4
+ function createServerCapabilitiesStore() {
5
+ const [state, setState] = createStore({
6
+ info: null
7
+ });
8
+ return {
9
+ set: (info) => setState("info", info),
10
+ info: () => state.info,
11
+ capabilities: () => {
12
+ var _a;
13
+ return ((_a = state.info) == null ? void 0 : _a.capabilities) ?? null;
14
+ },
15
+ serverInfo: () => {
16
+ var _a;
17
+ return ((_a = state.info) == null ? void 0 : _a.serverInfo) ?? null;
18
+ },
19
+ protocolVersion: () => {
20
+ var _a;
21
+ return ((_a = state.info) == null ? void 0 : _a.protocolVersion) ?? null;
22
+ },
23
+ hasCapability: (key) => {
24
+ var _a, _b;
25
+ return Boolean((_b = (_a = state.info) == null ? void 0 : _a.capabilities) == null ? void 0 : _b[key]);
26
+ }
27
+ };
28
+ }
29
+ const defaultStore = createServerCapabilitiesStore();
30
+ function setServerCapabilities(info) {
31
+ defaultStore.set(info);
32
+ }
33
+ const ServerCapabilitiesContext = createContext(void 0);
34
+ const ServerCapabilitiesProvider = (props) => {
35
+ const store = props.store ?? createServerCapabilitiesStore();
36
+ return createComponent(ServerCapabilitiesContext.Provider, {
37
+ value: store,
38
+ get children() {
39
+ return props.children;
40
+ }
41
+ });
42
+ };
43
+ function useServerCapabilities() {
44
+ const scoped = useContext(ServerCapabilitiesContext);
45
+ const handle = scoped ?? defaultStore;
46
+ return {
47
+ info: handle.info,
48
+ capabilities: handle.capabilities,
49
+ serverInfo: handle.serverInfo,
50
+ protocolVersion: handle.protocolVersion,
51
+ hasCapability: handle.hasCapability
52
+ };
53
+ }
54
+ export {
55
+ ServerCapabilitiesContext,
56
+ ServerCapabilitiesProvider,
57
+ createServerCapabilitiesStore,
58
+ setServerCapabilities,
59
+ useServerCapabilities
60
+ };
61
+ //# sourceMappingURL=server-capabilities-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-capabilities-store.js","sources":["../../src/stores/server-capabilities-store.tsx"],"sourcesContent":["/**\n * Server Capabilities Store — reactive snapshot of the MCP `initialize`\n * response echoed by the server.\n *\n * @experimental\n * @since v5.3.0\n *\n * mcp-ui doesn't speak MCP protocol directly — the consumer's transport\n * layer (stdio child process, HTTP/SSE client, ...) parses the\n * `initialize` JSON-RPC response and pushes the relevant fields into this\n * store via `setServerCapabilities(info)`. Components then read\n * reactively via `useServerCapabilities()` to gate behavior :\n *\n * ```tsx\n * const { capabilities } = useServerCapabilities()\n * <Show when={capabilities()?.tools?.listChanged}>\n * <ToolListSubscriber />\n * </Show>\n * ```\n *\n * ## Two consumption modes (mirrors `scratchpad-store`)\n *\n * 1. **Singleton mode (default)** — `setServerCapabilities(info)` mutates\n * the module-level singleton. `useServerCapabilities()` reads from it.\n * Use for single-MCP-server consumers (the common case).\n *\n * 2. **Multi-instance mode** — wrap a subtree in\n * `<ServerCapabilitiesProvider>` to scope a separate handle. Pass\n * `store={createServerCapabilitiesStore()}` explicitly when you need to\n * drive it from a non-reactive scope (e.g. a transport adapter living\n * at the app root).\n *\n * ## Note on `elicitation`\n *\n * Per MCP spec 2025-06-18, `elicitation` is a **CLIENT** capability, not\n * a server one. Servers do not declare it. If you need to gate\n * `<ElicitationForm>` rendering on whether the connected client *itself*\n * supports elicitation — that's a separate concern (your own state, set\n * by your transport layer based on its own configuration).\n */\n\nimport { createContext, useContext, type ParentComponent, type JSX } from 'solid-js'\nimport { createStore } from 'solid-js/store'\n\n// ─── Types ────────────────────────────────────────────────────\n\n/**\n * Server capabilities object as advertised in the MCP `initialize` response.\n * Mirrors the spec 2025-06-18 `ServerCapabilities` shape with permissive\n * `experimental` for forward compatibility.\n */\nexport interface ServerCapabilities {\n experimental?: Record<string, unknown>\n logging?: Record<string, never>\n tools?: { listChanged?: boolean }\n prompts?: { listChanged?: boolean }\n resources?: { listChanged?: boolean; subscribe?: boolean }\n completions?: Record<string, never>\n}\n\n/**\n * Subset of the MCP `initialize` response relevant to the UI layer.\n * Consumers may extend this via the `experimental` field.\n */\nexport interface ServerInitializeInfo {\n protocolVersion: string\n serverInfo: { name: string; version: string; title?: string; [key: string]: unknown }\n capabilities: ServerCapabilities\n instructions?: string\n}\n\n// ─── Handle ───────────────────────────────────────────────────\n\nexport interface ServerCapabilitiesStoreHandle {\n /** Push a fresh `initialize` snapshot into the store, or clear with `null`. */\n set: (info: ServerInitializeInfo | null) => void\n /** Reactive accessor for the full info (null when no initialize received). */\n info: () => ServerInitializeInfo | null\n /** Reactive accessor for just the `capabilities` field. */\n capabilities: () => ServerCapabilities | null\n /** Reactive accessor for just the `serverInfo` field. */\n serverInfo: () => ServerInitializeInfo['serverInfo'] | null\n /** Reactive accessor for the protocol version string. */\n protocolVersion: () => string | null\n /**\n * Helper : returns true if the server advertised the named capability key\n * with a truthy value (i.e. the key is present, even as an empty object).\n */\n hasCapability: (key: keyof ServerCapabilities) => boolean\n}\n\n// ─── Factory ──────────────────────────────────────────────────\n\n/**\n * Create an isolated server-capabilities store instance.\n *\n * Use this when you need to track multiple MCP servers in parallel (rare),\n * or to drive the store from a non-reactive transport adapter. Pair with\n * `<ServerCapabilitiesProvider store={...}>` to scope a SolidJS subtree.\n *\n * @experimental\n * @since v5.3.0\n */\nexport function createServerCapabilitiesStore(): ServerCapabilitiesStoreHandle {\n const [state, setState] = createStore<{ info: ServerInitializeInfo | null }>({ info: null })\n\n return {\n set: (info) => setState('info', info),\n info: () => state.info,\n capabilities: () => state.info?.capabilities ?? null,\n serverInfo: () => state.info?.serverInfo ?? null,\n protocolVersion: () => state.info?.protocolVersion ?? null,\n hasCapability: (key) => Boolean(state.info?.capabilities?.[key]),\n }\n}\n\n// ─── Module-level singleton ───────────────────────────────────\n\nconst defaultStore: ServerCapabilitiesStoreHandle = createServerCapabilitiesStore()\n\n/**\n * Push the parsed MCP `initialize` response into the module-level singleton\n * store. Pass `null` to clear (e.g. on disconnect / server change).\n *\n * @experimental\n * @since v5.3.0\n *\n * @example\n * // In your transport adapter, after receiving the initialize response :\n * setServerCapabilities({\n * protocolVersion: response.result.protocolVersion,\n * serverInfo: response.result.serverInfo,\n * capabilities: response.result.capabilities,\n * instructions: response.result.instructions,\n * })\n */\nexport function setServerCapabilities(info: ServerInitializeInfo | null): void {\n defaultStore.set(info)\n}\n\n// ─── Context ──────────────────────────────────────────────────\n\n/**\n * Context for a scoped server-capabilities store. Populated by\n * `<ServerCapabilitiesProvider>`. Read by `useServerCapabilities()` with\n * automatic fallback to the module-level singleton when absent.\n *\n * @experimental\n * @since v5.3.0\n */\nexport const ServerCapabilitiesContext = createContext<ServerCapabilitiesStoreHandle | undefined>(\n undefined\n)\n\n/**\n * Provide a scoped `ServerCapabilitiesStoreHandle` to a SolidJS subtree.\n * Children calling `useServerCapabilities()` bind to this store instead of\n * the module singleton.\n *\n * If no `store` prop is passed, a fresh store is created for the provider's\n * lifetime. Pass `store` explicitly when you need the handle outside the\n * tree (e.g. in a transport adapter living at the app root).\n *\n * @experimental\n * @since v5.3.0\n */\nexport const ServerCapabilitiesProvider: ParentComponent<{\n store?: ServerCapabilitiesStoreHandle\n}> = (props): JSX.Element => {\n const store = props.store ?? createServerCapabilitiesStore()\n return (\n <ServerCapabilitiesContext.Provider value={store}>\n {props.children}\n </ServerCapabilitiesContext.Provider>\n )\n}\n\n// ─── Reactive hook ────────────────────────────────────────────\n\n/**\n * Hook for components — reads the server capabilities reactively.\n *\n * If called inside a `<ServerCapabilitiesProvider>`, reads the scoped\n * handle; otherwise falls back to the module singleton.\n *\n * @experimental\n * @since v5.3.0\n *\n * @example\n * const { capabilities, serverInfo, hasCapability } = useServerCapabilities()\n *\n * <Show when={capabilities()}>\n * <p>Connected to {serverInfo()?.name} v{serverInfo()?.version}</p>\n * <Show when={hasCapability('tools')}>\n * <ToolPalette />\n * </Show>\n * </Show>\n */\nexport function useServerCapabilities(): {\n info: () => ServerInitializeInfo | null\n capabilities: () => ServerCapabilities | null\n serverInfo: () => ServerInitializeInfo['serverInfo'] | null\n protocolVersion: () => string | null\n hasCapability: (key: keyof ServerCapabilities) => boolean\n} {\n const scoped = useContext(ServerCapabilitiesContext)\n const handle = scoped ?? defaultStore\n return {\n info: handle.info,\n capabilities: handle.capabilities,\n serverInfo: handle.serverInfo,\n protocolVersion: handle.protocolVersion,\n hasCapability: handle.hasCapability,\n }\n}\n"],"names":["createServerCapabilitiesStore","state","setState","createStore","info","set","capabilities","serverInfo","protocolVersion","hasCapability","key","Boolean","defaultStore","setServerCapabilities","ServerCapabilitiesContext","createContext","undefined","ServerCapabilitiesProvider","props","store","_$createComponent","Provider","value","children","useServerCapabilities","scoped","useContext","handle"],"mappings":";;;AAuGO,SAASA,gCAA+D;AAC7E,QAAM,CAACC,OAAOC,QAAQ,IAAIC,YAAmD;AAAA,IAAEC,MAAM;AAAA,EAAA,CAAM;AAE3F,SAAO;AAAA,IACLC,KAAMD,CAAAA,SAASF,SAAS,QAAQE,IAAI;AAAA,IACpCA,MAAMA,MAAMH,MAAMG;AAAAA,IAClBE,cAAcA,MAAAA;;AAAML,0BAAMG,SAANH,mBAAYK,iBAAgB;AAAA;AAAA,IAChDC,YAAYA,MAAAA;;AAAMN,0BAAMG,SAANH,mBAAYM,eAAc;AAAA;AAAA,IAC5CC,iBAAiBA,MAAAA;;AAAMP,0BAAMG,SAANH,mBAAYO,oBAAmB;AAAA;AAAA,IACtDC,eAAgBC,CAAAA;;AAAQC,sBAAQV,iBAAMG,SAANH,mBAAYK,iBAAZL,mBAA2BS,IAAI;AAAA;AAAA,EAAA;AAEnE;AAIA,MAAME,eAA8CZ,8BAAAA;AAkB7C,SAASa,sBAAsBT,MAAyC;AAC7EQ,eAAaP,IAAID,IAAI;AACvB;AAYO,MAAMU,4BAA4BC,cACvCC,MACF;AAcO,MAAMC,6BAERA,CAACC,UAAuB;AAC3B,QAAMC,QAAQD,MAAMC,SAASnB,8BAAAA;AAC7B,SAAAoB,gBACGN,0BAA0BO,UAAQ;AAAA,IAACC,OAAOH;AAAAA,IAAK,IAAAI,WAAA;AAAA,aAC7CL,MAAMK;AAAAA,IAAQ;AAAA,EAAA,CAAA;AAGrB;AAuBO,SAASC,wBAMd;AACA,QAAMC,SAASC,WAAWZ,yBAAyB;AACnD,QAAMa,SAASF,UAAUb;AACzB,SAAO;AAAA,IACLR,MAAMuB,OAAOvB;AAAAA,IACbE,cAAcqB,OAAOrB;AAAAA,IACrBC,YAAYoB,OAAOpB;AAAAA,IACnBC,iBAAiBmB,OAAOnB;AAAAA,IACxBC,eAAekB,OAAOlB;AAAAA,EAAAA;AAE1B;"}
@@ -0,0 +1,171 @@
1
+ # Recipe — Pseudo-elicit → spec elicit adapter
2
+
3
+ > **Audience** : consumer chat apps that talk to an MCP server still emitting
4
+ > a *legacy* "pseudo-elicit" payload inline with `tools/call` results, but
5
+ > wanting to use mcp-ui's spec-correct `<ChatPrompt>` / `<ElicitationForm>`
6
+ > (MCP 2025-06-18).
7
+ >
8
+ > **Where this code lives** : in YOUR consumer app, not in mcp-ui. mcp-ui
9
+ > stays tool- and server-agnostic by design — it ships the spec helper
10
+ > (`elicitationToPromptConfig`, `<ElicitationForm>`) but does NOT bake in
11
+ > any vendor-specific wire shape.
12
+
13
+ ## Why this exists
14
+
15
+ The MCP spec 2025-06-18 defines elicitation as a server→client JSON-RPC
16
+ *request* (`elicitation/create`) carrying `{ message, requestedSchema }`,
17
+ with `requestedSchema` shaped as a JSON Schema object.
18
+
19
+ Some servers ship a different convention — they return an `elicitation`
20
+ **object inline** in the result of a `tools/call`, with a flat `fields[]`
21
+ array instead of a JSON Schema. Example (deposium_MCPs as of 2026-04) :
22
+
23
+ ```json
24
+ {
25
+ "jsonrpc": "2.0",
26
+ "id": 1,
27
+ "result": {
28
+ "elicitation": {
29
+ "type": "form",
30
+ "title": "Required Parameters Missing",
31
+ "description": "Please provide the following required parameters:",
32
+ "fields": [
33
+ { "name": "tenant_id", "type": "string", "label": "Tenant ID", "required": true, "default": "<uuid>" },
34
+ { "name": "space_id", "type": "string", "label": "Space ID", "required": true, "default": "default" }
35
+ ]
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ Until that server migrates to spec elicitation, the chat app can adapt the
42
+ shape on the fly, then drive `<ElicitationForm>` (or
43
+ `elicitationToPromptConfig`) as if everything were spec.
44
+
45
+ ## The adapter (drop-in TypeScript)
46
+
47
+ ```ts
48
+ import type { ElicitationEvent, ElicitationPropertySchema } from '@seed-ship/mcp-ui-solid'
49
+
50
+ interface PseudoElicit {
51
+ type: 'form'
52
+ title: string
53
+ description?: string
54
+ fields: Array<{
55
+ name: string
56
+ type: 'string' | 'number' | 'boolean'
57
+ label?: string
58
+ description?: string
59
+ required?: boolean
60
+ default?: unknown
61
+ enum?: Array<string | number>
62
+ }>
63
+ }
64
+
65
+ /**
66
+ * Convert a pseudo-elicit payload (legacy inline form spec) to a spec-shaped
67
+ * MCP `ElicitationEvent`. Returns `null` if the input does not look like a
68
+ * pseudo-elicit — the caller can then handle the tools/call result normally.
69
+ */
70
+ export function pseudoElicitToSpec(toolResult: unknown): ElicitationEvent | null {
71
+ const pseudo = (toolResult as { elicitation?: PseudoElicit })?.elicitation
72
+ if (!pseudo || pseudo.type !== 'form' || !Array.isArray(pseudo.fields)) {
73
+ return null
74
+ }
75
+
76
+ const properties: Record<string, ElicitationPropertySchema> = {}
77
+ const required: string[] = []
78
+
79
+ for (const field of pseudo.fields) {
80
+ const schema: ElicitationPropertySchema = {
81
+ type: mapType(field.type),
82
+ ...(field.label !== undefined && { title: field.label }),
83
+ ...(field.description !== undefined && { description: field.description }),
84
+ ...(field.default !== undefined && { default: field.default }),
85
+ ...(field.enum && { enum: field.enum }),
86
+ }
87
+ properties[field.name] = schema
88
+ if (field.required) required.push(field.name)
89
+ }
90
+
91
+ return {
92
+ message: [pseudo.title, pseudo.description].filter(Boolean).join(' — '),
93
+ requestedSchema: {
94
+ type: 'object',
95
+ properties,
96
+ ...(required.length > 0 && { required }),
97
+ },
98
+ }
99
+ }
100
+
101
+ function mapType(t: string): ElicitationPropertySchema['type'] {
102
+ if (t === 'number' || t === 'boolean') return t
103
+ return 'string' // safe fallback for unknown legacy types
104
+ }
105
+ ```
106
+
107
+ ## Wiring it into the chat app
108
+
109
+ ```ts
110
+ import { bus } from './your-bus-instance'
111
+ import { ElicitationForm } from '@seed-ship/mcp-ui-solid'
112
+ import { pseudoElicitToSpec } from './adapters/pseudo-elicit'
113
+
114
+ async function callTool(name: string, args: Record<string, unknown>) {
115
+ const response = await mcpClient.callTool(name, args)
116
+
117
+ // 1. Check for pseudo-elicit BEFORE treating result as a normal tool output.
118
+ const elicit = pseudoElicitToSpec(response.result)
119
+ if (elicit) {
120
+ showElicitationDialog(elicit, async (content) => {
121
+ // 2. Re-invoke the tool with the collected args merged in.
122
+ return callTool(name, { ...args, ...content })
123
+ })
124
+ return
125
+ }
126
+
127
+ // 3. Normal tool output path.
128
+ handleToolResult(response.result)
129
+ }
130
+
131
+ function showElicitationDialog(
132
+ event: ElicitationEvent,
133
+ onAccept: (content: Record<string, unknown>) => Promise<void>
134
+ ) {
135
+ // Mount <ElicitationForm> in your modal layer, or pipe through the bus :
136
+ bus.events.emit('onElicitation', { streamKey: 'main', elicitation: event })
137
+ }
138
+ ```
139
+
140
+ If you also want `<ElicitationForm>` to render directly :
141
+
142
+ ```tsx
143
+ <Show when={pendingElicit()}>
144
+ <ElicitationForm
145
+ event={pendingElicit()!}
146
+ onAccept={async (content) => {
147
+ setPendingElicit(null)
148
+ await retryToolCall(content)
149
+ }}
150
+ onCancel={() => setPendingElicit(null)}
151
+ />
152
+ </Show>
153
+ ```
154
+
155
+ ## Going both ways (spec + pseudo)
156
+
157
+ Once your server migrates to real `elicitation/create` (server→client
158
+ JSON-RPC request over a bidirectional transport), keep the adapter
159
+ in place — it's harmless on a normal `tools/call` result (returns
160
+ `null`) and lets you support both wire shapes for the duration of the
161
+ rollout.
162
+
163
+ For the spec path, your transport adapter handles the JSON-RPC request
164
+ directly and emits the same `onElicitation` event with a payload that
165
+ already matches `ElicitationEvent` — no adapter call needed.
166
+
167
+ ## Reference
168
+
169
+ - MCP spec : https://spec.modelcontextprotocol.io/specification/2025-06-18/client/elicitation/
170
+ - mcp-ui types : `ElicitationEvent`, `ElicitationRequestedSchema`, `ElicitationPropertySchema` (all exported from `@seed-ship/mcp-ui-solid`)
171
+ - mcp-ui helpers : `elicitationToPromptConfig` (services/chat-bus), `<ElicitationForm>` (components)
@@ -0,0 +1,142 @@
1
+ # Recipe — Wire `<FeedbackInline>` to a feedback HTTP endpoint
2
+
3
+ > **Audience** : consumer apps that ship `<FeedbackInline>` (per-message
4
+ > thumbs-up/down) and want to persist ratings to a backend.
5
+ >
6
+ > mcp-ui's `<FeedbackInline>` is intentionally endpoint-agnostic — it
7
+ > calls `onSubmit(rating, context)` and the consumer owns the HTTP / store
8
+ > wiring. This recipe shows the most common pattern, using the Deposium
9
+ > `POST /api/feedback` endpoint as a concrete example.
10
+
11
+ ## What `<FeedbackInline>` gives you
12
+
13
+ ```tsx
14
+ <FeedbackInline
15
+ messageHash={msg.hash}
16
+ context={{ intent: msg.intent, confidenceBand: msg.band }}
17
+ onSubmit={(rating, context) => persistFeedback(rating, context)}
18
+ />
19
+ ```
20
+
21
+ The component :
22
+ - Renders two buttons (positive / negative).
23
+ - Flips to "submitted" optimistically on click — UI does NOT revert on network error (best-effort design).
24
+ - Calls `onSubmit('positive' | 'negative', context?)` exactly once.
25
+
26
+ `rating` already matches the shape Deposium expects. Mapping is direct.
27
+
28
+ ## Endpoint reference (Deposium)
29
+
30
+ `POST /api/feedback` — no auth required, behind the chat-stream / standard
31
+ middleware chain.
32
+
33
+ ### Request body
34
+
35
+ ```ts
36
+ interface FeedbackRequest {
37
+ message_hash: string // REQUIRED — message ID being rated
38
+ rating: 'positive' | 'negative' | 'partial'
39
+ confidence_band?: 'high' | 'medium' | 'low' // optional, free-form string
40
+ intent?: string // optional, e.g. 'search_query'
41
+ space_ids?: string[] | string | null
42
+ comment?: string
43
+ tenant_id?: string
44
+ }
45
+ ```
46
+
47
+ ### Response
48
+
49
+ | Status | Body |
50
+ |---|---|
51
+ | 200 | `{ ok: true, id: 'fb_<timestamp>_<rand4>' }` |
52
+ | 400 | `{ error: 'rating must be one of: positive, negative, partial' }` |
53
+
54
+ ### Side effects (worth knowing)
55
+
56
+ - `INSERT` into `logs.feedback` (PostgreSQL) — drives dashboard analytics.
57
+ - `'positive' | 'negative'` ratings also update
58
+ `logs.intent_classifications.feedback_success`. `'partial'` does **not**
59
+ propagate (intentional — neither true nor false).
60
+
61
+ ## Wiring (the recipe)
62
+
63
+ ```tsx
64
+ import { FeedbackInline, type FeedbackInlineContext } from '@seed-ship/mcp-ui-solid'
65
+
66
+ function persistFeedback(
67
+ messageHash: string,
68
+ rating: 'positive' | 'negative',
69
+ ctx?: FeedbackInlineContext
70
+ ): Promise<void> {
71
+ return fetch('/api/feedback', {
72
+ method: 'POST',
73
+ headers: { 'Content-Type': 'application/json' },
74
+ body: JSON.stringify({
75
+ message_hash: messageHash,
76
+ rating, // 'positive' | 'negative' — matches endpoint as-is
77
+ ...(ctx?.intent && { intent: ctx.intent }),
78
+ ...(ctx?.confidenceBand && { confidence_band: ctx.confidenceBand }),
79
+ ...(ctx?.tenantId && { tenant_id: ctx.tenantId }),
80
+ ...(ctx?.spaceIds && { space_ids: ctx.spaceIds }),
81
+ ...(ctx?.comment && { comment: ctx.comment }),
82
+ }),
83
+ }).then(async (res) => {
84
+ if (!res.ok) {
85
+ console.warn('[feedback] persist failed', res.status, await res.text())
86
+ }
87
+ }).catch((err) => {
88
+ // Silent failure — UI is already in the optimistic "submitted" state.
89
+ console.warn('[feedback] network error', err)
90
+ })
91
+ }
92
+
93
+ function MessageRow(props: { msg: ChatMessage }) {
94
+ return (
95
+ <div class="message-row">
96
+ <p>{props.msg.text}</p>
97
+ <FeedbackInline
98
+ messageHash={props.msg.hash}
99
+ context={{
100
+ intent: props.msg.intent,
101
+ confidenceBand: props.msg.confidenceBand,
102
+ }}
103
+ onSubmit={(rating, ctx) => persistFeedback(props.msg.hash, rating, ctx)}
104
+ />
105
+ </div>
106
+ )
107
+ }
108
+ ```
109
+
110
+ ## Variations
111
+
112
+ ### "Partial" rating
113
+
114
+ `<FeedbackInline>` emits only `'positive'` / `'negative'`. If you need a
115
+ third state (`'partial'`), build a separate UI (e.g. a star rating or a
116
+ 3-button row) and call the endpoint directly with `rating: 'partial'`.
117
+
118
+ ### Free-text comment
119
+
120
+ Add a textarea below `<FeedbackInline>` that opens after the rating click.
121
+ Send a follow-up `POST /api/feedback` with the same `message_hash` and a
122
+ `comment` field — the endpoint accepts multiple records per message.
123
+
124
+ ### Optimistic vs strict semantics
125
+
126
+ Default behavior is best-effort (UI never reverts). If you need stricter
127
+ semantics — offline retry queue, edit-rating UX — wrap `<FeedbackInline>`
128
+ in your own component and own the state externally instead of relying on
129
+ the component's internal flip.
130
+
131
+ ## Where this code lives
132
+
133
+ In your consumer app. mcp-ui ships `<FeedbackInline>` and the `onSubmit`
134
+ contract; the HTTP wiring (URL, auth, retry policy, schema mapping) is
135
+ the consumer's responsibility by design — same pattern as
136
+ `pseudo-elicit-spec-adapter`.
137
+
138
+ ## Reference
139
+
140
+ - mcp-ui component : `<FeedbackInline>` (exported from `@seed-ship/mcp-ui-solid`)
141
+ - mcp-ui types : `FeedbackInlineProps`, `FeedbackInlineContext`
142
+ - Deposium endpoint : `POST /api/feedback` — see deposium_MCPs `src/routes/feedback.ts`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seed-ship/mcp-ui-solid",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "description": "SolidJS components for rendering MCP-generated UI resources",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",