A lightweight, minimal-dependency embedded Web UI for any MCP Server.
Project description
mcp-embedded-ui (Python)
The Python implementation of mcp-embedded-ui — a browser-based tool explorer for any MCP (Model Context Protocol) server.
What is this?
If you build an MCP server in Python, your users interact with tools through raw JSON — no visual feedback, no schema browser, no quick way to test. This library adds a full browser UI to your server with one import and one mount.
┌───────────────────────────────────┐
│ Browser │
│ Tool list → Schema → Try it │
└──────────────┬────────────────────┘
│ HTTP / JSON
┌──────────────▼────────────────────┐
│ Your Python MCP Server │
│ + mcp-embedded-ui │
│ (FastAPI / Starlette / ASGI) │
└───────────────────────────────────┘
What does the UI provide?
- Tool list — browse all registered tools with descriptions and annotation badges
- Schema inspector — expand any tool to view its full JSON Schema (
inputSchema) - Try-it console — type JSON arguments, execute the tool, see results instantly
- cURL export — copy a ready-made cURL command for any execution
- Auth support — enter a Bearer token in the UI, sent with all requests
No build step. No CDN. No external dependencies. The entire UI is a single self-contained HTML page embedded in the package.
Install
pip install mcp-embedded-ui
Requires Python 3.10+ and Starlette >= 0.35.
Quick Start
FastAPI / Starlette
from fastapi import FastAPI
from mcp_embedded_ui import create_mount
app = FastAPI()
# Mount at /explorer (default)
app.routes.append(create_mount(tools=my_tools, handle_call=my_handler))
# Or specify a custom prefix
app.routes.append(create_mount("/mcp-ui", tools=my_tools, handle_call=my_handler))
# Visit http://localhost:8000/explorer/
Any ASGI framework
from mcp_embedded_ui import create_app
# Returns a standard ASGI app — mount in any ASGI-compatible framework
ui_app = create_app(tools=my_tools, handle_call=my_handler)
Full working example
from fastapi import FastAPI
from mcp_embedded_ui import create_mount
# 1. Define your tools (any object with .name, .description, .inputSchema)
class MyTool:
def __init__(self, name, description, input_schema):
self.name = name
self.description = description
self.inputSchema = input_schema
tools = [
MyTool("greet", "Say hello", {
"type": "object",
"properties": {"name": {"type": "string"}},
}),
]
# 2. Define a handler: (name, args) -> (content, is_error, trace_id)
async def handle_call(name, args):
if name == "greet":
return [{"type": "text", "text": f"Hello, {args.get('name', 'world')}!"}], False, None
return [{"type": "text", "text": f"Unknown tool: {name}"}], True, None
# 3. Mount the UI
app = FastAPI()
app.routes.append(create_mount(tools=tools, handle_call=handle_call))
Dynamic tools
# Sync callable — re-evaluated on every request
def get_tools():
return registry.list_tools()
# Async callable
async def get_tools():
return await registry.async_list_tools()
app = create_app(tools=get_tools, handle_call=my_handler)
API
Three-tier API
| Function | Returns | Use case |
|---|---|---|
create_mount(prefix, *, tools, handle_call, **config) |
Mount |
FastAPI / Starlette — mount under a URL prefix |
create_app(tools, handle_call, **config) |
ASGIApp |
Any ASGI framework — standalone app |
build_ui_routes(tools, handle_call, **config) |
list[Route] |
Power users — fine-grained route control |
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
tools |
list | Callable | AsyncCallable |
required | MCP Tool objects (.name, .description, .inputSchema) |
handle_call |
ToolCallHandler |
required | async (name, args) -> (content, is_error, trace_id) |
allow_execute |
bool |
True |
Enable/disable tool execution (enforced server-side) |
auth_hook |
AuthHook | None |
None |
Sync/async context manager factory for auth |
title |
str |
"MCP Tool Explorer" |
Page title (HTML-escaped automatically) |
Auth Hook
The auth_hook receives a Starlette Request and returns a context manager (sync or async). Raise inside to reject with 401. The error response is always {"error": "Unauthorized"} — internal details are never leaked.
from contextlib import contextmanager
@contextmanager
def my_auth(request):
token = request.headers.get("Authorization")
if not valid(token):
raise ValueError("Bad token")
my_identity_var.set(decode(token))
yield
Auth only guards POST /tools/{name}/call. Discovery endpoints (GET /tools, GET /tools/{name}, GET /meta) are always public.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | / |
Self-contained HTML explorer page |
| GET | /meta |
JSON config — { title, allow_execute } |
| GET | /tools |
Summary list of all tools |
| GET | /tools/{name} |
Full tool detail with inputSchema |
| POST | /tools/{name}/call |
Execute a tool, returns MCP CallToolResult |
Development
# Install in editable mode with dev dependencies
pip install -e ".[dev]"
# Run the demo
python examples/fastapi_demo.py
# Visit http://localhost:8000/explorer/
# Run tests
pytest
Cross-Language Specification
This package implements the mcp-embedded-ui specification. The spec repo contains:
- PROTOCOL.md — endpoint spec, data shapes, security checklist
- explorer.html — shared HTML template (identical across all language implementations)
- Feature specs — detailed requirements and test criteria
License
Apache-2.0
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_embedded_ui-0.1.0.tar.gz.
File metadata
- Download URL: mcp_embedded_ui-0.1.0.tar.gz
- Upload date:
- Size: 16.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b282039ae94d4481a2cb25fbbbdd75b22dbea9dfad5c717ea6e53e2f3a69bcfb
|
|
| MD5 |
ad1330409e96b745459e778cfd2fae39
|
|
| BLAKE2b-256 |
44a0af9f7cdae32b8cb42e3b041e8d4a27b0c4c93de0bdf6dfb7c83342fe55a4
|
File details
Details for the file mcp_embedded_ui-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mcp_embedded_ui-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45f562d1c23a867951d8b0b0280728c610c2f245a2bace1af3785d312244811a
|
|
| MD5 |
37728958671d627181ab27961e87a0c2
|
|
| BLAKE2b-256 |
ea17574182311a528c23e5bf9e53094d93cee81e36028673a18ae4243f3f7c9e
|