@@ -54,9 +54,20 @@ interface HostProps {
5454type ToolCallEntry = ToolCallInfo & { id : number } ;
5555let nextToolCallId = 0 ;
5656
57+ // Parse URL query params for debugging: ?server=name&tool=name&call=true
58+ function getQueryParams ( ) {
59+ const params = new URLSearchParams ( window . location . search ) ;
60+ return {
61+ server : params . get ( "server" ) ,
62+ tool : params . get ( "tool" ) ,
63+ call : params . get ( "call" ) === "true" ,
64+ } ;
65+ }
66+
5767function Host ( { serversPromise } : HostProps ) {
5868 const [ toolCalls , setToolCalls ] = useState < ToolCallEntry [ ] > ( [ ] ) ;
5969 const [ destroyingIds , setDestroyingIds ] = useState < Set < number > > ( new Set ( ) ) ;
70+ const queryParams = useMemo ( ( ) => getQueryParams ( ) , [ ] ) ;
6071
6172 const requestClose = ( id : number ) => {
6273 setDestroyingIds ( ( s ) => new Set ( s ) . add ( id ) ) ;
@@ -85,6 +96,9 @@ function Host({ serversPromise }: HostProps) {
8596 < CallToolPanel
8697 serversPromise = { serversPromise }
8798 addToolCall = { ( info ) => setToolCalls ( [ ...toolCalls , { ...info , id : nextToolCallId ++ } ] ) }
99+ initialServer = { queryParams . server }
100+ initialTool = { queryParams . tool }
101+ autoCall = { queryParams . call }
88102 />
89103 </ >
90104 ) ;
@@ -95,11 +109,15 @@ function Host({ serversPromise }: HostProps) {
95109interface CallToolPanelProps {
96110 serversPromise : Promise < ServerInfo [ ] > ;
97111 addToolCall : ( info : ToolCallInfo ) => void ;
112+ initialServer ?: string | null ;
113+ initialTool ?: string | null ;
114+ autoCall ?: boolean ;
98115}
99- function CallToolPanel ( { serversPromise, addToolCall } : CallToolPanelProps ) {
116+ function CallToolPanel ( { serversPromise, addToolCall, initialServer , initialTool , autoCall } : CallToolPanelProps ) {
100117 const [ selectedServer , setSelectedServer ] = useState < ServerInfo | null > ( null ) ;
101118 const [ selectedTool , setSelectedTool ] = useState ( "" ) ;
102119 const [ inputJson , setInputJson ] = useState ( "{}" ) ;
120+ const [ hasAutoCalledRef ] = useState ( { called : false } ) ;
103121
104122 // Filter out app-only tools, prioritize tools with UIs
105123 const toolNames = selectedServer
@@ -118,16 +136,21 @@ function CallToolPanel({ serversPromise, addToolCall }: CallToolPanelProps) {
118136 }
119137 } , [ inputJson ] ) ;
120138
121- const handleServerSelect = ( server : ServerInfo ) => {
139+ const handleServerSelect = ( server : ServerInfo , preferredTool ?: string ) => {
122140 setSelectedServer ( server ) ;
123141 // Filter out app-only tools, prioritize tools with UIs
124142 const visibleTools = Array . from ( server . tools . values ( ) )
125143 . filter ( ( tool ) => isToolVisibleToModel ( tool ) )
126144 . sort ( compareTools ) ;
127- const firstTool = visibleTools [ 0 ] ?. name ?? "" ;
128- setSelectedTool ( firstTool ) ;
145+
146+ // Use preferred tool if it exists and is visible, otherwise first visible tool
147+ const targetTool = preferredTool && visibleTools . some ( t => t . name === preferredTool )
148+ ? preferredTool
149+ : visibleTools [ 0 ] ?. name ?? "" ;
150+
151+ setSelectedTool ( targetTool ) ;
129152 // Set input JSON to tool defaults (if any)
130- setInputJson ( getToolDefaults ( server . tools . get ( firstTool ) ) ) ;
153+ setInputJson ( getToolDefaults ( server . tools . get ( targetTool ) ) ) ;
131154 } ;
132155
133156 const handleToolSelect = ( toolName : string ) => {
@@ -136,10 +159,21 @@ function CallToolPanel({ serversPromise, addToolCall }: CallToolPanelProps) {
136159 setInputJson ( getToolDefaults ( selectedServer ?. tools . get ( toolName ) ) ) ;
137160 } ;
138161
139- const handleSubmit = ( ) => {
140- if ( ! selectedServer ) return ;
141- const toolCallInfo = callTool ( selectedServer , selectedTool , JSON . parse ( inputJson ) ) ;
162+ // Submit with optional override for server/tool (used by auto-call)
163+ const handleSubmit = ( overrideServer ?: ServerInfo , overrideTool ?: string ) => {
164+ const server = overrideServer ?? selectedServer ;
165+ const tool = overrideTool ?? selectedTool ;
166+ if ( ! server ) return ;
167+
168+ const toolCallInfo = callTool ( server , tool , JSON . parse ( inputJson ) ) ;
142169 addToolCall ( toolCallInfo ) ;
170+
171+ // Update URL for easy refresh/sharing (without triggering navigation)
172+ const url = new URL ( window . location . href ) ;
173+ url . searchParams . set ( "server" , server . name ) ;
174+ url . searchParams . set ( "tool" , tool ) ;
175+ url . searchParams . set ( "call" , "true" ) ; // Auto-call on refresh
176+ history . replaceState ( null , "" , url . toString ( ) ) ;
143177 } ;
144178
145179 return (
@@ -148,7 +182,17 @@ function CallToolPanel({ serversPromise, addToolCall }: CallToolPanelProps) {
148182 < label >
149183 Server
150184 < Suspense fallback = { < select disabled > < option > Loading...</ option > </ select > } >
151- < ServerSelect serversPromise = { serversPromise } onSelect = { handleServerSelect } />
185+ < ServerSelect
186+ serversPromise = { serversPromise }
187+ onSelect = { handleServerSelect }
188+ initialServer = { initialServer }
189+ initialTool = { initialTool }
190+ autoCall = { autoCall && ! hasAutoCalledRef . called }
191+ onAutoCall = { ( server , tool ) => {
192+ hasAutoCalledRef . called = true ;
193+ handleSubmit ( server , tool ) ;
194+ } }
195+ />
152196 </ Suspense >
153197 </ label >
154198 < label >
@@ -184,17 +228,47 @@ function CallToolPanel({ serversPromise, addToolCall }: CallToolPanelProps) {
184228// ServerSelect calls use() and renders the server <select>
185229interface ServerSelectProps {
186230 serversPromise : Promise < ServerInfo [ ] > ;
187- onSelect : ( server : ServerInfo ) => void ;
231+ onSelect : ( server : ServerInfo , toolName ?: string ) => void ;
232+ initialServer ?: string | null ;
233+ initialTool ?: string | null ;
234+ autoCall ?: boolean ;
235+ onAutoCall ?: ( server : ServerInfo , tool : string ) => void ;
188236}
189- function ServerSelect ( { serversPromise, onSelect } : ServerSelectProps ) {
237+ function ServerSelect ( { serversPromise, onSelect, initialServer , initialTool , autoCall , onAutoCall } : ServerSelectProps ) {
190238 const servers = use ( serversPromise ) ;
239+ const [ hasInitialized , setHasInitialized ] = useState ( false ) ;
191240 const [ selectedIndex , setSelectedIndex ] = useState ( 0 ) ;
192241
242+ // Initialize with the correct server/tool when servers are loaded
193243 useEffect ( ( ) => {
194- if ( servers . length > selectedIndex ) {
195- onSelect ( servers [ selectedIndex ] ) ;
244+ if ( hasInitialized || servers . length === 0 ) return ;
245+
246+ // Find initial server index if specified
247+ let idx = 0 ;
248+ if ( initialServer ) {
249+ const foundIdx = servers . findIndex ( s => s . name === initialServer ) ;
250+ if ( foundIdx >= 0 ) idx = foundIdx ;
196251 }
197- } , [ servers ] ) ;
252+
253+ const server = servers [ idx ] ;
254+ setSelectedIndex ( idx ) ;
255+
256+ // Find the tool to use
257+ const visibleTools = Array . from ( server . tools . values ( ) )
258+ . filter ( ( tool ) => isToolVisibleToModel ( tool ) )
259+ . sort ( compareTools ) ;
260+ const targetTool = initialTool && visibleTools . some ( t => t . name === initialTool )
261+ ? initialTool
262+ : visibleTools [ 0 ] ?. name ?? "" ;
263+
264+ onSelect ( server , targetTool ) ;
265+ setHasInitialized ( true ) ;
266+
267+ // Auto-call after initial selection if requested
268+ if ( autoCall && targetTool ) {
269+ onAutoCall ?.( server , targetTool ) ;
270+ }
271+ } , [ servers , hasInitialized , initialServer , initialTool , autoCall , onSelect , onAutoCall ] ) ;
198272
199273 if ( servers . length === 0 ) {
200274 return < select disabled > < option > No servers configured</ option > </ select > ;
@@ -349,6 +423,7 @@ function AppIFramePanel({ toolCallInfo, isDestroying, onTeardownComplete }: AppI
349423 const [ modelContext , setModelContext ] = useState < ModelContext | null > ( null ) ;
350424 const [ toolResult , setToolResult ] = useState < object | null > ( null ) ;
351425 const [ messages , setMessages ] = useState < AppMessage [ ] > ( [ ] ) ;
426+ const [ displayMode , setDisplayMode ] = useState < "inline" | "fullscreen" > ( "inline" ) ;
352427
353428 useEffect ( ( ) => {
354429 const iframe = iframeRef . current ! ;
@@ -365,6 +440,11 @@ function AppIFramePanel({ toolCallInfo, isDestroying, onTeardownComplete }: AppI
365440 const appBridge = newAppBridge ( toolCallInfo . serverInfo , iframe , {
366441 onContextUpdate : setModelContext ,
367442 onMessage : ( msg ) => setMessages ( ( prev ) => [ ...prev , msg ] ) ,
443+ onDisplayModeChange : setDisplayMode ,
444+ } , {
445+ // Provide container dimensions - maxHeight for flexible sizing
446+ containerDimensions : { maxHeight : 600 } ,
447+ displayMode : "inline" ,
368448 } ) ;
369449 appBridgeRef . current = appBridge ;
370450 initializeApp ( iframe , appBridge , toolCallInfo ) ;
@@ -431,8 +511,12 @@ function AppIFramePanel({ toolCallInfo, isDestroying, onTeardownComplete }: AppI
431511 } ;
432512 const messagesText = messages . map ( formatMessage ) . join ( "\n\n" ) ;
433513
514+ const panelClassName = displayMode === "fullscreen"
515+ ? `${ styles . appIframePanel } ${ styles . fullscreen } `
516+ : styles . appIframePanel ;
517+
434518 return (
435- < div className = { styles . appIframePanel } >
519+ < div className = { panelClassName } >
436520 < CollapsiblePanel icon = "📥" label = "Tool Input" content = { inputJson } />
437521 < iframe ref = { iframeRef } />
438522 { resultJson && (
0 commit comments