Conversation
| ); | ||
| } | ||
|
|
||
| interface ErrorResult { |
There was a problem hiding this comment.
tool results should probably all be exported in some common types for sharing
| function Router({ toolId, data }: { toolId: string; data: CallToolResult }) { | ||
| switch (toolId) { | ||
| case "list-projects": | ||
| return <ListProjects data={data} />; |
There was a problem hiding this comment.
Adding a handling for a new tool would just be a case of adding a new case with a lazy import.
| ], | ||
| _meta: { | ||
| ui: { | ||
| resourceUri: this.createAppUri("list-projects"), |
There was a problem hiding this comment.
adding a new tool call is just a case of adding the correct meta to another tool. The app will then be passed the toolId and data to handle.
src/common/server.ts
Outdated
| if (client.registerResources) { | ||
| client.registerResources((name, path, cb) => { | ||
| const url = `${client.toolPrefix}://${name}/${path}`; | ||
| const url = typeof path === 'string' ? `${client.toolPrefix}://${name}/${path}` : path.uri; |
There was a problem hiding this comment.
the default uri construction doesn't work for mcp apps. the uri schema must be ui://.... so I'm allowing passing a complete uri to override it here.
| } | ||
|
|
||
| // Serve static assets from dist/assets/ | ||
| if (req.method === "GET" && url.pathname.startsWith("/assets/")) { |
There was a problem hiding this comment.
basic static file serving. probably not the best solution
| * | ||
| * A meta tag with the tool id is expected mcp-tool-id. | ||
| */ | ||
| export function createMcpApp(config: McpAppConfig) { |
There was a problem hiding this comment.
Provide an easy setup for a react app. If you follow the conventions just create an html file and call this in the entry. The root component will then get the toolId and the tool result.
createMcpApp({ name, version, RootComponent })
|
|
||
| describe("API methods", async () => { | ||
| beforeEach(async () => { | ||
| client = await createConfiguredClient("test-token", "test-project-key"); |
There was a problem hiding this comment.
this setup is currently bad. if you don't run all tests then later tests will fail because they are relying on setup from previous tests to exist and not have been cleaned up.
| process.exit(1); | ||
| } | ||
|
|
||
| if (process.env.UI_DEV) { |
There was a problem hiding this comment.
run the vite dev server as a sub process to make life easier
| outDir: resolve(__dirname, "dist"), | ||
| emptyOutDir: false, | ||
| rollupOptions: { | ||
| input: [resolve(__dirname, "src/bugsnag/ui/app.html")], |
There was a problem hiding this comment.
When adding new apps, new entries would need adding here
| order: "post", | ||
| handler(html: string, context) { | ||
| const path = dirname(context.path); | ||
| return html |
There was a problem hiding this comment.
this isn't very robust but works fine if entries follow the convention of the simple app.html
| @@ -0,0 +1,26 @@ | |||
| import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; | |||
| import { lazy } from "react"; | |||
| import "./ListProjects.css"; | |||
There was a problem hiding this comment.
| import "./ListProjects.css"; |
Goal
Investigate MCP Apps and how they could be integrated
Design
Rather than the examples of MCP apps which have an html entry for each tool and then bundle it's resources in to a single file. I wanted to proof out being able to set up an easier to expand approach which requires less set up for each tool.
Example MCP app
Example when run in vs code
I found running the app in vs code painful as it seems to need completely quitting to update the mcp server.
Screen.Recording.2026-02-10.at.16.48.07.mov
Example when server is run with UI_DEV
Hot reloading works completely making development not a complete nighmare
Screen.Recording.2026-02-10.at.16.52.31.mov
Changeset
This is a POC and not expected to merge in the current state.
*to work with the basic-host for testing. So that needs more investigationTesting
Tested via vscode copilot + mcp basic-host example.