Showing preview only (883K chars total). Download the full file or copy to clipboard to get everything.
Repository: MCP-UI-Org/mcp-ui
Branch: main
Commit: af881f9eff2c
Files: 165
Total size: 833.0 KB
Directory structure:
gitextract_f5vcxr1e/
├── .github/
│ ├── CONTRIBUTING.md
│ ├── copilot-instructions.md
│ └── workflows/
│ ├── ci.yml
│ └── deploy-docs.yml
├── .gitignore
├── .prettierignore
├── .prettierrc.json
├── .releaserc.json
├── .ruby-version
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── SECURITY.md
├── docs/
│ ├── README.md
│ ├── package.json
│ └── src/
│ ├── .vitepress/
│ │ ├── config.ts
│ │ └── theme/
│ │ ├── custom.css
│ │ └── index.ts
│ ├── about.md
│ ├── guide/
│ │ ├── apps-sdk.md
│ │ ├── client/
│ │ │ ├── app-renderer.md
│ │ │ ├── overview.md
│ │ │ └── walkthrough.md
│ │ ├── embeddable-ui.md
│ │ ├── getting-started.md
│ │ ├── introduction.md
│ │ ├── mcp-apps.md
│ │ ├── protocol-details.md
│ │ ├── server/
│ │ │ ├── python/
│ │ │ │ ├── overview.md
│ │ │ │ ├── usage-examples.md
│ │ │ │ └── walkthrough.md
│ │ │ ├── ruby/
│ │ │ │ ├── overview.md
│ │ │ │ ├── usage-examples.md
│ │ │ │ └── walkthrough.md
│ │ │ └── typescript/
│ │ │ ├── overview.md
│ │ │ ├── usage-examples.md
│ │ │ └── walkthrough.md
│ │ └── supported-hosts.md
│ ├── index.md
│ └── team.md
├── eslint.config.mjs
├── examples/
│ ├── external-url-demo/
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── App.css
│ │ │ ├── App.tsx
│ │ │ ├── index.css
│ │ │ └── main.tsx
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── mcp-apps-demo/
│ │ ├── package.json
│ │ ├── src/
│ │ │ └── index.ts
│ │ └── tsconfig.json
│ ├── python-server-demo/
│ │ ├── README.md
│ │ ├── pyproject.toml
│ │ └── python_server_demo.py
│ ├── ruby-server-demo/
│ │ ├── Gemfile
│ │ ├── README.md
│ │ └── server.rb
│ ├── server/
│ │ ├── .react-router/
│ │ │ └── types/
│ │ │ ├── +future.ts
│ │ │ ├── +routes.ts
│ │ │ ├── +server-build.d.ts
│ │ │ └── app/
│ │ │ ├── +types/
│ │ │ │ └── root.ts
│ │ │ └── routes/
│ │ │ └── +types/
│ │ │ ├── home.ts
│ │ │ ├── task.ts
│ │ │ └── user.ts
│ │ ├── README.md
│ │ ├── app/
│ │ │ ├── app.css
│ │ │ ├── entry.server.tsx
│ │ │ ├── graph/
│ │ │ │ └── graph.tsx
│ │ │ ├── images.d.ts
│ │ │ ├── root.tsx
│ │ │ ├── routes/
│ │ │ │ ├── home.tsx
│ │ │ │ ├── task.tsx
│ │ │ │ └── user.tsx
│ │ │ ├── routes.ts
│ │ │ ├── user/
│ │ │ │ └── user.tsx
│ │ │ └── utils/
│ │ │ └── messageUtils.ts
│ │ ├── biome.json
│ │ ├── package.json
│ │ ├── pnpm-workspace.yaml
│ │ ├── react-router.config.ts
│ │ ├── src/
│ │ │ └── index.ts
│ │ ├── tsconfig.cloudflare.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ ├── vite.config.ts
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ └── typescript-server-demo/
│ ├── README.md
│ ├── package.json
│ ├── src/
│ │ └── index.ts
│ └── tsconfig.json
├── lefthook.yml
├── package.json
├── pnpm-workspace.yaml
├── sdks/
│ ├── python/
│ │ └── server/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── pyproject.toml
│ │ ├── release.config.js
│ │ ├── src/
│ │ │ └── mcp_ui_server/
│ │ │ ├── __init__.py
│ │ │ ├── core.py
│ │ │ ├── exceptions.py
│ │ │ ├── types.py
│ │ │ └── utils.py
│ │ └── tests/
│ │ ├── __init__.py
│ │ ├── test_create_ui_resource.py
│ │ ├── test_metadata.py
│ │ └── test_ui_action_results.py
│ ├── ruby/
│ │ ├── .rspec
│ │ ├── .rubocop.yml
│ │ ├── CHANGELOG.md
│ │ ├── Gemfile
│ │ ├── README.md
│ │ ├── Rakefile
│ │ ├── lib/
│ │ │ ├── mcp_ui_server/
│ │ │ │ └── version.rb
│ │ │ └── mcp_ui_server.rb
│ │ ├── mcp_ui_server.gemspec
│ │ ├── release.config.js
│ │ └── spec/
│ │ ├── mcp_ui_server_spec.rb
│ │ └── spec_helper.rb
│ └── typescript/
│ ├── client/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── scripts/
│ │ │ └── proxy/
│ │ │ └── index.html
│ │ ├── src/
│ │ │ ├── __tests__/
│ │ │ │ └── capabilities.test.ts
│ │ │ ├── capabilities.ts
│ │ │ ├── components/
│ │ │ │ ├── AppFrame.tsx
│ │ │ │ ├── AppRenderer.tsx
│ │ │ │ └── __tests__/
│ │ │ │ ├── AppFrame.test.tsx
│ │ │ │ ├── AppRenderer.test.tsx
│ │ │ │ └── ProxyScript.test.ts
│ │ │ ├── index.ts
│ │ │ ├── test-setup.ts
│ │ │ ├── types.ts
│ │ │ └── utils/
│ │ │ ├── __tests__/
│ │ │ │ ├── isUIResource.test.ts
│ │ │ │ └── metadataUtils.test.ts
│ │ │ ├── app-host-utils.ts
│ │ │ ├── isUIResource.ts
│ │ │ └── metadataUtils.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.test.json
│ │ ├── vite.config.ts
│ │ └── vitest.config.ts
│ ├── server/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── __tests__/
│ │ │ │ ├── index.test.ts
│ │ │ │ ├── test-utils.ts
│ │ │ │ └── utils.test.ts
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ ├── tsconfig.json
│ │ ├── vite.config.ts
│ │ └── vitest.config.ts
│ └── shared/
│ ├── package.json
│ ├── src/
│ │ └── index.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── tsconfig.base.json
├── vitest.config.ts
└── vitest.setup.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing to MCP-UI
First of all, thank you for your interest in contributing to MCP-UI! We appreciate the time and effort you're willing to invest in improving the project. This document provides guidelines and information to make the contribution process as smooth as possible.
## Table of Contents
- [Getting Started](#getting-started)
- [Local Development](#local-development)
- [Prerequisites](#prerequisites)
- [Setting Up Your Development Environment](#setting-up-your-development-environment)
- [Running the Project Locally](#running-the-project-locally)
- [Testing](#testing)
- [Code Formatting](#code-formatting)
- [Development Workflow](#development-workflow)
- [Project Structure](#project-structure)
- [How to Contribute](#how-to-contribute)
- [Reporting Bugs](#reporting-bugs)
- [Suggesting Enhancements](#suggesting-enhancements)
- [Submitting Pull Requests](#submitting-pull-requests)
- [Style Guidelines](#style-guidelines)
- [Code Style](#code-style)
- [Commit Messages](#commit-messages)
- [Additional Resources](#additional-resources)
## Getting Started
1. Fork the repository and clone it to your local machine.
2. Set up the development environment.
3. Explore the codebase, run tests, and verify that everything works as expected.
## Local Development
### Prerequisites
Before you start working on MCP-UI, make sure you have the following installed:
- [Node.js](https://nodejs.org/) (version 18 or higher recommended)
- [pnpm](https://pnpm.io/) (version 8.15.7 or higher)
- Git
### Setting Up Your Development Environment
1. Clone your forked repository:
```bash
git clone https://github.com/your-username/mcp-ui.git
cd mcp-ui
```
2. Install dependencies:
```bash
pnpm install
```
3. Set up environment variables:
- Create a `.env.local` file in the root directory
- Add any necessary environment variables (ask project maintainers if you need access to specific API keys)
### Running the Project Locally
To start the development server:
```bash
pnpm vercel dev
```
This will start the Next.js development server, typically at http://localhost:3000.
For running with the MCP Inspector (useful for debugging MCP endpoints):
```bash
pnpm run inspector
```
### Testing
To run tests:
```bash
pnpm test
```
MCP-UI uses Vitest as the testing framework. When adding new features, please include appropriate tests.
### Code Formatting
MCP-UI uses Prettier for code formatting and lint-staged to ensure code is properly formatted before committing. Pre-commit hooks are set up with Husky to run these checks automatically.
To manually format your code:
```bash
pnpm prettier --write .
```
### Development Workflow
1. Create a new branch for your feature/bugfix
2. Make your changes
3. Add tests for your changes when applicable
4. Run the tests to ensure they pass
5. Commit your changes following the commit message guidelines
6. Push your branch and open a pull request
### Project Structure
- `api/`: Contains the server-side code and MCP implementation
- `tools/`: MCP tools implementation
- `utils/`: Utility functions for the API
- `app/`: Next.js app directory with React components
- `pages/`: Additional Next.js pages
- `public/`: Static assets
- `shared/`: Shared utilities used across the codebase
## How to Contribute
### Reporting Bugs
If you encounter a bug or issue while using MCP-UI, please open a new issue on the [GitHub Issues](https://github.com/idosal/mcp-ui/issues) page. Provide a clear and concise description of the problem, steps to reproduce it, and any relevant error messages or logs.
### Suggesting Enhancements
We welcome ideas for improvements and new features. To suggest an enhancement, open a new issue on the [GitHub Issues](https://github.com/idosal/mcp-ui/issues) page. Describe the enhancement in detail, explain the use case, and outline the benefits it would bring to the project.
### Submitting Pull Requests
1. Create a new branch for your feature or bugfix. Use a descriptive name like `feature/your-feature-name` or `fix/your-bugfix-name`.
2. Make your changes, following the [Style Guidelines](#style-guidelines) below.
3. Test your changes and ensure that they don't introduce new issues or break existing functionality.
4. Commit your changes, following the [commit message guidelines](#commit-messages).
5. Push your branch to your fork on GitHub.
6. Open a new pull request against the `main` branch of the `mcp-ui` repository. Include a clear and concise description of your changes, referencing any related issues.
## Style Guidelines
### Code Style
MCP-UI uses [ESLint](https://eslint.org/) as its code style guide. Please ensure that your code follows these guidelines.
### Commit Messages
Write clear and concise commit messages that briefly describe the changes made in each commit. Use the imperative mood and start with a capitalized verb, e.g., "Add new feature" or "Fix bug in function".
## Additional Resources
- [GitHub Help](https://help.github.com/)
- [GitHub Pull Request Documentation](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests)
- [ESLint Style Guide](https://eslint.org/)
Thank you once again for your interest in contributing to MCP UI. We look forward to collaborating with you and creating an even better project together!
================================================
FILE: .github/copilot-instructions.md
================================================
# MCP-UI Development Instructions
**ALWAYS follow these instructions first and fallback to additional search and context gathering only if the information here is incomplete or found to be in error.**
MCP-UI is a Model Context Protocol UI SDK monorepo providing TypeScript and Ruby SDKs for building MCP enabled applications with interactive UI components. The repository includes client SDKs, server SDKs, documentation, and examples.
## Working Effectively
### Bootstrap and Build (CRITICAL - Set 60+ minute timeouts)
- Install Node.js 22.x and pnpm 9+: `npm install -g pnpm`
- Install Ruby 3.x and bundler: `sudo apt-get install -y ruby ruby-dev build-essential && sudo gem install bundler`
- Clone repository: `git clone https://github.com/idosal/mcp-ui.git && cd mcp-ui`
- Install dependencies: `pnpm install` -- takes ~60 seconds. NEVER CANCEL.
- Build all packages: `pnpm build` -- takes ~15 seconds. NEVER CANCEL. Set timeout to 120+ seconds.
- Build documentation: `pnpm docs:build` -- takes ~17 seconds. NEVER CANCEL. Set timeout to 180+ seconds.
### Testing (CRITICAL - Set 30+ minute timeouts)
- Run TypeScript tests: `pnpm test:ts` -- takes ~6 seconds. NEVER CANCEL. Set timeout to 300+ seconds.
- Run Ruby tests: `pnpm test:ruby` -- takes ~1 second. NEVER CANCEL. Set timeout to 300+ seconds.
- Run all tests: `pnpm test` -- combines TypeScript and Ruby tests. NEVER CANCEL. Set timeout to 300+ seconds.
- Run with coverage: `pnpm coverage` -- NEVER CANCEL. Set timeout to 600+ seconds.
### Code Quality (Always run before committing)
- Lint code: `pnpm lint` -- takes ~2.4 seconds. Uses ESLint with TypeScript parser.
- Fix linting issues: `pnpm lint:fix`
- Format code: `pnpm format` -- Uses Prettier with single quotes, trailing commas, 100 char width.
### Development Workflow
- Run TypeScript SDKs in development: `pnpm dev` -- starts all TypeScript package dev servers in parallel.
- Run docs in development: `pnpm docs:dev` -- starts VitePress dev server.
- Preview builds: `pnpm preview` -- preview built packages.
## Validation (CRITICAL - Always perform these after changes)
### Mandatory End-to-End Validation Scenarios
1. **Always build and test after making changes:** Run `pnpm build && pnpm test` to ensure nothing is broken.
2. **Validate examples work:** Test working examples by running:
- `cd examples/remote-dom-demo && npm run build` -- takes ~1 second. Always works.
- `cd examples/wc-demo && npm run build` -- takes ~1 second. Always works.
- NOTE: `typescript-server-demo` may have import issues and should be tested separately after SDK changes.
3. **Test UI components:** When changing client components, manually verify React rendering works by running example applications.
4. **Validate SDK functionality:** When changing server SDKs, test resource creation with both TypeScript and Ruby implementations.
5. **Documentation validation:** When updating docs, run `pnpm docs:build` and verify no broken links or build errors.
### Ruby SDK Specific Validation
- Ruby gems must be installed with `sudo bundle install` in `sdks/ruby/` directory
- Run Ruby linting: `cd sdks/ruby && sudo bundle exec rubocop`
- Ruby tests validate resource creation: `cd sdks/ruby && sudo bundle exec rake spec`
## Project Structure
### Key Directories
- `sdks/typescript/` - TypeScript SDKs (client, server, shared)
- `client/` - React components for rendering MCP-UI resources
- `server/` - Utilities for creating UI resources on MCP servers
- `shared/` - Common types and utilities
- `sdks/ruby/` - Ruby SDK (`mcp_ui_server` gem)
- `examples/` - Demo applications showcasing SDK usage
- `remote-dom-demo/` - Interactive UI script testing
- `typescript-server-demo/` - Complete TypeScript server example
- `ruby-server-demo/` - Complete Ruby server example
- `docs/` - VitePress documentation site
### Critical Files
- `package.json` - Root monorepo configuration with pnpm workspaces
- `pnpm-workspace.yaml` - Workspace configuration for TypeScript packages and examples
- `.github/workflows/ci.yml` - CI pipeline with build, test, and release steps
- `vitest.config.ts` - Test configuration for TypeScript packages
- `tsconfig.base.json` - Base TypeScript configuration
## Build System Details
### Package Management
- Uses pnpm workspaces for monorepo management
- TypeScript packages use Vite for building with dual ESM/CJS output
- Ruby uses standard gem building with bundler
### Dependencies and Versions
- Node.js 22.x (required for TypeScript SDKs)
- pnpm 9+ (required for workspace management)
- Ruby 3.x (required for Ruby SDK)
- React 18+ (peer dependency for client SDK)
### Build Outputs
- TypeScript client: Builds to `dist/` with ESM, CJS, and Web Component builds
- TypeScript server: Builds to `dist/` with ESM and CJS formats
- Ruby gem: Standard gem structure in `lib/`
- Documentation: Static site built to `docs/src/.vitepress/dist/`
## Common Development Tasks
### Adding New Features
1. Determine if change affects TypeScript SDKs, Ruby SDK, or both
2. Make changes following existing patterns
3. Add appropriate tests (Vitest for TypeScript, RSpec for Ruby)
4. Update documentation if needed
5. Run full validation workflow: `pnpm build && pnpm test && pnpm lint`
### Working with Examples
- Examples use workspace dependencies and build independently
- External URL demo shows partial externalUrl content type functionality
- Remote DOM demo shows partial remote-dom content type functionality
- The Server demo shows a full MCP server implementation with Cloudflare
- The Ruby and Typescript server demos show basic MCP server implementations
- WC-demo showcases the web components client implementation
- Always test example builds after making SDK changes
### Documentation Updates
- Documentation uses VitePress with enhanced styling
- Edit files in `docs/src/` directory
- Test changes with `pnpm docs:dev` before building
- Always build docs to check for errors: `pnpm docs:build`
## Troubleshooting
### Common Issues
- **TypeScript version warnings:** Current setup uses TypeScript 5.8.3 with ESLint plugins that support <5.4.0. This is expected and working.
- **Ruby permission errors:** Use `sudo bundle install` and `sudo bundle exec` for Ruby commands in CI environment.
- **Build failures:** Always run `pnpm install` first, then check individual package builds.
- **Test failures:** Check if all dependencies are installed and built before running tests.
### CI/CD Pipeline
- CI runs on Node.js 22.x with pnpm 10
- Separate jobs for TypeScript and Ruby testing
- Path filtering prevents unnecessary builds
- Semantic release handles versioning and publishing
### Performance Notes
- TypeScript builds are fast (~15 seconds total)
- Ruby tests run very quickly (~1 second)
- Documentation builds may take longer (~17 seconds)
- Full CI pipeline completes in under 5 minutes
## CRITICAL REMINDERS
- **NEVER CANCEL builds or long-running commands** - Always set appropriate timeouts
- **Always validate changes** with complete build and test cycle
- **Test examples** after SDK changes to ensure compatibility
- **Run linting and formatting** before committing changes
- **Update documentation** when adding or changing features
- **Use workspace commands** from root directory for consistency
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches:
- main
- alpha
pull_request:
branches:
- main
- alpha
release:
types: [published]
jobs:
filter_changed_paths:
runs-on: ubuntu-latest
outputs:
ts_client_files: ${{ steps.filter.outputs.ts_client_files }}
ts_server_files: ${{ steps.filter.outputs.ts_server_files }}
ruby_sdk_files: ${{ steps.filter.outputs.ruby_sdk_files }}
python_sdk_files: ${{ steps.filter.outputs.python_sdk_files }}
example_files: ${{ steps.filter.outputs.example_files }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dorny/paths-filter@v2
id: filter
with:
base: ${{ github.event_name == 'push' && github.event.before || github.base_ref }}
filters: |
ts_client_files:
- 'sdks/typescript/client/**'
ts_server_files:
- 'sdks/typescript/server/**'
ruby_sdk_files:
- 'sdks/ruby/**'
python_sdk_files:
- 'sdks/python/**'
example_files:
- 'examples/**'
js_build_and_test:
needs: filter_changed_paths
if: needs.filter_changed_paths.outputs.ts_client_files == 'true' || needs.filter_changed_paths.outputs.ts_server_files == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Lint
run: pnpm lint
- name: Test
run: pnpm test:ts
- name: Build
run: pnpm build
ruby_sdk_test:
needs: filter_changed_paths
if: needs.filter_changed_paths.outputs.ruby_sdk_files == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler: latest
bundler-cache: true
working-directory: 'sdks/ruby'
- name: Lint
run: bundle exec rubocop
working-directory: 'sdks/ruby'
- name: Run tests
run: pnpm test:ruby
python_sdk_test:
needs: filter_changed_paths
if: needs.filter_changed_paths.outputs.python_sdk_files == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Set up uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
- name: Install dependencies
run: uv sync --dev
working-directory: sdks/python/server
- name: Lint with ruff
run: uv run ruff check
working-directory: sdks/python/server
- name: Type check with pyright
run: uv run pyright
working-directory: sdks/python/server
- name: Run tests
run: uv run pytest
working-directory: sdks/python/server
- name: Build package
run: uv build
working-directory: sdks/python/server
release_ts_client:
needs: [js_build_and_test, filter_changed_paths]
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/alpha') && needs.filter_changed_paths.outputs.ts_client_files == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write # to be able to comment on released pull requests
issues: write
id-token: write
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Fetch all tags
run: git fetch --tags --force
- name: Setup pnpm
uses: pnpm/action-setup@v2
with: { version: 10 }
- name: Setup Node.js
uses: actions/setup-node@v4
with: { node-version: 22.x, cache: 'pnpm' }
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Release
working-directory: sdks/typescript/client
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release
release_ts_server:
needs: [js_build_and_test, release_ts_client, filter_changed_paths]
if: >
always() &&
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/alpha') &&
needs.filter_changed_paths.outputs.ts_server_files == 'true' &&
needs.js_build_and_test.result == 'success'
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
id-token: write
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Pull latest changes
run: git pull --rebase origin ${{ github.ref_name }}
- name: Fetch all tags
run: git fetch --tags --force
- name: Setup pnpm
uses: pnpm/action-setup@v2
with: { version: 10 }
- name: Setup Node.js
uses: actions/setup-node@v4
with: { node-version: 22.x, cache: 'pnpm' }
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Release
working-directory: sdks/typescript/server
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release
release_ruby_sdk:
name: Release Ruby SDK
needs: [ruby_sdk_test, filter_changed_paths, release_ts_server]
if: >
always() &&
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/alpha') &&
needs.filter_changed_paths.outputs.ruby_sdk_files == 'true' &&
needs.ruby_sdk_test.result == 'success'
runs-on: ubuntu-latest
environment: release
permissions:
contents: write # to push commits and tags
id-token: write # for trusted publishing
issues: write # to comment on issues
pull-requests: write # to comment on pull requests
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Pull latest changes
run: git pull --rebase origin ${{ github.ref_name }}
- name: Setup pnpm
uses: pnpm/action-setup@v2
with: { version: 10 }
- name: Setup Node.js
uses: actions/setup-node@v4
with: { node-version: 22.x, cache: 'pnpm' }
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler: latest
bundler-cache: true
working-directory: sdks/ruby
- name: Configure RubyGems Credentials
uses: rubygems/configure-rubygems-credentials@main
- name: Release
working-directory: sdks/ruby
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
release_python_sdk:
name: Release Python SDK
needs: [python_sdk_test, filter_changed_paths, release_ruby_sdk]
if: >
always() &&
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/alpha') &&
needs.filter_changed_paths.outputs.python_sdk_files == 'true' &&
needs.python_sdk_test.result == 'success'
runs-on: ubuntu-latest
environment: release
permissions:
contents: write # to push commits and tags
id-token: write # for trusted publishing to PyPI
issues: write # to comment on issues
pull-requests: write # to comment on pull requests
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Pull latest changes
run: git pull --rebase origin ${{ github.ref_name }}
- name: Fetch all tags
run: git fetch --tags --force
- name: Setup pnpm
uses: pnpm/action-setup@v2
with: { version: 10 }
- name: Setup Node.js
uses: actions/setup-node@v4
with: { node-version: 22.x, cache: 'pnpm' }
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Set up uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
- name: Install Python dependencies
run: uv sync --dev
working-directory: sdks/python/server
- name: Release
working-directory: sdks/python/server
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
================================================
FILE: .github/workflows/deploy-docs.yml
================================================
#
name: Deploy VitePress site to Pages
on:
# Runs on pushes targeting the `main` branch. Change this to `master` if you\'re
# using the `master` branch as the default branch.
push:
branches: [main]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: pages
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled
- name: Use pnpm
uses: pnpm/action-setup@v3
with:
version: 10 # Specify pnpm version from package.json
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: pnpm install
- name: Build with VitePress
run: pnpm docs:build # This script is \'vitepress build docs\'
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
# Path to the output directory of 'vitepress build docs/src'
path: docs/src/.vitepress/dist
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
examples/server/build
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# PNPM
.pnpm-store/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarnclean
# dotenv environment variables file
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
DynamoDBLocal_lib/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# Parcel cache files
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build output
.nuxt
# Svelte build output
.svelte-kit
# Docusaurus build output
.docusaurus
# Gatsby cache
.cache/
# Vite build output
dist
# VitePress
docs/src/.vitepress/dist
docs/.vitepress/cache
docs/src/.vitepress/cache
# Monorepo specific
/sdks/**/dist
/sdks/**/coverage
/examples/**/dist
/examples/**/coverage
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# IDEs and Editors #
####################
# VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# JetBrains
.idea/
*.iws
*.iml
*.ipr
# Sublime Text
*.sublime-project
*.sublime-workspace
# Compilation artifacts
#######################
*.o
*.obj
*.exe
*.dll
*.so
*.dylib
# C/C++ specific
*.gch
*.pch
# Python specific
__pycache__/
*.py[cod]
*$py.class
# Environment variables
.env*
!.env.example
# Husky
.husky/_/
.husky/.gitignore
# Auto-generated files
adapter-runtime.bundled.ts
================================================
FILE: .prettierignore
================================================
# Ignore artifacts:
node_modules
dist
build
coverage
venv
# Ignore package manager files (if you commit them)
package-lock.json
yarn.lock
pnpm-lock.yaml
# Ignore specific configuration files if managed elsewhere or intentionally unformatted
# .vscode/
# .idea/
# Ignore generated/bundled files
*.generated.*
*.bundled.ts
docs/src/.vitepress/cache
extensions/vscode/out
*.log
*.md
================================================
FILE: .prettierrc.json
================================================
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"plugins": ["prettier-plugin-tailwindcss"]
}
================================================
FILE: .releaserc.json
================================================
{
"branches": [
"main",
{
"name": "alpha",
"prerelease": true
}
],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
[
"@semantic-release/npm",
{
"pkgRoot": "sdks/typescript/client",
"npmPublish": true
}
],
[
"@semantic-release/npm",
{
"pkgRoot": "sdks/typescript/server",
"npmPublish": true
}
],
[
"@semantic-release/npm",
{
"npmPublish": false
}
],
[
"@semantic-release/exec",
{
"prepareCmd": "pnpm install --lockfile-only --ignore-scripts"
}
],
[
"@semantic-release/git",
{
"assets": [
"CHANGELOG.md",
"package.json",
"sdks/typescript/client/package.json",
"sdks/typescript/server/package.json",
"pnpm-lock.yaml"
],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
],
"@semantic-release/github"
]
}
================================================
FILE: .ruby-version
================================================
3.2.2
================================================
FILE: CHANGELOG.md
================================================
# [5.2.0](https://github.com/idosal/mcp-ui/compare/v5.1.2...v5.2.0) (2025-07-18)
### Features
* support generic messages response ([#35](https://github.com/idosal/mcp-ui/issues/35)) ([10b407b](https://github.com/idosal/mcp-ui/commit/10b407b279b3ee9608ef077445f4d714f88343c5))
## [5.1.2](https://github.com/idosal/mcp-ui/compare/v5.1.1...v5.1.2) (2025-07-18)
### Bug Fixes
* use targetOrigin in the proxy message relay ([#40](https://github.com/idosal/mcp-ui/issues/40)) ([b3fb54e](https://github.com/idosal/mcp-ui/commit/b3fb54e28ca7b8eeda896b5bcf478b6343dbba47))
## [5.1.1](https://github.com/idosal/mcp-ui/compare/v5.1.0...v5.1.1) (2025-07-18)
### Bug Fixes
* add a bridge to pass messages in and out of the proxy ([#38](https://github.com/idosal/mcp-ui/issues/38)) ([30ccac0](https://github.com/idosal/mcp-ui/commit/30ccac0706ad8e02ebcd8960924ed1d58ddedf85))
# [5.1.0](https://github.com/idosal/mcp-ui/compare/v5.0.0...v5.1.0) (2025-07-18)
### Features
* add proxy option to externalUrl ([#37](https://github.com/idosal/mcp-ui/issues/37)) ([7b95cd0](https://github.com/idosal/mcp-ui/commit/7b95cd0b3873fc1cde28748ec463e81c6ff1c494))
# [5.0.0](https://github.com/idosal/mcp-ui/compare/v4.1.4...v5.0.0) (2025-07-17)
### Bug Fixes
* rename delivery -> encoding and flavor -> framework ([#36](https://github.com/idosal/mcp-ui/issues/36)) ([9a509ed](https://github.com/idosal/mcp-ui/commit/9a509ed80d051b0a8042b36958b401a0a7c1e138))
### BREAKING CHANGES
* The existing naming is ambiguous. Renaming delivery to encoding and flavor to framework should clarify the intent.
## [4.1.4](https://github.com/idosal/mcp-ui/compare/v4.1.3...v4.1.4) (2025-07-16)
### Bug Fixes
* pass ref explicitly using iframeProps ([#33](https://github.com/idosal/mcp-ui/issues/33)) ([d01b5d1](https://github.com/idosal/mcp-ui/commit/d01b5d1e4cdaedc436ba2fa8984d866d93d59087))
## [4.1.3](https://github.com/idosal/mcp-ui/compare/v4.1.2...v4.1.3) (2025-07-15)
### Bug Fixes
* ref passing to UIResourceRenderer ([#32](https://github.com/idosal/mcp-ui/issues/32)) ([d28c23f](https://github.com/idosal/mcp-ui/commit/d28c23f9b8ee320f4e361200ae02a23f0d2a1c0c))
## [4.1.2](https://github.com/idosal/mcp-ui/compare/v4.1.1...v4.1.2) (2025-07-10)
### Bug Fixes
* validate URL ([b7c994d](https://github.com/idosal/mcp-ui/commit/b7c994dfdd947b3dfbb903fc8cb896d61004c8d8))
## [4.1.1](https://github.com/idosal/mcp-ui/compare/v4.1.0...v4.1.1) (2025-07-06)
### Bug Fixes
* text and blob support in RemoteDOM resources ([ec68eb9](https://github.com/idosal/mcp-ui/commit/ec68eb90df984da8b492cc25eafdafdeda79f299))
# [4.1.0](https://github.com/idosal/mcp-ui/compare/v4.0.0...v4.1.0) (2025-07-05)
### Features
* separate html and remote-dom props ([#24](https://github.com/idosal/mcp-ui/issues/24)) ([a7f0529](https://github.com/idosal/mcp-ui/commit/a7f05299dc9cc40184f9ab25c5b648ee7077be64))
# [4.0.0](https://github.com/idosal/mcp-ui/compare/v3.0.0...v4.0.0) (2025-07-05)
### Bug Fixes
* rename components and methods to fit new scope ([#22](https://github.com/idosal/mcp-ui/issues/22)) ([6bab1fe](https://github.com/idosal/mcp-ui/commit/6bab1fe3a168a18e7ba4762e23478abf4e0cc84c))
### BREAKING CHANGES
* exported names have changed
# [3.0.0](https://github.com/idosal/mcp-ui/compare/v2.5.1...v3.0.0) (2025-07-04)
### Features
* switch to UiResourceRenderer ([#21](https://github.com/idosal/mcp-ui/issues/21)) ([6fe3166](https://github.com/idosal/mcp-ui/commit/6fe316682675e27db914d60696754677e3783448))
### BREAKING CHANGES
* removed deprecated client API
## [2.5.1](https://github.com/idosal/mcp-ui/compare/v2.5.0...v2.5.1) (2025-06-28)
### Bug Fixes
* export RemoteDomResource ([2b86f2d](https://github.com/idosal/mcp-ui/commit/2b86f2dd4506de49c69908e23d84a2a323170446))
* export UiResourceRenderer and HtmlResource ([2b841a5](https://github.com/idosal/mcp-ui/commit/2b841a556c1111ed70ccb3d3987afd21fe7df897))
# [2.5.0](https://github.com/idosal/mcp-ui/compare/v2.4.0...v2.5.0) (2025-06-27)
### Features
* add remote-dom content type ([#18](https://github.com/idosal/mcp-ui/issues/18)) ([5dacf37](https://github.com/idosal/mcp-ui/commit/5dacf37c22b5ee6ae795049a8d573fc073b8a1f5))
# [2.4.0](https://github.com/idosal/mcp-ui/compare/v2.3.3...v2.4.0) (2025-06-20)
### Features
* **client:** allow setting supportedContentTypes for HtmlResource ([#17](https://github.com/idosal/mcp-ui/issues/17)) ([e009ef1](https://github.com/idosal/mcp-ui/commit/e009ef10010134ba3d9893314cc4d8e1274f1f07))
## [2.3.3](https://github.com/idosal/mcp-ui/compare/v2.3.2...v2.3.3) (2025-06-19)
### Bug Fixes
* typescript types to be compatible with MCP SDK ([#10](https://github.com/idosal/mcp-ui/issues/10)) ([74365d7](https://github.com/idosal/mcp-ui/commit/74365d7ed6422beef6cd9ee0f5a97c847bd9827b))
## [2.3.2](https://github.com/idosal/mcp-ui/compare/v2.3.1...v2.3.2) (2025-06-14)
### Bug Fixes
* trigger release ([aaca831](https://github.com/idosal/mcp-ui/commit/aaca83125c3f7825ccdebf0f04f8553e953c5249))
## [2.3.1](https://github.com/idosal/mcp-ui/compare/v2.3.0...v2.3.1) (2025-06-14)
### Bug Fixes
* iframe handle ([#15](https://github.com/idosal/mcp-ui/issues/15)) ([66bd4fd](https://github.com/idosal/mcp-ui/commit/66bd4fd3d04f82e3e4557f064e701b68e1d8af11))
# [2.3.0](https://github.com/idosal/mcp-ui/compare/v2.2.0...v2.3.0) (2025-06-13)
### Features
* pass iframe props down ([#14](https://github.com/idosal/mcp-ui/issues/14)) ([112539d](https://github.com/idosal/mcp-ui/commit/112539d28640a96e8375a6b416f2ba559370b312))
# [2.2.0](https://github.com/idosal/mcp-ui/compare/v2.1.0...v2.2.0) (2025-06-03)
### Features
* support ui action result types ([#6](https://github.com/idosal/mcp-ui/issues/6)) ([899d152](https://github.com/idosal/mcp-ui/commit/899d1527286a281a23fbb8f3a207d435dfc3fe96))
# [2.1.0](https://github.com/idosal/mcp-ui/compare/v2.0.0...v2.1.0) (2025-05-31)
### Features
* consolidate ui:// and ui-app:// ([#8](https://github.com/idosal/mcp-ui/issues/8)) ([2e08035](https://github.com/idosal/mcp-ui/commit/2e08035676bb6a46ef3c94dba916bc895f1fa3cc))
# [2.0.0](https://github.com/idosal/mcp-ui/compare/v1.1.0...v2.0.0) (2025-05-23)
### Documentation
* bump ([#4](https://github.com/idosal/mcp-ui/issues/4)) ([ad4d163](https://github.com/idosal/mcp-ui/commit/ad4d1632cc1f9c99072349a8f0cdaac343236132))
### BREAKING CHANGES
* (previous one didn't take due to semantic-release misalignment)
# [1.1.0](https://github.com/idosal/mcp-ui/compare/v1.0.7...v1.1.0) (2025-05-16)
### Bug Fixes
* update deps ([4091ef4](https://github.com/idosal/mcp-ui/commit/4091ef47da048fab3c4feb002f5287b2ff295744))
### Features
* change onGenericMcpAction to optional onUiAction ([1913b59](https://github.com/idosal/mcp-ui/commit/1913b5977c30811f9e67659949e2d961f2eda983))
## [1.0.7](https://github.com/idosal/mcp-ui/compare/v1.0.6...v1.0.7) (2025-05-16)
### Bug Fixes
* **client:** specify iframe ([fd0b70a](https://github.com/idosal/mcp-ui/commit/fd0b70a84948d3aa5d7a79269ff7c3bcd0946689))
## [1.0.6](https://github.com/idosal/mcp-ui/compare/v1.0.5...v1.0.6) (2025-05-16)
### Bug Fixes
* support react-router ([21ffb95](https://github.com/idosal/mcp-ui/commit/21ffb95fe6d77a348b95b38dbf3741ba6442894e))
## [1.0.5](https://github.com/idosal/mcp-ui/compare/v1.0.4...v1.0.5) (2025-05-16)
### Bug Fixes
* **client:** styling ([6ff9b68](https://github.com/idosal/mcp-ui/commit/6ff9b685fd1be770fd103943e45275e9ec86905c))
## [1.0.4](https://github.com/idosal/mcp-ui/compare/v1.0.3...v1.0.4) (2025-05-16)
### Bug Fixes
* packaging ([9e6babd](https://github.com/idosal/mcp-ui/commit/9e6babd3a587213452ea7aec4cc9ae3a50fa1965))
## [1.0.3](https://github.com/idosal/mcp-ui/compare/v1.0.2...v1.0.3) (2025-05-16)
### Bug Fixes
* exports ([3a93a16](https://github.com/idosal/mcp-ui/commit/3a93a16e1b7438ba7b2ef49ca854479f755abcc6))
## [1.0.2](https://github.com/idosal/mcp-ui/compare/v1.0.1...v1.0.2) (2025-05-16)
### Bug Fixes
* remove shared dependency ([e66e8f4](https://github.com/idosal/mcp-ui/commit/e66e8f49b1ba46090db6e4682060488566f4fe41))
## [1.0.1](https://github.com/idosal/mcp-ui/compare/v1.0.0...v1.0.1) (2025-05-16)
### Bug Fixes
* publish ([0943e7a](https://github.com/idosal/mcp-ui/commit/0943e7acaf17f32aae085c2313bfbec47bc59f1f))
# 1.0.0 (2025-05-16)
### Bug Fixes
* dependencies ([887f61f](https://github.com/idosal/mcp-ui/commit/887f61f827b4585c17493d4fa2dfb251ea598587))
* lint ([4487820](https://github.com/idosal/mcp-ui/commit/44878203a71c3c9173d463b809be36769e996ba9))
* lint ([d0a91f9](https://github.com/idosal/mcp-ui/commit/d0a91f9a07ec0042690240c3d8d0bad620f8c765))
* package config ([8dc1e53](https://github.com/idosal/mcp-ui/commit/8dc1e5358c3c8e641206a5e6851427d360cc1955))
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
idosalomon@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2025 Ido Salomon
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
## 📦 Model Context Protocol UI SDK
<p align="center">
<img width="250" alt="image" src="https://github.com/user-attachments/assets/65b9698f-990f-4846-9b2d-88de91d53d4d" />
</p>
<p align="center">
<a href="https://www.npmjs.com/package/@mcp-ui/server"><img src="https://img.shields.io/npm/v/@mcp-ui/server?label=server&color=green" alt="Server Version"></a>
<a href="https://www.npmjs.com/package/@mcp-ui/client"><img src="https://img.shields.io/npm/v/@mcp-ui/client?label=client&color=blue" alt="Client Version"></a>
<a href="https://rubygems.org/gems/mcp_ui_server"><img src="https://img.shields.io/gem/v/mcp_ui_server" alt="Ruby Server SDK Version"></a>
<a href="https://pypi.org/project/mcp-ui-server/"><img src="https://img.shields.io/pypi/v/mcp-ui-server?label=python&color=yellow" alt="Python Server SDK Version"></a>
<a href="https://discord.gg/CEAG4KW7ZH"><img src="https://img.shields.io/discord/1401195140436983879?logo=discord&label=discord" alt="Discord"></a>
<a href="https://gitmcp.io/idosal/mcp-ui"><img src="https://img.shields.io/endpoint?url=https://gitmcp.io/badge/idosal/mcp-ui" alt="MCP Documentation"></a>
</p>
<p align="center">
<a href="#-whats-mcp-ui">What's mcp-ui?</a> •
<a href="#-core-concepts">Core Concepts</a> •
<a href="#-installation">Installation</a> •
<a href="#-getting-started">Getting Started</a> •
<a href="#-walkthrough">Walkthrough</a> •
<a href="#-examples">Examples</a> •
<a href="#-supported-hosts">Supported Hosts</a> •
<a href="#-security">Security</a> •
<a href="#-roadmap">Roadmap</a> •
<a href="#-contributing">Contributing</a> •
<a href="#-license">License</a>
</p>
----
**`mcp-ui`** pioneered the concept of interactive UI over [MCP](https://modelcontextprotocol.io/introduction), enabling rich web interfaces for AI tools. Alongside Apps SDK, the patterns developed here directly influenced the [MCP Apps](https://github.com/modelcontextprotocol/ext-apps) specification, which standardized UI delivery over the protocol.
The `@mcp-ui/*` packages implement the MCP Apps standard. `@mcp-ui/client` is the recommended SDK for MCP Apps Hosts.
> *The @mcp-ui/* packages are fully compliant with the MCP Apps specification and ready for production use.*
<p align="center">
<video src="https://github.com/user-attachments/assets/7180c822-2dd9-4f38-9d3e-b67679509483"></video>
</p>
## 💡 What's `mcp-ui`?
`mcp-ui` is an SDK implementing the [MCP Apps](https://github.com/modelcontextprotocol/ext-apps) standard for UI over MCP. It provides:
* **`@mcp-ui/server` (TypeScript)**: Create UI resources with `createUIResource`. Works with `registerAppTool` and `registerAppResource` from `@modelcontextprotocol/ext-apps/server`.
* **`@mcp-ui/client` (TypeScript)**: Render tool UIs with `AppRenderer` (MCP Apps) or `UIResourceRenderer` (legacy MCP-UI hosts).
* **`mcp_ui_server` (Ruby)**: Create UI resources in Ruby.
* **`mcp-ui-server` (Python)**: Create UI resources in Python.
The MCP Apps pattern links tools to their UIs via `_meta.ui.resourceUri`. Hosts fetch and render the UI alongside tool results.
## ✨ Core Concepts
### MCP Apps Pattern (Recommended)
The MCP Apps standard links tools to their UIs via `_meta.ui.resourceUri`:
```ts
import { registerAppTool, registerAppResource } from '@modelcontextprotocol/ext-apps/server';
import { createUIResource } from '@mcp-ui/server';
// 1. Create UI resource
const widgetUI = await createUIResource({
uri: 'ui://my-server/widget',
content: { type: 'rawHtml', htmlString: '<h1>Widget</h1>' },
encoding: 'text',
});
// 2. Register resource handler
registerAppResource(server, 'widget_ui', widgetUI.resource.uri, {}, async () => ({
contents: [widgetUI.resource]
}));
// 3. Register tool with _meta linking
registerAppTool(server, 'show_widget', {
description: 'Show widget',
inputSchema: { query: z.string() },
_meta: { ui: { resourceUri: widgetUI.resource.uri } } // Links tool → UI
}, async ({ query }) => {
return { content: [{ type: 'text', text: `Query: ${query}` }] };
});
```
Hosts detect `_meta.ui.resourceUri`, fetch the UI via `resources/read`, and render it with `AppRenderer`.
### UIResource (Wire Format)
The underlying payload for UI content:
```ts
interface UIResource {
type: 'resource';
resource: {
uri: string; // e.g., ui://component/id
mimeType: 'text/html;profile=mcp-app';
text?: string; // HTML content
blob?: string; // Base64-encoded HTML content
};
}
```
* **`uri`**: Unique identifier using `ui://` scheme
* **`mimeType`**: `text/html;profile=mcp-app` — the MCP Apps standard MIME type
* **`text` vs. `blob`**: Plain text or Base64-encoded content
### Client Components
#### AppRenderer (MCP Apps)
For MCP Apps hosts, use `AppRenderer` to render tool UIs:
```tsx
import { AppRenderer } from '@mcp-ui/client';
function ToolUI({ client, toolName, toolInput, toolResult }) {
return (
<AppRenderer
client={client}
toolName={toolName}
sandbox={{ url: sandboxUrl }}
toolInput={toolInput}
toolResult={toolResult}
onOpenLink={async ({ url }) => window.open(url)}
onMessage={async (params) => console.log('Message:', params)}
/>
);
}
```
Key props:
- **`client`**: Optional MCP client for automatic resource fetching
- **`toolName`**: Tool name to render UI for
- **`sandbox`**: Sandbox configuration with proxy URL
- **`toolInput`** / **`toolResult`**: Tool arguments and results
- **`onOpenLink`** / **`onMessage`**: Handlers for UI requests
#### UIResourceRenderer (Legacy MCP-UI)
For legacy hosts that embed resources in tool responses:
```tsx
import { UIResourceRenderer } from '@mcp-ui/client';
<UIResourceRenderer
resource={mcpResource.resource}
onUIAction={(action) => console.log('Action:', action)}
/>
```
Props:
- **`resource`**: Resource object with `uri`, `mimeType`, and content (`text`/`blob`)
- **`onUIAction`**: Callback for handling tool, prompt, link, notify, and intent actions
Also available as a Web Component:
```html
<ui-resource-renderer
resource='{ "mimeType": "text/html", "text": "<h2>Hello!</h2>" }'
></ui-resource-renderer>
```
### Supported Resource Types
#### HTML (`text/html;profile=mcp-app`)
Rendered using the internal `<HTMLResourceRenderer />` component, which displays content inside an `<iframe>`. This is suitable for self-contained HTML.
* **`mimeType`**: `text/html;profile=mcp-app` (MCP Apps standard)
### UI Action
UI snippets must be able to interact with the agent. In `mcp-ui`, this is done by hooking into events sent from the UI snippet and reacting to them in the host (see `onUIAction` prop). For example, an HTML may trigger a tool call when a button is clicked by sending an event which will be caught handled by the client.
### Platform Adapters
MCP-UI SDKs includes adapter support for host-specific implementations, enabling your open MCP-UI widgets to work seamlessly regardless of host. Adapters automatically translate between MCP-UI's `postMessage` protocol and host-specific APIs. Over time, as hosts become compatible with the open spec, these adapters wouldn't be needed.
#### Available Adapters
##### Apps SDK Adapter
For Apps SDK environments (e.g., ChatGPT), this adapter translates MCP-UI protocol to Apps SDK API calls (e.g., `window.openai`).
**How it Works:**
- Intercepts MCP-UI `postMessage` calls from your widgets
- Translates them to appropriate Apps SDK API calls
- Handles bidirectional communication (tools, prompts, state management)
- Works transparently - your existing MCP-UI code continues to work without changes
**Usage:**
```ts
import { createUIResource } from '@mcp-ui/server';
const htmlResource = await createUIResource({
uri: 'ui://greeting/1',
content: {
type: 'rawHtml',
htmlString: `
<button onclick="window.parent.postMessage({ type: 'tool', payload: { toolName: 'myTool', params: {} } }, '*')">
Call Tool
</button>
`
},
encoding: 'text',
});
```
## 🏗️ Installation
### TypeScript
```bash
# using npm
npm install @mcp-ui/server @mcp-ui/client
# or pnpm
pnpm add @mcp-ui/server @mcp-ui/client
# or yarn
yarn add @mcp-ui/server @mcp-ui/client
```
### Ruby
```bash
gem install mcp_ui_server
```
### Python
```bash
# using pip
pip install mcp-ui-server
# or uv
uv add mcp-ui-server
```
## 🚀 Getting Started
You can use [GitMCP](https://gitmcp.io/idosal/mcp-ui) to give your IDE access to `mcp-ui`'s latest documentation!
### TypeScript (MCP Apps Pattern)
1. **Server-side**: Create a tool with UI using `_meta.ui.resourceUri`
```ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerAppTool, registerAppResource } from '@modelcontextprotocol/ext-apps/server';
import { createUIResource } from '@mcp-ui/server';
import { z } from 'zod';
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
// Create UI resource
const widgetUI = await createUIResource({
uri: 'ui://my-server/widget',
content: { type: 'rawHtml', htmlString: '<h1>Interactive Widget</h1>' },
encoding: 'text',
});
// Register resource handler
registerAppResource(server, 'widget_ui', widgetUI.resource.uri, {}, async () => ({
contents: [widgetUI.resource]
}));
// Register tool with _meta linking
registerAppTool(server, 'show_widget', {
description: 'Show widget',
inputSchema: { query: z.string() },
_meta: { ui: { resourceUri: widgetUI.resource.uri } }
}, async ({ query }) => {
return { content: [{ type: 'text', text: `Query: ${query}` }] };
});
```
2. **Client-side**: Render tool UIs with `AppRenderer`
```tsx
import { AppRenderer } from '@mcp-ui/client';
function ToolUI({ client, toolName, toolInput, toolResult }) {
return (
<AppRenderer
client={client}
toolName={toolName}
sandbox={{ url: sandboxUrl }}
toolInput={toolInput}
toolResult={toolResult}
onOpenLink={async ({ url }) => window.open(url)}
onMessage={async (params) => console.log('Message:', params)}
/>
);
}
```
### Legacy MCP-UI Pattern
For hosts that don't support MCP Apps yet:
```tsx
import { UIResourceRenderer } from '@mcp-ui/client';
<UIResourceRenderer
resource={mcpResource.resource}
onUIAction={(action) => console.log('Action:', action)}
/>
```
### Python
**Server-side**: Build your UI resources
```python
from mcp_ui_server import create_ui_resource
# Inline HTML
html_resource = create_ui_resource({
"uri": "ui://greeting/1",
"content": { "type": "rawHtml", "htmlString": "<p>Hello, from Python!</p>" },
"encoding": "text",
})
# External URL
external_url_resource = create_ui_resource({
"uri": "ui://greeting/2",
"content": { "type": "externalUrl", "iframeUrl": "https://example.com" },
"encoding": "text",
})
```
### Ruby
**Server-side**: Build your UI resources
```ruby
require 'mcp_ui_server'
# Inline HTML
html_resource = McpUiServer.create_ui_resource(
uri: 'ui://greeting/1',
content: { type: :raw_html, htmlString: '<p>Hello, from Ruby!</p>' },
encoding: :text
)
# External URL
external_url_resource = McpUiServer.create_ui_resource(
uri: 'ui://greeting/2',
content: { type: :external_url, iframeUrl: 'https://example.com' },
encoding: :text
)
# remote-dom
remote_dom_resource = McpUiServer.create_ui_resource(
uri: 'ui://remote-component/action-button',
content: {
type: :remote_dom,
script: "
const button = document.createElement('ui-button');
button.setAttribute('label', 'Click me from Ruby!');
button.addEventListener('press', () => {
window.parent.postMessage({ type: 'tool', payload: { toolName: 'uiInteraction', params: { action: 'button-click', from: 'ruby-remote-dom' } } }, '*');
});
root.appendChild(button);
",
framework: :react,
},
encoding: :text
)
```
## 🚶 Walkthrough
For a detailed, simple, step-by-step guide on how to integrate `mcp-ui` into your own server, check out the full server walkthroughs on the [mcp-ui documentation site](https://mcpui.dev):
- **[TypeScript Server Walkthrough](https://mcpui.dev/guide/server/typescript/walkthrough)**
- **[Ruby Server Walkthrough](https://mcpui.dev/guide/server/ruby/walkthrough)**
- **[Python Server Walkthrough](https://mcpui.dev/guide/server/python/walkthrough)**
These guides will show you how to add a `mcp-ui` endpoint to an existing server, create tools that return UI resources, and test your setup with the `ui-inspector`!
## 🌍 Examples
**Client Examples**
* [Goose](https://github.com/block/goose) - open source AI agent that supports `mcp-ui`.
* [LibreChat](https://github.com/danny-avila/LibreChat) - enhanced ChatGPT clone that supports `mcp-ui`.
* [ui-inspector](https://github.com/idosal/ui-inspector) - inspect local `mcp-ui`-enabled servers.
* [MCP-UI Chat](https://github.com/idosal/scira-mcp-ui-chat) - interactive chat built with the `mcp-ui` client. Check out the [hosted version](https://scira-mcp-chat-git-main-idosals-projects.vercel.app/)!
* MCP-UI RemoteDOM Playground (`examples/remote-dom-demo`) - local demo app to test RemoteDOM resources
* MCP-UI Web Component Demo (`examples/wc-demo`) - local demo app to test the Web Component integration in hosts
**Server Examples**
* **TypeScript**: A [full-featured server](examples/server) that is deployed to a hosted environment for easy testing.
* **[`typescript-server-demo`](./examples/typescript-server-demo)**: A simple Typescript server that demonstrates how to generate UI resources.
* **server**: A [full-featured Typescript server](examples/server) that is deployed to a hosted Cloudflare environment for easy testing.
* **HTTP Streaming**: `https://remote-mcp-server-authless.idosalomon.workers.dev/mcp`
* **SSE**: `https://remote-mcp-server-authless.idosalomon.workers.dev/sse`
* **Ruby**: A barebones [demo server](/examples/ruby-server-demo) that shows how to use `mcp_ui_server` and `mcp` gems together.
* **Python**: A simple [demo server](/examples/python-server-demo) that shows how to use the `mcp-ui-server` Python package.
* [XMCP](https://github.com/basementstudio/xmcp/tree/main/examples/mcp-ui) - Typescript MCP framework with `mcp-ui` starter example.
Drop those URLs into any MCP-compatible host to see `mcp-ui` in action. For a supported local inspector, see the [ui-inspector](https://github.com/idosal/ui-inspector).
## 💻 Supported Hosts
The `@mcp-ui/*` packages work with both MCP Apps hosts and legacy MCP-UI hosts.
### MCP Apps Hosts
These hosts implement the [MCP Apps specification](https://github.com/modelcontextprotocol/ext-apps) and support tools with `_meta.ui.resourceUri`:
| Host | Notes |
| :--- | :---- |
| [Claude](https://www.claude.ai/) | ✅ | ✅ |
| [VSCode](https://github.com/microsoft/vscode/issues/260218) | |
| [Postman](https://www.postman.com/) | |
| [Goose](https://block.github.io/goose/) | |
| [MCPJam](https://www.mcpjam.com/) | |
| [LibreChat](https://www.librechat.ai/) | |
| [mcp-use](https://mcp-use.com/) | |
| [Smithery](https://smithery.ai/playground) | |
### Legacy MCP-UI Hosts
These hosts expect UI resources embedded directly in tool responses:
| Host | Rendering | UI Actions | Notes |
| :--- | :-------: | :--------: | :---- |
| [Nanobot](https://www.nanobot.ai/) | ✅ | ✅ |
| [MCPJam](https://www.mcpjam.com/) | ✅ | ✅ |
| [Postman](https://www.postman.com/) | ✅ | ⚠️ | |
| [Goose](https://block.github.io/goose/) | ✅ | ⚠️ | |
| [LibreChat](https://www.librechat.ai/) | ✅ | ⚠️ | |
| [Smithery](https://smithery.ai/playground) | ✅ | ❌ | |
| [fast-agent](https://fast-agent.ai/mcp/mcp-ui/) | ✅ | ❌ | |
### Hosts Requiring Adapters
| Host | Protocol | Notes |
| :--- | :------: | :---- |
| [ChatGPT](https://chatgpt.com/) | Apps SDK | [Guide](https://mcpui.dev/guide/apps-sdk) |
**Legend:** ✅ Supported · ⚠️ Partial · ❌ Not yet supported
## 🔒 Security
Host and user security is one of `mcp-ui`'s primary concerns. In all content types, the remote code is executed in a sandboxed iframe.
## 🛣️ Roadmap
- [X] Add online playground
- [X] Expand UI Action API (beyond tool calls)
- [X] Support Web Components
- [X] Support Remote-DOM
- [ ] Add component libraries (in progress)
- [ ] Add SDKs for additional programming languages (in progress; Ruby, Python available)
- [ ] Support additional frontend frameworks
- [ ] Explore providing a UI SDK (in addition to the client and server one)
- [ ] Add declarative UI content type
- [ ] Support generative UI?
## Core Team
`mcp-ui` is a project by [Ido Salomon](https://x.com/idosal1), in collaboration with [Liad Yosef](https://x.com/liadyosef).
## 🤝 Contributing
Contributions, ideas, and bug reports are welcome! See the [contribution guidelines](https://github.com/idosal/mcp-ui/blob/main/.github/CONTRIBUTING.md) to get started.
## 📄 License
Apache License 2.0 © [The MCP-UI Authors](LICENSE)
## Disclaimer
This project is provided "as is", without warranty of any kind. The `mcp-ui` authors and contributors shall not be held liable for any damages, losses, or issues arising from the use of this software. Use at your own risk.
================================================
FILE: SECURITY.md
================================================
# Security Policy
MCP-UI is committed to maintaining the highest security standards, and actively works with web security experts to improve the spec and implementations. We welcome disclosures, suggestions, and feedback that can improve it.
## Disclosing an Issue
Please report any issues using this private [form](https://forms.gle/6WbAJU7m2LSxfY6K6). All disclosures will be handled as soon as possible.
================================================
FILE: docs/README.md
================================================
# MCP-UI Documentation
This directory contains the enhanced documentation for MCP UI, built with VitePress and featuring a professional, modern design.
## 🎨 Design Enhancements
The documentation has been significantly improved with:
### Visual Design
- **Modern Color Scheme**: Blue to green gradient branding with excellent contrast
- **Professional Typography**: Inter font family with proper font weights and spacing
- **Enhanced Shadows**: Subtle depth and visual hierarchy
- **Smooth Animations**: Fade-in effects and hover transitions
- **Responsive Design**: Mobile-first approach with breakpoint optimizations
### Content Improvements
- **Video Integration**: Demo video from README prominently featured on landing page
- **Rich Feature Cards**: Six detailed feature cards with icons and descriptions
- **Code Examples**: Syntax-highlighted examples with proper formatting
- **Call-to-Action Buttons**: Professional buttons with hover effects
- **Better Navigation**: Improved sidebar with collapsible sections
### Technical Features
- **Local Search**: Built-in search functionality
- **Dark Mode**: Full dark mode support with proper color schemes
- **SEO Optimization**: Meta tags, OpenGraph, and Twitter cards
- **Performance**: Optimized loading and rendering
- **Accessibility**: Proper ARIA labels and keyboard navigation
## 🚀 Getting Started
To run the documentation locally:
```bash
cd docs
npm install
npm run dev
```
To build for production:
```bash
npm run build
```
## 📁 Structure
```
docs/
├── src/
│ ├── .vitepress/
│ │ ├── config.ts # VitePress configuration
│ │ └── theme/
│ │ ├── index.ts # Custom theme setup
│ │ └── custom.css # Enhanced styling
│ ├── guide/ # Documentation pages
│ ├── public/ # Static assets
│ │ ├── logo.svg # Brand logo
│ │ └── favicon.png # Site favicon
│ └── index.md # Enhanced landing page
└── README.md # This file
```
## 🎯 Key Features
### Landing Page
- Hero section with gradient text and compelling tagline
- Demo video integration from the main README
- Six feature cards highlighting key capabilities
- Code examples showing quick usage
- Call-to-action buttons linking to guides and demos
### Navigation
- Clean, organized sidebar with collapsible sections
- Breadcrumb navigation
- "Edit on GitHub" links
- Social media links (GitHub, npm)
### Content
- Professional typography with proper hierarchy
- Enhanced code blocks with syntax highlighting
- Improved tables with hover effects
- Styled blockquotes and badges
- Responsive images and media
### Performance
- Optimized bundle size
- Fast loading times
- Efficient caching
- Mobile-optimized assets
## 🔧 Customization
The documentation uses CSS custom properties for easy theming:
```css
:root {
--vp-c-brand-1: #3c82f6; /* Primary brand color */
--vp-c-brand-2: #2563eb; /* Secondary brand color */
--vp-c-accent-1: #10b981; /* Accent color */
/* ... more variables */
}
```
## 📝 Content Guidelines
When adding new content:
1. Use proper markdown headers (##, ###) for structure
2. Include code examples with language tags
3. Add screenshots or diagrams where helpful
4. Keep paragraphs concise and scannable
5. Use callouts for important information
## 🤝 Contributing
To contribute to the documentation:
1. Edit files in the `src/` directory
2. Test locally with `npm run dev`
3. Build with `npm run build` to verify
4. Submit a pull request
The documentation automatically rebuilds on changes to the main branch.
================================================
FILE: docs/package.json
================================================
{
"name": "@mcp-ui/docs",
"version": "0.1.0",
"private": false,
"type": "module",
"scripts": {
"dev": "vitepress dev src",
"build": "vitepress build src",
"preview": "vitepress preview src"
},
"devDependencies": {
"mermaid": "^11.9.0",
"vitepress": "^1.0.0-rc.44",
"vitepress-plugin-mermaid": "^2.0.17",
"vue": "^3.3.0"
}
}
================================================
FILE: docs/src/.vitepress/config.ts
================================================
import { defineConfig } from 'vitepress';
import { withMermaid } from 'vitepress-plugin-mermaid';
export default withMermaid(
defineConfig({
lang: 'en-US',
title: 'MCP-UI',
description: 'Interactive UI for MCP - Build rich, dynamic interfaces with MCP-UI',
base: '/',
cleanUrls: true,
head: [
['meta', { name: 'theme-color', content: '#3c82f6' }],
['meta', { name: 'og:type', content: 'website' }],
['meta', { name: 'og:locale', content: 'en' }],
[
'meta',
{
name: 'og:title',
content: 'MCP-UI | Interactive UI for MCP',
},
],
['meta', { name: 'og:site_name', content: 'MCP-UI' }],
['meta', { name: 'og:image', content: 'https://mcpui.dev/og-image.png' }],
['meta', { name: 'og:url', content: 'https://mcpui.dev/' }],
['meta', { name: 'twitter:card', content: 'summary_large_image' }],
['meta', { name: 'twitter:site', content: '@idosal1' }],
['meta', { name: 'twitter:url', content: 'https://mcpui.dev/' }],
['meta', { name: 'twitter:domain', content: 'mcpui.dev' }],
['meta', { name: 'twitter:image', content: 'https://mcpui.dev/og-image.png' }],
[
'meta',
{
name: 'twitter:description',
content: 'Interactive UI for MCP - Build rich, dynamic interfaces with MCP-UI',
},
],
['link', { rel: 'icon', type: 'image/png', href: '/logo.png' }],
['link', { rel: 'icon', type: 'image/png', href: '/favicon.png' }],
[
'style',
{},
`.VPNavBar .VPNavBarSocialLinks a[href*="npmjs.com/package/@mcp-ui/server"] { border-left: 1px solid var(--vp-c-divider); margin-left: 8px; padding-left: 12px; }`,
],
],
vite: {
plugins: [],
optimizeDeps: {
include: ['vue', '@vue/shared', 'dayjs', 'mermaid'],
},
},
themeConfig: {
logo: {
light: '/logo-black.png',
dark: '/logo.png',
alt: 'MCP-UI Logo',
},
nav: [
{ text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/introduction' },
{ text: 'Team', link: '/team' },
{
text: 'Examples',
items: [
{
text: 'UI Inspector',
link: 'https://github.com/idosal/ui-inspector',
},
{
text: 'Server Examples',
items: [
{
text: 'TypeScript',
link: '/guide/server/typescript/usage-examples',
},
{ text: 'Ruby', link: '/guide/server/ruby/usage-examples' },
{ text: 'Python', link: '/guide/server/python/usage-examples' },
],
},
{
text: 'Client Examples',
items: [
{ text: 'Overview', link: '/guide/client/overview' },
],
},
],
},
{
text: 'Packages',
items: [
{
text: '@mcp-ui/client',
link: 'https://www.npmjs.com/package/@mcp-ui/client',
},
{
text: '@mcp-ui/server',
link: 'https://www.npmjs.com/package/@mcp-ui/server',
},
{
text: 'mcp_ui_server Gem',
link: 'https://rubygems.org/gems/mcp_ui_server',
},
{
text: 'mcp-ui-server (PyPI)',
link: 'https://pypi.org/project/mcp-ui-server/',
},
],
},
],
sidebar: {
'/guide/': [
{
text: 'Getting Started',
items: [
{ text: 'Introduction', link: '/guide/introduction' },
{ text: 'Installation', link: '/guide/getting-started' },
{ text: 'Core Concepts', link: '/guide/protocol-details' },
{ text: 'Embeddable UI', link: '/guide/embeddable-ui' },
{ text: 'Supported Hosts', link: '/guide/supported-hosts' },
],
},
{
text: 'Protocol Integrations',
collapsed: false,
items: [
{ text: 'MCP Apps', link: '/guide/mcp-apps' },
{ text: 'Apps SDK (ChatGPT)', link: '/guide/apps-sdk' },
],
},
{
text: 'Server SDKs',
collapsed: false,
items: [
{
text: 'TypeScript',
collapsed: false,
items: [
{
text: 'Overview',
link: '/guide/server/typescript/overview',
},
{
text: 'Walkthrough',
link: '/guide/server/typescript/walkthrough',
},
{
text: 'Usage & Examples',
link: '/guide/server/typescript/usage-examples',
},
],
},
{
text: 'Ruby',
collapsed: false,
items: [
{ text: 'Overview', link: '/guide/server/ruby/overview' },
{
text: 'Walkthrough',
link: '/guide/server/ruby/walkthrough',
},
{
text: 'Usage & Examples',
link: '/guide/server/ruby/usage-examples',
},
],
},
{
text: 'Python',
collapsed: false,
items: [
{ text: 'Overview', link: '/guide/server/python/overview' },
{ text: 'Walkthrough', link: '/guide/server/python/walkthrough' },
{ text: 'Usage & Examples', link: '/guide/server/python/usage-examples' },
],
},
],
},
{
text: 'Client SDK',
collapsed: false,
items: [
{ text: 'Overview', link: '/guide/client/overview' },
{ text: 'Walkthrough', link: '/guide/client/walkthrough' },
{
text: 'AppRenderer',
link: '/guide/client/app-renderer',
},
],
},
],
},
editLink: {
pattern: 'https://github.com/idosal/mcp-ui/edit/main/docs/src/:path',
text: 'Edit this page on GitHub',
},
search: {
provider: 'local',
options: {
locales: {
root: {
translations: {
button: {
buttonText: 'Search',
buttonAriaLabel: 'Search',
},
modal: {
displayDetails: 'Display detailed list',
resetButtonTitle: 'Reset search',
backButtonTitle: 'Close search',
noResultsText: 'No results for',
footer: {
selectText: 'to select',
navigateText: 'to navigate',
closeText: 'to close',
},
},
},
},
},
},
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/idosal/mcp-ui' },
{ icon: 'discord', link: 'https://discord.gg/CEAG4KW7ZH' },
{
icon: 'npm',
link: 'https://www.npmjs.com/package/@mcp-ui/server',
},
{
icon: {
svg: '<svg viewBox="0 0 256 293" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"><g fill="currentColor"><path d="M76.748 97.434l-.163-.163-36.11 36.11 87.674 87.512 36.11-35.948 51.564-51.563-36.11-36.11v-.164H76.584l.163.326z"/><path d="M127.823.976L.135 74.173v146.395l127.688 73.197 127.689-73.197V74.173L127.823.976zm103.29 205.603l-103.29 59.534-103.29-59.534V87.837l103.29-59.534 103.29 59.534v118.742z"/></g></svg>',
},
link: 'https://rubygems.org/gems/mcp_ui_server',
},
{
icon: 'pypi',
link: 'https://pypi.org/project/mcp-ui-server/',
},
],
footer: {
message:
'Released under the <a href="https://github.com/idosal/mcp-ui/blob/main/LICENSE">Apache 2.0 License</a>.',
copyright: 'Copyright © 2025-present <a href="https://github.com/idosal">Ido Salomon</a>',
},
lastUpdated: {
text: 'Last updated',
formatOptions: {
dateStyle: 'short',
timeStyle: 'medium',
},
},
outline: {
level: [2, 3],
label: 'On this page',
},
docFooter: {
prev: 'Previous page',
next: 'Next page',
},
darkModeSwitchLabel: 'Appearance',
lightModeSwitchTitle: 'Switch to light theme',
darkModeSwitchTitle: 'Switch to dark theme',
sidebarMenuLabel: 'Menu',
returnToTopLabel: 'Return to top',
langMenuLabel: 'Change language',
externalLinkIcon: true,
},
markdown: {
theme: {
light: 'github-light',
dark: 'github-dark',
},
lineNumbers: true,
config: (md) => {
// Add any markdown-it plugins here
},
},
sitemap: {
hostname: 'https://mcpui.dev',
},
// Mermaid configuration
mermaid: {
// Refer to https://mermaid.js.org/config/setup/modules/mermaidAPI.html#mermaidapi-configuration-defaults for options
theme: 'default',
},
// Optional plugin configuration
mermaidPlugin: {
class: 'mermaid', // Set additional CSS classes for parent container
},
}),
);
================================================
FILE: docs/src/.vitepress/theme/custom.css
================================================
/**
* Custom styles for MCP UI documentation
* These styles enhance the default VitePress theme
*/
/* CSS Variables for consistent theming */
:root {
--vp-c-brand-1: #3c82f6;
--vp-c-brand-2: #2563eb;
--vp-c-brand-3: #1d4ed8;
--vp-c-brand-soft: rgba(60, 130, 246, 0.14);
--vp-c-brand-softer: rgba(60, 130, 246, 0.08);
/* Custom accent colors */
--vp-c-accent-1: #10b981;
--vp-c-accent-2: #059669;
--vp-c-accent-soft: rgba(16, 185, 129, 0.14);
/* Enhanced typography */
--vp-font-family-base:
'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
--vp-font-family-mono: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
/* Custom shadows */
--vp-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.04);
--vp-shadow-2: 0 3px 12px rgba(0, 0, 0, 0.07);
--vp-shadow-3: 0 12px 32px rgba(0, 0, 0, 0.1);
--vp-shadow-4: 0 14px 44px rgba(0, 0, 0, 0.12);
--vp-shadow-5: 0 18px 56px rgba(0, 0, 0, 0.16);
/* Custom sidebar spacing */
--vp-sidebar-width: 310px;
--active-item-left-padding: 8px;
--items-left-padding: 18px;
}
/* Dark mode adjustments */
.dark {
--vp-c-brand-1: #60a5fa;
--vp-c-brand-2: #3b82f6;
--vp-c-brand-3: #2563eb;
--vp-c-brand-soft: rgba(96, 165, 250, 0.16);
--vp-c-brand-softer: rgba(96, 165, 250, 0.1);
--vp-c-accent-1: #34d399;
--vp-c-accent-2: #10b981;
--vp-c-accent-soft: rgba(52, 211, 153, 0.16);
}
/* Fix top bar overlap - Enhanced hero section */
.VPHero {
padding-top: 120px !important;
padding-bottom: 64px !important;
}
.VPHome {
margin-bottom: 16px !important;
}
.VPHero .container {
margin-top: 0 !important;
}
.VPHero .name {
background: linear-gradient(
-45deg,
#3b82f6 0%,
#10b981 25%,
#8b5cf6 50%,
#3b82f6 75%,
#10b981 100%
);
background-size: 400% 400%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-weight: 800;
letter-spacing: -0.02em;
animation: gradientShift 10s ease-in-out infinite;
}
.VPHero .text {
font-weight: 600;
letter-spacing: -0.01em;
}
.VPHero .tagline {
font-size: 1.25rem;
line-height: 1.6;
color: var(--vp-c-text-2);
max-width: 600px;
margin: 0 auto;
}
/* Animated gradient keyframes */
@keyframes gradientShift {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
/* Animated gradient text utility class */
.animated-gradient-text {
background: linear-gradient(
-45deg,
#3b82f6 0%,
#10b981 25%,
#8b5cf6 50%,
#3b82f6 75%,
#10b981 100%
);
background-size: 400% 400%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: gradientShift 10s ease-in-out infinite;
}
/* Enhanced feature cards */
.VPFeatures {
padding-top: 48px;
padding-bottom: 48px;
}
.VPFeature {
background: var(--vp-c-bg-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 12px;
padding: 28px;
height: 100%;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.VPFeature::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--vp-c-brand-1), var(--vp-c-accent-1));
opacity: 0;
transition: opacity 0.3s ease;
}
.VPFeature:hover {
border-color: var(--vp-c-brand-1);
box-shadow: var(--vp-shadow-3);
transform: translateY(-2px);
}
.VPFeature:hover::before {
opacity: 1;
}
/* Fix feature card icons - target the correct VitePress classes */
.VPFeature .icon,
.VPFeature .VPImage,
.VPFeature .box .icon {
font-size: 2.5rem !important;
line-height: 1 !important;
margin-bottom: 16px !important;
display: block !important;
text-align: center !important;
width: 100% !important;
height: auto !important;
}
/* Ensure emoji icons are properly sized */
.VPFeature .box .icon {
font-size: 2.5rem !important;
line-height: 1.2 !important;
margin: 0 0 16px 0 !important;
padding: 0 !important;
position: static !important;
top: auto !important;
left: auto !important;
transform: none !important;
}
.VPFeature .title {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 12px;
color: var(--vp-c-text-1);
}
.VPFeature .details {
color: var(--vp-c-text-2);
line-height: 1.6;
font-size: 0.95rem;
}
/* Enhanced code blocks */
.vp-code-group {
margin: 24px 0;
border-radius: 12px;
overflow: hidden;
box-shadow: var(--vp-shadow-2);
}
div[class*='language-'] {
border-radius: 8px;
margin: 16px 0;
overflow: hidden;
box-shadow: var(--vp-shadow-2);
}
div[class*='language-'] pre {
padding: 20px 24px;
background: var(--vp-code-bg);
overflow-x: auto;
}
div[class*='language-'] code {
font-family: var(--vp-font-family-mono);
font-size: 0.9em;
line-height: 1.7;
}
/* Enhanced buttons and links */
.VPButton {
border-radius: 8px;
font-weight: 600;
transition: all 0.3s ease;
box-shadow: var(--vp-shadow-1);
}
.VPButton:hover {
transform: translateY(-1px);
box-shadow: var(--vp-shadow-2);
}
.VPButton.brand {
background: linear-gradient(135deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
border: none;
}
.VPButton.brand:hover {
background: linear-gradient(135deg, var(--vp-c-brand-2), var(--vp-c-brand-3));
}
/* Enhanced navigation */
.VPNavBar {
backdrop-filter: blur(12px);
background: rgba(255, 255, 255, 0.85);
border-bottom: 1px solid var(--vp-c-divider);
box-shadow: var(--vp-shadow-1);
}
.dark .VPNavBar {
background: rgba(26, 26, 26, 0.85);
}
/* Enhanced sidebar */
.VPSidebar {
background: var(--vp-c-bg-soft);
border-right: 1px solid var(--vp-c-divider);
}
.VPSidebarItem[class*='level-']:not(.level-0) .items {
padding-left: var(--items-left-padding) !important;
}
.VPSidebarItem.level-0 > .item > .link {
font-weight: 600;
color: var(--vp-c-text-1);
}
.VPSidebarItem.is-active > .item > .indicator {
left: calc(calc(var(--items-left-padding) * -1) - 1px);
}
.VPSidebarItem.is-active > .item > .link {
background: var(--vp-c-brand-soft);
color: var(--vp-c-brand-1);
border-radius: 6px;
padding-left: var(--active-item-left-padding);
margin-left: calc(var(--active-item-left-padding) * -1);
}
/* Enhanced content area */
.vp-doc h1 {
font-size: 2.5rem;
font-weight: 800;
letter-spacing: -0.02em;
line-height: 1.2;
margin-bottom: 24px;
margin-top: 24px;
background: linear-gradient(135deg, var(--vp-c-text-1) 0%, var(--vp-c-brand-1) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.vp-doc h2 {
font-size: 1.875rem;
font-weight: 700;
margin-top: 12px;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 2px solid var(--vp-c-divider);
}
.vp-doc h3 {
font-size: 1.5rem;
font-weight: 600;
margin-top: 32px;
margin-bottom: 12px;
color: var(--vp-c-brand-1);
}
.vp-doc p {
line-height: 1.7;
margin: 16px 0;
}
.vp-doc a {
color: var(--vp-c-brand-1);
text-decoration: none;
font-weight: 500;
transition: all 0.2s ease;
}
.vp-doc a:hover {
color: var(--vp-c-brand-2);
text-decoration: underline;
}
/* Enhanced tables */
.vp-doc table {
border-collapse: collapse;
margin: 24px 0;
width: 100%;
border-radius: 8px;
overflow: hidden;
box-shadow: var(--vp-shadow-2);
}
.vp-doc th {
background: var(--vp-c-bg-soft);
font-weight: 600;
padding: 16px;
text-align: left;
border-bottom: 2px solid var(--vp-c-divider);
}
.vp-doc td {
padding: 12px 16px;
border-bottom: 1px solid var(--vp-c-divider-light);
}
.vp-doc tr:hover {
background: var(--vp-c-bg-softer);
}
/* Enhanced blockquotes */
.vp-doc blockquote {
border-left: 4px solid var(--vp-c-brand-1);
background: var(--vp-c-brand-softer);
padding: 16px 20px;
margin: 24px 0;
border-radius: 0 8px 8px 0;
}
.vp-doc blockquote p {
margin: 0;
color: var(--vp-c-text-2);
font-style: italic;
}
/* Enhanced badges */
.vp-doc .badge {
display: inline-block;
background: var(--vp-c-brand-soft);
color: var(--vp-c-brand-1);
padding: 4px 8px;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
margin-left: 8px;
}
/* Custom utility classes */
.text-gradient {
background: linear-gradient(135deg, var(--vp-c-brand-1), var(--vp-c-accent-1));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.card {
background: var(--vp-c-bg-soft);
border: 1px solid var(--vp-c-divider);
border-radius: 12px;
padding: 24px;
margin: 16px 0;
transition: all 0.3s ease;
}
.card:hover {
border-color: var(--vp-c-brand-1);
box-shadow: var(--vp-shadow-2);
}
/* Responsive improvements */
@media (max-width: 768px) {
.VPHero {
padding-top: 60px !important;
padding-bottom: 48px !important;
}
.VPHero .name {
font-size: 2.5rem;
}
.VPHero .text {
font-size: 1.5rem;
}
.VPHero .tagline {
font-size: 1.125rem;
}
.vp-doc h1 {
font-size: 2rem;
}
.vp-doc h2 {
font-size: 1.5rem;
}
.VPFeature {
padding: 20px;
}
.VPFeature .icon,
.VPFeature .VPImage,
.VPFeature .box .icon {
font-size: 2rem !important;
}
/* Responsive animated gradient text */
.animated-gradient-text {
font-size: 2.5rem !important;
line-height: 1.1 !important;
margin-bottom: 1.5rem !important;
}
}
/* Animation enhancements */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.VPFeature {
animation: fadeInUp 0.6s ease-out;
}
.VPFeature:nth-child(1) {
animation-delay: 0.1s;
}
.VPFeature:nth-child(2) {
animation-delay: 0.2s;
}
.VPFeature:nth-child(3) {
animation-delay: 0.3s;
}
.VPFeature:nth-child(4) {
animation-delay: 0.4s;
}
.VPFeature:nth-child(5) {
animation-delay: 0.5s;
}
.VPFeature:nth-child(6) {
animation-delay: 0.6s;
}
/* Announcement Banner */
.announcement-banner {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
padding: 12px 24px;
font-size: 14px;
font-weight: 500;
line-height: 1.5;
position: relative;
z-index: 1;
margin-bottom: 0;
border-radius: 0 0 8px 8px; /* Rounded bottom corners just for style */
width: 100%;
max-width: 1152px; /* Match typical container width */
margin-left: auto;
margin-right: auto;
}
.announcement-banner a:hover {
text-decoration: none !important;
opacity: 0.9;
}
/* Adjust VPHero top padding when banner is present (Home page) */
.VPHome .VPHero {
padding-top: 80px !important;
}
@media (max-width: 960px) {
.VPHome .VPHero {
padding-top: 64px !important;
}
/* Fix overlap by resetting layout shifts on image */
.VPHome .VPHero .image,
.VPHome .VPHero .image-container {
margin-top: 0 !important;
transform: none !important;
}
}
@media (max-width: 768px) {
.VPHome .VPHero {
padding-top: 32px !important;
}
.announcement-banner {
margin-bottom: 0;
}
}
/* Fix hero image overlap on tablet/mobile by removing negative margin */
@media (max-width: 960px) {
.VPHome .VPHero .image-container {
margin-top: 0 !important;
}
}
================================================
FILE: docs/src/.vitepress/theme/index.ts
================================================
import DefaultTheme from 'vitepress/theme';
import { h } from 'vue';
import type { Theme } from 'vitepress';
import './custom.css';
export default {
extends: DefaultTheme,
Layout() {
return h(DefaultTheme.Layout, null, {
'home-hero-before': () =>
h('div', { class: 'announcement-banner' }, [
h('span', '🎉 '),
h('span', { style: 'font-weight: bold;' }, 'MCP-UI is now standardized into MCP Apps!'),
h('span', ' '),
h(
'a',
{
href: 'https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1865',
style: 'text-decoration: underline; color: inherit;',
},
'Learn more →',
),
]),
});
},
enhanceApp({ app, router, siteData }) {
// Custom app enhancements can go here
},
} satisfies Theme;
================================================
FILE: docs/src/about.md
================================================
# About
## About
`mcp-ui` pioneered the concept of interactive UI over the [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP). When MCP was first introduced, tools could only return plain text responses. MCP-UI took the opportunity to transform how users interact with AI tools by enabling rich, dynamic interfaces delivered seamlessly through the protocol.
What started as an experimental project to bring web UIs to MCP tools has helped shape the ecosystem. The patterns we developed - embedding HTML resources in tool responses, secure sandboxed rendering, and bidirectional communication between UIs and hosts - directly influenced the creation of the [MCP Apps specification](https://github.com/modelcontextprotocol/ext-apps).
Today, `mcp-ui` implements the MCP Apps standard while continuing to push the boundaries of what's possible with UI over MCP. The `@mcp-ui/*` packages provide production-ready SDKs for both servers and clients, making it easy for developers to build interactive experiences for their MCP tools.
## What's Included
- **Client SDK** with React components for rendering tool UIs securely
- **Server SDKs** for TypeScript, Python, and Ruby to create UI resources
- **Documentation and examples** to help you get started quickly
- **Active community** pushing the MCP Apps standard forward
## Team
`mcp-ui` is a project by [Ido Salomon](https://x.com/idosal1), in collaboration with [Liad Yosef](https://x.com/liadyosef).
## Get Involved
- [GitHub Repository](https://github.com/idosal/mcp-ui)
- [Discord Community](https://discord.gg/CEAG4KW7ZH)
================================================
FILE: docs/src/guide/apps-sdk.md
================================================
# OpenAI Apps SDK Integration
::: warning ChatGPT-Specific
This page covers the **OpenAI Apps SDK** adapter for **ChatGPT** integration. This is separate from the **MCP Apps** standard.
- **MCP Apps**: The open standard for tool UIs (`_meta.ui.resourceUri`) - see [Getting Started](./getting-started)
- **Apps SDK**: ChatGPT's proprietary protocol (`openai/outputTemplate`) - covered on this page
:::
The Apps SDK adapter in `@mcp-ui/server` enables your MCP-UI HTML widget to run inside ChatGPT. However, for now, you still need to manually serve the resource according to the Apps SDK spec. This guide walks through the manual flow the adapter expects today to support both MCP-UI hosts and ChatGPT.
## Why two resources?
- **Static template for Apps SDK** – referenced from your tool descriptor via `_meta["openai/outputTemplate"]`. This version must enable the Apps SDK adapter so ChatGPT injects the bridge script and uses the `text/html+skybridge` MIME type.
- **Embedded resource in tool results** – returned each time your tool runs. This version should *not* enable the adapter so MCP-native hosts continue to receive standard MCP-UI HTML.
## Step-by-step walkthrough
### 1. Register the Apps SDK template
Use `createUIResource` with `adapters.appsSdk.enabled: true` and expose it through the MCP Resources API so both Apps SDK and traditional MCP hosts can fetch it.
```ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { createUIResource } from '@mcp-ui/server';
const server = new McpServer({ name: 'weather-bot', version: '1.0.0' });
const TEMPLATE_URI = 'ui://widgets/weather';
const appsSdkTemplate = await createUIResource({
uri: TEMPLATE_URI,
encoding: 'text',
content: {
type: 'rawHtml',
htmlString: renderForecastWidget(),
},
metadata: {
'openai/widgetDescription': widget.description,
'openai/widgetPrefersBorder': true,
},
});
server.registerResource(TEMPLATE_URI, async () => appsSdkTemplate.resource);
```
> **Note:** The adapter switches the MIME type to `text/html+skybridge` and injects the Apps bridge script automatically. The bridge translates MCP-UI primitives to Apps SDK compatible code so no HTML changes are required.
### Add Apps SDK widget metadata
Apps SDK surfaces a handful of `_meta` keys on the resource itself (description, CSP, borders, etc.). Provide them via the `metadata` option when you build the template so ChatGPT can present the widget correctly. For example:
```ts
const appsSdkTemplate = await createUIResource({
uri: TEMPLATE_URI,
encoding: 'text',
content: {
type: 'rawHtml',
htmlString: renderForecastWidget(),
},
metadata: {
'openai/widgetDescription': 'Interactive calculator',
'openai/widgetCSP': {
connect_domains: [],
resource_domains: [],
},
'openai/widgetPrefersBorder': true,
},
});
```
### 2. Reference the template in your tool descriptor
The Apps SDK looks for `_meta["openai/outputTemplate"]` to know which resource to render. Mirror the rest of the Apps-specific metadata you need (status text, accessibility hints, security schemes, etc.).
```ts
// Partial example (see Step 3 for complete example)
server.registerTool(
'forecast',
{
title: 'Get the forecast',
description: 'Returns a UI that displays the current weather.',
inputSchema: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
_meta: {
'openai/outputTemplate': TEMPLATE_URI,
'openai/toolInvocation/invoking': 'Fetching forecast…',
'openai/toolInvocation/invoked': 'Forecast ready',
'openai/widgetAccessible': true,
},
},
async ({ city }) => {
const forecast = await fetchForecast(city);
return {
content: [
{
type: 'text',
text: `Forecast prepared for ${city}.`,
},
],
structuredContent: {
forecast,
},
};
},
);
```
### 3. Add the MCP-UI embedded resource to the tool response
To support MCP-UI hosts, also return a standard `createUIResource` result (without the Apps adapter) alongside the Apps SDK payloads.
```ts
server.registerTool(
'forecast',
{
title: 'Get the forecast',
description: 'Returns a UI that displays the current weather.',
inputSchema: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
_meta: {
'openai/outputTemplate': TEMPLATE_URI,
'openai/toolInvocation/invoking': 'Fetching forecast…',
'openai/toolInvocation/invoked': 'Forecast ready',
'openai/widgetAccessible': true,
},
},
async ({ city }) => {
const forecast = await fetchForecast(city);
// MCP-UI embedded UI resource
const uiResource = await createUIResource({
uri: `ui://widgets/weather/${city}`,
encoding: 'text',
content: {
type: 'rawHtml',
htmlString: renderForecastWidget(forecast),
},
});
return {
content: [
{
type: 'text',
text: `Forecast prepared for ${city}.`,
},
uiResource
],
structuredContent: {
forecast,
},
};
},
);
```
> **Important:** The MCP-UI resource should **not** enable the Apps SDK adapter. It is for hosts that expect embedded resources. ChatGPT will ignore it and use the template registered in step 1 instead.
For the complete list of supported metadata fields, refer to the official documentation. [Apps SDK Reference](https://developers.openai.com/apps-sdk/reference)
================================================
FILE: docs/src/guide/client/app-renderer.md
================================================
# AppRenderer Component
`AppRenderer` is the recommended component for rendering MCP tool UIs in your host application. It implements the [MCP Apps](../mcp-apps) standard, handling the complete lifecycle: resource fetching, sandbox setup, JSON-RPC communication, and tool input/result delivery.
For lower-level control or when you already have HTML and an `AppBridge` instance, use [`AppFrame`](../mcp-apps#appframe-component) instead.
## Quick Example
```tsx
import { AppRenderer, type AppRendererHandle } from '@mcp-ui/client';
function ToolUI({ client, toolName, toolInput, toolResult }) {
const appRef = useRef<AppRendererHandle>(null);
return (
<AppRenderer
ref={appRef}
client={client}
toolName={toolName}
sandbox={{ url: new URL('http://localhost:8765/sandbox_proxy.html') }}
toolInput={toolInput}
toolResult={toolResult}
hostContext={{ theme: 'dark' }}
onOpenLink={async ({ url }) => {
window.open(url, '_blank');
return {};
}}
onMessage={async (params) => {
console.log('Message from tool UI:', params);
return {};
}}
onError={(error) => console.error('Tool UI error:', error)}
/>
);
}
```
## Props Reference
### Core Props
| Prop | Type | Description |
|------|------|-------------|
| `client` | `Client` | Optional MCP client for automatic resource fetching and MCP request forwarding. Omit to use custom handlers instead. |
| `toolName` | `string` | Name of the MCP tool to render UI for. |
| `sandbox` | `SandboxConfig` | Sandbox configuration with the proxy URL and optional CSP. |
| `html` | `string` | Optional pre-fetched HTML. If provided, skips all resource fetching. |
| `toolResourceUri` | `string` | Optional pre-fetched resource URI. If not provided, fetched via the client. |
| `toolInput` | `Record<string, unknown>` | Tool arguments to pass to the guest UI once it initializes. |
| `toolResult` | `CallToolResult` | Tool execution result to pass to the guest UI. |
| `toolInputPartial` | `object` | Partial/streaming tool input to send progressively. |
| `toolCancelled` | `boolean` | Set to `true` to notify the guest UI that tool execution was cancelled. |
| `hostContext` | `McpUiHostContext` | Host context (theme, locale, viewport, etc.) to pass to the guest UI. |
### Event Handlers
| Prop | Type | Description |
|------|------|-------------|
| `onOpenLink` | `(params, extra) => Promise<McpUiOpenLinkResult>` | Handler for open-link requests from the guest UI. |
| `onMessage` | `(params, extra) => Promise<McpUiMessageResult>` | Handler for message requests from the guest UI. |
| `onLoggingMessage` | `(params) => void` | Handler for logging messages from the guest UI. |
| `onSizeChanged` | `(params) => void` | Handler for size change notifications from the guest UI. |
| `onError` | `(error: Error) => void` | Callback invoked when an error occurs during setup or message handling. |
| `onFallbackRequest` | `(request, extra) => Promise<Record<string, unknown>>` | Catch-all for JSON-RPC requests not handled by built-in handlers. See [Handling Custom Requests](#handling-custom-requests). |
### MCP Request Handlers
These override the automatic forwarding to the MCP client when provided:
| Prop | Type | Description |
|------|------|-------------|
| `onCallTool` | `(params, extra) => Promise<CallToolResult>` | Handler for `tools/call` requests. |
| `onListResources` | `(params, extra) => Promise<ListResourcesResult>` | Handler for `resources/list` requests. |
| `onListResourceTemplates` | `(params, extra) => Promise<ListResourceTemplatesResult>` | Handler for `resources/templates/list` requests. |
| `onReadResource` | `(params, extra) => Promise<ReadResourceResult>` | Handler for `resources/read` requests. |
| `onListPrompts` | `(params, extra) => Promise<ListPromptsResult>` | Handler for `prompts/list` requests. |
### Ref Methods
Access via `useRef<AppRendererHandle>`:
| Method | Description |
|--------|-------------|
| `sendToolListChanged()` | Notify guest UI that the server's tool list has changed. |
| `sendResourceListChanged()` | Notify guest UI that the server's resource list has changed. |
| `sendPromptListChanged()` | Notify guest UI that the server's prompt list has changed. |
| `teardownResource()` | Notify the guest UI before unmounting (graceful shutdown). |
## Using Without an MCP Client
You can use `AppRenderer` without a full MCP client by providing custom handlers:
```tsx
<AppRenderer
// No client - use callbacks instead
toolName="my-tool"
toolResourceUri="ui://my-server/my-tool"
sandbox={{ url: sandboxUrl }}
onReadResource={async ({ uri }) => {
return myMcpProxy.readResource({ uri });
}}
onCallTool={async (params) => {
return myMcpProxy.callTool(params);
}}
/>
```
Or provide pre-fetched HTML directly:
```tsx
<AppRenderer
toolName="my-tool"
sandbox={{ url: sandboxUrl }}
html={preloadedHtml} // Skip all resource fetching
toolInput={args}
/>
```
## Handling Custom Requests
AppRenderer includes built-in handlers for standard MCP Apps methods (`tools/call`, `ui/message`, `ui/open-link`, etc.). The `onFallbackRequest` prop lets you handle **any JSON-RPC request that doesn't match a built-in handler**. This is useful for:
- **Experimental methods** -- prototype new capabilities (e.g., `x/clipboard/write`, `x/analytics/track`)
- **MCP methods not yet in the Apps spec** -- support standard MCP methods like `sampling/createMessage` before they're officially added to MCP Apps
Under the hood, this is wired to `AppBridge`'s `fallbackRequestHandler` from the MCP SDK `Protocol` class. The guest UI sends a standard JSON-RPC request via `postMessage`, and if AppBridge has no registered handler for the method, it delegates to `onFallbackRequest`.
### Host-side handler
```tsx
import { AppRenderer, type JSONRPCRequest } from '@mcp-ui/client';
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
<AppRenderer
client={client}
toolName="my-tool"
sandbox={sandboxConfig}
onFallbackRequest={async (request, extra) => {
switch (request.method) {
case 'x/clipboard/write':
await navigator.clipboard.writeText(request.params?.text as string);
return { success: true };
case 'sampling/createMessage':
// Forward to MCP server
return client.createMessage(request.params);
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown method: ${request.method}`);
}
}}
/>
```
### Guest-side (inside tool UI HTML)
```ts
import { sendExperimentalRequest } from '@mcp-ui/server';
// Send a custom request to the host -- returns a Promise with the response
const result = await sendExperimentalRequest('x/clipboard/write', { text: 'hello' });
```
The `sendExperimentalRequest` helper sends a properly formatted JSON-RPC request via `window.parent.postMessage`. The full request/response cycle flows through `PostMessageTransport` and the sandbox proxy, just like built-in methods.
::: tip Method Naming Convention
Use the `x/<namespace>/<action>` prefix for experimental methods (e.g., `x/clipboard/write`). Standard MCP methods not yet in the Apps spec (e.g., `sampling/createMessage`) should use their canonical method names. When an experimental method proves useful, it can be promoted to a standard method in the [ext-apps spec](https://github.com/modelcontextprotocol/ext-apps).
:::
## Sandbox Proxy
AppRenderer requires a sandbox proxy HTML file to be served. This provides security isolation for the guest UI by running it inside a double-iframe architecture. The sandbox proxy URL should point to a page that loads the MCP Apps sandbox proxy script.
See the [Client SDK Walkthrough](./walkthrough#_3-set-up-a-sandbox-proxy) for setup instructions.
## Related
- [Client SDK Walkthrough](./walkthrough) -- Step-by-step guide to building an MCP Apps client
- [MCP Apps Overview](../mcp-apps) -- Protocol details and server-side setup
- [Protocol Details](../protocol-details) -- Wire format reference
- [AppFrame Component](../mcp-apps#appframe-component) -- Lower-level rendering component
================================================
FILE: docs/src/guide/client/overview.md
================================================
# @mcp-ui/client Overview
The `@mcp-ui/client` package provides components for rendering MCP tool UIs in your host application using the MCP Apps standard.
## What's Included?
### MCP Apps Components
- **`<AppRenderer />`**: High-level component for MCP Apps hosts. Fetches resources, handles lifecycle, renders tool UIs.
- **`<AppFrame />`**: Lower-level component for when you have pre-fetched HTML and an AppBridge instance.
- **`AppBridge`**: Handles JSON-RPC communication between host and guest UI.
### Utility Functions
- **`getResourceMetadata(resource)`**: Extracts the resource's `_meta` content (standard MCP metadata)
- **`getUIResourceMetadata(resource)`**: Extracts only the MCP-UI specific metadata keys (prefixed with `mcpui.dev/ui-`) from the resource's `_meta` content
- **`isUIResource()`**: Utility function to check if content is a UI resource
- **`UI_EXTENSION_CAPABILITIES`**: Declares UI extension support for your MCP client
## Purpose
- **MCP Apps Compliance**: Implements the MCP Apps standard for UI over MCP
- **Simplified Rendering**: AppRenderer handles resource fetching, lifecycle, and rendering automatically
- **Security**: All UIs render in sandboxed iframes
- **Interactivity**: JSON-RPC communication between host and guest UI
## Quick Example: AppRenderer
```tsx
import { AppRenderer } from '@mcp-ui/client';
function ToolUI({ client, toolName, toolInput, toolResult }) {
return (
<AppRenderer
client={client}
toolName={toolName}
sandbox={{ url: sandboxUrl }}
toolInput={toolInput}
toolResult={toolResult}
onOpenLink={async ({ url }) => {
if (url.startsWith('https://') || url.startsWith('http://')) {
window.open(url);
}
}}
onMessage={async (params) => console.log('Message:', params)}
/>
);
}
```
## Building
This package uses Vite in library mode. It outputs ESM (`.mjs`) and UMD (`.js`) formats, plus TypeScript declarations (`.d.ts`). `react` is externalized.
To build just this package from the monorepo root:
```bash
pnpm build --filter @mcp-ui/client
```
## Utility Functions Reference
### `getResourceMetadata(resource)`
Extracts the standard MCP metadata from a resource's `_meta` property.
```typescript
import { getResourceMetadata } from '@mcp-ui/client';
const resource = {
uri: 'ui://example/demo',
mimeType: 'text/html',
text: '<div>Hello</div>',
_meta: {
title: 'Demo Component',
version: '1.0.0',
'mcpui.dev/ui-preferred-frame-size': ['800px', '600px'],
'mcpui.dev/ui-initial-render-data': { theme: 'dark' },
author: 'Development Team'
}
};
const metadata = getResourceMetadata(resource);
console.log(metadata);
// Output: {
// title: 'Demo Component',
// version: '1.0.0',
// 'mcpui.dev/ui-preferred-frame-size': ['800px', '600px'],
// 'mcpui.dev/ui-initial-render-data': { theme: 'dark' },
// author: 'Development Team'
// }
```
### `getUIResourceMetadata(resource)`
Extracts only the MCP-UI specific metadata keys (those prefixed with `mcpui.dev/ui-`) from a resource's `_meta` property, with the prefixes removed for easier access.
```typescript
import { getUIResourceMetadata } from '@mcp-ui/client';
const resource = {
uri: 'ui://example/demo',
mimeType: 'text/html',
text: '<div>Hello</div>',
_meta: {
title: 'Demo Component',
version: '1.0.0',
'mcpui.dev/ui-preferred-frame-size': ['800px', '600px'],
'mcpui.dev/ui-initial-render-data': { theme: 'dark' },
author: 'Development Team'
}
};
const uiMetadata = getUIResourceMetadata(resource);
console.log(uiMetadata);
// Output: {
// 'preferred-frame-size': ['800px', '600px'],
// 'initial-render-data': { theme: 'dark' },
// }
```
## See More
See the following pages for more details:
- [Client SDK Walkthrough](./walkthrough.md) - **Step-by-step guide to building an MCP Apps client**
- [AppRenderer Component](./app-renderer.md) - **Full API reference for the MCP Apps renderer**
================================================
FILE: docs/src/guide/client/walkthrough.md
================================================
# Client SDK Walkthrough
This guide provides a step-by-step walkthrough for building an MCP Apps client that can render tool UIs using the `@mcp-ui/client` package.
For a complete example, see the [`mcp-apps-demo`](https://github.com/idosal/mcp-ui/tree/main/examples/mcp-apps-demo) (server) and test it with the [ui-inspector](https://github.com/idosal/ui-inspector) (client).
## Prerequisites
- Node.js (v18+)
- An MCP server with tools that have `_meta.ui.resourceUri` (see [Server Walkthrough](../server/typescript/walkthrough))
- A React project (this guide uses Vite)
## 1. Set up a React Project
If you don't have an existing React project, create one with Vite:
```bash
npm create vite@latest my-mcp-client -- --template react-ts
cd my-mcp-client
npm install
```
## 2. Install Dependencies
Install the MCP SDK, client package, and ext-apps:
```bash
npm install @mcp-ui/client @modelcontextprotocol/sdk @modelcontextprotocol/ext-apps
```
## 3. Set Up a Sandbox Proxy
MCP Apps renders tool UIs in sandboxed iframes for security. You need a sandbox proxy HTML file that will host the guest content. Create `public/sandbox_proxy.html`:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sandbox Proxy</title>
<style>
html, body { margin: 0; padding: 0; width: 100%; height: 100%; }
</style>
</head>
<body>
<script>
// Sandbox proxy implementation for MCP Apps
// This receives HTML content from the host and renders it securely
// Listen for messages from the host
window.addEventListener('message', (event) => {
const data = event.data;
if (!data || typeof data !== 'object') return;
// Handle resource ready notification (HTML content to render)
if (data.method === 'ui/notifications/sandbox-resource-ready') {
const { html } = data.params || {};
if (html) {
// Replace the entire document with the received HTML
document.open();
document.write(html);
document.close();
}
}
});
// Signal that the sandbox proxy is ready
window.parent.postMessage({
method: 'ui/notifications/sandbox-proxy-ready',
params: {}
}, '*');
</script>
</body>
</html>
```
::: tip Production Setup
For production, consider implementing Content Security Policy (CSP) headers and additional security measures. See [@modelcontextprotocol/ext-apps](https://github.com/modelcontextprotocol/ext-apps) for more details on secure sandbox proxy implementation.
:::
## 4. Create an MCP Client
Create a file `src/mcp-client.ts` to handle the MCP connection:
```typescript
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import {
type ClientCapabilitiesWithExtensions,
UI_EXTENSION_CAPABILITIES,
} from '@mcp-ui/client';
export async function createMcpClient(serverUrl: string): Promise<Client> {
// Create the client with UI extension capabilities
const capabilities: ClientCapabilitiesWithExtensions = {
roots: { listChanged: true },
extensions: UI_EXTENSION_CAPABILITIES,
};
const client = new Client(
{ name: 'my-mcp-client', version: '1.0.0' },
{ capabilities }
);
// Connect to the MCP server
const transport = new StreamableHTTPClientTransport(new URL(serverUrl));
await client.connect(transport);
console.log('Connected to MCP server');
return client;
}
```
## 5. Create the Tool UI Component
Create a component that uses `AppRenderer` to render tool UIs. Create `src/ToolUI.tsx`:
```tsx
import { useState, useEffect, useRef } from 'react';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { AppRenderer, type AppRendererHandle } from '@mcp-ui/client';
interface ToolUIProps {
client: Client;
toolName: string;
toolInput?: Record<string, unknown>;
}
export function ToolUI({ client, toolName, toolInput }: ToolUIProps) {
const [toolResult, setToolResult] = useState<any>(null);
const [error, setError] = useState<string | null>(null);
const appRef = useRef<AppRendererHandle>(null);
// Call the tool when input changes
useEffect(() => {
if (!toolInput) return;
const callTool = async () => {
try {
const result = await client.callTool({
name: toolName,
arguments: toolInput,
});
setToolResult(result);
} catch (err) {
setError(err instanceof Error ? err.message : String(err));
}
};
callTool();
}, [client, toolName, toolInput]);
// Get the sandbox URL
const sandboxUrl = new URL('/sandbox_proxy.html', window.location.origin);
if (error) {
return <div style={{ color: 'red' }}>Error: {error}</div>;
}
return (
<div style={{ width: '100%', height: '600px' }}>
<AppRenderer
ref={appRef}
client={client}
toolName={toolName}
sandbox={{ url: sandboxUrl }}
toolInput={toolInput}
toolResult={toolResult}
onOpenLink={async ({ url }) => {
// Handle link requests from the UI
window.open(url, '_blank');
return { isError: false };
}}
onMessage={async (params) => {
// Handle message requests from the UI (e.g., follow-up prompts)
console.log('Message from UI:', params);
return { isError: false };
}}
onSizeChanged={(params) => {
// Handle size change notifications
console.log('Size changed:', params);
}}
onError={(error) => {
console.error('UI Error:', error);
setError(error.message);
}}
/>
</div>
);
}
```
## 6. Create the Main App
Update `src/App.tsx` to connect to the MCP server and render tool UIs:
```tsx
import { useState, useEffect } from 'react';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { createMcpClient } from './mcp-client';
import { ToolUI } from './ToolUI';
import './App.css';
function App() {
const [client, setClient] = useState<Client | null>(null);
const [tools, setTools] = useState<any[]>([]);
const [selectedTool, setSelectedTool] = useState<string | null>(null);
const [toolInput, setToolInput] = useState<Record<string, unknown>>({});
const [error, setError] = useState<string | null>(null);
// Connect to MCP server on mount
useEffect(() => {
const connect = async () => {
try {
// Replace with your MCP server URL
const mcpClient = await createMcpClient('http://localhost:3001/mcp');
setClient(mcpClient);
// List available tools
const toolsResult = await mcpClient.listTools({});
setTools(toolsResult.tools);
} catch (err) {
setError(err instanceof Error ? err.message : String(err));
}
};
connect();
}, []);
// Filter tools that have UI resources
const toolsWithUI = tools.filter((tool) =>
tool._meta?.ui?.resourceUri
);
if (error) {
return <div style={{ color: 'red', padding: '20px' }}>Error: {error}</div>;
}
if (!client) {
return <div style={{ padding: '20px' }}>Connecting to MCP server...</div>;
}
return (
<div style={{ padding: '20px' }}>
<h1>MCP Apps Client Demo</h1>
<div style={{ marginBottom: '20px' }}>
<h2>Available Tools with UI</h2>
{toolsWithUI.length === 0 ? (
<p>No tools with UI found. Make sure your server has tools with _meta.ui.resourceUri.</p>
) : (
<ul>
{toolsWithUI.map((tool) => (
<li key={tool.name}>
<button
onClick={() => {
setSelectedTool(tool.name);
setToolInput({ query: 'Hello from client!' });
}}
style={{
fontWeight: selectedTool === tool.name ? 'bold' : 'normal',
}}
>
{tool.name}
</button>
<span style={{ marginLeft: '10px', color: '#666' }}>
{tool.description}
</span>
</li>
))}
</ul>
)}
</div>
{selectedTool && client && (
<div style={{ border: '1px solid #ccc', padding: '20px', borderRadius: '8px' }}>
<h2>Tool UI: {selectedTool}</h2>
<ToolUI
client={client}
toolName={selectedTool}
toolInput={toolInput}
/>
</div>
)}
</div>
);
}
export default App;
```
## 7. Run the Application
Start your React development server:
```bash
npm run dev
```
Make sure your MCP server is running (e.g., the `mcp-apps-demo` example on port 3001):
```bash
# In the mcp-apps-demo directory
npm run build && npm start
```
Open your browser to `http://localhost:5173` (or the Vite dev server URL). You should see:
1. A list of tools with UI from the connected MCP server
2. Click a tool to render its UI in the sandboxed iframe
3. The UI can send messages back to your client via the `onMessage` callback
## 8. Handle Custom Tool Calls from UI
Tool UIs can request to call other tools. Add a custom handler:
```tsx
<AppRenderer
client={client}
toolName={selectedTool}
sandbox={{ url: sandboxUrl }}
// ... other props
onCallTool={async (params) => {
// Custom handling for tool calls from the UI
console.log('UI requested tool call:', params);
// You can filter, modify, or intercept tool calls here
const result = await client.callTool(params);
return result;
}}
/>
```
## 9. Using AppRenderer Without a Client
If you don't have direct access to an MCP client (e.g., the MCP connection is managed by a backend), you can use callbacks instead:
```tsx
<AppRenderer
toolName="my-tool"
toolResourceUri="ui://my-server/widget"
sandbox={{ url: sandboxUrl }}
onReadResource={async ({ uri }) => {
// Fetch the resource from your backend
const response = await fetch(`/api/mcp/resources?uri=${encodeURIComponent(uri)}`);
return response.json();
}}
onCallTool={async (params) => {
// Proxy tool calls through your backend
const response = await fetch('/api/mcp/tools/call', {
method: 'POST',
body: JSON.stringify(params),
});
return response.json();
}}
toolInput={{ query: 'hello' }}
/>
```
Or provide pre-fetched HTML directly:
```tsx
<AppRenderer
toolName="my-tool"
sandbox={{ url: sandboxUrl }}
html={preloadedHtml} // Skip resource fetching entirely
toolInput={{ query: 'hello' }}
/>
```
## Next Steps
- [AppRenderer Reference](./app-renderer.md) - Complete API documentation for AppRenderer
- [Protocol Details](../protocol-details.md) - Understanding the MCP Apps protocol
- [MCP Apps Overview](../mcp-apps.md) - Protocol details and server-side setup
- [Supported Hosts](../supported-hosts.md) - See which hosts support MCP Apps
================================================
FILE: docs/src/guide/embeddable-ui.md
================================================
# Embeddable UI
::: tip MCP Apps Standard
This page documents the **legacy MCP-UI `postMessage` protocol** for embedded iframes. For MCP Apps hosts, see the [MCP Apps JSON-RPC protocol](./protocol-details#mcp-apps-protocol) instead.
For new apps, we recommend using the MCP Apps pattern with `_meta.ui.resourceUri`. See [Getting Started](./getting-started).
:::
> This document describes the general communication protocol for any embeddable UIs.
> This is implemented by the mcp-ui iframe based solution, in the context of UI over MCP.
# Concepts
- Embedded iframes communicate with the parent window via `postMessage`.
- The parent window can send messages to the iframe.
- The iframe can send messages to the parent window.
# Communication Protocol
## Message Structure
```typescript
type Message = {
type: string;
messageId?: string; // optional, used for tracking the message
payload: Record<string, unknown>;
};
```
## Message Types
- [`intent`](#intent) - the user has interacted with the UI and expressed an intent, and the host should act on it
- [`notify`](#notify) - the iframe already acted upon the user interaction, and is notifying the host to trigger any side effects
- [`prompt`](#prompt) - the iframe asks the host to run a prompt
- [`tool`](#tool) - the iframe asks the host to run a tool call
- [`link`](#link) - the iframe asks the host to navigate to a link
### `intent`
- indicates that the user has interacted with the UI and expressed an intent, and the host should act on it
- the payload is an object with the following properties:
- `intent` - the intent that the user expressed
- `params` - the parameters to pass to the intent
**Example:**
```typescript
window.parent.postMessage(
{
type: "intent",
payload: {
intent: "create-task",
params: {
title: "Buy groceries",
description: "Buy groceries for the week",
},
},
},
"*"
);
```
### `notify`
- indicates that the iframe already acted upon the user interaction, and is notifying the host to trigger any side effects
- the payload is an object with the following properties:
- `message` - the message to notify the host with
**Example:**
```typescript
window.parent.postMessage(
{
type: "notify",
payload: {
message: "cart-updated",
},
},
"*"
);
```
### `prompt`
- indicates that the iframe asks the host to run a prompt
- the payload is an object with the following properties:
- `prompt` - the prompt to run
**Example:**
```typescript
window.parent.postMessage(
{
type: "prompt",
payload: {
prompt: "What is the weather in Tokyo?",
},
},
"*"
);
```
### `tool`
- indicates that the iframe asks the host to run a tool call
- the payload is an object with the following properties:
- `toolName` - the name of the tool to run
- `params` - the parameters to pass to the tool
**Example:**
```typescript
window.parent.postMessage(
{
type: "tool",
payload: {
toolName: "get-weather",
params: {
city: "Tokyo",
},
},
},
"*"
);
```
### `link`
- indicates that the iframe asks the host to navigate to a link
- the payload is an object with the following properties:
- `url` - the URL to navigate to
**Example:**
```typescript
window.parent.postMessage(
{
type: "link",
payload: {
url: "https://www.google.com",
},
},
"*"
);
```
## Reserved Message Types (iframe to host)
- [`ui-lifecycle-iframe-ready`](#ui-lifecycle-iframe-ready) - the iframe is ready to receive messages
- [`ui-size-change`](#ui-size-change) - the iframe's size has changed and the host should adjust the iframe's size
- [`ui-request-data`](#ui-request-data) - the iframe sends a request to the host to request data
- [`ui-request-render-data`](#ui-request-render-data) - the iframe requests render data from the host
### `ui-lifecycle-iframe-ready`
- indicates that the iframe is ready to receive messages
**Example:**
See [Render Data](#passing-render-data-to-the-iframe)
### `ui-size-change`
- indicates that the iframe's size has changed and the host should adjust the iframe's size
- the payload is an object with the following properties:
- `width` - the new width of the iframe
- `height` - the new height of the iframe
**Example:**
```typescript
const resizeObserver = new ResizeObserver((entries) => {
entries.forEach((entry) => {
window.parent.postMessage(
{
type: "ui-size-change",
payload: {
height: entry.contentRect.height,
},
},
"*"
);
});
});
resizeObserver.observe(document.documentElement);
```
### `ui-request-data`
- a message that the iframe sends to the host to request data. The message must include a `messageId` to allow the iframe to track the response.
- the payload is an object with the following properties:
- `requestType` - the type of the request
- `params` - the parameters to pass to the request
**Example:**
```typescript
window.parent.postMessage(
{
type: "ui-request-data",
messageId: "123",
payload: {
requestType: "get-payment-methods",
params: {
// any params needed for the request
},
},
},
"*"
);
```
See also [Asynchronous Data Requests with Message IDs](#asynchronous-data-requests-with-message-ids)
### `ui-request-render-data`
- a message that the iframe sends to the host to request render data. The message can optionally include a `messageId` to allow the iframe to track the response.
- this message has no payload
- the host responds with a [`ui-lifecycle-iframe-render-data`](#ui-lifecycle-iframe-render-data) message containing the render data
**Example:**
```typescript
window.parent.postMessage(
{
type: "ui-request-render-data",
messageId: "render-data-123", // optional
},
"*"
);
```
## Reserved Message Types (host to iframe)
- [`ui-lifecycle-iframe-render-data`](#ui-lifecycle-iframe-render-data) - the host sends the iframe render data
- [`ui-message-received`](#ui-message-received) - the host sends the iframe to indicate that the action has been received
- [`ui-message-response`](#ui-message-response) - the iframe sends the host to indicate that the action has been processed or failed
### `ui-lifecycle-iframe-render-data`
- a message that the host sends to the iframe to pass any relevant render data
- the payload is an object with the following properties:
- `renderData` - the render data to pass to the iframe
**Example:**
See [Render Data](#render-data)
### `ui-message-received`
- a message that the host sends to the iframe to indicate that the action has been received. The original messageId is passed back to the host to allow the host to track the action. This is useful for `request-data` messages, but is not limited to this type.
**Example:**
See [Asynchronous Data Requests with Message IDs](#asynchronous-data-requests-with-message-ids)
### `ui-message-response`
- a message that the iframe sends to the host to indicate that the action has been processed. The original messageId is passed back to the host to allow the host to track the action. This is useful for `request-data` messages, but is not limited to this type.
- the payload is an object with the following properties:
- `response` - the response to the action
- `error` - the error, if any, that occurred
**Example:**
See [Asynchronous Data Requests with Message IDs](#asynchronous-data-requests-with-message-ids)
## Query Parameters
### `waitForRenderData`
- a query parameter that can be passed to the iframe to indicate that the iframe should wait for the render data to be passed before sending any messages
- the value of the query parameter is a boolean
- if the query parameter is present, the iframe will wait for the render data to be passed before sending any messages
**Example:**
See [Render Data](#render-data)
# Usage Examples
## Passing Render Data to the Iframe
### In the host:
```typescript
iframeSrc = "https://my-embeddable-ui.com?waitForRenderData=true";
iframe = document.createElement("iframe");
iframe.src = iframeSrc; // the iframe will wait for the render data to be passed before rendering
document.body.appendChild(iframe);
window.addEventListener("message", (event) => {
if (event.data.type === "ui-lifecycle-iframe-ready") {
iframe.contentWindow.postMessage(
{
type: "ui-lifecycle-iframe-render-data",
payload: { renderData: { theme: "dark" } },
},
"*"
);
}
});
```
### In the iframe:
```typescript
// In the iframe's script
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get("waitForRenderData") === "true") {
let customRenderData = null;
// The parent will send this message on load or when we notify it we're ready
window.addEventListener("message", (event) => {
// Add origin checks for security
if (event.data.type === "ui-lifecycle-iframe-render-data") {
// If the iframe has already received data, we don't need to do anything
if (customRenderData) {
return;
} else {
customRenderData = event.data.payload.renderData;
// Now you can render the UI with the received data
renderUI(customRenderData);
}
}
});
// We can let the parent know we're ready to receive data
window.parent.postMessage({ type: "ui-lifecycle-iframe-ready" }, "*");
} else {
// If the iframe doesn't need to wait for data, we can render the default UI immediately
renderUI();
}
```
### Alternative: Requesting Render Data On-Demand
Instead of relying on the `ui-lifecycle-iframe-ready` lifecycle event, you can explicitly request render data when needed using `ui-request-render-data`:
#### In the iframe:
```typescript
// Request render data when ready
async function requestRenderData() {
return new Promise((resolve, reject) => {
const messageId = crypto.randomUUID();
window.parent.postMessage(
{ type: "ui-request-render-data", messageId },
"*"
);
function handleMessage(event) {
if (event.data?.type !== "ui-lifecycle-iframe-render-data") return;
if (event.data.messageId !== messageId) return;
window.removeEventListener("message", handleMessage);
const { renderData, error } = event.data.payload;
if (error) return reject(error);
return resolve(renderData);
}
window.addEventListener("message", handleMessage);
});
}
// Use it when your iframe is ready
const renderData = await requestRenderData();
renderUI(renderData);
```
## Asynchronous Data Requests with Message IDs
Actions initiated from the iframe are handled by the host asynchronously (e.g., data requests, tool calls, etc.). It's useful for the iframe to get feedback on the status of the request and its result. This is achieved using a `messageId` to track the request through its lifecycle. Example use cases include fetching additional information, displaying a progress bar in the iframe, signaling success or failure, and more.
The primary message types are:
- `ui-request-data`: Sent from the iframe to the host to request some data or action.
- `ui-message-received`: Sent from the host to the iframe to acknowledge that the request is being processed.
- `ui-message-response`: Sent from the host to the iframe with the final result (success or error).
While this example uses `ui-request-data`, any message from the iframe can include a `messageId` to leverage this asynchronous flow (e.g., `tool`, `intent`).
```mermaid
sequenceDiagram
participant Iframe
participant Host
Iframe->>Host: postMessage({ type: 'ui-request-data', messageId: '123', ... })
Note right of Iframe: 1. Iframe initiates request<br>with a unique messageId.
Host->>Iframe: postMessage({ type: 'ui-message-received', messageId: '123' })
Note left of Host: 2. Host acknowledges receipt (optional).
Note over Host: Process request...
alt Request succeeds
Host->>Iframe: postMessage({ type: 'ui-message-response', messageId: '123', payload: { response: ... } })
else Request fails
Host->>Iframe: postMessage({ type: 'ui-message-response', messageId: '123', payload: { error: ... } })
end
Note left of Host: 3. Host sends final response.
Note right of Iframe: 4. Iframe uses messageId to handle<br>the specific response.
```
================================================
FILE: docs/src/guide/getting-started.md
================================================
# Getting Started
This guide will help you get started with building MCP Apps using the `@mcp-ui/*` packages.
## Prerequisites
- Node.js (v22.x recommended for the TypeScript SDK)
- pnpm (v9 or later recommended for the TypeScript SDK)
- Ruby (v3.x recommended for the Ruby SDK)
- Python (v3.10+ recommended for the Python SDK)
## Installation
### For TypeScript
```bash
# Server SDK
npm install @mcp-ui/server @modelcontextprotocol/ext-apps
# Client SDK
npm install @mcp-ui/client
```
### For Ruby
```bash
gem install mcp_ui_server
```
### For Python
```bash
pip install mcp-ui-server
```
## Quick Start: MCP Apps Pattern
### Server Side
Create a tool with an interactive UI using `registerAppTool` and `_meta.ui.resourceUri`:
```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerAppTool, registerAppResource } from '@modelcontextprotocol/ext-apps/server';
import { createUIResource } from '@mcp-ui/server';
import { z } from 'zod';
// 1. Create your MCP server
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
// 2. Create the UI resource with interactive HTML
const widgetUI = await createUIResource({
uri: 'ui://my-server/widget',
content: {
type: 'rawHtml',
htmlString: `
<html>
<body>
<h1>Interactive Widget</h1>
<button onclick="sendMessage()">Send Message</button>
<div id="status">Ready</div>
<script type="module">
import { App } from 'https://esm.sh/@modelcontextprotocol/ext-apps@0.4.1';
// Initialize the MCP Apps client
const app = new App({ name: 'widget', version: '1.0.0' });
// Listen for tool input
app.ontoolinput = (params) => {
document.getElementById('status').textContent =
'Received: ' + JSON.stringify(params.input);
};
// Send a message to the conversation
window.sendMessage = async () => {
await app.sendMessage({
role: 'user',
content: [{ type: 'text', text: 'Tell me more about this widget' }]
});
};
// Connect to the host
await app.connect();
</script>
</body>
</html>
`,
},
encoding: 'text',
});
// 3. Register the resource handler
registerAppResource(
server,
'widget_ui',
widgetUI.resource.uri,
{},
async () => ({
contents: [widgetUI.resource]
})
);
// 4. Register the tool with _meta.ui.resourceUri
registerAppTool(
server,
'show_widget',
{
description: 'Show an interactive widget',
inputSchema: {
query: z.string().describe('User query'),
},
_meta: {
ui: {
resourceUri: widgetUI.resource.uri // Links tool to UI
}
}
},
async ({ query }) => {
return {
content: [{ type: 'text', text: `Processing: ${query}` }]
};
}
);
```
::: tip MCP Apps Protocol
The example above uses the [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps) `App` class for communication. See [Protocol Details](./protocol-details) for the full JSON-RPC API.
:::
### Client Side
Render tool UIs with `AppRenderer`:
```tsx
import { AppRenderer } from '@mcp-ui/client';
function ToolUI({ client, toolName, toolInput, toolResult }) {
return (
<AppRenderer
client={client}
toolName={toolName}
sandbox={{ url: new URL('/sandbox_proxy.html', window.location.origin) }}
toolInput={toolInput}
toolResult={toolResult}
onOpenLink={async ({ url }) => {
// Validate URL scheme before opening
if (url.startsWith('https://') || url.startsWith('http://')) {
window.open(url);
}
return { isError: false };
}}
onMessage={async (params) => {
console.log('Message from UI:', params);
// Handle the message (e.g., send to AI conversation)
return { isError: false };
}}
onError={(error) => console.error('UI error:', error)}
/>
);
}
```
### Using Without an MCP Client
You can use `AppRenderer` without a full MCP client by providing callbacks:
```tsx
<AppRenderer
toolName="show_widget"
toolResourceUri="ui://my-server/widget"
sandbox={{ url: sandboxUrl }}
onReadResource={async ({ uri }) => {
// Fetch resource from your backend
return myBackend.readResource({ uri });
}}
onCallTool={async (params) => {
return myBackend.callTool(params);
}}
toolInput={{ query: 'hello' }}
/>
```
Or provide pre-fetched HTML directly:
```tsx
<AppRenderer
toolName="show_widget"
sandbox={{ url: sandboxUrl }}
html={preloadedHtml} // Skip resource fetching
toolInput={{ query: 'hello' }}
/>
```
## Resource Types
MCP Apps supports several UI content types:
### 1. HTML Resources
Direct HTML content rendered in a sandboxed iframe:
```typescript
const htmlResource = await createUIResource({
uri: 'ui://my-tool/widget',
content: { type: 'rawHtml', htmlString: '<h1>Hello World</h1>' },
encoding: 'text',
});
```
### 2. External URLs
Fetch an external page's HTML and serve it as a UI resource. The SDK fetches the URL's contents server-side and injects a `<base>` tag so relative paths (CSS, JS, images) resolve correctly:
```typescript
const urlResource = await createUIResource({
uri: 'ui://my-tool/external',
content: { type: 'externalUrl', iframeUrl: 'https://example.com' },
encoding: 'text',
});
// The resource now contains the fetched HTML with a <base href="https://example.com"> tag
```
## Declaring UI Extension Support
When creating your MCP client, declare UI extension support:
```typescript
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import {
type ClientCapabilitiesWithExtensions,
UI_EXTENSION_CAPABILITIES,
} from '@mcp-ui/client';
const capabilities: ClientCapabilitiesWithExtensions = {
roots: { listChanged: true },
extensions: UI_EXTENSION_CAPABILITIES,
};
const client = new Client(
{ name: 'my-app', version: '1.0.0' },
{ capabilities }
);
```
## Building from Source
### Clone and Install
```bash
git clone https://github.com/idosal/mcp-ui.git
cd mcp-ui
pnpm install
```
### Build All Packages
```bash
pnpm --filter=!@mcp-ui/docs build
```
### Run Tests
```bash
pnpm test
```
## Next Steps
- **Server SDKs**: Learn how to create resources with our server-side packages.
- [TypeScript SDK Usage & Examples](./server/typescript/usage-examples.md)
- [Ruby SDK Usage & Examples](./server/ruby/usage-examples.md)
- [Python SDK Usage & Examples](./server/python/usage-examples.md)
- **Client SDK**: Learn how to render resources.
- [Client Overview](./client/overview.md)
- **Protocol & Components**:
- [Protocol Details](./protocol-details.md)
================================================
FILE: docs/src/guide/introduction.md
================================================
# Introduction
Welcome to the MCP Apps SDK documentation!
The `@mcp-ui/*` packages provide tools for building [MCP Apps](https://github.com/modelcontextprotocol/ext-apps) - interactive UI components for Model Context Protocol (MCP) tools. This SDK implements the MCP Apps standard, enabling rich HTML interfaces within AI applications.
You can use [GitMCP](https://gitmcp.io/idosal/mcp-ui) to give your IDE access to `mcp-ui`'s latest documentation!
<a href="https://gitmcp.io/idosal/mcp-ui"><img src="https://img.shields.io/endpoint?url=https://gitmcp.io/badge/idosal/mcp-ui" alt="MCP Documentation"></a>
## Background
MCP-UI pioneered the concept of interactive UI over the Model Context Protocol. Before MCP Apps existed as a standard, this project demonstrated how MCP tools could return rich, interactive HTML interfaces instead of plain text, enabling UI components within AI applications.
The patterns and ideas explored in MCP-UI directly influenced the development of the [MCP Apps specification](https://github.com/modelcontextprotocol/ext-apps), which standardized UI delivery over MCP. Today, the `@mcp-ui/*` packages implement this standard while maintaining the project's original vision: making it simple to build beautiful, interactive experiences for AI tools.
## What are MCP Apps?
MCP Apps is a standard for attaching interactive UIs to MCP tools. When a tool has an associated UI, hosts can render it alongside the tool's results, enabling rich user experiences like forms, charts, and interactive widgets.
### The Core Pattern
The MCP Apps pattern uses three key concepts:
1. **Tool with `_meta.ui.resourceUri`** - Links a tool to its UI resource
2. **Resource Handler** - Serves the UI content when the host requests it
3. **AppRenderer** - Client component that fetches and renders the UI
```typescript
// 1. Create UI content
const widgetUI = await createUIResource({
uri: 'ui://my-server/widget',
content: { type: 'rawHtml', htmlString: '<h1>Widget</h1>' },
encoding: 'text',
});
// 2. Register resource handler
registerAppResource(server, 'widget_ui', widgetUI.resource.uri, {}, async () => ({
contents: [widgetUI.resource]
}));
// 3. Register tool with _meta linking
registerAppTool(server, 'show_widget', {
description: 'Show interactive widget',
inputSchema: { query: z.string() },
_meta: { ui: { resourceUri: widgetUI.resource.uri } } // This links tool → UI
}, async ({ query }) => {
return { content: [{ type: 'text', text: `Result: ${query}` }] };
});
```
When a host calls `show_widget`, it sees the `_meta.ui.resourceUri` and fetches the UI from that resource URI to render alongside the tool result.
## SDK Packages
The `@mcp-ui/*` packages provide everything needed to build and render MCP Apps:
### Server SDK (`@mcp-ui/server`)
- **`createUIResource`**: Creates UI resource objects with HTML content or fetched external URLs
- Works with `registerAppTool` and `registerAppResource` from `@modelcontextprotocol/ext-apps/server`
### Client SDK (`@mcp-ui/client`)
- **`AppRenderer`**: High-level component for rendering tool UIs (fetches resources, handles lifecycle)
- **`AppFrame`**: Lower-level component for when you have pre-fetched HTML
### Additional Language SDKs
- **`mcp_ui_server`** (Ruby): Helper methods for creating UI resources
- **`mcp-ui-server`** (Python): Helper methods for creating UI resources
## How It Works
```
┌─────────────────────────────────────────────────────────────────┐
│ MCP Host │
│ 1. Calls tool │
│ 2. Sees _meta.ui.resourceUri in tool definition │
│ 3. Fetches resource via resources/read │
│ 4. Renders UI in sandboxed iframe (AppRenderer) │
└─────────────────────────────────────────────────────────────────┘
│ ▲
▼ │
┌─────────────────────────────────────────────────────────────────┐
│ MCP Server │
│ - registerAppTool with _meta.ui.resourceUri │
│ - registerAppResource to serve UI content │
│ - createUIResource to build UI payloads │
└─────────────────────────────────────────────────────────────────┘
```
### Example Flow
**Server (MCP Tool):**
::: code-group
```typescript [TypeScript]
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerAppTool, registerAppResource } from '@modelcontextprotocol/ext-apps/server';
import { createUIResource } from '@mcp-ui/server';
import { z } from 'zod';
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
const dashboardUI = await createUIResource({
uri: 'ui://my-tool/dashboard',
content: { type: 'rawHtml', htmlString: '<h1>Dashboard</h1>' },
encoding: 'text'
});
registerAppResource(server, 'dashboard_ui', dashboardUI.resource.uri, {}, async () => ({
contents: [dashboardUI.resource]
}));
registerAppTool(server, 'show_dashboard', {
description: 'Show dashboard',
inputSchema: {},
_meta: { ui: { resourceUri: dashboardUI.resource.uri } }
}, async () => {
return { content: [{ type: 'text', text: 'Dashboard loaded' }] };
});
```
```ruby [Ruby]
require 'mcp_ui_server'
resource = McpUiServer.create_ui_resource(
uri: 'ui://my-tool/dashboard',
content: { type: :raw_html, htmlString: '<h1>Dashboard</h1>' },
encoding: :text
)
# Return in MCP response
{ content: [resource] }
```
:::
**Client (Frontend App):**
```tsx
import { AppRenderer } from '@mcp-ui/client';
function ToolUI({ client, toolName, toolInput, toolResult }) {
return (
<AppRenderer
client={client}
toolName={toolName}
sandbox={{ url: new URL('http://localhost:8765/sandbox_proxy.html') }}
toolInput={toolInput}
toolResult={toolResult}
onOpenLink={async ({ url }) => {
if (url.startsWith('https://') || url.startsWith('http://')) {
window.open(url);
}
}}
onMessage={async (params) => {
console.log('Message from UI:', params);
return { isError: false };
}}
/>
);
}
```
## Key Benefits
- **Standardized**: Implements the MCP Apps specification for consistent behavior across hosts
- **Secure**: Sandboxed iframe execution prevents malicious code from affecting the host
- **Interactive**: Two-way communication between UI and host via JSON-RPC
- **Flexible**: Supports HTML content with the MCP Apps standard MIME type
## Wire Format: UIResource
The underlying data format for UI content is the `UIResource` object:
```typescript
interface UIResource {
type: 'resource';
resource: {
uri: string; // ui://component/id
mimeType: 'text/html;profile=mcp-app'; // MCP Apps standard
text?: string; // Inline HTML content
blob?: string; // Base64-encoded content
};
}
```
The MIME type `text/html;profile=mcp-app` is the MCP Apps standard for UI resources.
### Key Field Details:
- **`uri`**: Unique identifier using `ui://` scheme (e.g., `ui://my-tool/widget-01`)
- **`mimeType`**: `text/html;profile=mcp-app` — MCP Apps-compliant HTML
- **`text` or `blob`**: The actual content, either as plain text or Base64 encoded
## Next Steps
- [Getting Started](./getting-started.md) - Set up your development environment
- [Server Walkthroughs](./server/typescript/walkthrough.md) - Step-by-step guides
- [Client SDK](./client/overview.md) - Learn to render tool UIs with AppRenderer
- [TypeScript Server SDK](./server/typescript/overview.md) - Create tools with UI
- [Ruby Server SDK](./server/ruby/overview.md) - Ruby implementation
- [Protocol Details](./protocol-details.md) - Understand the underlying protocol
================================================
FILE: docs/src/guide/mcp-apps.md
================================================
# Legacy MCP-UI Adapter
::: tip For New Apps
**Building a new app?** Use the MCP Apps patterns directly - see [Getting Started](./getting-started) for the recommended approach with `registerAppTool`, `_meta.ui.resourceUri`, and `AppRenderer`.
This page is for **migrating existing MCP-UI widgets** to work in MCP Apps hosts.
:::
The MCP Apps adapter in `@mcp-ui/server` enables **existing MCP-UI HTML widgets** to run inside MCP Apps-compliant hosts. This is a backward-compatibility layer for apps that were built using the legacy MCP-UI `postMessage` protocol.
## When to Use This Adapter
- **Existing MCP-UI widgets**: You have HTML widgets using the `ui-lifecycle-*` message format
- **Gradual migration**: You want your existing widgets to work in both legacy MCP-UI hosts and new MCP Apps hosts
- **Protocol translation**: Your widget uses `postMessage` calls that need to be translated to JSON-RPC
## Overview
The adapter automatically translates between the MCP-UI `postMessage` protocol and MCP Apps JSON-RPC, allowing your existing widgets to work in MCP Apps hosts without code changes.
## How It Works
```
┌─────────────────────────────────────────────────────────────────┐
│ MCP Apps Host │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ Sandbox Iframe │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ Tool UI Iframe │ │ │
│ │ │ ┌───────────────┐ ┌──────────────────────────┐ │ │ │
│ │ │ │ MCP-UI │───▶│ MCP Apps Adapter │ │ │ │
│ │ │ │ Widget │◀───│ (injected script) │ │ │ │
│ │ │ └───────────────┘ └──────────────────────────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ MCP-UI Protocol │ JSON-RPC │ │ │
│ │ │ ▼ ▼ │ │ │
│ │ │ postMessage postMessage │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ MCP Apps SEP Protocol │
└─────────────────────────────────────────────────────────────────┘
```
The adapter:
1. Intercepts MCP-UI messages from your widget
2. Translates them to MCP Apps SEP JSON-RPC format
3. Sends them to the host via postMessage
4. Receives host responses and translates them back to MCP-UI format
## Quick Start
### 1. Create a UI Resource with the MCP Apps Adapter
```typescript
import { createUIResource } from '@mcp-ui/server';
const widgetUI = await createUIResource({
uri: 'ui://my-server/widget',
encoding: 'text',
content: {
type: 'rawHtml',
htmlString: `
<html>
<body>
<div id="app">Loading...</div>
<script>
// Listen for render data from the adapter
window.addEventListener('message', (event) => {
if (event.data.type === 'ui-lifecycle-iframe-render-data') {
const { toolInput, toolOutput } = event.data.payload.renderData;
document.getElementById('app').textContent =
JSON.stringify({ toolInput, toolOutput }, null, 2);
}
});
// Signal that the widget is ready
window.parent.postMessage({ type: 'ui-lifecycle-iframe-ready' }, '*');
</script>
</body>
</html>
`,
},
});
```
### 2. Register the Resource and Tool
```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { createUIResource } from '@mcp-ui/server';
import { registerAppTool, registerAppResource } from '@modelcontextprotocol/ext-apps/server';
import { z } from 'zod';
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
// Create the UI resource (from step 1)
const widgetUI = await createUIResource({
uri: 'ui://my-server/widget',
// ... (same as above)
});
// Register the resource so the host can fetch it
registerAppResource(
server,
'widget_ui', // Resource name
widgetUI.resource.uri, // Resource URI
{}, // Resource metadata
async () => ({
contents: [widgetUI.resource]
})
);
// Register the tool with _meta linking to the UI resource
registerAppTool(
server,
'my_widget',
{
description: 'An interactive widget',
inputSchema: {
query: z.string().describe('User query'),
},
// This tells MCP Apps hosts where to find the UI
_meta: {
ui: {
resourceUri: widgetUI.resource.uri
}
}
},
async ({ query }) => {
return {
content: [{ type: 'text', text: `Processing: ${query}` }],
};
}
);
```
The key requirement for MCP Apps hosts is that the tool's `_meta.ui.resourceUri` points to the UI resource URI. This tells the host where to fetch the widget HTML.
### 3. Add the MCP-UI Embedded Resource to Tool Responses
To support **MCP-UI hosts** (which expect embedded resources in tool responses), also return a `createUIResource` result:
```typescript
registerAppTool(
server,
'my_widget',
{
description: 'An interactive widget',
inputSchema: {
query: z.string().describe('User query'),
},
// For MCP Apps hosts - points to the registered resource
_meta: {
ui: {
resourceUri: widgetUI.resource.uri
}
}
},
async ({ query }) => {
// Create an embedded UI resource for MCP-UI hosts
const embeddedResource = await createUIResource({
uri: `ui://my-server/widget/${query}`,
encoding: 'text',
content: {
type: 'rawHtml',
htmlString: renderWidget(query), // Your widget HTML
},
});
return {
content: [
{ type: 'text', text: `Processing: ${query}` },
embeddedResource // Include for MCP-UI hosts
],
};
}
);
```
> **Important:** The embedded MCP-UI resource should **not** enable the MCP Apps adapter. It is for hosts that expect embedded resources in tool responses. MCP Apps hosts will ignore the embedded resource and instead fetch the UI from the registered resource URI in `_meta`.
## Protocol Translation Reference
### Widget → Host (Outgoing)
| MCP-UI Action | MCP Apps Method | Description |
|--------------|-----------------|-------------|
| `tool` | `tools/call` | Call another tool |
| `prompt` | `ui/message` | Send a follow-up message to the conversation |
| `link` | `ui/open-link` | Open a URL in a new tab |
| `notify` | `notifications/message` | Log a message to the host |
| `intent` | `ui/message` | Send an intent (translated to message) |
| `ui-size-change` | `ui/notifications/size-changed` | Request widget resize |
### Host → Widget (Incoming)
| MCP Apps Notification | MCP-UI Message | Description |
|----------------------|----------------|-------------|
| `ui/notifications/tool-input` | `ui-lifecycle-iframe-render-data` | Complete tool arguments |
| `ui/notifications/tool-input-partial` | `ui-lifecycle-iframe-render-data` | Streaming partial arguments |
| `ui/notifications/tool-result` | `ui-lifecycle-iframe-render-data` | Tool execution result |
| `ui/notifications/host-context-changed` | `ui-lifecycle-iframe-render-data` | Theme, locale, viewport changes |
| `ui/notifications/size-changed` | `ui-lifecycle-iframe-render-data` | Host informs of size constraints |
| `ui/notifications/tool-cancelled` | `ui-lifecycle-tool-cancelled` | Tool execution was cancelled |
| `ui/resource-teardown` | `ui-lifecycle-teardown` | Host notifies UI before teardown |
## Configuration Options
```typescript
createUIResource({
// ...
adapters: {
mcpApps: {
enabled: true,
config: {
// Timeout for async operations (default: 30000ms)
timeout: 60000,
},
},
},
});
```
## MIME Type
When the MCP Apps adapter is enabled, the resource MIME type is automatically set to `text/html;profile=mcp-app`, the MCP Apps equivalent to `text/html`.
## Receiving Data in Your Widget
The adapter sends data to your widget via the standard MCP-UI `ui-lifecycle-iframe-render-data` message:
```typescript
window.addEventListener('message', (event) => {
if (event.data.type === 'ui-lifecycle-iframe-render-data') {
const { renderData } = event.data.payload;
// Tool input arguments
const toolInput = renderData.toolInput;
// Tool execution result (if available)
const toolOutput = renderData.toolOutput;
// Widget state (if supported by host)
const widgetState = renderData.widgetState;
// Host context
const theme = renderData.theme; // 'light' | 'dark' | 'system'
const locale = renderData.locale; // e.g., 'en-US'
const displayMode = renderData.displayMode; // 'inline' | 'fullscreen' | 'pip'
const maxHeight = renderData.maxHeight;
// Update your UI with the data
updateWidget(renderData);
}
});
```
## Sending Actions from Your Widget
Use standard MCP-UI postMessage calls - the adapter translates them automatically:
```typescript
// Send a prompt to the conversation
window.parent.postMessage({
type: 'prompt',
payload: { prompt: 'What is the weather like today?' }
}, '*');
// Open a link
window.parent.postMessage({
type: 'link',
payload: { url: 'https://example.com' }
}, '*');
// Call another tool
window.parent.postMessage({
type: 'tool',
payload: {
toolName: 'get_weather',
params: { city: 'San Francisco' }
}
}, '*');
// Send a notification
window.parent.postMessage({
type: 'notify',
payload: { message: 'Widget loaded successfully' }
}, '*');
// Request resize
window.parent.postMessage({
type: 'ui-size-change',
payload: { width: 500, height: 400 }
}, '*');
```
## Mutual Exclusivity with Apps SDK Adapter
Only one adapter can be enabled at a time. The TypeScript types enforce this:
```typescript
// ✅ Valid: MCP Apps adapter only
adapters: { mcpApps: { enabled: true } }
// ✅ Valid: Apps SDK adapter only (for ChatGPT)
adapters: { appsSdk: { enabled: true } }
// ❌ TypeScript error: Cannot enable both
adapters: { mcpApps: { enabled: true }, appsSdk: { enabled: true } }
```
If you need to support both MCP Apps hosts and ChatGPT, create separate resources:
```typescript
// For MCP Apps hosts
const mcpAppsResource = await createUIResource({
uri: 'ui://my-server/widget-mcp-apps',
content: { type: 'rawHtml', htmlString: widgetHtml },
});
// For ChatGPT/Apps SDK hosts
const appsSdkResource = await createUIResource({
uri: 'ui://my-server/widget-apps-sdk',
content: { type: 'rawHtml', htmlString: widgetHtml },
});
```
## Complete Example
See the [mcp-apps-demo](https://github.com/idosal/mcp-ui/tree/main/examples/mcp-apps-demo) example for a complete working implementation.
```typescript
import express from 'express';
import cors from 'cors';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { createUIResource } from '@mcp-ui/server';
import { registerAppTool, registerAppResource } from '@modelcontextprotocol/ext-apps/server';
import { z } from 'zod';
const app = express();
app.use(cors({ origin: '*', exposedHeaders: ['Mcp-Session-Id'] }));
app.use(express.json());
// ... (transport setup)
const server = new McpServer({ name: 'demo', version: '1.0.0' });
const graphUI = await createUIResource({
uri: 'ui://demo/graph',
encoding: 'text',
content: {
type: 'rawHtml',
htmlString: `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui; padding: 20px; }
.data { background: #f5f5f5; padding: 10px; border-radius: 8px; }
</style>
</head>
<body>
<h1>graph</h1>
<div class="data" id="data">Waiting for data...</div>
<button onclick="sendPrompt()">Ask Follow-up</button>
<script>
window.addEventListener('message', (e) => {
if (e.data.type === 'ui-lifecycle-iframe-render-data') {
document.getElementById('data').textContent =
JSON.stringify(e.data.payload.renderData, null, 2);
}
});
function sendPrompt() {
window.parent.postMessage({
type: 'prompt',
payload: { prompt: 'Tell me more about this data' }
}, '*');
}
window.parent.postMessage({ type: 'ui-lifecycle-iframe-ready' }, '*');
</script>
</body>
</html>
`,
},
});
// Register the UI resource
registerAppResource(
server,
'graph_ui',
graphUI.resource.uri,
{},
async () => ({
contents: [graphUI.resource]
})
);
// Register the tool with _meta linking to the UI resource
registerAppTool(
server,
'show_graph',
{
description: 'Display an interactive graph',
inputSchema: {
title: z.string().describe('Graph title'),
},
// For MCP Apps hosts - points to the registered resource
_meta: {
ui: {
resourceUri: graphUI.resource.uri
}
}
},
async ({ title }) => {
// Create embedded resource for MCP-UI hosts
const embeddedResource = await createUIResource({
uri: `ui://demo/graph/${encodeURIComponent(title)}`,
encoding: 'text',
content: {
type: 'rawHtml',
htmlString: `<html><body><h1>Graph: ${title}</h1></body></html>`,
},
});
return {
content: [
{ type: 'text', text: `Graph: ${title}` },
embeddedResource // Included for MCP-UI hosts
],
};
}
);
// ... (server setup)
```
## Debugging
The adapter logs debug information to the browser console. Look for messages prefixed with `[MCP Apps Adapter]`:
```
[MCP Apps Adapter] Initializing adapter...
[MCP Apps Adapter] Sending ui/initialize request
[MCP Apps Adapter] Received JSON-RPC message: {...}
[MCP Apps Adapter] Intercepted MCP-UI message: prompt
```
## Host-Side Rendering (Client SDK)
The `@mcp-ui/client` package provides React components for rendering MCP Apps tool UIs in your host application.
### AppRenderer Component
`AppRenderer` is the high-level component that handles the complete lifecycle of rendering an MCP tool's UI:
```tsx
import { AppRenderer, type AppRendererHandle } from '@mcp-ui/client';
function ToolUI({ client, toolName, toolInput, toolResult }) {
const appRef = useRef<AppRendererHandle>(null);
return (
<AppRenderer
ref={appRef}
client={client}
toolName={toolName}
sandbox={{ url: new URL('http://localhost:8765/sandbox_proxy.html') }}
toolInput={toolInput}
toolResult={toolResult}
hostContext={{ theme: 'dark' }}
onOpenLink={async ({ url }) => {
if (url.startsWith('https://') || url.startsWith('http://')) {
window.open(url);
}
}}
onMessage={async (params) => {
console.log('Message from tool UI:', params);
return { isError: false };
}}
onError={(error) => console.error('Tool UI error:', error)}
/>
);
}
```
**Key Props:**
- `client` - Optional MCP client for automatic resource fetching and MCP request forwarding
- `toolName` - Name of the tool to render UI for
- `sandbox` - Sandbox configuration with the sandbox proxy URL
- `html` - Optional pre-fetched HTML (skips resource fetching)
- `toolResourceUri` - Optional pre-fetched resource URI
- `toolInput` / `toolResult` - Tool arguments and results to pass to the UI
- `hostContext` - Theme, locale, viewport info for the guest UI
- `onOpenLink` / `onMessage` / `onLoggingMessage` - Handlers for guest UI requests
- `onFallbackRequest` - Catch-all for JSON-RPC requests not handled by the built-in handlers (see [Handling Custom Requests](#handling-custom-requests-onfallbackrequest))
**Ref Methods:**
- `sendToolListChanged()` - Notify guest when tools change
- `sendResourceListChanged()` - Notify guest when resources change
- `sendPromptListChanged()` - Notify guest when prompts change
- `teardownResource()` - Clean up before unmounting
### Handling Custom Requests (`onFallbackRequest`)
AppRenderer includes built-in handlers for standard MCP Apps methods (`tools/call`, `ui/message`, `ui/open-link`, etc.). The `onFallbackRequest` prop lets you handle **any JSON-RPC request that doesn't match a built-in handler**. This is useful for:
- **Experimental methods** — prototype new capabilities (e.g., `x/clipboard/write`, `x/analytics/track`)
- **MCP methods not yet in the Apps spec** — support standard MCP methods like `sampling/createMessage` before they're officially added to MCP Apps
Under the hood, this is wired to `AppBridge`'s `fallbackRequestHandler` from the MCP SDK `Protocol` class. The guest UI sends a standard JSON-RPC request via `postMessage`, and if AppBridge has no registered handler for the method, it delegates to `onFallbackRequest`.
**Host-side handler:**
```tsx
import { AppRenderer, type JSONRPCRequest } from '@mcp-ui/client';
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
<AppRenderer
client={client}
toolName="my-tool"
sandbox={sandboxConfig}
onFallbackRequest={async (request, extra) => {
switch (request.method) {
case 'x/clipboard/write':
await navigator.clipboard.writeText(request.params?.text as string);
return { success: true };
case 'sampling/createMessage':
// Forward to MCP server
return client.createMessage(request.params);
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown method: ${request.method}`);
}
}}
/>
```
**Guest-side (inside tool UI HTML):**
```ts
import { sendExperimentalRequest } from '@mcp-ui/server';
// Send a custom request to the host — returns a Promise with the response
const result = await sendExperimentalRequest('x/clipboard/write', { text: 'hello' });
```
The `sendExperimentalRequest` helper sends a properly formatted JSON-RPC request via `window.parent.postMessage`. The full request/response cycle flows through `PostMessageTransport` and the sandbox proxy, just like built-in methods.
::: tip Method Naming Convention
Use the `x/<namespace>/<action>` prefix for experimental methods (e.g., `x/clipboard/write`). Standard MCP methods not yet in the Apps spec (e.g., `sampling/createMessage`) should use their canonical method names. When an experimental method proves useful, it can be promoted to a standard method in the [ext-apps spec](https://github.com/modelcontextprotocol/ext-apps).
:::
### Using Without an MCP Client
You can use `AppRenderer` without a full MCP client by providing custom handlers:
```tsx
<AppRenderer
// No client - use callbacks instead
toolName="my-tool"
toolResourceUri="ui://my-server/my-tool"
sandbox={{ url: sandboxUrl }}
onReadResource={async ({ uri }) => {
// Proxy to your MCP client in a different context
return myMcpProxy.readResource({ uri });
}}
onCallTool={async (params) => {
return myMcpProxy.callTool(params);
}}
/>
```
Or provide pre-fetched HTML directly:
```tsx
<AppRenderer
toolName="my-tool"
sandbox={{ url: sandboxUrl }}
html={preloadedHtml} // Skip all resource fetching
toolInput={args}
/>
```
### AppFrame Component
`AppFrame` is the lower-level component for when you already have the HTML content and an `AppBridge` instance:
```tsx
import { AppFrame, AppBridge } from '@mcp-ui/client';
function LowLevelToolUI({ html, client }) {
const bridge = useMemo(() => new AppBridge(client, hostInfo, capabilities), [client]);
return (
<AppFrame
html={html}
sandbox={{ url: sandboxUrl }}
appBridge={bridge}
toolInput={{ query: 'test' }}
onSizeChanged={(size) => console.log('Size changed:', size)}
/>
);
}
```
### Sandbox Proxy
Both components require a sandbox proxy HTML file to be served. This provides security isolation for the guest UI. The sandbox proxy URL should point to a page that loads the MCP Apps sandbox proxy script.
## Declaring UI Extension Support
When creating your MCP client, declare UI extension support using the provided type and capabilities:
```typescript
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import {
type ClientCapabilitiesWithExtensions,
UI_EXTENSION_CAPABILITIES,
} from '@mcp-ui/client';
const capabilities: ClientCapabilitiesWithExtensions = {
// Standard capabilities
roots: { listChanged: true },
// UI extension support (SEP-1724 pattern)
extensions: UI_EXTENSION_CAPABILITIES,
};
const client = new Client(
{ name: 'my-app', version: '1.0.0' },
{ capabilities }
);
```
This tells MCP servers that your client can render UI resources with MIME type `text/html;profile=mcp-app`.
> **Note:** This uses the `extensions` field pattern from [SEP-1724](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1724), which is not yet part of the official MCP protocol.
## Related Resources
- [Getting Started](./getting-started) - Recommended patterns for new MCP Apps
- [MCP Apps SEP Specification](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx)
- [@modelcontextprotocol/ext-apps](https://github.com/modelcontextprotocol/ext-apps)
- [Apps SDK Integration](./apps-sdk.md) - For ChatGPT integration (separate from MCP Apps)
- [Protocol Details](./protocol-details.md) - MCP-UI wire format reference
================================================
FILE: docs/src/guide/protocol-details.md
================================================
# Protocol Details
This section covers the wire protocols for MCP Apps and legacy MCP-UI.
## MCP Apps Protocol
MCP Apps uses JSON-RPC over `postMessage` for communication between host and guest UI.
### Tool → UI Linking
Tools declare their associated UI via `_meta.ui.resourceUri`:
```typescript
// Tool definition
{
name: 'show_widget',
description: 'Show an interactive widget',
inputSchema: { ... },
_meta: {
ui: {
resourceUri: 'ui://my-server/widget' // Points to registered resource
}
}
}
```
### Host → Guest Communication
The host sends JSON-RPC notifications to the guest UI:
| Notification | Description |
|-------------|-------------|
| `ui/notifications/tool-input` | Complete tool arguments |
| `ui/notifications/tool-input-partial` | Streaming partial arguments |
| `ui/notifications/tool-result` | Tool execution result |
| `ui/notifications/host-context-changed` | Theme, locale, viewport changes |
| `ui/notifications/size-changed` | Host informs of size constraints |
| `ui/notifications/tool-cancelled` | Tool execution was cancelled |
| `ui/resource-teardown` | Host notifies UI before teardown |
### Guest → Host Communication
The guest UI sends JSON-RPC requests to the host:
| Method | Description |
|--------|-------------|
| `tools/call` | Call another MCP tool |
| `ui/message` | Send a follow-up message to the conversation |
| `ui/open-link` | Open a URL in a new tab |
| `notifications/message` | Log a message to the host |
| `ui/notifications/size-changed` | Request widget resize |
### MIME Type
MCP Apps resources use `text/html;profile=mcp-app` to indicate MCP Apps compliance.
## UIResource Wire Format
```typescript
export interface UIResource {
type: 'resource';
resource: {
uri: string;
mimeType: 'text/html;profile=mcp-app';
text?: string;
blob?: string;
};
}
```
## URI Schemes
- **`ui://<component-name>/<instance-id>`**
- **Purpose**: For all UI resources.
- **Content**: `text` or `blob` contains HTML content.
- **Client Action**: Render in a sandboxed iframe
- **Examples**: A custom button, a small form, a data visualization snippet, a fetched external page
## Content encoding: `text` vs. `blob`
- **`text`**: Simple, direct string. Good for smaller, less complex content.
- **`blob`**: Base64 encoded string.
- **Pros**: Handles special characters robustly, can be better for larger payloads, ensures integrity during JSON transport.
- **Cons**: Requires Base64 decoding on the client, slightly increases payload size.
## External URL Handling
When using `createUIResource` with `content.type: 'externalUrl'`, the behavior depends on the SDK:
- **TypeScript SDK**: Fetches the URL's HTML content server-side, injects a `<base>` tag so relative paths (CSS, JS, images) resolve against the original URL, and returns the resulting HTML as the resource content. It also validates the URL (http/https only, blocks private/localhost addresses) and enforces a timeout and response size limit. The SDK automatically populates `_meta.csp.baseUriDomains` with the external URL's origin, so the host's sandbox iframe can set appropriate CSP headers.
- **Python and Ruby SDKs**: Store the URL string directly as the resource content without fetching it. The host client is responsible for fetching and rendering the external page.
> **Note:** Not all hosts support `baseUriDomains`. Those that don't will ignore this field, which may cause the `<base>` tag to be blocked by the sandbox CSP.
>
> **Security:** The TypeScript SDK's server-side fetch introduces SSRF risk if the URL is derived from untrusted user input. The SDK blocks private IP ranges and localhost by default, but server developers should apply additional validation (e.g., URL allowlists) when the URL originates from user input. DNS rebinding attacks are not mitigated at the SDK level.
## Recommended Client-Side Pattern
Client-side hosts should check for the `ui://` URI scheme to identify MCP-UI resources:
```tsx
if (
mcpResource.type === 'resource' &&
mcpResource.resource.uri?.startsWith('ui://')
) {
return <AppRenderer client={client} toolName={toolName} ... />;
}
```
## Communication (Client <-> Iframe)
For `ui://` resources, you can use `window.parent.postMessage` to send data or actions from the iframe back to the host client application. The client application should set up an event listener for `message` events.
### Basic Communication
**Iframe Script Example:**
```html
<button onclick="handleAction()">Submit Data</button>
<script>
function handleAction() {
const data = { action: 'formData', value: 'someValue' };
// IMPORTANT: Always specify the targetOrigin for security!
// Use '*' only if the parent origin is unknown or variable and security implications are understood.
window.parent.postMessage(
{ type: 'tool', payload: { toolName: 'myCustomTool', params: data } },
'*',
);
}
</script>
```
**Client-Side Handler:**
```typescript
window.addEventListener('message', (event) => {
// Add origin check for security: if (event.origin !== "expectedOrigin") return;
if (event.data && event.data.tool) {
// Call the onUIAction prop of UIResourceRenderer
}
});
```
### Asynchronous Communication with Message IDs
For iframe content that needs to handle asynchronous responses, you can include a `messageId` field in your UI action messages. When the host provides an `onUIAction` callback, the iframe will receive acknowledgment and response messages.
**Message Flow:**
1. **Iframe sends message with `messageId`:**
```javascript
window.parent.postMessage({
type: 'tool',
messageId: 'unique-request-id-123',
payload: { toolName: 'myAsyncTool', params: { data: 'some data' } }
}, '*');
```
2. **Host responds with acknowledgment:**
```javascript
// The iframe receives this message back
{
type: 'ui-message-received',
messageId: 'unique-request-id-123',
}
```
3. **When `onUIAction` completes successfully:**
```javascript
// The iframe receives the actual response
{
type: 'ui-message-response',
messageId: 'unique-request-id-123',
payload: {
response: { /* the result from onUIAction */ }
}
}
```
4. **If `onUIAction` encounters an error:**
```javascript
// The iframe receives the error
{
type: 'ui-message-response',
messageId: 'unique-request-id-123',
payload: {
error: { /* the error object */ }
}
}
```
**Complete Iframe Example with Async Handling:**
```html
<button onclick="handleAsyncAction()">Async Action</button>
<div id="status">Ready</div>
<div id="result"></div>
<script>
let messageCounter = 0;
const pendingRequests = new Map();
function generateMessageId() {
return `msg-${Date.now()}-${++messageCounter}`;
}
function handleAsyncAction() {
const messageId = generateMessageId();
const statusEl = document.getElementById('status');
const resultEl = document.getElementById('result');
statusEl.textContent = 'Sending request...';
// Store the request context
pendingRequests.set(messageId, {
startTime: Date.now(),
action: 'async-tool-call'
});
// Send the message with messageId
window.parent.postMessage({
type: 'tool',
messageId: messageId,
payload: {
toolName: 'processData',
params: { data: 'example data', timestamp: Date.now() }
}
}, '*');
}
// Listen for responses from the host
window.addEventListener('message', (event) => {
const message = event.data;
if (!message.messageId || !pendingRequests.has(message.messageId)) {
return; // Not for us or unknown request
}
const statusEl = document.getElementById('status');
const resultEl = document.getElementById('result');
const request = pendingRequests.get(message.messageId);
switch (message.type) {
case 'ui-message-received':
statusEl.textContent = 'Request acknowledged, processing...';
break;
case 'ui-message-response':
if (message.payload.error) {
statusEl.textContent = 'Error occurred!';
resultEl.innerHTML = `<div style="color: red;">Error: ${JSON.stringify(message.payload.error)}</div>`;
pendingRequests.delete(message.messageId);
break;
}
statusEl.textContent = 'Completed successfully!';
resultEl.innerHTML = `<pre>${JSON.stringify(message.payload.response, null, 2)}</pre>`;
pendingRequests.delete(message.messageId);
break;
}
});
</script>
```
### Message Types
The following internal message types are available as constants:
- `InternalMessageType.UI_MESSAGE_RECEIVED` (`'ui-message-received'`)
- `InternalMessageType.UI_MESSAGE_RESPONSE` (`'ui-message-response'`)
These types are exported from both `@mcp-ui/client` and `@mcp-ui/server` packages.
**Important Notes:**
- **Message ID is optional**: If you don't provide a `messageId`, the iframe will not receive response messages.
- **Only with `onUIAction`**: Response messages are only sent when the host provides an `onUIAction` callback.
- **Unique IDs**: Ensure `messageId` values are unique to avoid conflicts between multiple pending requests.
- **Cleanup**: Always clean up pending request tracking when you receive responses to avoid memory leaks.
================================================
FILE: docs/src/guide/server/python/overview.md
================================================
# mcp-ui-server Overview
The `mcp-ui-server` package provides utilities to generate UI resources (`UIResource`) on your MCP server. It allows you to define UI snippets on the server-side, which can then be seamlessly and securely rendered on the client.
::: tip MCP Apps Compatibility
The `create_ui_resource` function creates UIResource objects that work with both MCP Apps hosts (via `_meta.ui.resourceUri`) and legacy MCP-UI hosts.
For the recommended MCP Apps pattern with TypeScript, see [Getting Started](../../getting-started).
:::
For a complete example, see the [`python-server-demo`](https://github.com/idosal/mcp-ui/tree/main/examples/python-server-demo).
## Key Exports
- **`create_ui_resource(options_dict: dict[str, Any]) -> UIResource`**:
The primary function for creating UI snippets. It takes a dictionary of options to define the URI, content (direct HTML or external URL), and encoding method (text or blob).
## Purpose
- **Ease of Use**: Simplifies the creation of valid `UIResource` objects.
- **Validation**: Includes basic validation (e.g., URI prefixes matching content type).
- **Encoding**: Handles Base64 encoding when `encoding: 'blob'` is specified.
- **MCP Integration**: Proper integration with the MCP Python SDK using `EmbeddedResource`.
## Installation
Install the package using pip or your preferred package manager:
```bash
pip install mcp-ui-server
```
Or with uv:
```bash
uv add mcp-ui-server
```
## Building
This package is built using Python's standard build tools and distributed via PyPI. It includes full type annotations and is compatible with Python 3.10+.
To build the package from source:
```bash
uv build
```
See the [Server SDK Usage & Examples](./usage-examples.md) page for practical examples.
================================================
FILE: docs/src/guide/server/python/usage-examples.md
================================================
# mcp-ui-server Usage & Examples
This page provides practical examples for using the `mcp-ui-server` package.
For a complete example, see the [`python-server-demo`](https://github.com/idosal/mcp-ui/tree/main/examples/python-server-demo).
## Basic Setup
First, ensure you have `mcp-ui-server` available in your project:
```bash
pip install mcp-ui-server
```
Or with uv:
```bash
uv add mcp-ui-server
```
## Basic Usage
The core function is `create_ui_resource`.
```python
from mcp_ui_server import create_ui_resource
# Example 1: Direct HTML, delivered as text
resource1 = create_ui_resource({
"uri": "ui://my-component/instance-1",
"content": {
"type": "rawHtml",
"htmlString": "<p>Hello World</p>"
},
"encoding": "text"
})
print("Resource 1:", resource1.model_dump_json(indent=2))
# Output for Resource 1:
# {
# "type": "resource",
# "resource": {
# "uri": "ui://my-component/instance-1",
# "mimeType": "text/html;profile=mcp-app",
# "text": "<p>Hello World</p>"
# }
# }
# Example 2: Direct HTML, delivered as a Base64 blob
resource2 = create_ui_resource({
"uri": "ui://my-component/instance-2",
"content": {
"type": "rawHtml",
"htmlString": "<h1>Complex HTML</h1>"
},
"encoding": "blob"
})
print("Resource 2 (blob will be Base64):", resource2.model_dump_json(indent=2))
# Output for Resource 2:
# {
# "type": "resource",
# "resource": {
# "uri": "ui://my-component/instance-2",
# "mimeType": "text/html;profile=mcp-app",
# "blob": "PGgxPkNvbXBsZXggSFRNTDwvaDE+"
# }
# }
# Example 3: External URL, text encoding
dashboard_url = "https://my.analytics.com/dashboard/123"
resource3 = create_ui_resource({
"uri": "ui://analytics-dashboard/main",
"content": {
"type": "externalUrl",
"iframeUrl": dashboard_url
},
"encoding": "text"
})
print("Resource 3:", resource3.model_dump_json(indent=2))
# Output for Resource 3:
# {
# "type": "resource",
# "resource": {
# "uri": "ui://analytics-dashboard/main",
# "mimeType": "text/html;profile=mcp-app",
# "text": "https://my.analytics.com/dashboard/123"
# }
# }
# Example 4: External URL, blob encoding (URL is Base64 encoded)
chart_api_url = "https://charts.example.com/api?type=pie&data=1,2,3"
resource4 = create_ui_resource({
"uri": "ui://live-chart/session-xyz",
"content": {
"type": "externalUrl",
"iframeUrl": chart_api_url
},
"encoding": "blob"
})
print("Resource 4 (blob will be Base64 of URL):", resource4.model_dump_json(indent=2))
# Output for Resource 4:
# {
# "type": "resource",
# "resource": {
# "uri": "ui://live-chart/session-xyz",
# "mimeType": "text/html;profile=mcp-app",
# "blob": "aHR0cHM6Ly9jaGFydHMuZXhhbXBsZS5jb20vYXBpP3R5cGU9cGllJmRhdGE9MSwyLDM="
# }
# }
# These resource objects would then be included in the 'content' array
# of a toolResult in an MCP interaction.
```
## Using with FastMCP
Here's how to use `create_ui_resource` with the FastMCP framework:
```python
import argparse
from mcp.server.fastmcp import FastMCP
from mcp_ui_server import create_ui_resource
from mcp_ui_server.core import UIResource
# Create FastMCP instance
mcp = FastMCP("my-server")
@mcp.tool()
def show_dashboard() -> list[UIResource]:
"""Display an analytics dashboard."""
ui_resource = create_ui_resource({
"uri": "ui://dashboard/analytics",
"content": {
"type": "externalUrl",
"iframeUrl": "https://my.analytics.com/dashboard"
},
"encoding": "text"
})
return [ui_resource]
@mcp.tool()
def show_welcome() -> list[UIResource]:
"""Display a welcome message."""
ui_resource = create_ui_resource({
"uri": "ui://welcome/main",
"content": {
"type": "rawHtml",
"htmlString": "<h1>Welcome to My MCP Server!</h1><p>How can I help you today?</p>"
},
"encoding": "text"
})
return [ui_resource]
if __name__ == "__main__":
mcp.run()
```
## Error Handling
The `create_ui_resource` function will raise exceptions if invalid combinations are provided, for example:
- URI not starting with `ui://` for any content type
- Invalid content type specified
```python
from mcp_ui_server.exceptions import InvalidURIError
try:
create_ui_resource({
"uri": "invalid://should-be-ui",
"content": {
"type": "externalUrl",
"iframeUrl": "https://example.com"
},
"encoding": "text"
})
except InvalidURIError as e:
print(f"Caught expected error: {e}")
# URI must start with 'ui://' but got: invalid://should-be-ui
```
================================================
FILE: docs/src/guide/server/python/walkthrough.md
================================================
# Python Server Walkthrough
This guide provides a step-by-step walkthrough for creating an MCP server with UI resources using `mcp-ui-server` and FastMCP.
For a complete, runnable example, see the [`python-server-demo`](https://github.com/idosal/mcp-ui/tree/main/examples/python-server-demo).
## 1. Set up Your Python Environment
First, create a new Python project and set up your dependencies:
```bash
# Create a new directory
mkdir my-mcp-server
cd my-mcp-server
# Initialize with uv (recommended) or pip
uv init
# or: python -m venv venv && source venv/bin/activate
```
## 2. Install Dependencies
Install the necessary packages:
```bash
uv add mcp mcp-ui-server
```
Or with pip:
```bash
pip install mcp mcp-ui-server
```
The `mcp` package provides FastMCP and core MCP functionality, while `mcp-ui-server` includes helpers for creating UI resources.
## 3. Create Your MCP Server
Create a file called `server.py`:
```python
import argparse
from mcp.server.fastmcp import FastMCP
from mcp_ui_server import create_ui_resource
from mcp_ui_server.core import UIResource
# Create FastMCP instance
mcp = FastMCP("my-mcp-server")
@mcp.tool()
def greet() -> list[UIResource]:
"""A simple greeting tool that returns a UI resource."""
ui_resource = create_ui_resource({
"uri": "ui://greeting/simple",
"content": {
"type": "rawHtml",
"htmlString": """
<div style="padding: 20px; text-align: center; font-family: Arial, sans-serif;">
<h1 style="color: #2563eb;">Hello from Python MCP Server!</h1>
<p>This UI resource was generated server-side using mcp-ui-server.</p>
</div>
"""
},
"encoding": "text"
})
return [ui_resource]
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="My MCP Server")
parser.add_argument("--http", action="store_true", help="Use HTTP transport instead of stdio")
parser.add_argument("--port", type=int, default=3000, help="Port for HTTP transport (default: 3000)")
args = parser.parse_args()
if args.http:
print("🚀 Starting MCP server on HTTP (SSE transport)")
mcp.settings.port = args.port
mcp.run(transport="sse")
else:
print("🚀 Starting MCP server with stdio transport")
mcp.run()
```
## 4. Add More UI Tools
Let's add more sophisticated tools with different types of UI resources:
```python
@mcp.tool()
def show_dashboard() -> list[UIResource]:
"""Display a sample dashboard with metrics."""
dashboard_html = """
<div style="padding: 20px; font-family: Arial, sans-serif;">
<h1>Server Dashboard</h1>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-top: 20px;">
<div style="background: #f0f9ff; border: 1px solid #0ea5e9; border-radius: 8px; padding: 16px;">
<h3 style="margin-top: 0; color: #0369a1;">Active Connections</h3>
<p style="font-size: 24px; font-weight: bold; margin: 0; color: #0c4a6e;">42</p>
</div>
<div style="background: #f0fdf4; border: 1px solid #22c55e; border-radius: 8px; padding: 16px;">
<h3 style="margin-top: 0; color: #15803d;">CPU Usage</h3>
<p style="font-size: 24px; font-weight: bold; margin: 0; color: #14532d;">23%</p>
</div>
<div style="background: #fefce8; border: 1px solid #eab308; border-radius: 8px; padding: 16px;">
<h3 style="margin-top: 0; color: #a16207;">Memory Usage</h3>
<p style="font-size: 24px; font-weight: bold; margin: 0; color: #713f12;">67%</p>
</div>
</div>
</div>
"""
ui_resource = create_ui_resource({
"uri": "ui://dashboard/main",
"content": {
"type"
gitextract_f5vcxr1e/ ├── .github/ │ ├── CONTRIBUTING.md │ ├── copilot-instructions.md │ └── workflows/ │ ├── ci.yml │ └── deploy-docs.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── .releaserc.json ├── .ruby-version ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── docs/ │ ├── README.md │ ├── package.json │ └── src/ │ ├── .vitepress/ │ │ ├── config.ts │ │ └── theme/ │ │ ├── custom.css │ │ └── index.ts │ ├── about.md │ ├── guide/ │ │ ├── apps-sdk.md │ │ ├── client/ │ │ │ ├── app-renderer.md │ │ │ ├── overview.md │ │ │ └── walkthrough.md │ │ ├── embeddable-ui.md │ │ ├── getting-started.md │ │ ├── introduction.md │ │ ├── mcp-apps.md │ │ ├── protocol-details.md │ │ ├── server/ │ │ │ ├── python/ │ │ │ │ ├── overview.md │ │ │ │ ├── usage-examples.md │ │ │ │ └── walkthrough.md │ │ │ ├── ruby/ │ │ │ │ ├── overview.md │ │ │ │ ├── usage-examples.md │ │ │ │ └── walkthrough.md │ │ │ └── typescript/ │ │ │ ├── overview.md │ │ │ ├── usage-examples.md │ │ │ └── walkthrough.md │ │ └── supported-hosts.md │ ├── index.md │ └── team.md ├── eslint.config.mjs ├── examples/ │ ├── external-url-demo/ │ │ ├── index.html │ │ ├── package.json │ │ ├── src/ │ │ │ ├── App.css │ │ │ ├── App.tsx │ │ │ ├── index.css │ │ │ └── main.tsx │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ └── vite.config.ts │ ├── mcp-apps-demo/ │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── python-server-demo/ │ │ ├── README.md │ │ ├── pyproject.toml │ │ └── python_server_demo.py │ ├── ruby-server-demo/ │ │ ├── Gemfile │ │ ├── README.md │ │ └── server.rb │ ├── server/ │ │ ├── .react-router/ │ │ │ └── types/ │ │ │ ├── +future.ts │ │ │ ├── +routes.ts │ │ │ ├── +server-build.d.ts │ │ │ └── app/ │ │ │ ├── +types/ │ │ │ │ └── root.ts │ │ │ └── routes/ │ │ │ └── +types/ │ │ │ ├── home.ts │ │ │ ├── task.ts │ │ │ └── user.ts │ │ ├── README.md │ │ ├── app/ │ │ │ ├── app.css │ │ │ ├── entry.server.tsx │ │ │ ├── graph/ │ │ │ │ └── graph.tsx │ │ │ ├── images.d.ts │ │ │ ├── root.tsx │ │ │ ├── routes/ │ │ │ │ ├── home.tsx │ │ │ │ ├── task.tsx │ │ │ │ └── user.tsx │ │ │ ├── routes.ts │ │ │ ├── user/ │ │ │ │ └── user.tsx │ │ │ └── utils/ │ │ │ └── messageUtils.ts │ │ ├── biome.json │ │ ├── package.json │ │ ├── pnpm-workspace.yaml │ │ ├── react-router.config.ts │ │ ├── src/ │ │ │ └── index.ts │ │ ├── tsconfig.cloudflare.json │ │ ├── tsconfig.json │ │ ├── tsconfig.node.json │ │ ├── vite.config.ts │ │ ├── worker-configuration.d.ts │ │ └── wrangler.jsonc │ └── typescript-server-demo/ │ ├── README.md │ ├── package.json │ ├── src/ │ │ └── index.ts │ └── tsconfig.json ├── lefthook.yml ├── package.json ├── pnpm-workspace.yaml ├── sdks/ │ ├── python/ │ │ └── server/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── pyproject.toml │ │ ├── release.config.js │ │ ├── src/ │ │ │ └── mcp_ui_server/ │ │ │ ├── __init__.py │ │ │ ├── core.py │ │ │ ├── exceptions.py │ │ │ ├── types.py │ │ │ └── utils.py │ │ └── tests/ │ │ ├── __init__.py │ │ ├── test_create_ui_resource.py │ │ ├── test_metadata.py │ │ └── test_ui_action_results.py │ ├── ruby/ │ │ ├── .rspec │ │ ├── .rubocop.yml │ │ ├── CHANGELOG.md │ │ ├── Gemfile │ │ ├── README.md │ │ ├── Rakefile │ │ ├── lib/ │ │ │ ├── mcp_ui_server/ │ │ │ │ └── version.rb │ │ │ └── mcp_ui_server.rb │ │ ├── mcp_ui_server.gemspec │ │ ├── release.config.js │ │ └── spec/ │ │ ├── mcp_ui_server_spec.rb │ │ └── spec_helper.rb │ └── typescript/ │ ├── client/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── package.json │ │ ├── scripts/ │ │ │ └── proxy/ │ │ │ └── index.html │ │ ├── src/ │ │ │ ├── __tests__/ │ │ │ │ └── capabilities.test.ts │ │ │ ├── capabilities.ts │ │ │ ├── components/ │ │ │ │ ├── AppFrame.tsx │ │ │ │ ├── AppRenderer.tsx │ │ │ │ └── __tests__/ │ │ │ │ ├── AppFrame.test.tsx │ │ │ │ ├── AppRenderer.test.tsx │ │ │ │ └── ProxyScript.test.ts │ │ │ ├── index.ts │ │ │ ├── test-setup.ts │ │ │ ├── types.ts │ │ │ └── utils/ │ │ │ ├── __tests__/ │ │ │ │ ├── isUIResource.test.ts │ │ │ │ └── metadataUtils.test.ts │ │ │ ├── app-host-utils.ts │ │ │ ├── isUIResource.ts │ │ │ └── metadataUtils.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.test.json │ │ ├── vite.config.ts │ │ └── vitest.config.ts │ ├── server/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── __tests__/ │ │ │ │ ├── index.test.ts │ │ │ │ ├── test-utils.ts │ │ │ │ └── utils.test.ts │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── vite.config.ts │ │ └── vitest.config.ts │ └── shared/ │ ├── package.json │ ├── src/ │ │ └── index.ts │ ├── tsconfig.json │ └── vite.config.ts ├── tsconfig.base.json ├── vitest.config.ts └── vitest.setup.ts
SYMBOL INDEX (716 symbols across 43 files)
FILE: docs/src/.vitepress/theme/index.ts
method Layout (line 8) | Layout() {
method enhanceApp (line 26) | enhanceApp({ app, router, siteData }) {
FILE: examples/external-url-demo/src/App.tsx
function App (line 5) | function App() {
FILE: examples/python-server-demo/python_server_demo.py
function show_external_url (line 22) | def show_external_url() -> list[UIResource]:
function show_raw_html (line 39) | def show_raw_html() -> list[UIResource]:
function show_action_html (line 53) | def show_action_html() -> list[UIResource]:
FILE: examples/ruby-server-demo/server.rb
class ExternalUrlTool (line 7) | class ExternalUrlTool < MCP::Tool
method call (line 15) | def call(server_context:)
class RawHtmlTool (line 29) | class RawHtmlTool < MCP::Tool
method call (line 37) | def call(server_context:)
class MCPServlet (line 56) | class MCPServlet < WEBrick::HTTPServlet::AbstractServlet
method initialize (line 57) | def initialize(server, mcp_instance)
method do_OPTIONS (line 62) | def do_OPTIONS(_request, response)
method do_POST (line 69) | def do_POST(request, response)
FILE: examples/server/.react-router/types/+future.ts
type Future (line 6) | interface Future {
FILE: examples/server/.react-router/types/+routes.ts
type Register (line 6) | interface Register {
type Pages (line 12) | type Pages = {
type RouteFiles (line 24) | type RouteFiles = {
FILE: examples/server/.react-router/types/app/+types/root.ts
type Module (line 5) | type Module = typeof import('../root.js');
type Info (line 7) | type Info = GetInfo<{
type Matches (line 12) | type Matches = [
type Annotations (line 19) | type Annotations = GetAnnotations<Info & { module: Module; matches: Matc...
type LinkDescriptors (line 23) | type LinkDescriptors = Annotations['LinkDescriptors'];
type LinksFunction (line 24) | type LinksFunction = Annotations['LinksFunction'];
type MetaArgs (line 27) | type MetaArgs = Annotations['MetaArgs'];
type MetaDescriptors (line 28) | type MetaDescriptors = Annotations['MetaDescriptors'];
type MetaFunction (line 29) | type MetaFunction = Annotations['MetaFunction'];
type HeadersArgs (line 32) | type HeadersArgs = Annotations['HeadersArgs'];
type HeadersFunction (line 33) | type HeadersFunction = Annotations['HeadersFunction'];
type unstable_MiddlewareFunction (line 36) | type unstable_MiddlewareFunction = Annotations['unstable_MiddlewareFunct...
type unstable_ClientMiddlewareFunction (line 39) | type unstable_ClientMiddlewareFunction = Annotations['unstable_ClientMid...
type LoaderArgs (line 42) | type LoaderArgs = Annotations['LoaderArgs'];
type ClientLoaderArgs (line 45) | type ClientLoaderArgs = Annotations['ClientLoaderArgs'];
type ActionArgs (line 48) | type ActionArgs = Annotations['ActionArgs'];
type ClientActionArgs (line 51) | type ClientActionArgs = Annotations['ClientActionArgs'];
type HydrateFallbackProps (line 54) | type HydrateFallbackProps = Annotations['HydrateFallbackProps'];
type ComponentProps (line 57) | type ComponentProps = Annotations['ComponentProps'];
type ErrorBoundaryProps (line 60) | type ErrorBoundaryProps = Annotations['ErrorBoundaryProps'];
FILE: examples/server/.react-router/types/app/routes/+types/home.ts
type Module (line 5) | type Module = typeof import('../home.js');
type Info (line 7) | type Info = GetInfo<{
type Matches (line 12) | type Matches = [
type Annotations (line 23) | type Annotations = GetAnnotations<Info & { module: Module; matches: Matc...
type LinkDescriptors (line 27) | type LinkDescriptors = Annotations['LinkDescriptors'];
type LinksFunction (line 28) | type LinksFunction = Annotations['LinksFunction'];
type MetaArgs (line 31) | type MetaArgs = Annotations['MetaArgs'];
type MetaDescriptors (line 32) | type MetaDescriptors = Annotations['MetaDescriptors'];
type MetaFunction (line 33) | type MetaFunction = Annotations['MetaFunction'];
type HeadersArgs (line 36) | type HeadersArgs = Annotations['HeadersArgs'];
type HeadersFunction (line 37) | type HeadersFunction = Annotations['HeadersFunction'];
type unstable_MiddlewareFunction (line 40) | type unstable_MiddlewareFunction = Annotations['unstable_MiddlewareFunct...
type unstable_ClientMiddlewareFunction (line 43) | type unstable_ClientMiddlewareFunction = Annotations['unstable_ClientMid...
type LoaderArgs (line 46) | type LoaderArgs = Annotations['LoaderArgs'];
type ClientLoaderArgs (line 49) | type ClientLoaderArgs = Annotations['ClientLoaderArgs'];
type ActionArgs (line 52) | type ActionArgs = Annotations['ActionArgs'];
type ClientActionArgs (line 55) | type ClientActionArgs = Annotations['ClientActionArgs'];
type HydrateFallbackProps (line 58) | type HydrateFallbackProps = Annotations['HydrateFallbackProps'];
type ComponentProps (line 61) | type ComponentProps = Annotations['ComponentProps'];
type ErrorBoundaryProps (line 64) | type ErrorBoundaryProps = Annotations['ErrorBoundaryProps'];
FILE: examples/server/.react-router/types/app/routes/+types/task.ts
type Module (line 5) | type Module = typeof import('../task.js');
type Info (line 7) | type Info = GetInfo<{
type Matches (line 12) | type Matches = [
type Annotations (line 23) | type Annotations = GetAnnotations<Info & { module: Module; matches: Matc...
type LinkDescriptors (line 27) | type LinkDescriptors = Annotations['LinkDescriptors'];
type LinksFunction (line 28) | type LinksFunction = Annotations['LinksFunction'];
type MetaArgs (line 31) | type MetaArgs = Annotations['MetaArgs'];
type MetaDescriptors (line 32) | type MetaDescriptors = Annotations['MetaDescriptors'];
type MetaFunction (line 33) | type MetaFunction = Annotations['MetaFunction'];
type HeadersArgs (line 36) | type HeadersArgs = Annotations['HeadersArgs'];
type HeadersFunction (line 37) | type HeadersFunction = Annotations['HeadersFunction'];
type unstable_MiddlewareFunction (line 40) | type unstable_MiddlewareFunction = Annotations['unstable_MiddlewareFunct...
type unstable_ClientMiddlewareFunction (line 43) | type unstable_ClientMiddlewareFunction = Annotations['unstable_ClientMid...
type LoaderArgs (line 46) | type LoaderArgs = Annotations['LoaderArgs'];
type ClientLoaderArgs (line 49) | type ClientLoaderArgs = Annotations['ClientLoaderArgs'];
type ActionArgs (line 52) | type ActionArgs = Annotations['ActionArgs'];
type ClientActionArgs (line 55) | type ClientActionArgs = Annotations['ClientActionArgs'];
type HydrateFallbackProps (line 58) | type HydrateFallbackProps = Annotations['HydrateFallbackProps'];
type ComponentProps (line 61) | type ComponentProps = Annotations['ComponentProps'];
type ErrorBoundaryProps (line 64) | type ErrorBoundaryProps = Annotations['ErrorBoundaryProps'];
FILE: examples/server/.react-router/types/app/routes/+types/user.ts
type Module (line 5) | type Module = typeof import('../user.js');
type Info (line 7) | type Info = GetInfo<{
type Matches (line 12) | type Matches = [
type Annotations (line 23) | type Annotations = GetAnnotations<Info & { module: Module; matches: Matc...
type LinkDescriptors (line 27) | type LinkDescriptors = Annotations['LinkDescriptors'];
type LinksFunction (line 28) | type LinksFunction = Annotations['LinksFunction'];
type MetaArgs (line 31) | type MetaArgs = Annotations['MetaArgs'];
type MetaDescriptors (line 32) | type MetaDescriptors = Annotations['MetaDescriptors'];
type MetaFunction (line 33) | type MetaFunction = Annotations['MetaFunction'];
type HeadersArgs (line 36) | type HeadersArgs = Annotations['HeadersArgs'];
type HeadersFunction (line 37) | type HeadersFunction = Annotations['HeadersFunction'];
type unstable_MiddlewareFunction (line 40) | type unstable_MiddlewareFunction = Annotations['unstable_MiddlewareFunct...
type unstable_ClientMiddlewareFunction (line 43) | type unstable_ClientMiddlewareFunction = Annotations['unstable_ClientMid...
type LoaderArgs (line 46) | type LoaderArgs = Annotations['LoaderArgs'];
type ClientLoaderArgs (line 49) | type ClientLoaderArgs = Annotations['ClientLoaderArgs'];
type ActionArgs (line 52) | type ActionArgs = Annotations['ActionArgs'];
type ClientActionArgs (line 55) | type ClientActionArgs = Annotations['ClientActionArgs'];
type HydrateFallbackProps (line 58) | type HydrateFallbackProps = Annotations['HydrateFallbackProps'];
type ComponentProps (line 61) | type ComponentProps = Annotations['ComponentProps'];
type ErrorBoundaryProps (line 64) | type ErrorBoundaryProps = Annotations['ErrorBoundaryProps'];
FILE: examples/server/app/entry.server.tsx
function handleRequest (line 6) | async function handleRequest(
FILE: examples/server/app/graph/graph.tsx
type TaskDetails (line 14) | interface TaskDetails {
type TeamMemberId (line 21) | type TeamMemberId = 'alice' | 'bob' | 'charlie';
type TeamMemberInfo (line 23) | interface TeamMemberInfo {
type SprintDayDataEntry (line 31) | interface SprintDayDataEntry {
type ProcessedChartItemFullView (line 39) | interface ProcessedChartItemFullView {
type ProcessedChartItemZoomedView (line 46) | interface ProcessedChartItemZoomedView {
type CustomTooltipProps (line 210) | interface CustomTooltipProps {
function Graph (line 454) | function Graph() {
FILE: examples/server/app/root.tsx
function Layout (line 26) | function Layout({ children }: { children: React.ReactNode }) {
function App (line 44) | function App() {
function ErrorBoundary (line 48) | function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
FILE: examples/server/app/routes/home.tsx
function meta (line 1) | function meta() {
function Home (line 8) | function Home() {
FILE: examples/server/app/routes/task.tsx
function meta (line 3) | function meta() {
function Task (line 15) | function Task() {
FILE: examples/server/app/routes/user.tsx
function meta (line 3) | function meta() {
function UserProfile (line 20) | function UserProfile({
FILE: examples/server/app/user/user.tsx
type UserInfo (line 13) | interface UserInfo {
function User (line 20) | function User({ user }: { user: UserInfo }) {
FILE: examples/server/app/utils/messageUtils.ts
function postMessageToParent (line 1) | function postMessageToParent(message: any) {
FILE: examples/server/src/index.ts
type AppLoadContext (line 8) | interface AppLoadContext {
class MyMCP (line 22) | class MyMCP extends McpAgent {
method init (line 28) | async init() {
method fetch (line 193) | fetch(request: Request, env: Env, ctx: ExecutionContext) {
FILE: examples/server/worker-configuration.d.ts
type Env (line 5) | interface Env {
type Env (line 9) | interface Env extends Cloudflare.Env {}
class DOMException (line 34) | class DOMException extends Error {
type WorkerGlobalScopeEventMap (line 74) | type WorkerGlobalScopeEventMap = {
type Console (line 85) | interface Console {
type BufferSource (line 126) | type BufferSource = ArrayBufferView | ArrayBuffer;
type TypedArray (line 127) | type TypedArray =
class CompileError (line 140) | class CompileError extends Error {
class RuntimeError (line 143) | class RuntimeError extends Error {
type ValueType (line 146) | type ValueType = 'anyfunc' | 'externref' | 'f32' | 'f64' | 'i32' | 'i64'...
type GlobalDescriptor (line 147) | interface GlobalDescriptor {
class Global (line 151) | class Global {
type ImportValue (line 156) | type ImportValue = ExportValue | number;
type ModuleImports (line 157) | type ModuleImports = Record<string, ImportValue>;
type Imports (line 158) | type Imports = Record<string, ModuleImports>;
type ExportValue (line 159) | type ExportValue = Function | Global | Memory | Table;
type Exports (line 160) | type Exports = Record<string, ExportValue>;
class Instance (line 161) | class Instance {
type MemoryDescriptor (line 165) | interface MemoryDescriptor {
class Memory (line 170) | class Memory {
type ImportExportKind (line 175) | type ImportExportKind = 'function' | 'global' | 'memory' | 'table';
type ModuleExportDescriptor (line 176) | interface ModuleExportDescriptor {
type ModuleImportDescriptor (line 180) | interface ModuleImportDescriptor {
type TableKind (line 190) | type TableKind = 'anyfunc' | 'externref';
type TableDescriptor (line 191) | interface TableDescriptor {
class Table (line 196) | class Table {
type ServiceWorkerGlobalScope (line 212) | interface ServiceWorkerGlobalScope extends WorkerGlobalScope {
type TestController (line 379) | interface TestController {}
type ExecutionContext (line 380) | interface ExecutionContext {
type ExportedHandlerFetchHandler (line 385) | type ExportedHandlerFetchHandler<Env = unknown, CfHostMetadata = unknown...
type ExportedHandlerTailHandler (line 390) | type ExportedHandlerTailHandler<Env = unknown> = (
type ExportedHandlerTraceHandler (line 395) | type ExportedHandlerTraceHandler<Env = unknown> = (
type ExportedHandlerTailStreamHandler (line 400) | type ExportedHandlerTailStreamHandler<Env = unknown> = (
type ExportedHandlerScheduledHandler (line 405) | type ExportedHandlerScheduledHandler<Env = unknown> = (
type ExportedHandlerQueueHandler (line 410) | type ExportedHandlerQueueHandler<Env = unknown, Message = unknown> = (
type ExportedHandlerTestHandler (line 415) | type ExportedHandlerTestHandler<Env = unknown> = (
type ExportedHandler (line 420) | interface ExportedHandler<Env = unknown, QueueHandlerMessage = unknown, ...
type StructuredSerializeOptions (line 430) | interface StructuredSerializeOptions {
type Performance (line 461) | interface Performance {
type AlarmInvocationInfo (line 467) | interface AlarmInvocationInfo {
type Cloudflare (line 471) | interface Cloudflare {
type DurableObject (line 474) | interface DurableObject {
type DurableObjectStub (line 486) | type DurableObjectStub<T extends Rpc.DurableObjectBranded | undefined = ...
type DurableObjectId (line 493) | interface DurableObjectId {
type DurableObjectNamespace (line 498) | interface DurableObjectNamespace<T extends Rpc.DurableObjectBranded | un...
type DurableObjectJurisdiction (line 508) | type DurableObjectJurisdiction = 'eu' | 'fedramp';
type DurableObjectNamespaceNewUniqueIdOptions (line 509) | interface DurableObjectNamespaceNewUniqueIdOptions {
type DurableObjectLocationHint (line 512) | type DurableObjectLocationHint =
type DurableObjectNamespaceGetDurableObjectOptions (line 522) | interface DurableObjectNamespaceGetDurableObjectOptions {
type DurableObjectState (line 525) | interface DurableObjectState {
type DurableObjectTransaction (line 541) | interface DurableObjectTransaction {
type DurableObjectStorage (line 554) | interface DurableObjectStorage {
type DurableObjectListOptions (line 574) | interface DurableObjectListOptions {
type DurableObjectGetOptions (line 584) | interface DurableObjectGetOptions {
type DurableObjectGetAlarmOptions (line 588) | interface DurableObjectGetAlarmOptions {
type DurableObjectPutOptions (line 591) | interface DurableObjectPutOptions {
type DurableObjectSetAlarmOptions (line 596) | interface DurableObjectSetAlarmOptions {
class WebSocketRequestResponsePair (line 600) | class WebSocketRequestResponsePair {
type AnalyticsEngineDataset (line 605) | interface AnalyticsEngineDataset {
type AnalyticsEngineDataPoint (line 608) | interface AnalyticsEngineDataPoint {
class Event (line 618) | class Event {
type EventInit (line 733) | interface EventInit {
type EventListener (line 738) | type EventListener<EventType extends Event = Event> = (event: EventType)...
type EventListenerObject (line 739) | interface EventListenerObject<EventType extends Event = Event> {
type EventListenerOrEventListenerObject (line 742) | type EventListenerOrEventListenerObject<EventType extends Event = Event> =
class EventTarget (line 750) | class EventTarget<EventMap extends Record<string, Event> = Record<string...
type EventTargetEventListenerOptions (line 791) | interface EventTargetEventListenerOptions {
type EventTargetAddEventListenerOptions (line 794) | interface EventTargetAddEventListenerOptions {
type EventTargetHandlerObject (line 800) | interface EventTargetHandlerObject {
class AbortController (line 808) | class AbortController {
type Scheduler (line 850) | interface Scheduler {
type SchedulerWaitOptions (line 853) | interface SchedulerWaitOptions {
class CustomEvent (line 866) | class CustomEvent<T = any> extends Event {
type CustomEventCustomEventInit (line 875) | interface CustomEventCustomEventInit {
class Blob (line 886) | class Blob {
type BlobOptions (line 903) | interface BlobOptions {
class File (line 911) | class File extends Blob {
type FileOptions (line 922) | interface FileOptions {
type CacheQueryOptions (line 949) | interface CacheQueryOptions {
type CryptoKeyPair (line 1095) | interface CryptoKeyPair {
type JsonWebKey (line 1099) | interface JsonWebKey {
type RsaOtherPrimesInfo (line 1119) | interface RsaOtherPrimesInfo {
type SubtleCryptoDeriveKeyAlgorithm (line 1124) | interface SubtleCryptoDeriveKeyAlgorithm {
type SubtleCryptoEncryptAlgorithm (line 1132) | interface SubtleCryptoEncryptAlgorithm {
type SubtleCryptoGenerateKeyAlgorithm (line 1141) | interface SubtleCryptoGenerateKeyAlgorithm {
type SubtleCryptoHashAlgorithm (line 1149) | interface SubtleCryptoHashAlgorithm {
type SubtleCryptoImportKeyAlgorithm (line 1152) | interface SubtleCryptoImportKeyAlgorithm {
type SubtleCryptoSignAlgorithm (line 1159) | interface SubtleCryptoSignAlgorithm {
type CryptoKeyKeyAlgorithm (line 1165) | interface CryptoKeyKeyAlgorithm {
type CryptoKeyAesKeyAlgorithm (line 1168) | interface CryptoKeyAesKeyAlgorithm {
type CryptoKeyHmacKeyAlgorithm (line 1172) | interface CryptoKeyHmacKeyAlgorithm {
type CryptoKeyRsaKeyAlgorithm (line 1177) | interface CryptoKeyRsaKeyAlgorithm {
type CryptoKeyEllipticKeyAlgorithm (line 1183) | interface CryptoKeyEllipticKeyAlgorithm {
type CryptoKeyArbitraryKeyAlgorithm (line 1187) | interface CryptoKeyArbitraryKeyAlgorithm {
class DigestStream (line 1193) | class DigestStream extends WritableStream<ArrayBuffer | ArrayBufferView> {
class TextDecoder (line 1203) | class TextDecoder {
class TextEncoder (line 1230) | class TextEncoder {
type TextDecoderConstructorOptions (line 1246) | interface TextDecoderConstructorOptions {
type TextDecoderDecodeOptions (line 1250) | interface TextDecoderDecodeOptions {
type TextEncoderEncodeIntoResult (line 1253) | interface TextEncoderEncodeIntoResult {
class ErrorEvent (line 1262) | class ErrorEvent extends Event {
type ErrorEventErrorEventInit (line 1275) | interface ErrorEventErrorEventInit {
class FormData (line 1287) | class FormData {
type ContentOptions (line 1317) | interface ContentOptions {
class HTMLRewriter (line 1320) | class HTMLRewriter {
type HTMLRewriterElementContentHandlers (line 1326) | interface HTMLRewriterElementContentHandlers {
type HTMLRewriterDocumentContentHandlers (line 1331) | interface HTMLRewriterDocumentContentHandlers {
type Doctype (line 1337) | interface Doctype {
type Element (line 1342) | interface Element {
type EndTag (line 1361) | interface EndTag {
type Comment (line 1367) | interface Comment {
type Text (line 1375) | interface Text {
type DocumentEnd (line 1384) | interface DocumentEnd {
type HeadersInit (line 1399) | type HeadersInit = Headers | Iterable<Iterable<string>> | Record<string,...
class Headers (line 1405) | class Headers {
type BodyInit (line 1432) | type BodyInit =
type Response (line 1475) | interface Response extends Body {
type ResponseInit (line 1495) | interface ResponseInit {
type RequestInfo (line 1503) | type RequestInfo<CfHostMetadata = unknown, Cf = CfProperties<CfHostMetad...
type Request (line 1523) | interface Request<CfHostMetadata = unknown, Cf = CfProperties<CfHostMeta...
type RequestInit (line 1577) | interface RequestInit<Cf = CfProperties> {
type Service (line 1596) | type Service<T extends Rpc.WorkerEntrypointBranded | undefined = undefin...
type Fetcher (line 1597) | type Fetcher<
type KVNamespaceListKey (line 1606) | interface KVNamespaceListKey<Metadata, Key extends string = string> {
type KVNamespaceListResult (line 1611) | type KVNamespaceListResult<Metadata, Key extends string = string> =
type KVNamespace (line 1623) | interface KVNamespace<Key extends string = string> {
type KVNamespaceListOptions (line 1719) | interface KVNamespaceListOptions {
type KVNamespaceGetOptions (line 1724) | interface KVNamespaceGetOptions<Type> {
type KVNamespacePutOptions (line 1728) | interface KVNamespacePutOptions {
type KVNamespaceGetWithMetadataResult (line 1733) | interface KVNamespaceGetWithMetadataResult<Value, Metadata> {
type QueueContentType (line 1738) | type QueueContentType = 'text' | 'bytes' | 'json' | 'v8';
type Queue (line 1739) | interface Queue<Body = unknown> {
type QueueSendOptions (line 1746) | interface QueueSendOptions {
type QueueSendBatchOptions (line 1750) | interface QueueSendBatchOptions {
type MessageSendRequest (line 1753) | interface MessageSendRequest<Body = unknown> {
type QueueRetryOptions (line 1758) | interface QueueRetryOptions {
type Message (line 1761) | interface Message<Body = unknown> {
type QueueEvent (line 1769) | interface QueueEvent<Body = unknown> extends ExtendableEvent {
type MessageBatch (line 1775) | interface MessageBatch<Body = unknown> {
type R2Error (line 1781) | interface R2Error extends Error {
type R2ListOptions (line 1788) | interface R2ListOptions {
type R2MultipartUpload (line 1822) | interface R2MultipartUpload {
type R2UploadedPart (line 1833) | interface R2UploadedPart {
type R2ObjectBody (line 1852) | interface R2ObjectBody extends R2Object {
type R2Range (line 1860) | type R2Range =
type R2Conditional (line 1872) | interface R2Conditional {
type R2GetOptions (line 1879) | interface R2GetOptions {
type R2PutOptions (line 1884) | interface R2PutOptions {
type R2MultipartOptions (line 1896) | interface R2MultipartOptions {
type R2Checksums (line 1902) | interface R2Checksums {
type R2StringChecksums (line 1910) | interface R2StringChecksums {
type R2HTTPMetadata (line 1917) | interface R2HTTPMetadata {
type R2Objects (line 1925) | type R2Objects = {
type R2UploadPartOptions (line 1937) | interface R2UploadPartOptions {
type ScheduledController (line 1945) | interface ScheduledController {
type QueuingStrategy (line 1950) | interface QueuingStrategy<T = any> {
type UnderlyingSink (line 1954) | interface UnderlyingSink<W = any> {
type UnderlyingByteSource (line 1961) | interface UnderlyingByteSource {
type UnderlyingSource (line 1968) | interface UnderlyingSource<R = any> {
type Transformer (line 1975) | interface Transformer<I = any, O = any> {
type StreamPipeOptions (line 1984) | interface StreamPipeOptions {
type ReadableStreamReadResult (line 2007) | type ReadableStreamReadResult<R = any> =
type ReadableStream (line 2021) | interface ReadableStream<R = any> {
class ReadableStreamDefaultReader (line 2059) | class ReadableStreamDefaultReader<R = any> {
class ReadableStreamBYOBReader (line 2069) | class ReadableStreamBYOBReader {
type ReadableStreamBYOBReaderReadableStreamBYOBReaderReadOptions (line 2082) | interface ReadableStreamBYOBReaderReadableStreamBYOBReaderReadOptions {
type ReadableStreamGetReaderOptions (line 2085) | interface ReadableStreamGetReaderOptions {
type ReadableWritablePair (line 2149) | interface ReadableWritablePair<R = any, W = any> {
class WritableStream (line 2163) | class WritableStream<W = any> {
class WritableStreamDefaultWriter (line 2179) | class WritableStreamDefaultWriter<W = any> {
class TransformStream (line 2197) | class TransformStream<I = any, O = any> {
class FixedLengthStream (line 2208) | class FixedLengthStream extends IdentityTransformStream {
class IdentityTransformStream (line 2214) | class IdentityTransformStream extends TransformStream<
type IdentityTransformStreamQueuingStrategy (line 2220) | interface IdentityTransformStreamQueuingStrategy {
type ReadableStreamValuesOptions (line 2223) | interface ReadableStreamValuesOptions {
class CompressionStream (line 2227) | class CompressionStream extends TransformStream<ArrayBuffer | ArrayBuffe...
class DecompressionStream (line 2231) | class DecompressionStream extends TransformStream<
class TextEncoderStream (line 2238) | class TextEncoderStream extends TransformStream<string, Uint8Array> {
class TextDecoderStream (line 2243) | class TextDecoderStream extends TransformStream<ArrayBuffer | ArrayBuffe...
type TextDecoderStreamTextDecoderStreamInit (line 2249) | interface TextDecoderStreamTextDecoderStreamInit {
class ByteLengthQueuingStrategy (line 2258) | class ByteLengthQueuingStrategy implements QueuingStrategy<ArrayBufferVi...
class CountQueuingStrategy (line 2270) | class CountQueuingStrategy implements QueuingStrategy {
type QueuingStrategyInit (line 2277) | interface QueuingStrategyInit {
type ScriptVersion (line 2285) | interface ScriptVersion {
type TraceItem (line 2294) | interface TraceItem {
type TraceItemAlarmEventInfo (line 2323) | interface TraceItemAlarmEventInfo {
type TraceItemCustomEventInfo (line 2326) | interface TraceItemCustomEventInfo {}
type TraceItemScheduledEventInfo (line 2327) | interface TraceItemScheduledEventInfo {
type TraceItemQueueEventInfo (line 2331) | interface TraceItemQueueEventInfo {
type TraceItemEmailEventInfo (line 2335) | interface TraceItemEmailEventInfo {
type TraceItemTailEventInfo (line 2340) | interface TraceItemTailEventInfo {
type TraceItemTailEventInfoTailItem (line 2343) | interface TraceItemTailEventInfoTailItem {
type TraceItemFetchEventInfo (line 2346) | interface TraceItemFetchEventInfo {
type TraceItemFetchEventInfoRequest (line 2350) | interface TraceItemFetchEventInfoRequest {
type TraceItemFetchEventInfoResponse (line 2357) | interface TraceItemFetchEventInfoResponse {
type TraceItemJsRpcEventInfo (line 2360) | interface TraceItemJsRpcEventInfo {
type TraceItemHibernatableWebSocketEventInfo (line 2363) | interface TraceItemHibernatableWebSocketEventInfo {
type TraceItemHibernatableWebSocketEventInfoMessage (line 2369) | interface TraceItemHibernatableWebSocketEventInfoMessage {
type TraceItemHibernatableWebSocketEventInfoClose (line 2372) | interface TraceItemHibernatableWebSocketEventInfoClose {
type TraceItemHibernatableWebSocketEventInfoError (line 2377) | interface TraceItemHibernatableWebSocketEventInfoError {
type TraceLog (line 2380) | interface TraceLog {
type TraceException (line 2385) | interface TraceException {
type TraceDiagnosticChannelEvent (line 2391) | interface TraceDiagnosticChannelEvent {
type TraceMetrics (line 2396) | interface TraceMetrics {
type UnsafeTraceMetrics (line 2400) | interface UnsafeTraceMetrics {
class URL (line 2408) | class URL {
class URLSearchParams (line 2468) | class URLSearchParams {
class URLPattern (line 2524) | class URLPattern {
type URLPatternInit (line 2541) | interface URLPatternInit {
type URLPatternComponentResult (line 2552) | interface URLPatternComponentResult {
type URLPatternResult (line 2556) | interface URLPatternResult {
type URLPatternOptions (line 2567) | interface URLPatternOptions {
class CloseEvent (line 2575) | class CloseEvent extends Event {
type CloseEventInit (line 2596) | interface CloseEventInit {
class MessageEvent (line 2606) | class MessageEvent extends Event {
type MessageEventInit (line 2615) | interface MessageEventInit {
type WebSocketEventMap (line 2618) | type WebSocketEventMap = {
type WebSocket (line 2646) | interface WebSocket extends EventTarget<WebSocketEventMap> {
type SqlStorage (line 2693) | interface SqlStorage {
type SqlStorageValue (line 2703) | type SqlStorageValue = ArrayBuffer | string | number | null;
type Socket (line 2722) | interface Socket {
type SocketOptions (line 2732) | interface SocketOptions {
type SocketAddress (line 2737) | interface SocketAddress {
type TlsOptions (line 2741) | interface TlsOptions {
type SocketInfo (line 2744) | interface SocketInfo {
class EventSource (line 2749) | class EventSource extends EventTarget {
type EventSourceEventSourceInit (line 2792) | interface EventSourceEventSourceInit {
type Container (line 2796) | interface Container {
type ContainerStartupOptions (line 2804) | interface ContainerStartupOptions {
type AiImageClassificationInput (line 2809) | type AiImageClassificationInput = {
type AiImageClassificationOutput (line 2812) | type AiImageClassificationOutput = {
type AiImageToTextInput (line 2820) | type AiImageToTextInput = {
type AiImageToTextOutput (line 2834) | type AiImageToTextOutput = {
type AiImageTextToTextInput (line 2841) | type AiImageTextToTextInput = {
type AiImageTextToTextOutput (line 2856) | type AiImageTextToTextOutput = {
type AiObjectDetectionInput (line 2863) | type AiObjectDetectionInput = {
type AiObjectDetectionOutput (line 2866) | type AiObjectDetectionOutput = {
type AiSentenceSimilarityInput (line 2874) | type AiSentenceSimilarityInput = {
type AiSentenceSimilarityOutput (line 2878) | type AiSentenceSimilarityOutput = number[];
type AiAutomaticSpeechRecognitionInput (line 2883) | type AiAutomaticSpeechRecognitionInput = {
type AiAutomaticSpeechRecognitionOutput (line 2886) | type AiAutomaticSpeechRecognitionOutput = {
type AiSummarizationInput (line 2899) | type AiSummarizationInput = {
type AiSummarizationOutput (line 2903) | type AiSummarizationOutput = {
type AiTextClassificationInput (line 2910) | type AiTextClassificationInput = {
type AiTextClassificationOutput (line 2913) | type AiTextClassificationOutput = {
type AiTextEmbeddingsInput (line 2921) | type AiTextEmbeddingsInput = {
type AiTextEmbeddingsOutput (line 2924) | type AiTextEmbeddingsOutput = {
type RoleScopedChatInput (line 2932) | type RoleScopedChatInput = {
type AiTextGenerationToolLegacyInput (line 2937) | type AiTextGenerationToolLegacyInput = {
type AiTextGenerationToolInput (line 2951) | type AiTextGenerationToolInput = {
type AiTextGenerationFunctionsInput (line 2968) | type AiTextGenerationFunctionsInput = {
type AiTextGenerationResponseFormat (line 2972) | type AiTextGenerationResponseFormat = {
type AiTextGenerationInput (line 2976) | type AiTextGenerationInput = {
type AiTextGenerationOutput (line 2996) | type AiTextGenerationOutput =
type AiTextToSpeechInput (line 3009) | type AiTextToSpeechInput = {
type AiTextToSpeechOutput (line 3013) | type AiTextToSpeechOutput =
type AiTextToImageInput (line 3022) | type AiTextToImageInput = {
type AiTextToImageOutput (line 3035) | type AiTextToImageOutput = ReadableStream<Uint8Array>;
type AiTranslationInput (line 3040) | type AiTranslationInput = {
type AiTranslationOutput (line 3045) | type AiTranslationOutput = {
type Ai_Cf_Openai_Whisper_Input (line 3052) | type Ai_Cf_Openai_Whisper_Input =
type Ai_Cf_Openai_Whisper_Output (line 3060) | interface Ai_Cf_Openai_Whisper_Output {
type Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input (line 3083) | type Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input =
type Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output (line 3124) | interface Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output {
type Ai_Cf_Openai_Whisper_Tiny_En_Input (line 3131) | type Ai_Cf_Openai_Whisper_Tiny_En_Input =
type Ai_Cf_Openai_Whisper_Tiny_En_Output (line 3139) | interface Ai_Cf_Openai_Whisper_Tiny_En_Output {
type Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input (line 3162) | interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input {
type Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output (line 3188) | interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output {
type Ai_Cf_Baai_Bge_M3_Input (line 3268) | type Ai_Cf_Baai_Bge_M3_Input = BGEM3InputQueryAndContexts | BGEM3InputEm...
type BGEM3InputQueryAndContexts (line 3269) | interface BGEM3InputQueryAndContexts {
type BGEM3InputEmbedding (line 3288) | interface BGEM3InputEmbedding {
type Ai_Cf_Baai_Bge_M3_Output (line 3295) | type Ai_Cf_Baai_Bge_M3_Output =
type BGEM3OuputQuery (line 3299) | interface BGEM3OuputQuery {
type BGEM3OutputEmbeddingForContexts (line 3311) | interface BGEM3OutputEmbeddingForContexts {
type BGEM3OuputEmbedding (line 3319) | interface BGEM3OuputEmbedding {
type Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input (line 3334) | interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Input {
type Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output (line 3344) | interface Ai_Cf_Black_Forest_Labs_Flux_1_Schnell_Output {
type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input (line 3354) | type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Input = Prompt | Messages;
type Prompt (line 3355) | interface Prompt {
type Messages (line 3406) | interface Messages {
type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output (line 3552) | type Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Output =
type Ai_Cf_Meta_Llama_Guard_3_8B_Input (line 3577) | interface Ai_Cf_Meta_Llama_Guard_3_8B_Input {
type Ai_Cf_Meta_Llama_Guard_3_8B_Output (line 3609) | interface Ai_Cf_Meta_Llama_Guard_3_8B_Output {
type Ai_Cf_Baai_Bge_Reranker_Base_Input (line 3644) | interface Ai_Cf_Baai_Bge_Reranker_Base_Input {
type Ai_Cf_Baai_Bge_Reranker_Base_Output (line 3662) | interface Ai_Cf_Baai_Bge_Reranker_Base_Output {
type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input (line 3678) | type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Input =
type Ai_Cf_Meta_Llama_4_Prompt (line 3681) | interface Ai_Cf_Meta_Llama_4_Prompt {
type Ai_Cf_Meta_Llama_4_Messages (line 3731) | interface Ai_Cf_Meta_Llama_4_Messages {
type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output (line 3912) | type Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Output =
type AiModels (line 3954) | interface AiModels {
type AiOptions (line 4022) | type AiOptions = {
type ConversionResponse (line 4028) | type ConversionResponse = {
type AiModelsSearchParams (line 4035) | type AiModelsSearchParams = {
type AiModelsSearchObject (line 4044) | type AiModelsSearchObject = {
type InferenceUpstreamError (line 4060) | interface InferenceUpstreamError extends Error {}
type AiInternalError (line 4061) | interface AiInternalError extends Error {}
type AiModelListType (line 4062) | type AiModelListType = Record<string, any>;
type GatewayRetries (line 4100) | type GatewayRetries = {
type GatewayOptions (line 4105) | type GatewayOptions = {
type AiGatewayPatchLog (line 4116) | type AiGatewayPatchLog = {
type AiGatewayLog (line 4121) | type AiGatewayLog = {
type AIGatewayProviders (line 4148) | type AIGatewayProviders =
type AIGatewayHeaders (line 4169) | type AIGatewayHeaders = {
type AIGatewayUniversalRequest (line 4193) | type AIGatewayUniversalRequest = {
type AiGatewayInternalError (line 4199) | interface AiGatewayInternalError extends Error {}
type AiGatewayLogNotFound (line 4200) | interface AiGatewayLogNotFound extends Error {}
type AutoRAGInternalError (line 4213) | interface AutoRAGInternalError extends Error {}
type AutoRAGNotFoundError (line 4214) | interface AutoRAGNotFoundError extends Error {}
type AutoRAGUnauthorizedError (line 4215) | interface AutoRAGUnauthorizedError extends Error {}
type ComparisonFilter (line 4216) | type ComparisonFilter = {
type CompoundFilter (line 4221) | type CompoundFilter = {
type AutoRagSearchRequest (line 4225) | type AutoRagSearchRequest = {
type AutoRagAiSearchRequest (line 4235) | type AutoRagAiSearchRequest = AutoRagSearchRequest & {
type AutoRagAiSearchRequestStreaming (line 4238) | type AutoRagAiSearchRequestStreaming = Omit<AutoRagAiSearchRequest, 'str...
type AutoRagSearchResponse (line 4241) | type AutoRagSearchResponse = {
type AutoRagAiSearchResponse (line 4257) | type AutoRagAiSearchResponse = AutoRagSearchResponse & {
type BasicImageTransformations (line 4266) | interface BasicImageTransformations {
type BasicImageTransformationsGravityCoordinates (line 4335) | interface BasicImageTransformationsGravityCoordinates {
type RequestInitCfProperties (line 4349) | interface RequestInitCfProperties extends Record<string, unknown> {
type RequestInitCfPropertiesImageDraw (line 4400) | interface RequestInitCfPropertiesImageDraw extends BasicImageTransformat...
type RequestInitCfPropertiesImage (line 4437) | interface RequestInitCfPropertiesImage extends BasicImageTransformations {
type RequestInitCfPropertiesImageMinify (line 4594) | interface RequestInitCfPropertiesImageMinify {
type RequestInitCfPropertiesR2 (line 4599) | interface RequestInitCfPropertiesR2 {
type IncomingRequestCfProperties (line 4608) | type IncomingRequestCfProperties<HostMetadata = unknown> = IncomingReque...
type IncomingRequestCfPropertiesBase (line 4613) | interface IncomingRequestCfPropertiesBase extends Record<string, unknown> {
type IncomingRequestCfPropertiesBotManagementBase (line 4691) | interface IncomingRequestCfPropertiesBotManagementBase {
type IncomingRequestCfPropertiesBotManagement (line 4718) | interface IncomingRequestCfPropertiesBotManagement {
type IncomingRequestCfPropertiesBotManagementEnterprise (line 4730) | interface IncomingRequestCfPropertiesBotManagementEnterprise extends Inc...
type IncomingRequestCfPropertiesCloudflareForSaaSEnterprise (line 4742) | interface IncomingRequestCfPropertiesCloudflareForSaaSEnterprise<HostMet...
type IncomingRequestCfPropertiesCloudflareAccessOrApiShield (line 4751) | interface IncomingRequestCfPropertiesCloudflareAccessOrApiShield {
type IncomingRequestCfPropertiesExportedAuthenticatorMetadata (line 4773) | interface IncomingRequestCfPropertiesExportedAuthenticatorMetadata {
type IncomingRequestCfPropertiesGeographicInformation (line 4802) | interface IncomingRequestCfPropertiesGeographicInformation {
type IncomingRequestCfPropertiesTLSClientAuth (line 4879) | interface IncomingRequestCfPropertiesTLSClientAuth {
type IncomingRequestCfPropertiesTLSClientAuthPlaceholder (line 4972) | interface IncomingRequestCfPropertiesTLSClientAuthPlaceholder {
type CertVerificationStatus (line 4992) | type CertVerificationStatus =
type IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus (line 5010) | type IncomingRequestCfPropertiesEdgeRequestKeepAliveStatus =
type Iso3166Alpha2Code (line 5018) | type Iso3166Alpha2Code =
type ContinentCode (line 5269) | type ContinentCode = 'AF' | 'AN' | 'AS' | 'EU' | 'NA' | 'OC' | 'SA';
type CfProperties (line 5270) | type CfProperties<HostMetadata = unknown> =
type D1Meta (line 5273) | interface D1Meta {
type D1Response (line 5296) | interface D1Response {
type D1Result (line 5301) | type D1Result<T = unknown> = D1Response & {
type D1ExecResult (line 5304) | interface D1ExecResult {
type D1SessionConstraint (line 5308) | type D1SessionConstraint =
type D1SessionBookmark (line 5317) | type D1SessionBookmark = string;
type Disposable (line 5359) | interface Disposable {}
type EmailMessage (line 5363) | interface EmailMessage {
type ForwardableEmailMessage (line 5376) | interface ForwardableEmailMessage extends EmailMessage {
type SendEmail (line 5412) | interface SendEmail {
type EmailExportedHandler (line 5418) | type EmailExportedHandler<Env = unknown> = (
type Hyperdrive (line 5430) | interface Hyperdrive {
type ImageInfoResponse (line 5480) | type ImageInfoResponse =
type ImageTransform (line 5490) | type ImageTransform = {
type ImageDrawOptions (line 5545) | type ImageDrawOptions = {
type ImageOutputOptions (line 5553) | type ImageOutputOptions = {
type ImagesBinding (line 5558) | interface ImagesBinding {
type ImageTransformer (line 5572) | interface ImageTransformer {
type ImageTransformationResult (line 5596) | interface ImageTransformationResult {
type ImagesError (line 5610) | interface ImagesError extends Error {
type Params (line 5615) | type Params<P extends string = any> = Record<P, string | string[]>;
type EventContext (line 5616) | type EventContext<Env, P extends string, Data> = {
type PagesFunction (line 5630) | type PagesFunction<
type EventPluginContext (line 5635) | type EventPluginContext<Env, P extends string, Data, PluginArgs> = {
type PagesPluginFunction (line 5650) | type PagesPluginFunction<
type PipelineRecord (line 5680) | type PipelineRecord = Record<string, unknown>;
type PipelineBatchMetadata (line 5681) | type PipelineBatchMetadata = {
type Pipeline (line 5685) | interface Pipeline<T extends PipelineRecord = PipelineRecord> {
type PubSubMessage (line 5698) | interface PubSubMessage {
type JsonWebKeyWithKid (line 5724) | interface JsonWebKeyWithKid extends JsonWebKey {
type RateLimitOptions (line 5728) | interface RateLimitOptions {
type RateLimitOutcome (line 5731) | interface RateLimitOutcome {
type RateLimit (line 5734) | interface RateLimit {
type RpcTargetBranded (line 5755) | interface RpcTargetBranded {
type WorkerEntrypointBranded (line 5758) | interface WorkerEntrypointBranded {
type DurableObjectBranded (line 5761) | interface DurableObjectBranded {
type WorkflowEntrypointBranded (line 5764) | interface WorkflowEntrypointBranded {
type EntrypointBranded (line 5767) | type EntrypointBranded =
type Stubable (line 5772) | type Stubable = RpcTargetBranded | ((...args: any[]) => any);
type Serializable (line 5777) | type Serializable<T> =
type StubBase (line 5796) | interface StubBase<T extends Stubable> extends Disposable {
type Stub (line 5800) | type Stub<T extends Stubable> = Provider<T> & StubBase<T>;
type BaseType (line 5802) | type BaseType =
type Stubify (line 5823) | type Stubify<T> = T extends Stubable ? Stub<T> : T extends Map<infer K, ...
type Unstubify (line 5832) | type Unstubify<T> = T extends StubBase<infer V> ? V : T extends Map<infe...
type UnstubifyAll (line 5837) | type UnstubifyAll<A extends any[]> = {
type MaybeProvider (line 5842) | type MaybeProvider<T> = T extends object ? Provider<T> : unknown;
type MaybeDisposable (line 5843) | type MaybeDisposable<T> = T extends object ? Disposable : unknown;
type Result (line 5852) | type Result<R> = R extends Stubable ? Promise<Stub<R>> & Provider<R> : R...
type MethodOrProperty (line 5858) | type MethodOrProperty<V> = V extends (...args: infer P) => infer R
type MaybeCallableProvider (line 5863) | type MaybeCallableProvider<T> = T extends (...args: any[]) => any ? Meth...
type Provider (line 5867) | type Provider<
type Env (line 5875) | interface Env {}
type RpcStub (line 5878) | type RpcStub<T extends Rpc.Stubable> = Rpc.Stub<T>;
type WorkflowDurationLabel (line 5914) | type WorkflowDurationLabel =
type WorkflowSleepDuration (line 5922) | type WorkflowSleepDuration = `${number} ${WorkflowDurationLabel}${'s' | ...
type WorkflowDelayDuration (line 5923) | type WorkflowDelayDuration = WorkflowSleepDuration;
type WorkflowTimeoutDuration (line 5924) | type WorkflowTimeoutDuration = WorkflowSleepDuration;
type WorkflowBackoff (line 5925) | type WorkflowBackoff = 'constant' | 'linear' | 'exponential';
type WorkflowStepConfig (line 5926) | type WorkflowStepConfig = {
type WorkflowEvent (line 5934) | type WorkflowEvent<T> = {
type WorkflowStepEvent (line 5939) | type WorkflowStepEvent<T> = {
type SecretsStoreSecret (line 5975) | interface SecretsStoreSecret {
type Header (line 5987) | interface Header {
type FetchEventInfo (line 5991) | interface FetchEventInfo {
type JsRpcEventInfo (line 5998) | interface JsRpcEventInfo {
type ScheduledEventInfo (line 6002) | interface ScheduledEventInfo {
type AlarmEventInfo (line 6007) | interface AlarmEventInfo {
type QueueEventInfo (line 6011) | interface QueueEventInfo {
type EmailEventInfo (line 6016) | interface EmailEventInfo {
type TraceEventInfo (line 6022) | interface TraceEventInfo {
type HibernatableWebSocketEventInfoMessage (line 6026) | interface HibernatableWebSocketEventInfoMessage {
type HibernatableWebSocketEventInfoError (line 6029) | interface HibernatableWebSocketEventInfoError {
type HibernatableWebSocketEventInfoClose (line 6032) | interface HibernatableWebSocketEventInfoClose {
type HibernatableWebSocketEventInfo (line 6037) | interface HibernatableWebSocketEventInfo {
type Resume (line 6044) | interface Resume {
type CustomEventInfo (line 6048) | interface CustomEventInfo {
type FetchResponseInfo (line 6051) | interface FetchResponseInfo {
type EventOutcome (line 6055) | type EventOutcome =
type ScriptVersion (line 6067) | interface ScriptVersion {
type Trigger (line 6072) | interface Trigger {
type Onset (line 6077) | interface Onset {
type Outcome (line 6097) | interface Outcome {
type Hibernate (line 6103) | interface Hibernate {
type SpanOpen (line 6106) | interface SpanOpen {
type SpanClose (line 6111) | interface SpanClose {
type DiagnosticChannelEvent (line 6115) | interface DiagnosticChannelEvent {
type Exception (line 6120) | interface Exception {
type Log (line 6126) | interface Log {
type Return (line 6131) | interface Return {
type Link (line 6135) | interface Link {
type Attribute (line 6142) | interface Attribute {
type Attributes (line 6146) | interface Attributes {
type TailEvent (line 6150) | interface TailEvent {
type TailEventHandler (line 6169) | type TailEventHandler = (event: TailEvent) => void | Promise<void>;
type TailEventHandlerName (line 6170) | type TailEventHandlerName =
type TailEventHandlerObject (line 6181) | type TailEventHandlerObject = Record<TailEventHandlerName, TailEventHand...
type TailEventHandlerType (line 6182) | type TailEventHandlerType = TailEventHandler | TailEventHandlerObject;
type VectorizeVectorMetadataValue (line 6190) | type VectorizeVectorMetadataValue = string | number | boolean | string[];
type VectorizeVectorMetadata (line 6194) | type VectorizeVectorMetadata =
type VectorFloatArray (line 6197) | type VectorFloatArray = Float32Array | Float64Array;
type VectorizeError (line 6198) | interface VectorizeError {
type VectorizeVectorMetadataFilterOp (line 6207) | type VectorizeVectorMetadataFilterOp = '$eq' | '$ne';
type VectorizeVectorMetadataFilter (line 6211) | type VectorizeVectorMetadataFilter = {
type VectorizeDistanceMetric (line 6226) | type VectorizeDistanceMetric = 'euclidean' | 'cosine' | 'dot-product';
type VectorizeMetadataRetrievalLevel (line 6236) | type VectorizeMetadataRetrievalLevel = 'all' | 'indexed' | 'none';
type VectorizeQueryOptions (line 6237) | interface VectorizeQueryOptions {
type VectorizeIndexConfig (line 6247) | type VectorizeIndexConfig =
type VectorizeIndexDetails (line 6261) | interface VectorizeIndexDetails {
type VectorizeIndexInfo (line 6276) | interface VectorizeIndexInfo {
type VectorizeVector (line 6289) | interface VectorizeVector {
type VectorizeMatch (line 6302) | type VectorizeMatch = Pick<Partial<VectorizeVector>, 'values'> &
type VectorizeMatches (line 6310) | interface VectorizeMatches {
type VectorizeVectorMutation (line 6321) | interface VectorizeVectorMutation {
type VectorizeAsyncMutation (line 6331) | interface VectorizeAsyncMutation {
type WorkerVersionMetadata (line 6439) | type WorkerVersionMetadata = {
type DynamicDispatchLimits (line 6447) | interface DynamicDispatchLimits {
type DynamicDispatchOptions (line 6457) | interface DynamicDispatchOptions {
type DispatchNamespace (line 6469) | interface DispatchNamespace {
class NonRetryableError (line 6490) | class NonRetryableError extends Error {
type WorkflowInstanceCreateOptions (line 6515) | interface WorkflowInstanceCreateOptions<PARAMS = unknown> {
type InstanceStatus (line 6525) | type InstanceStatus = {
type WorkflowError (line 6539) | interface WorkflowError {
FILE: sdks/python/server/src/mcp_ui_server/core.py
class UIResource (line 24) | class UIResource(EmbeddedResource):
method __init__ (line 27) | def __init__(self, resource: TextResourceContents | BlobResourceConten...
function _get_additional_resource_props (line 36) | def _get_additional_resource_props(options: CreateUIResourceOptions) -> ...
function create_ui_resource (line 69) | def create_ui_resource(options_dict: dict[str, Any]) -> UIResource:
function ui_action_result_tool_call (line 167) | def ui_action_result_tool_call(
function ui_action_result_prompt (line 188) | def ui_action_result_prompt(prompt: str) -> UIActionResultPrompt:
function ui_action_result_link (line 205) | def ui_action_result_link(url: str) -> UIActionResultLink:
function ui_action_result_intent (line 222) | def ui_action_result_intent(
function ui_action_result_notification (line 243) | def ui_action_result_notification(message: str) -> UIActionResultNotific...
FILE: sdks/python/server/src/mcp_ui_server/exceptions.py
class MCPUIServerError (line 4) | class MCPUIServerError(Exception):
class InvalidURIError (line 9) | class InvalidURIError(MCPUIServerError):
class InvalidContentError (line 14) | class InvalidContentError(MCPUIServerError):
class EncodingError (line 19) | class EncodingError(MCPUIServerError):
FILE: sdks/python/server/src/mcp_ui_server/types.py
class RawHtmlPayload (line 18) | class RawHtmlPayload(BaseModel):
class ExternalUrlPayload (line 24) | class ExternalUrlPayload(BaseModel):
class UIMetadataKey (line 37) | class UIMetadataKey:
class CreateUIResourceOptions (line 81) | class CreateUIResourceOptions(BaseModel):
class GenericActionMessage (line 102) | class GenericActionMessage(BaseModel):
class UIActionResultToolCall (line 107) | class UIActionResultToolCall(GenericActionMessage):
class ToolCallPayload (line 110) | class ToolCallPayload(BaseModel):
class UIActionResultPrompt (line 120) | class UIActionResultPrompt(GenericActionMessage):
class PromptPayload (line 123) | class PromptPayload(BaseModel):
class UIActionResultLink (line 131) | class UIActionResultLink(GenericActionMessage):
class LinkPayload (line 134) | class LinkPayload(BaseModel):
class UIActionResultIntent (line 142) | class UIActionResultIntent(GenericActionMessage):
class IntentPayload (line 145) | class IntentPayload(BaseModel):
class UIActionResultNotification (line 154) | class UIActionResultNotification(GenericActionMessage):
class NotificationPayload (line 157) | class NotificationPayload(BaseModel):
FILE: sdks/python/server/src/mcp_ui_server/utils.py
function validate_uri (line 12) | def validate_uri(uri: str) -> bool:
function serialize_ui_resource (line 24) | def serialize_ui_resource(ui_resource: "UIResource") -> dict[str, Any]:
function serialize_ui_action_result (line 36) | def serialize_ui_action_result(action_result: UIActionResult) -> str:
function deserialize_ui_action_result (line 48) | def deserialize_ui_action_result(json_str: str) -> UIActionResult:
class ReservedUrlParams (line 95) | class ReservedUrlParams:
class InternalMessageType (line 100) | class InternalMessageType:
function create_iframe_communication_script (line 109) | def create_iframe_communication_script() -> str:
function wrap_html_with_communication (line 137) | def wrap_html_with_communication(html_content: str, include_script: bool...
FILE: sdks/python/server/tests/test_create_ui_resource.py
function raw_html_text_options (line 13) | def raw_html_text_options():
function raw_html_blob_options (line 23) | def raw_html_blob_options():
function external_url_text_options (line 33) | def external_url_text_options():
function external_url_blob_options (line 46) | def external_url_blob_options():
class TestCreateUIResource (line 58) | class TestCreateUIResource:
method test_create_text_based_raw_html_resource (line 61) | def test_create_text_based_raw_html_resource(self, raw_html_text_optio...
method test_create_blob_based_raw_html_resource (line 77) | def test_create_blob_based_raw_html_resource(self, raw_html_blob_optio...
method test_create_text_based_external_url_resource (line 93) | def test_create_text_based_external_url_resource(self, external_url_te...
method test_create_blob_based_external_url_resource (line 109) | def test_create_blob_based_external_url_resource(self, external_url_bl...
class TestCreateUIResourceValidationErrors (line 126) | class TestCreateUIResourceValidationErrors:
method test_invalid_uri_prefix_with_raw_html (line 129) | def test_invalid_uri_prefix_with_raw_html(self):
method test_invalid_uri_prefix_with_external_url (line 139) | def test_invalid_uri_prefix_with_external_url(self):
method test_empty_html_string_error (line 152) | def test_empty_html_string_error(self):
method test_empty_iframe_url_error (line 162) | def test_empty_iframe_url_error(self):
method test_invalid_encoding_error (line 172) | def test_invalid_encoding_error(self):
FILE: sdks/python/server/tests/test_metadata.py
function basic_raw_html_options (line 10) | def basic_raw_html_options():
class TestUIMetadata (line 19) | class TestUIMetadata:
method test_create_resource_with_ui_metadata (line 22) | def test_create_resource_with_ui_metadata(self, basic_raw_html_options):
method test_create_resource_with_multiple_ui_metadata_fields (line 44) | def test_create_resource_with_multiple_ui_metadata_fields(self, basic_...
method test_create_resource_with_custom_metadata (line 70) | def test_create_resource_with_custom_metadata(self, basic_raw_html_opt...
method test_create_resource_with_both_ui_and_custom_metadata (line 88) | def test_create_resource_with_both_ui_and_custom_metadata(self, basic_...
method test_metadata_override_behavior (line 109) | def test_metadata_override_behavior(self, basic_raw_html_options):
method test_create_resource_without_metadata (line 128) | def test_create_resource_without_metadata(self, basic_raw_html_options):
method test_metadata_with_external_url_content (line 137) | def test_metadata_with_external_url_content(self):
method test_metadata_with_blob_encoding (line 158) | def test_metadata_with_blob_encoding(self):
method test_empty_ui_metadata_dict (line 179) | def test_empty_ui_metadata_dict(self, basic_raw_html_options):
method test_empty_custom_metadata_dict (line 192) | def test_empty_custom_metadata_dict(self, basic_raw_html_options):
FILE: sdks/python/server/tests/test_ui_action_results.py
class TestUIActionResultHelpers (line 13) | class TestUIActionResultHelpers:
method test_ui_action_result_tool_call (line 16) | def test_ui_action_result_tool_call(self):
method test_ui_action_result_prompt (line 29) | def test_ui_action_result_prompt(self):
method test_ui_action_result_link (line 41) | def test_ui_action_result_link(self):
method test_ui_action_result_intent (line 53) | def test_ui_action_result_intent(self):
method test_ui_action_result_notification (line 66) | def test_ui_action_result_notification(self):
method test_ui_action_result_tool_call_with_complex_params (line 78) | def test_ui_action_result_tool_call_with_complex_params(self):
method test_ui_action_result_intent_with_empty_params (line 97) | def test_ui_action_result_intent_with_empty_params(self):
FILE: sdks/ruby/lib/mcp_ui_server.rb
type McpUiServer (line 8) | module McpUiServer
class Error (line 9) | class Error < StandardError; end
function create_ui_resource (line 50) | def self.create_ui_resource(uri:, content:, encoding: :text)
function validate_uri_scheme (line 66) | def self.validate_uri_scheme(uri)
function validate_content_type (line 71) | def self.validate_content_type(content_type)
function process_content (line 79) | def self.process_content(content, resource)
function process_raw_html_content (line 92) | def self.process_raw_html_content(content, resource)
function process_external_url_content (line 99) | def self.process_external_url_content(content, resource)
function process_encoding (line 106) | def self.process_encoding(encoding, resource, content_value)
FILE: sdks/ruby/lib/mcp_ui_server/version.rb
type McpUiServer (line 3) | module McpUiServer
FILE: sdks/typescript/client/src/capabilities.ts
type ClientCapabilitiesWithExtensions (line 12) | interface ClientCapabilitiesWithExtensions extends ClientCapabilities {
constant UI_EXTENSION_NAME (line 25) | const UI_EXTENSION_NAME = 'io.modelcontextprotocol/ui' as const;
constant UI_EXTENSION_CONFIG (line 32) | const UI_EXTENSION_CONFIG = {
constant UI_EXTENSION_CAPABILITIES (line 65) | const UI_EXTENSION_CAPABILITIES = {
FILE: sdks/typescript/client/src/components/AppFrame.tsx
function buildSandboxUrl (line 24) | function buildSandboxUrl(baseUrl: URL, csp?: McpUiResourceCsp): URL {
type AppInfo (line 35) | interface AppInfo {
type SandboxConfig (line 45) | interface SandboxConfig {
type AppFrameProps (line 69) | interface AppFrameProps {
FILE: sdks/typescript/client/src/components/AppRenderer.tsx
type RequestHandlerExtra (line 39) | type RequestHandlerExtra = Parameters<Parameters<AppBridge['setRequestHa...
type AppRendererHandle (line 45) | interface AppRendererHandle {
type AppRendererProps (line 59) | interface AppRendererProps {
FILE: sdks/typescript/client/src/components/__tests__/AppRenderer.test.tsx
function createMockExtra (line 77) | function createMockExtra() {
FILE: sdks/typescript/client/src/components/__tests__/ProxyScript.test.ts
function nextTick (line 7) | function nextTick(): Promise<void> {
FILE: sdks/typescript/client/src/types.ts
constant UI_METADATA_PREFIX (line 6) | const UI_METADATA_PREFIX = 'mcpui.dev/ui-';
type UIResourceMetadata (line 8) | type UIResourceMetadata = {
FILE: sdks/typescript/client/src/utils/app-host-utils.ts
constant DEFAULT_SANDBOX_TIMEOUT_MS (line 8) | const DEFAULT_SANDBOX_TIMEOUT_MS = 10000;
function setupSandboxProxyIframe (line 10) | async function setupSandboxProxyIframe(sandboxProxyUrl: URL): Promise<{
type ToolUiResourceInfo (line 68) | type ToolUiResourceInfo = {
function getToolUiResourceUri (line 72) | async function getToolUiResourceUri(
function readToolUiResourceHtml (line 100) | async function readToolUiResourceHtml(
FILE: sdks/typescript/client/src/utils/isUIResource.ts
function isUIResource (line 3) | function isUIResource<
FILE: sdks/typescript/client/src/utils/metadataUtils.ts
function getResourceMetadata (line 4) | function getResourceMetadata(resource: Partial<Resource>): Record<string...
function getUIResourceMetadata (line 8) | function getUIResourceMetadata(
FILE: sdks/typescript/server/src/__tests__/index.test.ts
function simulateResponse (line 291) | function simulateResponse(data: Record<string, unknown>, source: unknown...
FILE: sdks/typescript/server/src/__tests__/test-utils.ts
function mockFetchWithBody (line 7) | function mockFetchWithBody(body: string, headers?: Record<string, string...
FILE: sdks/typescript/server/src/index.ts
type UIResource (line 15) | type UIResource = {
function createUIResource (line 32) | async function createUIResource(options: CreateUIResourceOptions): Promi...
constant DEFAULT_EXPERIMENTAL_REQUEST_TIMEOUT_MS (line 125) | const DEFAULT_EXPERIMENTAL_REQUEST_TIMEOUT_MS = 30_000;
function sendExperimentalRequest (line 148) | function sendExperimentalRequest(
FILE: sdks/typescript/server/src/types.ts
type URI (line 8) | type URI = `ui://${string}`;
type MimeType (line 11) | type MimeType = 'text/html;profile=mcp-app';
type HTMLTextContent (line 13) | type HTMLTextContent = {
type Base64BlobContent (line 21) | type Base64BlobContent = {
type ResourceContentPayload (line 29) | type ResourceContentPayload =
type CreateUIResourceOptions (line 33) | interface CreateUIResourceOptions {
type UIResourceProps (line 47) | type UIResourceProps = Omit<Partial<Resource>, 'uri' | 'mimeType'>;
type EmbeddedUIResourceProps (line 48) | type EmbeddedUIResourceProps = Omit<Partial<EmbeddedResource>, 'resource...
constant UI_METADATA_PREFIX (line 55) | const UI_METADATA_PREFIX = 'mcpui.dev/ui-';
type UIResourceMetadata (line 57) | type UIResourceMetadata = {
FILE: sdks/typescript/server/src/utils.ts
constant MAX_RESPONSE_BYTES (line 5) | const MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
constant FETCH_TIMEOUT_MS (line 8) | const FETCH_TIMEOUT_MS = 30_000;
constant BLOCKED_HOSTNAMES (line 11) | const BLOCKED_HOSTNAMES = new Set([
function isPrivateIPv4 (line 23) | function isPrivateIPv4(hostname: string): boolean {
function validateExternalUrl (line 48) | function validateExternalUrl(url: string): URL {
function fetchExternalUrl (line 89) | async function fetchExternalUrl(url: string): Promise<string> {
function injectBaseTag (line 148) | function injectBaseTag(html: string, url: string): string {
function escapeHtmlAttr (line 167) | function escapeHtmlAttr(str: string): string {
function extractOrigin (line 175) | function extractOrigin(url: string): string | undefined {
function getAdditionalResourceProps (line 183) | function getAdditionalResourceProps(
function utf8ToBase64 (line 213) | function utf8ToBase64(str: string): string {
Condensed preview — 165 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (895K chars).
[
{
"path": ".github/CONTRIBUTING.md",
"chars": 5352,
"preview": "# Contributing to MCP-UI\n\nFirst of all, thank you for your interest in contributing to MCP-UI! We appreciate the time an"
},
{
"path": ".github/copilot-instructions.md",
"chars": 7299,
"preview": "# MCP-UI Development Instructions\n\n**ALWAYS follow these instructions first and fallback to additional search and contex"
},
{
"path": ".github/workflows/ci.yml",
"chars": 9042,
"preview": "name: CI\n\non:\n push:\n branches:\n - main\n - alpha\n pull_request:\n branches:\n - main\n - alpha\n"
},
{
"path": ".github/workflows/deploy-docs.yml",
"chars": 1904,
"preview": "#\nname: Deploy VitePress site to Pages\n\non:\n # Runs on pushes targeting the `main` branch. Change this to `master` if y"
},
{
"path": ".gitignore",
"chars": 2674,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\n# Diagnostic reports "
},
{
"path": ".prettierignore",
"chars": 384,
"preview": "# Ignore artifacts:\nnode_modules\ndist\nbuild\ncoverage\nvenv\n\n# Ignore package manager files (if you commit them)\npackage-l"
},
{
"path": ".prettierrc.json",
"chars": 152,
"preview": "{\n \"semi\": true,\n \"trailingComma\": \"all\",\n \"singleQuote\": true,\n \"printWidth\": 100,\n \"tabWidth\": 2,\n \"plugins\": [\""
},
{
"path": ".releaserc.json",
"chars": 1117,
"preview": "{\n \"branches\": [\n \"main\",\n {\n \"name\": \"alpha\",\n \"prerelease\": true\n }\n ],\n \"plugins\": [\n \"@sema"
},
{
"path": ".ruby-version",
"chars": 6,
"preview": "3.2.2\n"
},
{
"path": "CHANGELOG.md",
"chars": 8767,
"preview": "# [5.2.0](https://github.com/idosal/mcp-ui/compare/v5.1.2...v5.2.0) (2025-07-18)\n\n\n### Features\n\n* support generic messa"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5222,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "LICENSE",
"chars": 11341,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 17362,
"preview": "## 📦 Model Context Protocol UI SDK\n\n<p align=\"center\">\n <img width=\"250\" alt=\"image\" src=\"https://github.com/user-attac"
},
{
"path": "SECURITY.md",
"chars": 410,
"preview": "# Security Policy\n\nMCP-UI is committed to maintaining the highest security standards, and actively works with web securi"
},
{
"path": "docs/README.md",
"chars": 3617,
"preview": "# MCP-UI Documentation\n\nThis directory contains the enhanced documentation for MCP UI, built with VitePress and featurin"
},
{
"path": "docs/package.json",
"chars": 368,
"preview": "{\n \"name\": \"@mcp-ui/docs\",\n \"version\": \"0.1.0\",\n \"private\": false,\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"vit"
},
{
"path": "docs/src/.vitepress/config.ts",
"chars": 9756,
"preview": "import { defineConfig } from 'vitepress';\nimport { withMermaid } from 'vitepress-plugin-mermaid';\n\nexport default withMe"
},
{
"path": "docs/src/.vitepress/theme/custom.css",
"chars": 11271,
"preview": "/**\n * Custom styles for MCP UI documentation\n * These styles enhance the default VitePress theme\n */\n\n/* CSS Variables "
},
{
"path": "docs/src/.vitepress/theme/index.ts",
"chars": 863,
"preview": "import DefaultTheme from 'vitepress/theme';\nimport { h } from 'vue';\nimport type { Theme } from 'vitepress';\nimport './c"
},
{
"path": "docs/src/about.md",
"chars": 1610,
"preview": "# About\n\n## About\n\n`mcp-ui` pioneered the concept of interactive UI over the [Model Context Protocol](https://modelconte"
},
{
"path": "docs/src/guide/apps-sdk.md",
"chars": 5589,
"preview": "# OpenAI Apps SDK Integration\n\n::: warning ChatGPT-Specific\nThis page covers the **OpenAI Apps SDK** adapter for **ChatG"
},
{
"path": "docs/src/guide/client/app-renderer.md",
"chars": 8159,
"preview": "# AppRenderer Component\n\n`AppRenderer` is the recommended component for rendering MCP tool UIs in your host application."
},
{
"path": "docs/src/guide/client/overview.md",
"chars": 3981,
"preview": "# @mcp-ui/client Overview\n\nThe `@mcp-ui/client` package provides components for rendering MCP tool UIs in your host appl"
},
{
"path": "docs/src/guide/client/walkthrough.md",
"chars": 10964,
"preview": "# Client SDK Walkthrough\n\nThis guide provides a step-by-step walkthrough for building an MCP Apps client that can render"
},
{
"path": "docs/src/guide/embeddable-ui.md",
"chars": 12400,
"preview": "# Embeddable UI\n\n::: tip MCP Apps Standard\nThis page documents the **legacy MCP-UI `postMessage` protocol** for embedded"
},
{
"path": "docs/src/guide/getting-started.md",
"chars": 6831,
"preview": "# Getting Started\n\nThis guide will help you get started with building MCP Apps using the `@mcp-ui/*` packages.\n\n## Prere"
},
{
"path": "docs/src/guide/introduction.md",
"chars": 7841,
"preview": "# Introduction\n\nWelcome to the MCP Apps SDK documentation!\n\nThe `@mcp-ui/*` packages provide tools for building [MCP App"
},
{
"path": "docs/src/guide/mcp-apps.md",
"chars": 21712,
"preview": "# Legacy MCP-UI Adapter\n\n::: tip For New Apps\n**Building a new app?** Use the MCP Apps patterns directly - see [Getting "
},
{
"path": "docs/src/guide/protocol-details.md",
"chars": 9437,
"preview": "# Protocol Details\n\nThis section covers the wire protocols for MCP Apps and legacy MCP-UI.\n\n## MCP Apps Protocol\n\nMCP Ap"
},
{
"path": "docs/src/guide/server/python/overview.md",
"chars": 1764,
"preview": "# mcp-ui-server Overview\n\nThe `mcp-ui-server` package provides utilities to generate UI resources (`UIResource`) on your"
},
{
"path": "docs/src/guide/server/python/usage-examples.md",
"chars": 4689,
"preview": "# mcp-ui-server Usage & Examples\n\nThis page provides practical examples for using the `mcp-ui-server` package.\n\nFor a co"
},
{
"path": "docs/src/guide/server/python/walkthrough.md",
"chars": 13481,
"preview": "# Python Server Walkthrough\n\nThis guide provides a step-by-step walkthrough for creating an MCP server with UI resources"
},
{
"path": "docs/src/guide/server/ruby/overview.md",
"chars": 1235,
"preview": "# mcp_ui_server Overview\n\nThe `mcp_ui_server` gem provides server-side utilities in Ruby to help construct `UIResource` "
},
{
"path": "docs/src/guide/server/ruby/usage-examples.md",
"chars": 3243,
"preview": "# mcp_ui_server Usage & Examples\n\nThis page provides practical examples for using the `mcp_ui_server` gem.\n\nFor a comple"
},
{
"path": "docs/src/guide/server/ruby/walkthrough.md",
"chars": 4544,
"preview": "# Ruby Server Walkthrough\n\nThis guide provides a step-by-step walkthrough for creating a barebones MCP UI server using R"
},
{
"path": "docs/src/guide/server/typescript/overview.md",
"chars": 3618,
"preview": "# @mcp-ui/server Overview\n\nThe `@mcp-ui/server` package provides utilities to build MCP Apps - tools with interactive UI"
},
{
"path": "docs/src/guide/server/typescript/usage-examples.md",
"chars": 8917,
"preview": "# @mcp-ui/server Usage & Examples\n\nThis page provides practical examples for using the `@mcp-ui/server` package.\n\nFor a "
},
{
"path": "docs/src/guide/server/typescript/walkthrough.md",
"chars": 6203,
"preview": "# TypeScript Server Walkthrough\n\nThis guide provides a step-by-step walkthrough for integrating `@mcp-ui/server` into a "
},
{
"path": "docs/src/guide/supported-hosts.md",
"chars": 1347,
"preview": "# Supported Hosts\n\nThe `@mcp-ui/*` packages work with both MCP Apps hosts and legacy MCP-UI hosts.\n\n## MCP Apps Hosts\n\nT"
},
{
"path": "docs/src/index.md",
"chars": 4968,
"preview": "---\nlayout: home\n\nhero:\n name: MCP-UI -> MCP Apps\n text: Interactive UI Components over MCP\n tagline: Build rich, dyn"
},
{
"path": "docs/src/team.md",
"chars": 132,
"preview": "# Team\n\n`mcp-ui` is a project by [Ido Salomon](https://x.com/idosal1), in collaboration with [Liad Yosef](https://x.com/"
},
{
"path": "eslint.config.mjs",
"chars": 1970,
"preview": "import js from '@eslint/js';\nimport tseslint from 'typescript-eslint';\nimport react from 'eslint-plugin-react';\nimport r"
},
{
"path": "examples/external-url-demo/index.html",
"chars": 422,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-w"
},
{
"path": "examples/external-url-demo/package.json",
"chars": 792,
"preview": "{\n \"name\": \"external-url-demo\",\n \"private\": true,\n \"version\": \"0.0.0\",\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": "
},
{
"path": "examples/external-url-demo/src/App.css",
"chars": 758,
"preview": "body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n max-width: 1200px;\n margin: "
},
{
"path": "examples/external-url-demo/src/App.tsx",
"chars": 2944,
"preview": "import { useState } from 'react';\nimport './App.css';\nimport { UIResourceRenderer } from '@mcp-ui/client';\n\nfunction App"
},
{
"path": "examples/external-url-demo/src/index.css",
"chars": 362,
"preview": "body {\n margin: 0;\n font-family:\n -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Can"
},
{
"path": "examples/external-url-demo/src/main.tsx",
"chars": 241,
"preview": "import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport App from './App.tsx';\nimport './index.css';\n\n"
},
{
"path": "examples/external-url-demo/tsconfig.app.json",
"chars": 667,
"preview": "{\n \"compilerOptions\": {\n \"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n \"target\": \"ES2020\",\n"
},
{
"path": "examples/external-url-demo/tsconfig.json",
"chars": 107,
"preview": "{\n \"files\": [],\n \"references\": [{ \"path\": \"./tsconfig.app.json\" }, { \"path\": \"./tsconfig.node.json\" }]\n}\n"
},
{
"path": "examples/external-url-demo/tsconfig.node.json",
"chars": 233,
"preview": "{\n \"compilerOptions\": {\n \"composite\": true,\n \"skipLibCheck\": true,\n \"module\": \"ESNext\",\n \"moduleResolution\""
},
{
"path": "examples/external-url-demo/vite.config.ts",
"chars": 166,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\n// https://vitejs.dev/config/\nexport def"
},
{
"path": "examples/mcp-apps-demo/package.json",
"chars": 546,
"preview": "{\n \"name\": \"mcp-apps-demo\",\n \"version\": \"1.0.0\",\n \"type\": \"module\",\n \"scripts\": {\n \"start\": \"node dist/index.js\","
},
{
"path": "examples/mcp-apps-demo/src/index.ts",
"chars": 12738,
"preview": "import express from 'express';\nimport cors from 'cors';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp."
},
{
"path": "examples/mcp-apps-demo/tsconfig.json",
"chars": 312,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"NodeNext\",\n \"moduleResolution\": \"NodeNext\",\n \"outD"
},
{
"path": "examples/python-server-demo/README.md",
"chars": 3871,
"preview": "# Python MCP Server Demo\n\nA Python MCP server implementation inspired by the TypeScript server demo. This server demonst"
},
{
"path": "examples/python-server-demo/pyproject.toml",
"chars": 786,
"preview": "[project]\nname = \"python-server-demo\"\nversion = \"0.1.0\"\ndescription = \"Python MCP server demo inspired by the TypeScript"
},
{
"path": "examples/python-server-demo/python_server_demo.py",
"chars": 5073,
"preview": "#!/usr/bin/env python3\n\"\"\"Python MCP server demo inspired by the TypeScript version.\n\nThis server demonstrates how to cr"
},
{
"path": "examples/ruby-server-demo/Gemfile",
"chars": 157,
"preview": "source \"https://rubygems.org\"\n\ngem \"mcp\", git: \"https://github.com/modelcontextprotocol/ruby-sdk\"\ngem \"mcp_ui_server\", p"
},
{
"path": "examples/ruby-server-demo/README.md",
"chars": 1025,
"preview": "# ruby-server-demo\n\nThis barebones server demonstrates how to use `mcp_ui_server` to generate three types of UI resource"
},
{
"path": "examples/ruby-server-demo/server.rb",
"chars": 2275,
"preview": "require 'mcp'\nrequire 'mcp_ui_server'\nrequire 'webrick'\nrequire 'json'\n\n# --- Define MCP Tools ---\nclass ExternalUrlTool"
},
{
"path": "examples/server/.react-router/types/+future.ts",
"chars": 145,
"preview": "// Generated by React Router\n\nimport 'react-router';\n\ndeclare module 'react-router' {\n interface Future {\n unstable_"
},
{
"path": "examples/server/.react-router/types/+routes.ts",
"chars": 580,
"preview": "// Generated by React Router\n\nimport 'react-router';\n\ndeclare module 'react-router' {\n interface Register {\n pages: "
},
{
"path": "examples/server/.react-router/types/+server-build.d.ts",
"chars": 777,
"preview": "// Generated by React Router\n\ndeclare module 'virtual:react-router/server-build' {\n import { ServerBuild } from 'react-"
},
{
"path": "examples/server/.react-router/types/app/+types/root.ts",
"chars": 1671,
"preview": "// Generated by React Router\n\nimport type { GetInfo, GetAnnotations } from 'react-router/internal';\n\ntype Module = typeo"
},
{
"path": "examples/server/.react-router/types/app/routes/+types/home.ts",
"chars": 1754,
"preview": "// Generated by React Router\n\nimport type { GetInfo, GetAnnotations } from 'react-router/internal';\n\ntype Module = typeo"
},
{
"path": "examples/server/.react-router/types/app/routes/+types/task.ts",
"chars": 1754,
"preview": "// Generated by React Router\n\nimport type { GetInfo, GetAnnotations } from 'react-router/internal';\n\ntype Module = typeo"
},
{
"path": "examples/server/.react-router/types/app/routes/+types/user.ts",
"chars": 1754,
"preview": "// Generated by React Router\n\nimport type { GetInfo, GetAnnotations } from 'react-router/internal';\n\ntype Module = typeo"
},
{
"path": "examples/server/README.md",
"chars": 298,
"preview": "This example features a complete MCP remote server hosted on Cloudflare.\n\nThe server is the standard Cloudflare auth-les"
},
{
"path": "examples/server/app/app.css",
"chars": 316,
"preview": "@import 'tailwindcss' source('.');\n\n@theme {\n --font-sans:\n 'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Co"
},
{
"path": "examples/server/app/entry.server.tsx",
"chars": 1426,
"preview": "import type { EntryContext } from 'react-router';\nimport { ServerRouter } from 'react-router';\nimport { isbot } from 'is"
},
{
"path": "examples/server/app/graph/graph.tsx",
"chars": 21065,
"preview": "import { useState, useMemo } from 'react';\nimport {\n BarChart,\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n R"
},
{
"path": "examples/server/app/images.d.ts",
"chars": 79,
"preview": "declare module '*.svg' {\n const content: unknown;\n export default content;\n}\n"
},
{
"path": "examples/server/app/root.tsx",
"chars": 1771,
"preview": "import {\n isRouteErrorResponse,\n Links,\n Meta,\n Outlet,\n Scripts,\n ScrollRestoration,\n} from 'react-router';\n\nimpo"
},
{
"path": "examples/server/app/routes/home.tsx",
"chars": 213,
"preview": "export function meta() {\n return [\n { title: 'New React Router App' },\n { name: 'description', content: 'Welcome "
},
{
"path": "examples/server/app/routes/task.tsx",
"chars": 376,
"preview": "import { Graph } from '../graph/graph';\n\nexport function meta() {\n return [{ title: 'Task Status' }];\n}\n\nexport const l"
},
{
"path": "examples/server/app/routes/user.tsx",
"chars": 592,
"preview": "import { User } from '../user/user';\n\nexport function meta() {\n return [{ title: 'User' }];\n}\n\nexport const loader = as"
},
{
"path": "examples/server/app/routes.ts",
"chars": 218,
"preview": "import { type RouteConfig, index, route } from '@react-router/dev/routes';\n\nexport default [\n index('routes/home.tsx'),"
},
{
"path": "examples/server/app/user/user.tsx",
"chars": 8327,
"preview": "import React from 'react';\nimport {\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveConta"
},
{
"path": "examples/server/app/utils/messageUtils.ts",
"chars": 280,
"preview": "export function postMessageToParent(message: any) {\n console.log('Sending message:', message);\n // @ts-expect-error - "
},
{
"path": "examples/server/biome.json",
"chars": 658,
"preview": "{\n \"$schema\": \"https://biomejs.dev/schemas/1.6.2/schema.json\",\n \"organizeImports\": {\n \"enabled\": true\n },\n \"files"
},
{
"path": "examples/server/package.json",
"chars": 1480,
"preview": "{\n \"name\": \"remote-mcp-server-authless\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"build\": \"react-rout"
},
{
"path": "examples/server/pnpm-workspace.yaml",
"chars": 13,
"preview": "packages: []\n"
},
{
"path": "examples/server/react-router.config.ts",
"chars": 163,
"preview": "import type { Config } from '@react-router/dev/config';\n\nexport default {\n ssr: true,\n future: {\n unstable_viteEnvi"
},
{
"path": "examples/server/src/index.ts",
"chars": 8247,
"preview": "import { McpAgent } from 'agents/mcp';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z }"
},
{
"path": "examples/server/tsconfig.cloudflare.json",
"chars": 666,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"include\": [\n \".react-router/types/**/*\",\n \"app/**/*\",\n \"app/**/.server/**/"
},
{
"path": "examples/server/tsconfig.json",
"chars": 507,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2021\",\n \"lib\": [\"es2021\"],\n \"jsx\": \"react-jsx\",\n \"module\": \"es2022\",\n"
},
{
"path": "examples/server/tsconfig.node.json",
"chars": 267,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"include\": [\"vite.config.ts\"],\n \"compilerOptions\": {\n \"composite\": true,\n \"st"
},
{
"path": "examples/server/vite.config.ts",
"chars": 405,
"preview": "import { reactRouter } from '@react-router/dev/vite';\nimport { cloudflare } from '@cloudflare/vite-plugin';\nimport tailw"
},
{
"path": "examples/server/worker-configuration.d.ts",
"chars": 231018,
"preview": "/* eslint-disable */\n// Generated by Wrangler by running `wrangler types` (hash: 15dd420dfccac8afafeb181790b9eddb)\n// Ru"
},
{
"path": "examples/server/wrangler.jsonc",
"chars": 618,
"preview": "/**\n * For more details on how to configure Wrangler, refer to:\n * https://developers.cloudflare.com/workers/wrangler/co"
},
{
"path": "examples/typescript-server-demo/README.md",
"chars": 1023,
"preview": "# typescript-server-demo\n\nThis barebones server demonstrates how to use `@mcp-ui/server` to generate all three types of "
},
{
"path": "examples/typescript-server-demo/package.json",
"chars": 722,
"preview": "{\n \"name\": \"typescript-server-demo\",\n \"version\": \"1.0.0\",\n \"type\": \"module\",\n \"description\": \"Barebones TypeScript M"
},
{
"path": "examples/typescript-server-demo/src/index.ts",
"chars": 4259,
"preview": "import express from 'express';\nimport cors from 'cors';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp."
},
{
"path": "examples/typescript-server-demo/tsconfig.json",
"chars": 259,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"NodeNext\",\n \"moduleResolution\": \"NodeNext\",\n \"stri"
},
{
"path": "lefthook.yml",
"chars": 542,
"preview": "pre-commit:\n parallel: true\n stage_fixed: true\n jobs:\n - name: lint typescript\n glob: '*.{ts,tsx}'\n run:"
},
{
"path": "package.json",
"chars": 2416,
"preview": "{\n \"name\": \"mcp-ui\",\n \"private\": true,\n \"scripts\": {\n \"preinstall\": \"npx only-allow pnpm\",\n \"dev\": \"pnpm --filt"
},
{
"path": "pnpm-workspace.yaml",
"chars": 110,
"preview": "packages:\n # all packages in subdirs of sdks/typescript/\n - 'sdks/typescript/*'\n - 'examples/*'\n - 'docs'\n"
},
{
"path": "sdks/python/server/CHANGELOG.md",
"chars": 10454,
"preview": "# 1.0.0 (2025-11-04)\n\n\n### Bug Fixes\n\n* add a bridge to pass messages in and out of the proxy ([#38](https://github.com/"
},
{
"path": "sdks/python/server/README.md",
"chars": 9850,
"preview": "# MCP UI Server SDK for Python\n\nA Python SDK for creating MCP UI resources on the server side, enabling rich interactive"
},
{
"path": "sdks/python/server/pyproject.toml",
"chars": 1811,
"preview": "[project]\nname = \"mcp-ui-server\"\nversion = \"1.0.0\"\ndescription = \"mcp-ui Server SDK for Python\"\nreadme = \"README.md\"\nreq"
},
{
"path": "sdks/python/server/release.config.js",
"chars": 965,
"preview": "module.exports = {\n branches: [\n 'main',\n {\n name: 'alpha',\n prerelease: true,\n },\n ],\n repository"
},
{
"path": "sdks/python/server/src/mcp_ui_server/__init__.py",
"chars": 1218,
"preview": "\"\"\"MCP UI Server SDK for Python.\n\nThis package provides server-side functionality for creating MCP UI resources.\n\"\"\"\n\nfr"
},
{
"path": "sdks/python/server/src/mcp_ui_server/core.py",
"chars": 7973,
"preview": "\"\"\"Core functionality for MCP UI Server SDK.\"\"\"\n\nimport base64\nfrom typing import Any\n\nfrom mcp.types import BlobResourc"
},
{
"path": "sdks/python/server/src/mcp_ui_server/exceptions.py",
"chars": 463,
"preview": "\"\"\"Custom exceptions for MCP UI Server SDK.\"\"\"\n\n\nclass MCPUIServerError(Exception):\n \"\"\"Base exception for MCP UI Ser"
},
{
"path": "sdks/python/server/src/mcp_ui_server/types.py",
"chars": 5251,
"preview": "\"\"\"Type definitions for MCP UI Server SDK.\"\"\"\n\nfrom typing import Any, Literal\n\nfrom pydantic import BaseModel\n\n# Primar"
},
{
"path": "sdks/python/server/src/mcp_ui_server/utils.py",
"chars": 4820,
"preview": "\"\"\"Utility functions for MCP UI Server SDK.\"\"\"\n\nimport json\nfrom typing import TYPE_CHECKING, Any, cast\n\nfrom .types imp"
},
{
"path": "sdks/python/server/tests/__init__.py",
"chars": 39,
"preview": "\"\"\"Test suite for MCP UI Server SDK.\"\"\""
},
{
"path": "sdks/python/server/tests/test_create_ui_resource.py",
"chars": 6152,
"preview": "\"\"\"Tests for create_ui_resource functionality.\"\"\"\n\nimport base64\n\nimport pytest\nfrom pydantic import AnyUrl, ValidationE"
},
{
"path": "sdks/python/server/tests/test_metadata.py",
"chars": 7765,
"preview": "\"\"\"Tests for UI metadata functionality.\"\"\"\n\nimport pytest\n\nfrom mcp_ui_server import create_ui_resource\nfrom mcp_ui_serv"
},
{
"path": "sdks/python/server/tests/test_ui_action_results.py",
"chars": 3428,
"preview": "\"\"\"Tests for UI action result helper functions.\"\"\"\n\n\nfrom mcp_ui_server import (\n ui_action_result_intent,\n ui_act"
},
{
"path": "sdks/ruby/.rspec",
"chars": 52,
"preview": "--require spec_helper\n--color\n--format documentation"
},
{
"path": "sdks/ruby/.rubocop.yml",
"chars": 396,
"preview": "AllCops:\n NewCops: enable\n Exclude:\n - 'bin/*'\n - 'vendor/**/*'\n - 'tmp/**/*'\n\nplugins:\n - rubocop-rake\n - "
},
{
"path": "sdks/ruby/CHANGELOG.md",
"chars": 240,
"preview": "## [1.0.1](https://github.com/idosal/mcp-ui/compare/ruby-server-sdk/v1.0.0...ruby-server-sdk/v1.0.1) (2025-07-19)\n\n\n### "
},
{
"path": "sdks/ruby/Gemfile",
"chars": 70,
"preview": "# frozen_string_literal: true\n\nsource 'https://rubygems.org'\n\ngemspec\n"
},
{
"path": "sdks/ruby/README.md",
"chars": 4905,
"preview": "# MCP UI Server SDK for Ruby\n\nThis is the Ruby server-side SDK for MCP UI. It provides helper methods to create UI resou"
},
{
"path": "sdks/ruby/Rakefile",
"chars": 145,
"preview": "# frozen_string_literal: true\n\nrequire 'bundler/gem_tasks'\nrequire 'rspec/core/rake_task'\n\nRSpec::Core::RakeTask.new(:sp"
},
{
"path": "sdks/ruby/lib/mcp_ui_server/version.rb",
"chars": 74,
"preview": "# frozen_string_literal: true\n\nmodule McpUiServer\n VERSION = '0.1.0'\nend\n"
},
{
"path": "sdks/ruby/lib/mcp_ui_server.rb",
"chars": 4106,
"preview": "# frozen_string_literal: true\n\nrequire_relative 'mcp_ui_server/version'\nrequire 'base64'\n\n# The McpUiServer module provi"
},
{
"path": "sdks/ruby/mcp_ui_server.gemspec",
"chars": 1027,
"preview": "# frozen_string_literal: true\n\nrequire_relative 'lib/mcp_ui_server/version'\n\nGem::Specification.new do |spec|\n spec.nam"
},
{
"path": "sdks/ruby/release.config.js",
"chars": 1026,
"preview": "module.exports = {\n branches: [\n 'main',\n {\n name: 'alpha',\n prerelease: true,\n },\n ],\n repository"
},
{
"path": "sdks/ruby/spec/mcp_ui_server_spec.rb",
"chars": 4669,
"preview": "# frozen_string_literal: true\n\nrequire 'mcp_ui_server'\nrequire 'base64'\n\nRSpec.describe McpUiServer do\n it 'has a versi"
},
{
"path": "sdks/ruby/spec/spec_helper.rb",
"chars": 1976,
"preview": "# frozen_string_literal: true\n\n# This file was generated by the `rspec --init` command. Conventionally, all\n# specs live"
},
{
"path": "sdks/typescript/client/CHANGELOG.md",
"chars": 21563,
"preview": "# [7.0.0](https://github.com/MCP-UI-Org/mcp-ui/compare/client/v6.1.1...client/v7.0.0) (2026-03-12)\n\n\n### Bug Fixes\n\n* bu"
},
{
"path": "sdks/typescript/client/README.md",
"chars": 13342,
"preview": "## 📦 Model Context Protocol UI SDK\n\n<p align=\"center\">\n <img width=\"250\" alt=\"image\" src=\"https://github.com/user-attac"
},
{
"path": "sdks/typescript/client/package.json",
"chars": 2125,
"preview": "{\n \"name\": \"@mcp-ui/client\",\n \"version\": \"7.0.0\",\n \"description\": \"mcp-ui Client SDK\",\n \"private\": false,\n \"type\": "
},
{
"path": "sdks/typescript/client/scripts/proxy/index.html",
"chars": 4525,
"preview": "<!doctype html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n <title>MCP-UI Proxy</title>\n <style>\n html,\n "
},
{
"path": "sdks/typescript/client/src/__tests__/capabilities.test.ts",
"chars": 2064,
"preview": "import { describe, it, expect } from 'vitest';\nimport {\n type ClientCapabilitiesWithExtensions,\n UI_EXTENSION_NAME,\n "
},
{
"path": "sdks/typescript/client/src/capabilities.ts",
"chars": 2039,
"preview": "import type { ClientCapabilities } from '@modelcontextprotocol/sdk/types.js';\nimport { RESOURCE_MIME_TYPE } from '@model"
},
{
"path": "sdks/typescript/client/src/components/AppFrame.tsx",
"chars": 10055,
"preview": "import { useEffect, useRef, useState } from 'react';\n\nimport type { CallToolResult, Implementation } from '@modelcontext"
},
{
"path": "sdks/typescript/client/src/components/AppRenderer.tsx",
"chars": 18306,
"preview": "import { useEffect, useRef, useState, useImperativeHandle, forwardRef } from 'react';\n\nimport { type Client } from '@mod"
},
{
"path": "sdks/typescript/client/src/components/__tests__/AppFrame.test.tsx",
"chars": 11836,
"preview": "import { render, screen, waitFor, act } from '@testing-library/react';\nimport { vi, describe, it, expect, beforeEach, af"
},
{
"path": "sdks/typescript/client/src/components/__tests__/AppRenderer.test.tsx",
"chars": 21228,
"preview": "import React from 'react';\nimport { render, screen, waitFor, act } from '@testing-library/react';\nimport { vi, describe,"
},
{
"path": "sdks/typescript/client/src/components/__tests__/ProxyScript.test.ts",
"chars": 2772,
"preview": "import { describe, it, expect } from 'vitest';\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimp"
},
{
"path": "sdks/typescript/client/src/index.ts",
"chars": 1000,
"preview": "export { getUIResourceMetadata, getResourceMetadata } from './utils/metadataUtils';\nexport { isUIResource } from './util"
},
{
"path": "sdks/typescript/client/src/test-setup.ts",
"chars": 422,
"preview": "import '@testing-library/jest-dom';\n\n// Polyfill for requestAnimationFrame\nif (typeof window !== 'undefined' && typeof w"
},
{
"path": "sdks/typescript/client/src/types.ts",
"chars": 352,
"preview": "export const UIMetadataKey = {\n PREFERRED_FRAME_SIZE: 'preferred-frame-size',\n INITIAL_RENDER_DATA: 'initial-render-da"
},
{
"path": "sdks/typescript/client/src/utils/__tests__/isUIResource.test.ts",
"chars": 1534,
"preview": "import { describe, it, expect } from 'vitest';\nimport { isUIResource } from '../isUIResource';\n\ndescribe('isUIResource',"
},
{
"path": "sdks/typescript/client/src/utils/__tests__/metadataUtils.test.ts",
"chars": 825,
"preview": "import { describe, it, expect } from 'vitest';\nimport { getResourceMetadata, getUIResourceMetadata } from '../metadataUt"
},
{
"path": "sdks/typescript/client/src/utils/app-host-utils.ts",
"chars": 3669,
"preview": "import {\n SANDBOX_PROXY_READY_METHOD,\n getToolUiResourceUri as _getToolUiResourceUri,\n RESOURCE_MIME_TYPE,\n} from '@m"
},
{
"path": "sdks/typescript/client/src/utils/isUIResource.ts",
"chars": 391,
"preview": "import type { EmbeddedResource } from '@modelcontextprotocol/sdk/types.js';\n\nexport function isUIResource<\n T extends {"
},
{
"path": "sdks/typescript/client/src/utils/metadataUtils.ts",
"chars": 708,
"preview": "import type { Resource } from '@modelcontextprotocol/sdk/types.js';\nimport { UI_METADATA_PREFIX, type UIResourceMetadata"
},
{
"path": "sdks/typescript/client/tsconfig.json",
"chars": 222,
"preview": "{\n \"extends\": \"../../../tsconfig.base.json\",\n \"compilerOptions\": {\n \"jsx\": \"react-jsx\",\n \"forceConsistentCasingI"
},
{
"path": "sdks/typescript/client/tsconfig.test.json",
"chars": 155,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"composite\": true,\n \"jsx\": \"react-jsx\"\n },\n \"include\":"
},
{
"path": "sdks/typescript/client/vite.config.ts",
"chars": 1231,
"preview": "import { defineConfig } from 'vite';\nimport dts from 'vite-plugin-dts';\nimport path from 'path';\nimport react from '@vit"
},
{
"path": "sdks/typescript/client/vitest.config.ts",
"chars": 354,
"preview": "import { defineConfig } from 'vitest/config';\nimport react from '@vitejs/plugin-react-swc';\nimport tsconfigPaths from 'v"
},
{
"path": "sdks/typescript/server/CHANGELOG.md",
"chars": 18217,
"preview": "# [6.1.0](https://github.com/MCP-UI-Org/mcp-ui/compare/server/v6.0.1...server/v6.1.0) (2026-02-13)\n\n\n### Features\n\n* sup"
},
{
"path": "sdks/typescript/server/README.md",
"chars": 10721,
"preview": "## 📦 Model Context Protocol UI SDK\n\n<p align=\"center\">\n <img width=\"250\" alt=\"image\" src=\"https://github.com/user-attac"
},
{
"path": "sdks/typescript/server/package.json",
"chars": 1715,
"preview": "{\n \"name\": \"@mcp-ui/server\",\n \"version\": \"6.1.0\",\n \"private\": false,\n \"description\": \"mcp-ui Server SDK\",\n \"type\": "
},
{
"path": "sdks/typescript/server/src/__tests__/index.test.ts",
"chars": 16780,
"preview": "import {\n createUIResource,\n sendExperimentalRequest,\n} from '../index';\nimport { UI_METADATA_PREFIX } from '../types."
},
{
"path": "sdks/typescript/server/src/__tests__/test-utils.ts",
"chars": 887,
"preview": "import { vi } from 'vitest';\n\n/**\n * Stubs the global `fetch` to return a successful response with a streaming body\n * c"
},
{
"path": "sdks/typescript/server/src/__tests__/utils.test.ts",
"chars": 12577,
"preview": "import { describe, it, expect, vi } from 'vitest';\nimport {\n extractOrigin,\n fetchExternalUrl,\n getAdditionalResource"
},
{
"path": "sdks/typescript/server/src/index.ts",
"chars": 6985,
"preview": "import {\n type Base64BlobContent,\n type CreateUIResourceOptions,\n type HTMLTextContent,\n type MimeType,\n RESOURCE_M"
},
{
"path": "sdks/typescript/server/src/types.ts",
"chars": 2051,
"preview": "import type { EmbeddedResource, Resource } from '@modelcontextprotocol/sdk/types.js';\n\n// Re-export constants from the o"
},
{
"path": "sdks/typescript/server/src/utils.ts",
"chars": 7554,
"preview": "import type { CreateUIResourceOptions, UIResourceProps } from './types.js';\nimport { UI_METADATA_PREFIX } from './types."
},
{
"path": "sdks/typescript/server/tsconfig.json",
"chars": 198,
"preview": "{\n \"extends\": \"../../../tsconfig.base.json\",\n \"compilerOptions\": {\n \"forceConsistentCasingInFileNames\": true\n },\n "
},
{
"path": "sdks/typescript/server/vite.config.ts",
"chars": 678,
"preview": "import { defineConfig } from 'vite';\nimport dts from 'vite-plugin-dts';\nimport path from 'path';\nimport react from '@vit"
},
{
"path": "sdks/typescript/server/vitest.config.ts",
"chars": 375,
"preview": "import { defineConfig } from 'vitest/config';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';"
},
{
"path": "sdks/typescript/shared/package.json",
"chars": 558,
"preview": "{\n \"name\": \"@mcp-ui/shared\",\n \"version\": \"0.1.0\",\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist"
},
{
"path": "sdks/typescript/shared/src/index.ts",
"chars": 0,
"preview": ""
},
{
"path": "sdks/typescript/shared/tsconfig.json",
"chars": 220,
"preview": "{\n \"extends\": \"../../../tsconfig.base.json\",\n \"compilerOptions\": {\n \"baseUrl\": \"src\",\n \"paths\": {\n \"@/*\": ["
},
{
"path": "sdks/typescript/shared/vite.config.ts",
"chars": 779,
"preview": "import { defineConfig } from 'vite';\nimport dts from 'vite-plugin-dts';\nimport react from '@vitejs/plugin-react-swc';\nim"
},
{
"path": "tsconfig.base.json",
"chars": 488,
"preview": "{\n \"compilerOptions\": {\n \"composite\": true,\n \"declaration\": true,\n \"declarationMap\": true,\n \"module\": \"ESNe"
},
{
"path": "vitest.config.ts",
"chars": 822,
"preview": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n test: {\n globals: true, // Use global "
},
{
"path": "vitest.setup.ts",
"chars": 36,
"preview": "import '@testing-library/jest-dom';\n"
}
]
About this extraction
This page contains the full source code of the MCP-UI-Org/mcp-ui GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 165 files (833.0 KB), approximately 224.1k tokens, and a symbol index with 716 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.