@seed-ship/mcp-ui-solid 5.1.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 +97 -0
- package/README.md +64 -13
- 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/FeedbackInline.cjs +57 -0
- package/dist/components/FeedbackInline.cjs.map +1 -0
- package/dist/components/FeedbackInline.d.ts +71 -0
- package/dist/components/FeedbackInline.d.ts.map +1 -0
- package/dist/components/FeedbackInline.js +57 -0
- package/dist/components/FeedbackInline.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 +17 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/dist/services/chat-bus.cjs +71 -0
- package/dist/services/chat-bus.cjs.map +1 -1
- package/dist/services/chat-bus.d.ts +31 -1
- package/dist/services/chat-bus.d.ts.map +1 -1
- package/dist/services/chat-bus.js +71 -0
- package/dist/services/chat-bus.js.map +1 -1
- package/dist/services/chat-prompt-controller.cjs +83 -0
- package/dist/services/chat-prompt-controller.cjs.map +1 -0
- package/dist/services/chat-prompt-controller.d.ts +93 -0
- package/dist/services/chat-prompt-controller.d.ts.map +1 -0
- package/dist/services/chat-prompt-controller.js +83 -0
- package/dist/services/chat-prompt-controller.js.map +1 -0
- package/dist/stores/scratchpad-store.cjs +105 -77
- package/dist/stores/scratchpad-store.cjs.map +1 -1
- package/dist/stores/scratchpad-store.d.ts +88 -19
- package/dist/stores/scratchpad-store.d.ts.map +1 -1
- package/dist/stores/scratchpad-store.js +105 -77
- package/dist/stores/scratchpad-store.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/dist/types/chat-bus.d.ts +39 -0
- package/dist/types/chat-bus.d.ts.map +1 -1
- 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/FeedbackInline.test.tsx +117 -0
- package/src/components/FeedbackInline.tsx +143 -0
- package/src/components/index.ts +4 -0
- package/src/index.ts +39 -1
- package/src/services/chat-bus.test.ts +154 -2
- package/src/services/chat-bus.ts +115 -0
- package/src/services/chat-prompt-controller.test.ts +144 -0
- package/src/services/chat-prompt-controller.ts +214 -0
- package/src/stores/scratchpad-store.test.tsx +140 -0
- package/src/stores/scratchpad-store.tsx +244 -0
- package/src/stores/server-capabilities-store.test.tsx +206 -0
- package/src/stores/server-capabilities-store.tsx +215 -0
- package/src/types/chat-bus.ts +40 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/src/stores/scratchpad-store.ts +0 -126
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for server-capabilities-store — v5.3.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
6
|
+
import { render, cleanup } from '@solidjs/testing-library'
|
|
7
|
+
import { createEffect, createRoot } from 'solid-js'
|
|
8
|
+
import {
|
|
9
|
+
createServerCapabilitiesStore,
|
|
10
|
+
setServerCapabilities,
|
|
11
|
+
useServerCapabilities,
|
|
12
|
+
ServerCapabilitiesProvider,
|
|
13
|
+
} from './server-capabilities-store'
|
|
14
|
+
import type { ServerInitializeInfo } from './server-capabilities-store'
|
|
15
|
+
|
|
16
|
+
const sampleInfo: ServerInitializeInfo = {
|
|
17
|
+
protocolVersion: '2025-06-18',
|
|
18
|
+
serverInfo: { name: 'deposium-mcp', version: '1.4.2' },
|
|
19
|
+
capabilities: {
|
|
20
|
+
tools: { listChanged: true },
|
|
21
|
+
prompts: { listChanged: false },
|
|
22
|
+
resources: { listChanged: true, subscribe: false },
|
|
23
|
+
},
|
|
24
|
+
instructions: 'Use deposium_chat for synthesis questions.',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('createServerCapabilitiesStore — v5.3.0', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
cleanup()
|
|
30
|
+
setServerCapabilities(null) // reset module singleton between tests
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('two factory stores do not share state', () => {
|
|
34
|
+
const storeA = createServerCapabilitiesStore()
|
|
35
|
+
const storeB = createServerCapabilitiesStore()
|
|
36
|
+
|
|
37
|
+
storeA.set(sampleInfo)
|
|
38
|
+
|
|
39
|
+
expect(storeA.info()?.serverInfo.name).toBe('deposium-mcp')
|
|
40
|
+
expect(storeB.info()).toBeNull()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('accessors return null until set is called', () => {
|
|
44
|
+
const store = createServerCapabilitiesStore()
|
|
45
|
+
|
|
46
|
+
expect(store.info()).toBeNull()
|
|
47
|
+
expect(store.capabilities()).toBeNull()
|
|
48
|
+
expect(store.serverInfo()).toBeNull()
|
|
49
|
+
expect(store.protocolVersion()).toBeNull()
|
|
50
|
+
expect(store.hasCapability('tools')).toBe(false)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('set(info) populates all derived accessors', () => {
|
|
54
|
+
const store = createServerCapabilitiesStore()
|
|
55
|
+
store.set(sampleInfo)
|
|
56
|
+
|
|
57
|
+
expect(store.protocolVersion()).toBe('2025-06-18')
|
|
58
|
+
expect(store.serverInfo()?.version).toBe('1.4.2')
|
|
59
|
+
expect(store.capabilities()?.tools?.listChanged).toBe(true)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('set(null) clears the store', () => {
|
|
63
|
+
const store = createServerCapabilitiesStore()
|
|
64
|
+
store.set(sampleInfo)
|
|
65
|
+
expect(store.info()).not.toBeNull()
|
|
66
|
+
|
|
67
|
+
store.set(null)
|
|
68
|
+
expect(store.info()).toBeNull()
|
|
69
|
+
expect(store.hasCapability('tools')).toBe(false)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('hasCapability returns true for present keys, false for absent', () => {
|
|
73
|
+
const store = createServerCapabilitiesStore()
|
|
74
|
+
store.set(sampleInfo)
|
|
75
|
+
|
|
76
|
+
expect(store.hasCapability('tools')).toBe(true)
|
|
77
|
+
expect(store.hasCapability('prompts')).toBe(true)
|
|
78
|
+
expect(store.hasCapability('resources')).toBe(true)
|
|
79
|
+
expect(store.hasCapability('logging')).toBe(false)
|
|
80
|
+
expect(store.hasCapability('completions')).toBe(false)
|
|
81
|
+
expect(store.hasCapability('experimental')).toBe(false)
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('setServerCapabilities + useServerCapabilities (singleton path)', () => {
|
|
86
|
+
beforeEach(() => {
|
|
87
|
+
cleanup()
|
|
88
|
+
setServerCapabilities(null)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('useServerCapabilities falls back to module singleton outside provider', () => {
|
|
92
|
+
setServerCapabilities(sampleInfo)
|
|
93
|
+
|
|
94
|
+
let captured: ReturnType<typeof useServerCapabilities> | null = null
|
|
95
|
+
const Probe = () => {
|
|
96
|
+
captured = useServerCapabilities()
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
render(() => <Probe />)
|
|
100
|
+
|
|
101
|
+
expect(captured).not.toBeNull()
|
|
102
|
+
expect(captured!.serverInfo()?.name).toBe('deposium-mcp')
|
|
103
|
+
expect(captured!.hasCapability('tools')).toBe(true)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('singleton state survives across renders', () => {
|
|
107
|
+
setServerCapabilities(sampleInfo)
|
|
108
|
+
|
|
109
|
+
const captures: Array<string | undefined> = []
|
|
110
|
+
const Probe = () => {
|
|
111
|
+
const { serverInfo } = useServerCapabilities()
|
|
112
|
+
captures.push(serverInfo()?.name)
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
render(() => <Probe />)
|
|
116
|
+
render(() => <Probe />)
|
|
117
|
+
|
|
118
|
+
expect(captures).toEqual(['deposium-mcp', 'deposium-mcp'])
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
describe('ServerCapabilitiesProvider scoping', () => {
|
|
123
|
+
beforeEach(() => {
|
|
124
|
+
cleanup()
|
|
125
|
+
setServerCapabilities(null)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('provider with explicit store overrides the singleton for descendants', () => {
|
|
129
|
+
setServerCapabilities(sampleInfo) // singleton has deposium-mcp
|
|
130
|
+
|
|
131
|
+
const scopedStore = createServerCapabilitiesStore()
|
|
132
|
+
scopedStore.set({
|
|
133
|
+
...sampleInfo,
|
|
134
|
+
serverInfo: { name: 'other-mcp', version: '0.1.0' },
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
let inside: string | undefined
|
|
138
|
+
let outside: string | undefined
|
|
139
|
+
|
|
140
|
+
const Inside = () => {
|
|
141
|
+
inside = useServerCapabilities().serverInfo()?.name
|
|
142
|
+
return null
|
|
143
|
+
}
|
|
144
|
+
const Outside = () => {
|
|
145
|
+
outside = useServerCapabilities().serverInfo()?.name
|
|
146
|
+
return null
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
render(() => (
|
|
150
|
+
<>
|
|
151
|
+
<Outside />
|
|
152
|
+
<ServerCapabilitiesProvider store={scopedStore}>
|
|
153
|
+
<Inside />
|
|
154
|
+
</ServerCapabilitiesProvider>
|
|
155
|
+
</>
|
|
156
|
+
))
|
|
157
|
+
|
|
158
|
+
expect(outside).toBe('deposium-mcp')
|
|
159
|
+
expect(inside).toBe('other-mcp')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('provider without store creates a fresh isolated handle', () => {
|
|
163
|
+
setServerCapabilities(sampleInfo)
|
|
164
|
+
|
|
165
|
+
let inside: ReturnType<typeof useServerCapabilities> | null = null
|
|
166
|
+
const Inside = () => {
|
|
167
|
+
inside = useServerCapabilities()
|
|
168
|
+
return null
|
|
169
|
+
}
|
|
170
|
+
render(() => (
|
|
171
|
+
<ServerCapabilitiesProvider>
|
|
172
|
+
<Inside />
|
|
173
|
+
</ServerCapabilitiesProvider>
|
|
174
|
+
))
|
|
175
|
+
|
|
176
|
+
expect(inside).not.toBeNull()
|
|
177
|
+
expect(inside!.info()).toBeNull() // fresh, not the singleton's state
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('set() updates trigger reactive effects', () => {
|
|
181
|
+
const store = createServerCapabilitiesStore()
|
|
182
|
+
const captures: Array<string | null> = []
|
|
183
|
+
|
|
184
|
+
let dispose: (() => void) | undefined
|
|
185
|
+
createRoot((d) => {
|
|
186
|
+
dispose = d
|
|
187
|
+
createEffect(() => {
|
|
188
|
+
captures.push(store.serverInfo()?.name ?? null)
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
// Initial effect run
|
|
193
|
+
expect(captures).toEqual([null])
|
|
194
|
+
|
|
195
|
+
store.set(sampleInfo)
|
|
196
|
+
expect(captures).toEqual([null, 'deposium-mcp'])
|
|
197
|
+
|
|
198
|
+
store.set({ ...sampleInfo, serverInfo: { name: 'renamed', version: '2.0.0' } })
|
|
199
|
+
expect(captures).toEqual([null, 'deposium-mcp', 'renamed'])
|
|
200
|
+
|
|
201
|
+
store.set(null)
|
|
202
|
+
expect(captures).toEqual([null, 'deposium-mcp', 'renamed', null])
|
|
203
|
+
|
|
204
|
+
dispose?.()
|
|
205
|
+
})
|
|
206
|
+
})
|
|
@@ -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
|
+
}
|
package/src/types/chat-bus.ts
CHANGED
|
@@ -53,6 +53,7 @@ export interface ChatEvents {
|
|
|
53
53
|
// --- Interactions ---
|
|
54
54
|
onChatPromptResponse: (event: ChatEventBase & { response: ChatPromptResponse }) => void
|
|
55
55
|
onClarificationNeeded: (event: ChatEventBase & { clarification: ClarificationEvent }) => void
|
|
56
|
+
onElicitation: (event: ChatEventBase & { elicitation: ElicitationEvent }) => void
|
|
56
57
|
|
|
57
58
|
// --- Agentic (handled by app, not MCP-UI) ---
|
|
58
59
|
onAgentSwitch: (event: ChatEventBase & { agent: AgentContext }) => void
|
|
@@ -568,6 +569,45 @@ export interface ToolCallEvent {
|
|
|
568
569
|
duration_ms?: number
|
|
569
570
|
}
|
|
570
571
|
|
|
572
|
+
/**
|
|
573
|
+
* MCP `elicitation/create` request payload — server asks the client to
|
|
574
|
+
* collect input from the user according to a JSON Schema.
|
|
575
|
+
*
|
|
576
|
+
* Derived from MCP spec 2025-06-18. See
|
|
577
|
+
* `elicitationToPromptConfig()` in `services/chat-bus.ts` for the
|
|
578
|
+
* helper that converts this to a `ChatPromptConfig`.
|
|
579
|
+
*
|
|
580
|
+
* @experimental
|
|
581
|
+
* @since v5.2.0
|
|
582
|
+
*/
|
|
583
|
+
export interface ElicitationEvent {
|
|
584
|
+
/** Question / instruction to present to the user. */
|
|
585
|
+
message: string
|
|
586
|
+
/** JSON Schema describing the expected response shape. Object with primitive properties only. */
|
|
587
|
+
requestedSchema: ElicitationRequestedSchema
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
export interface ElicitationRequestedSchema {
|
|
591
|
+
type: 'object'
|
|
592
|
+
properties: Record<string, ElicitationPropertySchema>
|
|
593
|
+
required?: string[]
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
export interface ElicitationPropertySchema {
|
|
597
|
+
type: 'string' | 'number' | 'integer' | 'boolean'
|
|
598
|
+
title?: string
|
|
599
|
+
description?: string
|
|
600
|
+
/** Enum of allowed values (strings or numbers). */
|
|
601
|
+
enum?: Array<string | number>
|
|
602
|
+
/** Parallel array with display labels for each enum entry. */
|
|
603
|
+
enumNames?: string[]
|
|
604
|
+
default?: unknown
|
|
605
|
+
minimum?: number
|
|
606
|
+
maximum?: number
|
|
607
|
+
/** String format hint — date, date-time, email, uri. */
|
|
608
|
+
format?: 'date' | 'date-time' | 'email' | 'uri'
|
|
609
|
+
}
|
|
610
|
+
|
|
571
611
|
export interface ClarificationEvent {
|
|
572
612
|
/** The question to ask the user */
|
|
573
613
|
question: string
|