@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.
- package/CHANGELOG.md +43 -0
- package/dist/components/ElicitationForm.cjs +51 -0
- package/dist/components/ElicitationForm.cjs.map +1 -0
- package/dist/components/ElicitationForm.d.ts +68 -0
- package/dist/components/ElicitationForm.d.ts.map +1 -0
- package/dist/components/ElicitationForm.js +51 -0
- package/dist/components/ElicitationForm.js.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components.cjs +2 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +2 -0
- package/dist/components.d.ts +2 -0
- package/dist/components.js +2 -0
- package/dist/components.js.map +1 -1
- package/dist/index.cjs +8 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/stores/server-capabilities-store.cjs +61 -0
- package/dist/stores/server-capabilities-store.cjs.map +1 -0
- package/dist/stores/server-capabilities-store.d.ts +172 -0
- package/dist/stores/server-capabilities-store.d.ts.map +1 -0
- package/dist/stores/server-capabilities-store.js +61 -0
- package/dist/stores/server-capabilities-store.js.map +1 -0
- package/docs/recipes/elicitation-pseudo-spec-adapter.md +171 -0
- package/docs/recipes/feedback-inline-wiring.md +142 -0
- package/package.json +1 -1
- package/src/components/ElicitationForm.test.tsx +197 -0
- package/src/components/ElicitationForm.tsx +126 -0
- package/src/components/index.ts +4 -0
- package/src/index.ts +16 -0
- package/src/stores/server-capabilities-store.test.tsx +206 -0
- package/src/stores/server-capabilities-store.tsx +215 -0
- 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
|
+
}
|