Quickstart: Python server
Synapse has two halves. The React quickstart covers the
client. This one covers the server: nimblebrain-synapse, the PyPI package that
attaches a self-contained ui:// component to a FastMCP
server. From one declaration the component renders across ChatGPT (OpenAI Apps
SDK), Claude (MCP Apps), and the NimbleBrain runtime, with the client SDK inlined
so the component ships as one asset.
This walks you from an empty server to a tool whose result renders in a UI. It assumes you already have a FastMCP server, or can start one.
-
Install the package.
Terminal window pip install nimblebrain-synapse # or: uv add nimblebrain-synapseIt pairs with FastMCP (the
mcpPython package), which you use to define the server and its tools. -
Write a data-free template.
The component’s HTML carries no data. It carries two markers the package fills in: one for the inlined client SDK, one for the data slot.
TEMPLATE = """<div id="root"></div><!--__SYNAPSE_SDK__--><script type="application/json" id="synapse-ui-data">/*__SYNAPSE_DATA__*/</script>"""<!--__SYNAPSE_SDK__-->is replaced with the inlined client SDK<script>, so the component is self-contained (no CDN, CSP-safe). Thesynapse-ui-datascript is the data slot: the served copy leaves the marker in place, so the client readsnulland falls back to the host’s push, and a per-result copy substitutes the escaped payload there. -
Declare the component and register it.
from mcp.server.fastmcp import FastMCPfrom nimblebrain_synapse import SynapseUImcp = FastMCP("weather")forecast_ui = SynapseUI(uri="ui://weather/forecast",template=TEMPLATE,preferred_size=("100%", "auto"),)forecast_ui.register(mcp)registerserves theui://resource with the client SDK inlined.uriis the resource id the host loads;preferred_sizeis the size hint the component asks for. -
Add a tool and bind the UI to it.
@mcp.tool(meta=forecast_ui.tool_meta(invoking="Checking the forecast", invoked="Forecast ready"),)async def get_forecast(city: str) -> dict:return {"city": city, "high": 78, "low": 61, "summary": "Sunny"}forecast_ui.bind(mcp, tool="get_forecast", should_render=lambda d: "city" in d)tool_metaattaches the status strings the host shows while the tool runs and after it finishes, plus the metadata that points the tool at this component.bindpost-processes the tool’sCallToolResult: it appends the embeddedui://resource (the data baked into a<script>) and mirrors the ChatGPT_meta.should_renderreceives the tool’s output and decides whether to render the UI for that call. -
Run the server. Start it however you normally run FastMCP (stdio or HTTP). In a host that supports embedded UIs, calling
get_forecastnow renders your component. A plain MCP client that renders no UI ignores the extra resource and still reads the tool’sstructuredContent, so the same server works everywhere.
What just happened
Section titled “What just happened”SynapseUIdescribed one component once: a URI, a data-free template, and a size hint.registerpublished it as aui://resource with the client SDK inlined, so the component is fully self-contained.tool_metaandbindwired the component to a tool, so its result is delivered both asstructuredContent(for any MCP client) and as a rendered UI (for hosts that support it).
Because the component is self-contained and the wiring is declarative, the same server renders across ChatGPT, Claude, and NimbleBrain without per-host code.
Related
Section titled “Related”- Cross-host architecture: how one component reaches three different host bridges.
- Quickstart: React app: the client half of the same app.
- Standalone and non-NimbleBrain hosts: what renders where.