@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,215 @@
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
+
42
+ import { createContext, useContext, type ParentComponent, type JSX } from 'solid-js'
43
+ import { createStore } from 'solid-js/store'
44
+
45
+ // ─── Types ────────────────────────────────────────────────────
46
+
47
+ /**
48
+ * Server capabilities object as advertised in the MCP `initialize` response.
49
+ * Mirrors the spec 2025-06-18 `ServerCapabilities` shape with permissive
50
+ * `experimental` for forward compatibility.
51
+ */
52
+ export interface ServerCapabilities {
53
+ experimental?: Record<string, unknown>
54
+ logging?: Record<string, never>
55
+ tools?: { listChanged?: boolean }
56
+ prompts?: { listChanged?: boolean }
57
+ resources?: { listChanged?: boolean; subscribe?: boolean }
58
+ completions?: Record<string, never>
59
+ }
60
+
61
+ /**
62
+ * Subset of the MCP `initialize` response relevant to the UI layer.
63
+ * Consumers may extend this via the `experimental` field.
64
+ */
65
+ export interface ServerInitializeInfo {
66
+ protocolVersion: string
67
+ serverInfo: { name: string; version: string; title?: string; [key: string]: unknown }
68
+ capabilities: ServerCapabilities
69
+ instructions?: string
70
+ }
71
+
72
+ // ─── Handle ───────────────────────────────────────────────────
73
+
74
+ export interface ServerCapabilitiesStoreHandle {
75
+ /** Push a fresh `initialize` snapshot into the store, or clear with `null`. */
76
+ set: (info: ServerInitializeInfo | null) => void
77
+ /** Reactive accessor for the full info (null when no initialize received). */
78
+ info: () => ServerInitializeInfo | null
79
+ /** Reactive accessor for just the `capabilities` field. */
80
+ capabilities: () => ServerCapabilities | null
81
+ /** Reactive accessor for just the `serverInfo` field. */
82
+ serverInfo: () => ServerInitializeInfo['serverInfo'] | null
83
+ /** Reactive accessor for the protocol version string. */
84
+ protocolVersion: () => string | null
85
+ /**
86
+ * Helper : returns true if the server advertised the named capability key
87
+ * with a truthy value (i.e. the key is present, even as an empty object).
88
+ */
89
+ hasCapability: (key: keyof ServerCapabilities) => boolean
90
+ }
91
+
92
+ // ─── Factory ──────────────────────────────────────────────────
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 function createServerCapabilitiesStore(): ServerCapabilitiesStoreHandle {
105
+ const [state, setState] = createStore<{ info: ServerInitializeInfo | null }>({ info: null })
106
+
107
+ return {
108
+ set: (info) => setState('info', info),
109
+ info: () => state.info,
110
+ capabilities: () => state.info?.capabilities ?? null,
111
+ serverInfo: () => state.info?.serverInfo ?? null,
112
+ protocolVersion: () => state.info?.protocolVersion ?? null,
113
+ hasCapability: (key) => Boolean(state.info?.capabilities?.[key]),
114
+ }
115
+ }
116
+
117
+ // ─── Module-level singleton ───────────────────────────────────
118
+
119
+ const defaultStore: ServerCapabilitiesStoreHandle = createServerCapabilitiesStore()
120
+
121
+ /**
122
+ * Push the parsed MCP `initialize` response into the module-level singleton
123
+ * store. Pass `null` to clear (e.g. on disconnect / server change).
124
+ *
125
+ * @experimental
126
+ * @since v5.3.0
127
+ *
128
+ * @example
129
+ * // In your transport adapter, after receiving the initialize response :
130
+ * setServerCapabilities({
131
+ * protocolVersion: response.result.protocolVersion,
132
+ * serverInfo: response.result.serverInfo,
133
+ * capabilities: response.result.capabilities,
134
+ * instructions: response.result.instructions,
135
+ * })
136
+ */
137
+ export function setServerCapabilities(info: ServerInitializeInfo | null): void {
138
+ defaultStore.set(info)
139
+ }
140
+
141
+ // ─── Context ──────────────────────────────────────────────────
142
+
143
+ /**
144
+ * Context for a scoped server-capabilities store. Populated by
145
+ * `<ServerCapabilitiesProvider>`. Read by `useServerCapabilities()` with
146
+ * automatic fallback to the module-level singleton when absent.
147
+ *
148
+ * @experimental
149
+ * @since v5.3.0
150
+ */
151
+ export const ServerCapabilitiesContext = createContext<ServerCapabilitiesStoreHandle | undefined>(
152
+ undefined
153
+ )
154
+
155
+ /**
156
+ * Provide a scoped `ServerCapabilitiesStoreHandle` to a SolidJS subtree.
157
+ * Children calling `useServerCapabilities()` bind to this store instead of
158
+ * the module singleton.
159
+ *
160
+ * If no `store` prop is passed, a fresh store is created for the provider's
161
+ * lifetime. Pass `store` explicitly when you need the handle outside the
162
+ * tree (e.g. in a transport adapter living at the app root).
163
+ *
164
+ * @experimental
165
+ * @since v5.3.0
166
+ */
167
+ export const ServerCapabilitiesProvider: ParentComponent<{
168
+ store?: ServerCapabilitiesStoreHandle
169
+ }> = (props): JSX.Element => {
170
+ const store = props.store ?? createServerCapabilitiesStore()
171
+ return (
172
+ <ServerCapabilitiesContext.Provider value={store}>
173
+ {props.children}
174
+ </ServerCapabilitiesContext.Provider>
175
+ )
176
+ }
177
+
178
+ // ─── Reactive hook ────────────────────────────────────────────
179
+
180
+ /**
181
+ * Hook for components — reads the server capabilities reactively.
182
+ *
183
+ * If called inside a `<ServerCapabilitiesProvider>`, reads the scoped
184
+ * handle; otherwise falls back to the module singleton.
185
+ *
186
+ * @experimental
187
+ * @since v5.3.0
188
+ *
189
+ * @example
190
+ * const { capabilities, serverInfo, hasCapability } = useServerCapabilities()
191
+ *
192
+ * <Show when={capabilities()}>
193
+ * <p>Connected to {serverInfo()?.name} v{serverInfo()?.version}</p>
194
+ * <Show when={hasCapability('tools')}>
195
+ * <ToolPalette />
196
+ * </Show>
197
+ * </Show>
198
+ */
199
+ export function useServerCapabilities(): {
200
+ info: () => ServerInitializeInfo | null
201
+ capabilities: () => ServerCapabilities | null
202
+ serverInfo: () => ServerInitializeInfo['serverInfo'] | null
203
+ protocolVersion: () => string | null
204
+ hasCapability: (key: keyof ServerCapabilities) => boolean
205
+ } {
206
+ const scoped = useContext(ServerCapabilitiesContext)
207
+ const handle = scoped ?? defaultStore
208
+ return {
209
+ info: handle.info,
210
+ capabilities: handle.capabilities,
211
+ serverInfo: handle.serverInfo,
212
+ protocolVersion: handle.protocolVersion,
213
+ hasCapability: handle.hasCapability,
214
+ }
215
+ }