MCP server that loads ChatKit widgets from a directory and registers them as tools for AI agents to invoke and generate ChatKit widgets.
Project description
ChatKit Widget MCP Server
A Model Context Protocol (MCP) server that automatically transforms ChatKit Studio widget definitions into callable MCP tools, enabling AI agents to dynamically generate rich, interactive UI components that can be rendered in the ChatKit UI.
Purpose
mcp-chatkit-widget bridges the gap between static UI component definitions and runtime tool invocation for AI agents. It automatically converts ChatKit Studio .widget files into MCP tools that agents can call to generate interactive widgets.
Key Features
- Automatic Tool Generation: Converts every
.widgetfile into an MCP tool with type-safe input validation - Dynamic Schema Conversion: Transforms JSON Schema definitions into Pydantic models for runtime validation
- Template Rendering: Uses Jinja2 to render widget templates with validated data
- Rich Widget Library: Includes 16 pre-built widgets from ChatKit Studio's gallery (flight tracker, weather, email composer, etc.), extendable with custom widgets
- Type Safety: Full type annotations and validation using Pydantic v2
- Curated Discovery Guardrails: Requires passing an explicit
widgets_dirso only curated definitions are discovered and registered, preventing accidental loads from arbitrary paths
How It Works
- Widget Discovery: Requires the CLI
--widgets-dirargument to point at the curated directory; loading fails fast if the argument is missing or invalid. - Schema Parsing: Extracts JSON Schema and Jinja2 templates from widget definitions
- Model Generation: Creates dynamic Pydantic models for input validation
- Tool Registration: Registers MCP tools with FastMCP server
- Runtime Execution: Validates inputs, renders templates, and returns ChatKit widget components
Installation
From PyPI
uv add mcp-chatkit-widget
From Source (Development)
# Clone the repository
git clone https://github.com/ShaojieJiang/mcp-chatkit-widget.git
cd mcp-chatkit-widget
# Install with uv (recommended)
uv sync --all-groups
# Or with pip
pip install -e ".[dev,docs]"
Requirements
- Python 3.12 or higher
- FastMCP >= 2.13.0.2
- OpenAI ChatKit >= 1.1.0
Usage
Running the MCP Server
Start the server with the required --widgets-dir argument that points to your curated
widget directory:
uv run mcp-chatkit-widget --widgets-dir /path/to/widgets
Point to examples/widgets to expose the built-in definitions or supply your own
curated .widget directory.
Integrating with MCP Clients
Claude Desktop
Add the server to your Claude Desktop configuration (claude_desktop_config.json):
{
"mcpServers": {
"chatkit-widget": {
"command": "/path/to/uvx",
"args": [
"--from",
"mcp-chatkit-widget@latest",
"mcp-chatkit-widget",
"--widgets-dir",
"/path/to/widgets"
]
}
}
}
LangGraph Agents
from langgraph.prebuilt import create_react_agent
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
# Connect to the MCP server
server_params = StdioServerParameters(
command="mcp-chatkit-widget",
args=[]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# Initialize session
await session.initialize()
# List available tools
tools_result = await session.list_tools()
print(f"Available widgets: {[tool.name for tool in tools_result.tools]}")
# Create agent with widget tools
agent = create_react_agent(model, tools=tools_result.tools)
Direct Tool Invocation
from pathlib import Path
from mcp_chatkit_widget.server import register_widget_tools, server
# Load the curated widgets directory so the FastMCP tool manager
# knows about the bundled widget tools before invocation.
widgets_dir = Path(__file__).resolve().parents[1] / "examples" / "widgets"
register_widget_tools(widgets_dir)
# Example: Generate a flight tracker widget
flight_widget = server.call_tool(
"flight_tracker",
arguments={
"number": "PA 845",
"date": "Fri, Apr 25",
"progress": "60",
"airline": {
"name": "Pan American Airways",
"logo": "/panam_logo.png"
},
"departure": {
"airport": "SFO",
"city": "San Francisco",
"time": "10:30 AM"
},
"arrival": {
"airport": "JFK",
"city": "New York",
"time": "7:45 PM"
}
}
)
# `result.content[0].text` is the JSON string of the ChatKit WidgetComponentBase instance
print(result.content[0].text)
Importable Helpers
The package exports helpers so scripts, demos, and tests can reuse the same rendering pipeline without running the full MCP server.
load_widgets(widgets_dir: Path)enforces the curated directory, loads.widgetfiles, and raises when the path is missing, invalid, or contains malformed templates.render_widget_definition(widget_def, **kwargs)validates inputs against the schema-backed Pydantic model, renders the stored template, and returns aWidgetComponentBasethat mirrors the preview payload.generate_widget_tools(server, widget_defs)registers sanitized tools on your FastMCP-like server so you can reuse the same helpers elsewhere.
from pathlib import Path
from mcp_chatkit_widget import (
generate_widget_tools,
load_widgets,
render_widget_definition,
)
widgets = load_widgets(Path("/path/to/widgets"))
widget = render_widget_definition(widgets[0], title="Hello")
# Optionally wire the helpers into your own FastMCP server.
generate_widget_tools(custom_server, widgets)
Inspecting tool output
FastMCP returns the JSON emitted by the widget template's .build() helper, so the response already matches the ChatKit schema. Scripts such as examples/run_widget/run_widget.py show how to print that JSON and summarize it via display_widget_payload. There is no need to instantiate ChatKit classes manually—the helpers always re-render widgets through render_widget_definition, so the payload you see is the canonical structure the server would send to agents.
Available Widgets
The server includes 16 pre-built widgets:
- Communication: Channel Message, Draft Email
- Travel: Flight Tracker, Ride Status
- Events: Create Event, View Event, Event Session
- Tasks: Create Task, Enable Notification
- Entertainment: Player Card, Playlist
- Weather: Weather Current, Weather Forecast
- Shopping: Purchase Complete, Software Purchase, Purchase Items
Each widget automatically becomes an MCP tool named in snake_case (e.g., "Flight Tracker" → flight_tracker).
Adding Custom Widgets
- Export a
.widgetfile from ChatKit Studio - Place the
.widgetfile into a curated directory that you control - Start the MCP server with
--widgets-dirpointing to that directory
The loader only inspects the directory passed via --widgets-dir, so all
discovered widgets are explicitly approved by your deployment workflow. Use
examples/widgets as the argument when you want to boot the packaged
definitions, or swap in a custom directory to opt in to bespoke widgets.
Architecture
flowchart TD
A["MCP Client<br/>(Claude, LangGraph, Custom Agents)<br/>(AI Agent)"]
B["FastMCP Server<br/><code>mcp-chatkit-widget</code>"]
B1["Widget Loader<br/><small>Discovers *.widget files<br/>Parses JSON definitions</small>"]
B2["Schema & Rendering<br/><small>JSON Schema → Pydantic models<br/>WidgetTemplate .build() → WidgetRoot</small>"]
B3["MCP Tools<br/><small>Registers tools dynamically<br/>flight_tracker, weather_current, etc.</small>"]
C["ChatKit Widget<br/>(JSON Structure)"]
A -->|"MCP Protocol (JSON-RPC)"| B
B --> B1 --> B2 --> B3 --> C
Data Flow
- Startup: Server discovers all
.widgetfiles - Registration: Each widget becomes an MCP tool with validated schema
- Invocation: Agent calls tool with parameters
- Validation: Pydantic model validates input data
- Rendering: Jinja2 template renders with validated data
- Construction:
render_widget_definitioninvokes the template's.build()so the rendered JSON becomes aWidgetRootthat matches the preview hierarchy - Return: Widget instance sent back to agent
Development
Running Tests
Always lint and test through the project-managed environment before merging:
# Run linting and type checking
uv run make lint
# Run all tests with coverage
uv run make test
You can run targeted tests directly when experimenting:
pytest tests/test_server.py
pytest -v tests/
Code Quality
# Run linting and type checking
uv run make lint
# Auto-format code
uv run make format
# Type check only
uv run mypy mcp_chatkit_widget/
Building Documentation
# Serve documentation locally
uv run make doc
# Documentation will be available at http://0.0.0.0:8080
Project Layout
mcp-chatkit-widget/
├── mcp_chatkit_widget/
│ ├── __init__.py
│ ├── server.py # FastMCP server entrypoint
│ ├── widget_loader.py # Discovers .widget files
│ ├── schema_utils.py # JSON Schema → Pydantic helpers
│ ├── pydantic_conversion.py # Schema conversion helpers
│ ├── rendering.py # Jinja rendering helpers
│ ├── tooling.py # MCP tool registration utilities
│ ├── naming.py # Widget ⇄ tool name helpers
│ └── py.typed
├── examples/
│ ├── run_widget/ # Sample rendering scripts
│ └── widgets/ # Packaged widget definitions
├── custom_widgets/ # Optional curated widget sources
├── docs/
│ ├── release-notes.md
│ └── plan.md
├── tests/
│ ├── test_server.py
│ ├── test_tooling.py
│ ├── test_rendering.py
│ ├── schema_utils/
│ ├── widget_loader/
│ └── widget_integration/
├── Makefile
├── mkdocs.yml
├── pyproject.toml
├── uv.lock
├── langgraph.json
├── LICENSE.txt
└── README.md
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Run tests and linting (
make test && make lint) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE.txt file for details.
Resources
- ChatKit Studio - Create and export widget definitions
- Model Context Protocol - MCP specification
- FastMCP - High-level MCP server framework
- OpenAI ChatKit Python SDK - Widget component library
Support
- Issues: GitHub Issues
- Documentation: docs/
Project details
Release history Release notifications | RSS feed
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_chatkit_widget-0.2.0.tar.gz.
File metadata
- Download URL: mcp_chatkit_widget-0.2.0.tar.gz
- Upload date:
- Size: 162.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d4f4284adbebe148d8fedbe1312931eb703bdaf44a56c58db58132108c0ad395
|
|
| MD5 |
0e2c5324f0df0dfc859bee574db1ba4f
|
|
| BLAKE2b-256 |
f75a03674a3a7cfc0c94ee87b3ae29240a58df521df877f0cc3e536bc51829fb
|
Provenance
The following attestation bundles were made for mcp_chatkit_widget-0.2.0.tar.gz:
Publisher:
ci.yml on ShaojieJiang/mcp-chatkit-widget
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_chatkit_widget-0.2.0.tar.gz -
Subject digest:
d4f4284adbebe148d8fedbe1312931eb703bdaf44a56c58db58132108c0ad395 - Sigstore transparency entry: 759783585
- Sigstore integration time:
-
Permalink:
ShaojieJiang/mcp-chatkit-widget@58d29e421725649e8426275ff54eb9f31b3b994a -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/ShaojieJiang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@58d29e421725649e8426275ff54eb9f31b3b994a -
Trigger Event:
push
-
Statement type:
File details
Details for the file mcp_chatkit_widget-0.2.0-py3-none-any.whl.
File metadata
- Download URL: mcp_chatkit_widget-0.2.0-py3-none-any.whl
- Upload date:
- Size: 14.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbdcf8a71ddec57149600c853709862641b3a07f64df2c6a2c981472aa5bcda8
|
|
| MD5 |
7b462d9cc7020f7b5cc505c45495325d
|
|
| BLAKE2b-256 |
0ab936c2c9fd034a2ec7864cecdd2af34b530565e018094ece9af60a798406a2
|
Provenance
The following attestation bundles were made for mcp_chatkit_widget-0.2.0-py3-none-any.whl:
Publisher:
ci.yml on ShaojieJiang/mcp-chatkit-widget
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_chatkit_widget-0.2.0-py3-none-any.whl -
Subject digest:
cbdcf8a71ddec57149600c853709862641b3a07f64df2c6a2c981472aa5bcda8 - Sigstore transparency entry: 759783587
- Sigstore integration time:
-
Permalink:
ShaojieJiang/mcp-chatkit-widget@58d29e421725649e8426275ff54eb9f31b3b994a -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/ShaojieJiang
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@58d29e421725649e8426275ff54eb9f31b3b994a -
Trigger Event:
push
-
Statement type: