This commit is contained in:
Luigi Pederzani
2025-10-15 18:21:08 +02:00
parent 310efdf4bf
commit 0c719cdd0f
10 changed files with 645 additions and 838 deletions
+191
View File
@@ -0,0 +1,191 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
Pipfile.lock
# poetry
poetry.lock
# Environment variables
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# VS Code
.vscode/
*.code-workspace
# PyCharm
.idea/
*.iml
# macOS
.DS_Store
# AI
.cursor
.claude
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Dependencies
*.tsbuildinfo
dist/
node_modules
.pnp
.pnp.js
# Local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Testing
coverage
# Turbo
.turbo
# Vercel
.vercel
# Build Oututs
.next/
out/
build
dist
# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Misc
.DS_Store
*.pem
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
.coverage
agent_history.gif
static/browser_history/*.gif
# Virtual environments
.venv
# user conf
conf.yaml
# Agents
.cursor
.cursorrules
.claude
+453
View File
@@ -0,0 +1,453 @@
# Contributing to MCP-Use
Thank you for your interest in contributing to MCP-Use! This document provides guidelines and instructions for contributing to both the Python and TypeScript implementations of MCP-Use.
## 📋 Table of Contents
- [Code of Conduct](#code-of-conduct)
- [Getting Started](#getting-started)
- [Development Setup](#development-setup)
- [How to Contribute](#how-to-contribute)
- [Coding Standards](#coding-standards)
- [Testing](#testing)
- [Documentation](#documentation)
- [Pull Request Process](#pull-request-process)
- [Release Process](#release-process)
## Code of Conduct
Please read and follow our [Code of Conduct](./CODE_OF_CONDUCT.md) to ensure a welcoming and inclusive environment for all contributors.
## Getting Started
### Prerequisites
- **Git**: Version control
- **Python**: 3.11 or higher (for Python library)
- **Node.js**: 20 or higher (for TypeScript library)
- **pnpm**: 9 or higher (for TypeScript library)
### Repository Structure
This is a monorepo containing both Python and TypeScript implementations:
```
mcp-use-monorepo/
├── libraries/
│ ├── python/ # Python implementation
│ └── typescript/ # TypeScript implementation
├── .github/ # GitHub Actions workflows
└── docs/ # Unified documentation
```
## Development Setup
### 1. Fork and Clone
```bash
# Fork the repository on GitHub, then:
git clone https://github.com/YOUR_USERNAME/mcp-use.git
cd mcp-use-monorepo
```
### 2. Install Dependencies
#### Option A: Using Make (Recommended)
```bash
make install
```
#### Option B: Manual Installation
```bash
# Install Python dependencies
cd libraries/python
pip install -e ".[dev,search,e2b]" # Include optional dependencies
# Install TypeScript dependencies
cd ../typescript
pnpm install
```
### 3. Set Up Pre-commit Hooks (Python)
Pre-commit hooks ensure code quality before committing. The hooks will:
- Format code using Ruff
- Run linting checks
- Check for trailing whitespace and fix it
- Ensure files end with a newline
- Validate YAML files
- Check for large files
- Remove debug statements
```bash
cd libraries/python
pip install pre-commit
pre-commit install
```
### 4. Verify Setup
```bash
# Run tests for both libraries
make test
# Or individually:
make test-python
make test-ts
```
## How to Contribute
### Types of Contributions
We welcome various types of contributions:
- **Bug Fixes**: Help us squash bugs
- **Features**: Implement new features or enhance existing ones
- **Documentation**: Improve or expand our documentation
- **Tests**: Add test coverage
- **Examples**: Create example applications
- **Performance**: Optimize performance
- **Refactoring**: Improve code quality
### Finding Issues
1. Check our [GitHub Issues](https://github.com/mcp-use/mcp-use/issues)
2. Look for issues labeled:
- `good first issue` - Perfect for newcomers
- `help wanted` - We need your expertise
- `python` - Python-specific issues
- `typescript` - TypeScript-specific issues
### Creating Issues
Before creating an issue:
1. Search existing issues to avoid duplicates
2. Use our issue templates
3. Provide clear reproduction steps for bugs
4. Include relevant system information
## Coding Standards
### Python Guidelines
#### Style Guide
- Follow [PEP 8](https://pep8.org/)
- Use `ruff` for linting and formatting
- Maximum line length: 100 characters
#### Code Quality Tools
```bash
# Format code
cd libraries/python
ruff format .
# Lint code
ruff check .
# Type checking
mypy mcp_use
```
#### Python Best Practices
- Use type hints for all public functions
- Write docstrings for all public modules, classes, and functions (use Google-style)
- Prefer f-strings for string formatting
- Use async/await for asynchronous code
- Follow PEP 8 naming conventions
- Add type hints to function signatures
#### Python Docstring Example
```python
def function_name(param1: type, param2: type) -> return_type:
"""Short description.
Longer description if needed.
Args:
param1: Description of param1
param2: Description of param2
Returns:
Description of return value
Raises:
ExceptionType: When and why this exception is raised
"""
```
### TypeScript Guidelines
#### Style Guide
- Follow the project's ESLint configuration
- Use Prettier for formatting
- Use TypeScript strict mode
#### Code Quality Tools
```bash
# Format code
cd libraries/typescript
pnpm format
# Lint code
pnpm lint
# Type checking
pnpm type-check
```
#### TypeScript Best Practices
- Always define explicit types (avoid `any`)
- Use interfaces for object shapes
- Prefer `const` over `let` when possible
- Use async/await over promises when appropriate
## Testing
### Writing Tests
#### Python Tests
```python
# Test file: tests/test_feature.py
import pytest
from mcp_use import Feature
def test_feature_functionality():
"""Test that feature works as expected."""
feature = Feature()
result = feature.do_something()
assert result == expected_value
@pytest.mark.asyncio
async def test_async_feature():
"""Test async functionality."""
result = await async_feature()
assert result is not None
@pytest.mark.slow
def test_slow_operation():
"""Test marked as slow for optional execution."""
# Long-running test code
pass
@pytest.mark.integration
async def test_integration_feature():
"""Test marked as integration for network-dependent tests."""
# Integration test code
pass
```
**Test Organization:**
- Add unit tests in `tests/unit/`
- Add integration tests in `tests/integration/`
- Mark slow or network-dependent tests with `@pytest.mark.slow` or `@pytest.mark.integration`
- Aim for high test coverage of new code
#### TypeScript Tests
```typescript
// Test file: __tests__/feature.test.ts
import { describe, it, expect } from "vitest";
import { Feature } from "../src/feature";
describe("Feature", () => {
it("should work as expected", () => {
const feature = new Feature();
const result = feature.doSomething();
expect(result).toBe(expectedValue);
});
it("should handle async operations", async () => {
const result = await asyncFeature();
expect(result).toBeDefined();
});
});
```
### Running Tests
```bash
# Run all tests
make test
# Run Python tests with coverage
cd libraries/python
pytest --cov=mcp_use --cov-report=html
# Run TypeScript tests with coverage
cd libraries/typescript
pnpm test --coverage
# Run tests in watch mode
make dev
```
## Documentation
### Documentation Standards
- Write clear, concise documentation
- Include code examples
- Update relevant documentation when changing functionality
- Add docstrings/JSDoc comments for all public APIs
### Types of Documentation
1. **API Documentation**: In-code documentation
2. **User Guides**: How-to guides and tutorials
3. **Examples**: Working example applications
4. **README files**: Package and project overviews
### Building Documentation
```bash
# Python documentation (if using Sphinx)
cd libraries/python/docs
make html
# TypeScript documentation (if using TypeDoc)
cd libraries/typescript
pnpm docs
```
## Pull Request Process
### 1. Create a Branch
The `main` branch contains the latest stable code. Create feature or fix branches from `main`:
```bash
# Create a feature branch
git checkout -b feature/your-feature-name
# Or a fix branch
git checkout -b fix/bug-description
```
### 2. Make Your Changes
- Write clean, well-documented code
- Follow the coding standards
- Add or update tests
- Update documentation if needed
### 3. Commit Your Changes
Follow conventional commit format (recommended but not strictly enforced):
```bash
# Format: <type>(<scope>): <subject>
git commit -m "feat(python): add new MCP server connection"
git commit -m "fix(typescript): resolve memory leak in agent"
git commit -m "docs: update installation instructions"
```
Types:
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation changes
- `style`: Code style changes (formatting, etc.)
- `refactor`: Code refactoring
- `test`: Test changes
- `chore`: Build process or auxiliary tool changes
**Note:** Try to keep your commit messages informational and descriptive of the changes made.
### 4. Push and Create PR
Before pushing, ensure:
- Your code passes all tests
- Pre-commit hooks pass (for Python)
- No linting errors remain
```bash
git push origin your-branch-name
```
Then create a Pull Request on GitHub with:
- Clear title and description
- Link to related issues
- Screenshots/recordings for UI changes
- Test results
### 5. PR Review Process
- PRs require at least one approval
- Address all review comments
- Keep PRs focused and atomic
- Update your branch with main if needed
## Release Process
### Version Numbering
We follow [Semantic Versioning](https://semver.org/):
- MAJOR.MINOR.PATCH (e.g., 2.1.3)
- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes (backward compatible)
### Python Releases
```bash
# Update version in pyproject.toml
# Update CHANGELOG.md
# Create tag
git tag python-v1.2.3
git push origin python-v1.2.3
```
### TypeScript Releases
```bash
# Create changeset
cd libraries/typescript
pnpm changeset
# Version packages
pnpm changeset version
# Commit and push
git commit -m "chore: version packages"
git push
```
## Getting Help
- 💬 [GitHub Discussions](https://github.com/mcp-use/mcp-use/discussions) - Ask questions and share ideas
- 🐛 [GitHub Issues](https://github.com/mcp-use/mcp-use/issues) - Report bugs and request features
- 📧 Email: maintainers@mcp-use.com
- 💼 [Discord](https://discord.gg/mcp-use) - Join our community
## Recognition
Contributors will be recognized in:
- The project README
- Release notes
- Our website's contributors page
## License
By contributing, you agree that your contributions will be licensed under the same MIT License that covers the project.
---
Thank you for contributing to MCP-Use! Your efforts help make this project better for everyone. 🎉
View File
-146
View File
@@ -1,146 +0,0 @@
# Contributing to MCP-Use
Thank you for your interest in contributing to MCP-Use! This document provides guidelines and instructions for contributing to this project.
## Table of Contents
- [Getting Started](#getting-started)
- [Development Environment](#development-environment)
- [Installation from Source](#installation-from-source)
- [Development Workflow](#development-workflow)
- [Branching Strategy](#branching-strategy)
- [Commit Messages](#commit-messages)
- [Code Style](#code-style)
- [Pre-commit Hooks](#pre-commit-hooks)
- [Testing](#testing)
- [Running Tests](#running-tests)
- [Adding Tests](#adding-tests)
- [Pull Requests](#pull-requests)
- [Creating a Pull Request](#creating-a-pull-request)
- [Pull Request Template](#pull-request-template)
- [Documentation](#documentation)
- [Release Process](#release-process)
- [Getting Help](#getting-help)
## Getting Started
### Development Environment
MCP-Use requires:
- Python 3.11 or later
### Installation from Source
1. Fork the repository on GitHub.
2. Clone your fork locally:
```bash
git clone https://github.com/YOUR_USERNAME/mcp-use.git
cd mcp-use
```
3. Install the package in development mode:
```bash
pip install -e ".[dev,search,e2b]"
```
4. Set up pre-commit hooks:
```bash
pip install pre-commit
pre-commit install
```
## Development Workflow
### Branching Strategy
- `main` branch contains the latest stable code
- Create feature branches from `main` named according to the feature you're implementing: `feature/your-feature-name`
- For bug fixes, use: `fix/bug-description`
### Commit Messages
For now no commit style is enforced, try to keep your commit messages informational.
### Code Style
We use [Ruff](https://github.com/astral-sh/ruff) for code formatting and linting. The configuration is in `ruff.toml`.
Key style guidelines:
- Line length: 100 characters
- Use double quotes for strings
- Follow PEP 8 naming conventions
- Add type hints to function signatures
### Pre-commit Hooks
We use pre-commit hooks to ensure code quality before committing. The configuration is in `.pre-commit-config.yaml`.
The hooks will:
- Format code using Ruff
- Run linting checks
- Check for trailing whitespace and fix it
- Ensure files end with a newline
- Validate YAML files
- Check for large files
- Remove debug statements
## Testing
### Running Tests
Run the test suite with pytest:
```bash
pytest
```
To run specific test categories:
```bash
pytest tests/
```
### Adding Tests
- Add unit tests for new functionality in `tests/unit/`
- For slow or network-dependent tests, mark them with `@pytest.mark.slow` or `@pytest.mark.integration`
- Aim for high test coverage of new code
## Pull Requests
### Creating a Pull Request
1. Ensure your code passes all tests and pre-commit hooks
2. Push your changes to your fork
3. Submit a pull request to the main repository
4. Follow the pull request template
## Documentation
- Update docstrings for new or modified functions, classes, and methods
- Use Google-style docstrings:
```python
def function_name(param1: type, param2: type) -> return_type:
"""Short description.
Longer description if needed.
Args:
param1: Description of param1
param2: Description of param2
Returns:
Description of return value
Raises:
ExceptionType: When and why this exception is raised
"""
```
- Update README.md for user-facing changes
## Getting Help
If you need help with your contribution:
- Open an issue for discussion
- Reach out to the maintainers
- Check existing code for examples
Thank you for contributing to MCP-Use!
-51
View File
@@ -1,51 +0,0 @@
{
// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off", "fixable": true },
{ "rule": "format/*", "severity": "off", "fixable": true },
{ "rule": "*-indent", "severity": "off", "fixable": true },
{ "rule": "*-spacing", "severity": "off", "fixable": true },
{ "rule": "*-spaces", "severity": "off", "fixable": true },
{ "rule": "*-order", "severity": "off", "fixable": true },
{ "rule": "*-dangle", "severity": "off", "fixable": true },
{ "rule": "*-newline", "severity": "off", "fixable": true },
{ "rule": "*quotes", "severity": "off", "fixable": true },
{ "rule": "*semi", "severity": "off", "fixable": true }
],
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"json5",
"jsonc",
"yaml",
"toml",
"xml",
"gql",
"graphql",
"astro",
"svelte",
"css",
"less",
"scss",
"pcss",
"postcss"
]
}
@@ -1,124 +0,0 @@
# MCP Inspector Integration
## Overview
The MCP Inspector is now automatically mounted at `/inspector` for all MCP servers created with `createMCPServer`, similar to how FastAPI provides automatic Swagger documentation at `/docs`.
## Key Changes
### 1. Inspector Package (`@mcp-use/inspector`)
- **New Middleware Function**: Created `mountInspector()` function that can mount the inspector UI on any Express app
- **Package Configuration**:
- Added `main` and `exports` fields to make it importable
- Added Express as a peer dependency
- Built server components are available in `dist/server/`
### 2. MCP Server (`mcp-use`)
- **Automatic Mounting**: Modified `createMCPServer()` to automatically mount the inspector at `/inspector`
- **Optional Dependency**: Added `@mcp-use/inspector` as an optional peer dependency
- **Graceful Degradation**: Server works fine if inspector package is not installed
### 3. Templates (`create-mcp-use-app`)
- **Automatic Setup**: All new projects created with `create-mcp-use-app` include `@mcp-use/inspector` as a dependency
- **No Manual Configuration**: Developers don't need to manually call `mountInspector()` anymore
- **Console Message**: Server startup logs include the inspector URL
## Usage
### For New Projects
When developers create a new MCP server:
```typescript
import { createMCPServer } from 'mcp-use/server'
const server = createMCPServer('my-server', {
version: '1.0.0',
description: 'My awesome MCP server'
})
// Define tools, resources, prompts...
server.listen(3000)
// Inspector automatically available at http://localhost:3000/inspector
```
### For Existing Projects
1. Install the inspector package:
```bash
pnpm add @mcp-use/inspector
```
2. Build the inspector:
```bash
cd packages/inspector && pnpm build
```
3. The inspector will automatically be available at `/inspector` when you start your server
### Manual Mounting (Advanced)
If you need custom mounting:
```typescript
import { mountInspector } from '@mcp-use/inspector'
// Mount at custom path
mountInspector(server, '/my-custom-path')
```
## Implementation Details
### How It Works
1. When `createMCPServer()` is called, it attempts to dynamically import `@mcp-use/inspector`
2. If the package is installed, `mountInspector()` is called automatically with the Express app instance
3. The inspector middleware:
- Serves the built React UI from `dist/client/`
- Handles static assets (JS, CSS)
- Serves the HTML for all inspector routes (client-side routing)
### Workspace Setup
For local development in the monorepo:
```json
{
"dependencies": {
"@mcp-use/inspector": "workspace:*"
}
}
```
For published packages:
```json
{
"dependencies": {
"@mcp-use/inspector": "^0.1.0"
}
}
```
## Benefits
1. **Zero Configuration**: Works out of the box, just like FastAPI's `/docs`
2. **Developer Experience**: Instant visual debugging and testing of MCP servers
3. **Optional**: Doesn't break existing servers if inspector is not installed
4. **Consistent**: All MCP servers have the same inspector experience
## Files Modified
- `packages/inspector/src/server/middleware.ts` - New middleware function
- `packages/inspector/src/server/index.ts` - Export mountInspector
- `packages/inspector/package.json` - Added exports and peer dependencies
- `packages/inspector/tsconfig.server.json` - Fixed TypeScript config
- `packages/mcp-use/src/server/mcp-server.ts` - Auto-mount inspector
- `packages/mcp-use/package.json` - Added inspector as optional peer dependency
- `packages/create-mcp-use-app/src/templates/ui/package.json` - Added inspector dependency
- `packages/create-mcp-use-app/src/templates/ui/src/server.ts` - Updated comments
@@ -1,516 +0,0 @@
# MCP-UI Integration Implementation Plan
## Overview
This document outlines the plan to implement MCP-UI integration into the mcp-use library, providing a fancy way to expose UI widgets as MCP resources with automatic discovery, prop extraction, and tool generation.
## Current Architecture Analysis
### Existing Components
1. **Widget Serving**: The `McpServer` class already serves widgets from `/mcp-use/widgets/*` through `setupWidgetRoutes()` method (mcp-server.ts:445-481)
2. **MCP-UI Support**: The `@mcp-ui/server` package provides `createUIResource` function with support for:
- External URLs with iframe rendering
- Raw HTML content
- Remote DOM scripts
3. **Widget Implementation**: Widgets like the kanban-board are React components that can accept props via URL query parameters
4. **Manual Integration**: Currently requires manual creation of both tools and resources for each widget
### Key Opportunities
- **Automatic Widget Discovery**: Scan filesystem for widgets and auto-register them
- **Props Extraction**: Parse TypeScript/React component props to generate tool input schemas
- **Unified Interface**: Create a `uiResource` method that handles both tool and resource registration
- **Dynamic URL Generation**: Automatically construct iframe URLs with query parameters based on tool inputs
## Proposed Architecture
### Core Concepts
#### 1. UIResource Method
A specialized method on the McpServer class that:
- Accepts widget configuration (name, path, props)
- Automatically creates both a tool and a UI resource
- Handles prop-to-query-parameter conversion
- Returns UIResource format compatible with MCP-UI
#### 2. Widget Discovery System
- Scan `dist/resources/mcp-use/widgets/*` directories
- Parse widget manifest files or TypeScript interfaces
- Extract component props and their types
- Generate input schemas automatically
#### 3. Automatic Tool Generation
- Create tools that return both text and UI resources
- Pass tool inputs as query parameters to widget iframes
- Support complex data types through JSON encoding
## Implementation Phases
### Phase 1: Core UIResource Infrastructure
#### 1.1 Create UIResource Type Definitions
**File**: `packages/mcp-use/src/server/types.ts`
```typescript
export interface UIResourceDefinition {
name: string
widget: string
title?: string
description?: string
props?: WidgetProps
size?: [string, string]
annotations?: ResourceAnnotations
}
export interface WidgetProps {
[key: string]: {
type: 'string' | 'number' | 'boolean' | 'object' | 'array'
required?: boolean
default?: any
description?: string
}
}
export interface WidgetConfig {
name: string
path: string
manifest?: WidgetManifest
component?: string
}
```
#### 1.2 Implement uiResource Method
**File**: `packages/mcp-use/src/server/mcp-server.ts`
Add methods to McpServer class:
```typescript
/**
* Create a UIResource object for a widget with the given parameters
* This method is shared between tool and resource handlers to avoid duplication
*/
private createWidgetUIResource(
widget: string,
params: Record<string, any>,
size?: [string, string]
): any {
const iframeUrl = this.buildWidgetUrl(widget, params)
return createUIResource({
uri: `ui://widget/${widget}`,
content: {
type: 'externalUrl',
iframeUrl
},
encoding: 'text',
uiMetadata: size ? {
'preferred-frame-size': size
} : undefined
})
}
/**
* Register a widget as both a tool and a resource
* The tool allows passing parameters, the resource provides static access
*/
uiResource(definition: UIResourceDefinition): this {
// Register the tool - returns UIResource with parameters
this.tool({
name: `ui_${definition.widget}`,
description: definition.description || `Display ${definition.widget} widget`,
inputs: this.convertPropsToInputs(definition.props),
fn: async (params) => {
// Create the UIResource with user-provided params
const uiResource = this.createWidgetUIResource(
definition.widget,
params,
definition.size
)
return {
content: [
{
type: 'text',
text: `Displaying ${definition.title || definition.widget} widget`
},
uiResource // Reuse the same UIResource
]
}
}
})
// Register the resource - returns UIResource with defaults
this.resource({
name: definition.name,
uri: `ui://widget/${definition.widget}`,
title: definition.title,
description: definition.description,
mimeType: 'text/html',
annotations: definition.annotations,
fn: async () => {
// Create the UIResource with default/empty params
const uiResource = this.createWidgetUIResource(
definition.widget,
this.applyDefaultProps(definition.props),
definition.size
)
return {
contents: [uiResource] // Return the UIResource directly
}
}
})
return this
}
/**
* Apply default values to widget props
*/
private applyDefaultProps(props?: WidgetProps): Record<string, any> {
if (!props) return {}
const defaults: Record<string, any> = {}
for (const [key, prop] of Object.entries(props)) {
if (prop.default !== undefined) {
defaults[key] = prop.default
}
}
return defaults
}
```
### Phase 2: Widget Discovery System
#### 2.1 Create Widget Discovery Module
**File**: `packages/mcp-use/src/server/widget-discovery.ts`
```typescript
import { readdirSync, existsSync, readFileSync } from 'fs'
import { join } from 'path'
export interface WidgetManifest {
name: string
title?: string
description?: string
props?: Record<string, PropDefinition>
size?: [string, string]
}
export class WidgetDiscovery {
private widgetsPath: string
constructor(widgetsPath: string) {
this.widgetsPath = widgetsPath
}
async discoverWidgets(): Promise<WidgetConfig[]> {
const widgets: WidgetConfig[] = []
if (!existsSync(this.widgetsPath)) {
return widgets
}
const dirs = readdirSync(this.widgetsPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
for (const dir of dirs) {
const widgetPath = join(this.widgetsPath, dir.name)
const manifestPath = join(widgetPath, 'widget.json')
if (existsSync(manifestPath)) {
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'))
widgets.push({
name: dir.name,
path: widgetPath,
manifest
})
} else {
// Try to auto-detect from index.html or component files
widgets.push({
name: dir.name,
path: widgetPath
})
}
}
return widgets
}
}
```
#### 2.2 Add discoverWidgets Method to McpServer
**File**: `packages/mcp-use/src/server/mcp-server.ts`
```typescript
async discoverWidgets(options?: DiscoverWidgetsOptions): Promise<void> {
const discovery = new WidgetDiscovery(
options?.path || join(process.cwd(), 'dist/resources/mcp-use/widgets')
)
const widgets = await discovery.discoverWidgets()
for (const widget of widgets) {
if (widget.manifest) {
this.uiResource({
name: widget.name,
widget: widget.name,
title: widget.manifest.title,
description: widget.manifest.description,
props: widget.manifest.props,
size: widget.manifest.size
})
} else if (options?.autoRegister) {
// Register with minimal configuration
this.uiResource({
name: widget.name,
widget: widget.name
})
}
}
}
```
### Phase 3: Props and Schema Generation
#### 3.1 Implement Prop Extraction Utilities
**File**: `packages/mcp-use/src/server/widget-props.ts`
```typescript
import * as ts from 'typescript'
export class PropExtractor {
extractPropsFromFile(filePath: string): WidgetProps {
const program = ts.createProgram([filePath], {})
const sourceFile = program.getSourceFile(filePath)
if (!sourceFile) return {}
const props: WidgetProps = {}
// Find interface or type definitions for props
ts.forEachChild(sourceFile, (node) => {
if (ts.isInterfaceDeclaration(node) &&
node.name?.text.includes('Props')) {
node.members.forEach(member => {
if (ts.isPropertySignature(member) && member.name) {
const propName = member.name.getText()
const propType = this.getTypeString(member.type)
const isOptional = !!member.questionToken
props[propName] = {
type: this.mapTsTypeToSchemaType(propType),
required: !isOptional
}
}
})
}
})
return props
}
private mapTsTypeToSchemaType(tsType: string): string {
switch (tsType) {
case 'string': return 'string'
case 'number': return 'number'
case 'boolean': return 'boolean'
case 'any[]':
case 'Array': return 'array'
default: return 'object'
}
}
}
```
#### 3.2 Create Query Parameter Builder
**File**: `packages/mcp-use/src/server/mcp-server.ts` (addition)
```typescript
private buildWidgetUrl(widget: string, params: Record<string, any>): string {
const baseUrl = `http://localhost:${this.serverPort}/mcp-use/widgets/${widget}`
if (Object.keys(params).length === 0) {
return baseUrl
}
const queryParams = new URLSearchParams()
for (const [key, value] of Object.entries(params)) {
if (value !== undefined && value !== null) {
if (typeof value === 'object') {
queryParams.append(key, JSON.stringify(value))
} else {
queryParams.append(key, String(value))
}
}
}
return `${baseUrl}?${queryParams.toString()}`
}
private convertPropsToInputs(props?: WidgetProps): InputDefinition[] {
if (!props) return []
return Object.entries(props).map(([name, prop]) => ({
name,
type: prop.type,
description: prop.description,
required: prop.required,
default: prop.default
}))
}
```
### Phase 4: Widget Manifest System
#### 4.1 Define Widget Manifest Format
**File**: `widget.json` (example for kanban-board)
```json
{
"name": "kanban-board",
"title": "Kanban Board",
"description": "Interactive task management board with drag-and-drop",
"version": "1.0.0",
"props": {
"initialTasks": {
"type": "array",
"description": "Initial tasks to display on the board",
"required": false
},
"columns": {
"type": "array",
"description": "Column configuration",
"required": false,
"default": [
{ "id": "todo", "title": "To Do" },
{ "id": "in-progress", "title": "In Progress" },
{ "id": "done", "title": "Done" }
]
},
"theme": {
"type": "string",
"description": "Visual theme (light/dark)",
"required": false,
"default": "light"
}
},
"size": ["900px", "600px"],
"assets": {
"main": "index.html",
"scripts": ["assets/index.js"],
"styles": ["assets/style.css"]
}
}
```
#### 4.2 Update Build Process
**File**: `packages/mcp-use-cli/src/commands/build.ts` (conceptual)
- Add step to scan for React/TypeScript components
- Extract prop interfaces automatically
- Generate widget.json if not present
- Bundle widgets with manifests
### Phase 5: Integration and Testing
#### 5.1 Update Server Template
**File**: `packages/create-mcp-use-app/src/templates/ui/src/server.ts`
```typescript
import { createMCPServer } from 'mcp-use/server'
const server = createMCPServer('ui-mcp-server', {
version: '1.0.0',
description: 'MCP server with auto-discovered UI widgets',
})
const PORT = process.env.PORT || 3000
// Manual widget registration with full control
server.uiResource({
name: 'kanban-board',
widget: 'kanban-board',
title: 'Kanban Board',
description: 'Task management with drag-and-drop',
props: {
initialTasks: {
type: 'array',
description: 'Initial task list',
required: false
},
theme: {
type: 'string',
description: 'Visual theme',
default: 'light'
}
},
size: ['900px', '600px']
})
// OR: Automatic discovery (alternative approach)
await server.discoverWidgets({
path: './dist/resources/mcp-use/widgets',
autoRegister: true
})
server.listen(PORT)
```
#### 5.2 Create Example Widgets
**Additional widgets to create**:
1. **Chart Widget** - Data visualization with configurable chart type
2. **Form Builder** - Dynamic form with field configuration
3. **Data Table** - Sortable/filterable table with pagination
Each widget should:
- Have TypeScript prop interfaces
- Include a widget.json manifest
- Support query parameter initialization
- Demonstrate different prop types
## Benefits of This Implementation
### Developer Experience
- **Simplified API**: Single `uiResource` method instead of separate tool and resource definitions
- **Auto-discovery**: Widgets automatically registered from filesystem
- **Type Safety**: Props extracted from TypeScript interfaces
- **Zero Config**: Works out of the box with sensible defaults
### Features
- **Automatic Tool Generation**: Each widget gets a corresponding tool
- **Props to Query Params**: Seamless data passing to widgets
- **Manifest System**: Declarative widget configuration
- **Asset Management**: Automatic handling of JS/CSS assets
### Extensibility
- **Plugin Architecture**: Easy to add new widget types
- **Custom Prop Types**: Support for complex data structures
- **Framework Agnostic**: Works with React, Vue, or vanilla JS
- **Build Integration**: Hooks into existing build pipeline
## Migration Path
For existing implementations:
1. Keep backward compatibility with manual tool/resource registration
2. Add deprecation warnings for old patterns
3. Provide migration tool to generate manifests from existing code
4. Document migration guide with examples
## Success Criteria
- [ ] Widgets can be registered with a single method call
- [ ] Automatic discovery finds and registers all widgets in a directory
- [ ] Props are extracted from TypeScript interfaces
- [ ] Tool inputs are converted to widget props via query parameters
- [ ] Each widget exposes both tool and resource endpoints
- [ ] UIResources render correctly in MCP-UI compatible clients
- [ ] Documentation and examples are comprehensive
## Next Steps
1. Implement Phase 1 (Core Infrastructure)
2. Test with existing kanban-board widget
3. Implement Phase 2 (Discovery System)
4. Create additional example widgets
5. Write comprehensive documentation
6. Create migration guide for existing users
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "mcp-use-monorepo",
"name": "mcp-use-ts-monorepo",
"version": "1.0.0",
"private": true,
"type": "module",