| name | castella-mcp |
| description | Enable AI agents to introspect and control Castella UIs via MCP. Create MCP servers, expose UI resources, handle MCP tools, and use semantic IDs. |
Castella MCP Integration
MCP (Model Context Protocol) enables AI agents to introspect and control Castella UIs programmatically. This provides a standard protocol for AI-UI interaction.
When to use: "enable MCP for Castella", "MCP server", "semantic ID", "MCP resources", "MCP tools", "SSE transport", "CastellaMCPServer", "control UI with MCP"
Quick Start
Create an MCP-enabled Castella app:
from castella import App, Column, Button, Input, Text
from castella.frame import Frame
from castella.mcp import CastellaMCPServer
ui = Column(
Text("Hello MCP!").semantic_id("greeting"),
Input("").semantic_id("name-input"),
Button("Submit").semantic_id("submit-btn"),
)
app = App(Frame("MCP Demo", 800, 600), ui)
mcp = CastellaMCPServer(app, name="my-castella-app")
mcp.run_in_background()
app.run()
Installation
uv sync --extra mcp
Semantic IDs
Assign stable, human-readable identifiers to widgets:
Button("Submit").semantic_id("submit-btn")
Input("").semantic_id("email-input")
CheckBox(state).semantic_id("newsletter-checkbox")
Text("Status").semantic_id("status-text")
Auto-generated IDs (if not specified): button_0, input_1, etc.
Best Practices for Semantic IDs
- Use descriptive names:
submit-form-btn, not btn1
- Use kebab-case:
user-name-input
- Include widget type:
email-input, save-btn
- Match action/purpose:
login-btn, search-input
MCP Resources
Read-only data available to AI agents:
| URI | Description |
|---|
ui://tree | Complete UI tree structure |
ui://focus | Currently focused element |
ui://elements | All interactive elements |
ui://element/{id} | Specific element details |
a2ui://surfaces | A2UI surfaces (if A2UI enabled) |
Example: UI Tree Resource
{
"type": "tree",
"root": {
"id": "root",
"type": "Column",
"children": [
{"id": "greeting", "type": "Text", "value": "Hello MCP!"},
{"id": "name-input", "type": "Input", "value": "", "interactive": true},
{"id": "submit-btn", "type": "Button", "label": "Submit", "interactive": true}
]
}
}
MCP Tools
Actions AI agents can perform:
| Tool | Description | Parameters |
|---|
click | Click/tap element | element_id |
type_text | Type into input | element_id, text, replace |
focus | Set focus | element_id |
scroll | Scroll container | element_id, direction, amount |
toggle | Toggle checkbox/switch | element_id |
select | Select in picker/tabs | element_id, value |
list_actionable | List interactive elements | - |
send_a2ui | Send A2UI message | message |
Tool Examples
click(element_id="submit-btn")
type_text(element_id="name-input", text="Alice", replace=True)
type_text(element_id="name-input", text=" Smith", replace=False)
toggle(element_id="newsletter-checkbox")
select(element_id="main-tabs", value="settings")
scroll(element_id="message-list", direction="down", amount=100)
Transports
stdio (Default)
For MCP clients that communicate via stdin/stdout:
mcp = CastellaMCPServer(app, name="my-app")
mcp.run_in_background()
SSE (HTTP)
For HTTP-based MCP clients (Claude Desktop, web clients):
mcp = CastellaMCPServer(app, name="my-app")
mcp.run_sse_in_background(host="localhost", port=8765)
SSE endpoints:
GET /sse - SSE event stream
POST /message - Send MCP messages
GET /health - Health check
Example: MCP Client (Python)
Control a Castella app via HTTP:
import json
import urllib.request
def call_tool(name: str, **kwargs) -> dict:
message = {
"type": "call_tool",
"params": {"name": name, "arguments": kwargs}
}
data = json.dumps(message).encode("utf-8")
req = urllib.request.Request(
"http://localhost:8765/message",
data=data,
headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(req) as response:
return json.loads(response.read())
call_tool("type_text", element_id="name-input", text="Alice", replace=True)
call_tool("click", element_id="submit-btn")
call_tool("toggle", element_id="newsletter-checkbox")
result = call_tool("list_actionable")
print(result)
A2UI + MCP Integration
Combine A2UI rendering with MCP control:
from castella.a2ui import A2UIRenderer, A2UIComponent
from castella.mcp import CastellaMCPServer
renderer = A2UIRenderer(on_action=on_action)
renderer.render_json(a2ui_json)
surface = renderer.get_surface("default")
app = App(Frame("A2UI + MCP", 800, 600), A2UIComponent(surface))
mcp = CastellaMCPServer(app, a2ui_renderer=renderer)
mcp.run_sse_in_background(port=8766)
app.run()
A2UI component IDs automatically become MCP semantic IDs.
send_a2ui Tool
When A2UI renderer is provided, the send_a2ui tool becomes available:
send_a2ui(message={
"updateDataModel": {
"surfaceId": "default",
"data": {"/counter": 42}
}
})
API Reference
CastellaMCPServer
from castella.mcp import CastellaMCPServer
mcp = CastellaMCPServer(
app=app,
name="my-app",
version="1.0.0",
a2ui_renderer=None,
)
mcp.run()
mcp.run_sse(host, port)
mcp.run_in_background()
mcp.run_sse_in_background(host, port)
mcp.refresh_registry()
mcp.stop()
ElementInfo
Information about a UI element:
element = {
"id": "submit-btn",
"type": "Button",
"label": "Submit",
"value": None,
"bounds": {"x": 10, "y": 100, "width": 80, "height": 40},
"interactive": True,
"focused": False,
}
Best Practices
- Use descriptive semantic IDs for all interactive elements
- Refresh registry after major UI changes:
mcp.refresh_registry()
- Use SSE transport for remote/HTTP clients
- Combine with A2UI for full agent-UI integration
- Handle errors in tool calls gracefully
Reference
references/resources.md - Complete resource URI reference
references/tools.md - Complete tool reference
references/types.md - ElementInfo, UITreeNode types
scripts/ - Executable examples (mcp_basic.py, mcp_sse.py)