Skip to content

Conversation

@infoxicator
Copy link

@infoxicator infoxicator commented Feb 8, 2026

Summary

Fixes Apps tab missing tool result data causing Budget Allocator Server example not to display correctly.

This was to do with missing tool result not passed to the AppsRenderer component

Note: Inspector V2 is under development to address architectural and UX improvements. During this time, V1 contributions should focus on bug fixes and MCP spec compliance. See CONTRIBUTING.md for more details.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Documentation update
  • Refactoring (no functional changes)
  • Test updates
  • Build/CI improvements

Changes Made

In Apps tab, apps received ui/notifications/tool-input but not ui/notifications/tool-result. Apps that initialize from ontoolresult (for example, budget allocator) could render empty due to missing tool data

File changes:

Added Apps tool execution in AppRenderer.tsx
After app/tool input changes, Inspector now calls MCP tools/call with the selected tool and current arguments.
Forwarded the returned result to the iframe via toolResult (which emits ui/notifications/tool-result).
Added race safety with AbortController + run-id latest-wins guard to drop stale results during switch/restart/unmount.
Added error fallback: failed tools/call is converted to app-consumable error result (isError: true) to keep UI stable.
Left Tools tab behavior unchanged.

Tests updated in AppRenderer.test.tsx

  • sendToolInput occurs
  • MCP tools/call occurs with expected arguments.
  • sendToolResult occurs after app initialization.
  • Stale result is ignored after app switch.
  • Failed tools/call produces an error tool result payload.

Related Issues

Testing

  • Tested in UI mode
  • Tested in CLI mode
  • Tested with STDIO transport
  • Tested with SSE transport
  • Tested with Streamable HTTP transport
  • Added/updated automated tests
  • Manual testing performed

Test Results and/or Instructions

Run the budget allocation server example: https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/budget-allocator-server

Broken version:
Screenshot 2026-02-08 at 00 26 25

Fixed version:

image Screenshots are encouraged to share your testing results for this change.

Checklist

  • Code follows the style guidelines (ran npm run prettier-fix)
  • Self-review completed
  • Code is commented where necessary
  • Documentation updated (README, comments, etc.)

Breaking Changes

Additional Context


const runTool = async () => {
try {
const result = await mcpClient.request(
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While calling the tool from here works.

I wonder if the tool result is already present and can be passed as a prop to this component instead of making a brand new tool request.

let me know if that's preferable

Copy link
Member

@cliffhall cliffhall Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this PR and it works. However, I would rather this part operate more like the ToolsTab tool calling scheme. To test the theory, I removed these changes and passed down toolResult from App -> AppsTab -> AppRenderer and it works, but only if I run the tool in the tool tab first.

To complete the scenario, we would need to pass down callTool from the App, and in AppsTab.handleSelectTool, where we set the app to be open if there is no form to fill first, call the tool. Also in AppsTab.handleOpenApp which handles the Open App button if there was a form to fill, call the tool. Probably there can be a common function that is called from both those places.

@infoxicator
Copy link
Author

@cliffhall this should also fix the pdf server and the other examples that were failing

Copy link
Member

@cliffhall cliffhall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree that using toolResult and the existing callTool function is better. Following the pattern with ToolsTab I see it can be done.


const runTool = async () => {
try {
const result = await mcpClient.request(
Copy link
Member

@cliffhall cliffhall Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this PR and it works. However, I would rather this part operate more like the ToolsTab tool calling scheme. To test the theory, I removed these changes and passed down toolResult from App -> AppsTab -> AppRenderer and it works, but only if I run the tool in the tool tab first.

To complete the scenario, we would need to pass down callTool from the App, and in AppsTab.handleSelectTool, where we set the app to be open if there is no form to fill first, call the tool. Also in AppsTab.handleOpenApp which handles the Open App button if there was a form to fill, call the tool. Probably there can be a common function that is called from both those places.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants