-
Notifications
You must be signed in to change notification settings - Fork 170
feat: implement MCP-UI support #783
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
d484367 to
e223056
Compare
- Added UIRegistry class to manage custom and default UI HTML for tools. - Integrated custom UI support in the Server and ToolBase classes. - Created ListDatabases tool with a corresponding UI component. - Introduced Vite configuration for building UI components and generating HTML entries. - Updated TypeScript configuration to support JSX and include UI components. - Enhanced package.json with new dependencies for React and Vite. - Updated .gitignore to exclude generated UI files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds MCP-UI support to the MongoDB MCP server, enabling tools to render rich React-based UIs in compatible clients. The implementation includes build tooling for React components, a UI registry system for managing component HTML, and an initial UI component for the list-databases tool.
Key changes:
- Adds build infrastructure using Vite to compile React components into self-contained HTML files
- Implements a UI registry system that maps tool names to UI components and manages HTML loading
- Updates the
list-databasestool to return structured content and automatically attach UI resources
Reviewed changes
Copilot reviewed 18 out of 20 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| vite.ui.config.ts | New Vite configuration for building React UI components into standalone HTML files |
| tsconfig.build.json | Enables JSX compilation for React components |
| src/ui/registry/uiMap.ts | Maps tool names to UI component names |
| src/ui/registry/registry.ts | Core UIRegistry class for managing and loading UI HTML strings |
| src/ui/registry/index.ts | Registry module exports |
| src/ui/index.ts | Main UI module exports |
| src/ui/hooks/useRenderData.ts | React hook for receiving render data via postMessage |
| src/ui/hooks/index.ts | Hooks module exports |
| src/ui/components/ListDatabases/schema.ts | Zod schema defining list-databases output contract |
| src/ui/components/ListDatabases/index.ts | ListDatabases component module exports |
| src/ui/components/ListDatabases/ListDatabases.tsx | React component rendering database list as a table |
| src/ui/components/ListDatabases/ListDatabases.styles.ts | Styles for ListDatabases component |
| src/ui/build/template.html | HTML template for generated component entry files |
| src/ui/build/mount.tsx | Entry point that mounts React components in the browser |
| src/tools/tool.ts | Adds UIRegistry integration and automatic UI resource attachment to tools |
| src/tools/mongodb/metadata/listDatabases.tsx | Updates tool to return structured content for UI rendering |
| src/server.ts | Adds UIRegistry instantiation and customUIs configuration option |
| package.json | Adds React, Vite, and MCP-UI dependencies plus UI build script |
| } | ||
|
|
||
| const uiResource = createUIResource({ | ||
| uri: `ui://${this.name}/${Date.now()}`, |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using Date.now() for URI generation creates non-deterministic URIs that change on each execution. This makes testing difficult and prevents URI-based caching. Consider using a deterministic approach or accepting a URI parameter that can be controlled in tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we'd want this deterministic. If the same tool is called multiple times, each invocation generates new data, so unique URIs ensure each result is treated as a distinct resource. Open to other ideas here if my thinking is incorrect!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how the caching works exactly, but I think a static uri would be more performant? I don't think it caches the uiMetadata
Pull Request Test Coverage Report for Build 19972168195Details
💛 - Coveralls |
package.json
Outdated
| "dist" | ||
| ], | ||
| "scripts": { | ||
| "start": "node dist/index.js --transport http --loggers stderr mcp --previewFeatures vectorSearch", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like this was renamed
| "start": "node dist/index.js --transport http --loggers stderr mcp --previewFeatures search", |
vite.ui.config.ts
Outdated
| * Discover all component directories in src/ui/components/ | ||
| * Each directory should have an index.ts that exports the component | ||
| */ | ||
| function discoverComponents(): string[] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: why discover?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed this completely in the change to inline HTML strings
vite.ui.config.ts
Outdated
| }; | ||
| } | ||
|
|
||
| const components = discoverComponents(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: we're running this search here, and then again in the generateHtmlEntries plugin. Could either use this pre-computed value in the plugin, or memoize it to speed up builds a bit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed this completely in the change to inline HTML strings
package.json
Outdated
| "react": "^18.3.0", | ||
| "react-dom": "^18.3.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dependencies or devDependencies?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah, good point! Will update to dev
src/tools/tool.ts
Outdated
| * @param result - The result from the tool's `execute()` method | ||
| * @returns The result with UIResource appended if conditions are met, otherwise unchanged | ||
| */ | ||
| private appendUIResourceIfAvailable(result: CallToolResult): CallToolResult { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: not sure if this is a naming convention across the repo, but just appendUIResource with docs for what happens if it's not available feels more correct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No argument on this from me. "if available" just seemed more technically correct based on what the method does, but I don't see an issue with shortening it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this need to be .tsx?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! I changed this at the very beginning so I could inline the jsx and validate things were working locally. Will change back
Summary
list-databasetool callsTesting
I've been using MCPJam Inspector which has MCP-UI implemented in the client.
Screenshot