Skip to main content

nimblebrain-synapse (Python)

The server half of the Synapse cross-host UI framework. Pairs with the @nimblebrain/synapse client (connectUI / window.SynapseUI).

pip install nimblebrain-synapse   # or: uv add nimblebrain-synapse

One SynapseUI declaration serves a self-contained HTML component to every MCP Apps host — ChatGPT, Claude, and the NimbleBrain runtime — replacing the per-app hand-rolled shim. It is an MCP extension (SEP-2133): hand the instance to MCPServer and it contributes its ui:// resource and its tools/call interceptor.

from mcp.server.mcpserver import MCPServer
from nimblebrain_synapse import SynapseUI

report_ui = SynapseUI(
    uri="ui://bassethound/report",
    template=load_template(),            # data-free HTML (carries the SDK + data markers)
    preferred_size=("100%", "auto"),
)
mcp = MCPServer("bassethound", extensions=[report_ui])

@mcp.tool(meta=report_ui.tool_meta(invoking="Picking up the scent…", invoked="Dossier ready"))
async def analyze_domain(domain: str) -> Dossier:
    ...

Passing the instance in extensions= serves one data-free ui:// resource under text/html;profile=mcp-app and installs the tools/call interceptor. tool_meta on the tool descriptor carries the binding, ui.resourceUri; ChatGPT and Claude both resolve it, read the resource and render the component from the result's structuredContent, with no UI HTML in the result content. Plain MCP clients ignore the _meta and still read structuredContent.

bind(tool, embed_resource=True) additionally bakes the rendered component (dossier in a <script>) into that tool's result content, for a host that renders solely from an embedded copy. It is off by default so that audience: ["user"] HTML can't leak into a client that won't render it. bind may be called before or after the server is constructed; the resource is built at construction, so a SynapseUI is fully formed before MCPServer reads it.

The keys each input reaches

Each input is declared once. The ext-apps ui.* key is the one the spec defines, and the one every host reads.

Input ext-apps key ChatGPT key
the component tool ui.resourceUri —
tool_meta(visibility=) tool ui.visibility openai/widgetAccessible; openai/visibility: "private" when "model" is absent
connect_domains, resource_domains resource ui.csp —
always true resource ui.prefersBorder —
mcp_app_domain resource ui.domain —
widget_domain — resource openai/widgetDomain, only when given
tool_meta(invoking=, invoked=) — tool openai/toolInvocation/*, only when given

ChatGPT enforces ui.visibility in developer mode. The visibility aliases stay because whether a reviewed, directory-listed app enforces it the same way has not been measured, and they are derived from ui.visibility, so they cannot disagree with it.

The two origins stay separate inputs because the hosts disagree on the value: ChatGPT takes an origin the developer declares, while Claude derives sha256(<connector URL>)[:32] + ".claudemcpcontent.com" and rejects anything else. Leave both unset unless the component needs a stable origin. Every host reads ui.domain from the one resource, so mcp_app_domain reaches ChatGPT too: set it only when each host the server targets accepts that value.

tool_meta(widget_accessible=) still works and warns: it is visibility=["model", "app"] or visibility=["model"].

Sign-in

A server that requires auth declares it on each tool, and answers a call it cannot authorize with a result carrying the challenge. ChatGPT needs both to offer sign-in mid-conversation (Apps SDK auth guide).

from typing import Annotated

from mcp.types import CallToolResult
from nimblebrain_synapse import auth_error_result

@mcp.tool(meta=report_ui.tool_meta(security_schemes=[{"type": "oauth2", "scopes": ["report.read"]}]))
async def analyze_domain(domain: str) -> Annotated[CallToolResult, Dossier]:
    if not signed_in():
        return auth_error_result(
            resource_metadata="https://example.com/.well-known/oauth-protected-resource",
            error="insufficient_scope",
            description="Sign in to analyze a domain",
        )
    return build_dossier(domain)  # a Dossier still becomes structuredContent

securitySchemes rides the tool's _meta, which ChatGPT documents as the mirror for clients that read only _meta. The MCP SDK builds a tool descriptor from a fixed set of fields, so the top-level form cannot be emitted through it.

Template contract

The template is data-free HTML that carries two markers:

  • <!--__SYNAPSE_SDK__--> — replaced with the inlined client SDK <script>.
  • <script type="application/json" id="synapse-ui-data">/*__SYNAPSE_DATA__*/</script> — the data slot; render_html(data) substitutes the escaped payload here (the served copy leaves the marker, so the client reads null and falls back to the host's push).

SynapseUI._safe_json escapes the payload for <script> embedding (the XSS defense) — framework-owned and on by default.

Relationship to the SDK's own MCP Apps extension

SynapseUI advertises the spec's MCP Apps identifier, io.modelcontextprotocol/ui — that is the extension it implements, and the few ChatGPT keys above ride alongside in _meta keys the spec does not claim. So a server uses SynapseUI instead of mcp.server.apps.Apps, not next to it: two extensions cannot share an identifier.

One consequence: one SynapseUI per server. A second instance fails at server construction with Extension 'io.modelcontextprotocol/ui' is already registered.

Client SDK asset

nimblebrain_synapse/_assets/synapse-ui.iife.js is the vendored client IIFE (window.SynapseUI), regenerated from the JS build (dist/synapse-ui.iife.global.js) and inlined at register time so a component is fully self-contained (CSP-safe, no CDN). CI fails on drift from the build. nimblebrain_synapse.__client_version__ records which @nimblebrain/synapse release the bundled IIFE was built from.

Versioning & compatibility

nimblebrain-synapse (PyPI) versions independently of @nimblebrain/synapse (npm). They change for different reasons at different cadences — the server descriptor is thin and stable; the JS client evolves with host adapters and theming — so they do not share a version number. The exact client build a given release bundles is recorded in nimblebrain_synapse.__client_version__ (and, per release, in the CHANGELOG); CI keeps it equal to the sibling package.json at HEAD.

What both halves share is the wire protocol — the ui:// resource MIME, the _meta keys, and the data-element contract:

  • ext-apps 2026-01-26
  • MCP Apps (SEP-1865)

MCP SDK compatibility

This package requires mcp 2.x from 2.1 (mcp>=2.1.0,<3) and does not run on 1.x. It is built on two things that exist only in 2.x: the SEP-2133 extension interface (mcp.server.extension.Extension), and mcp.server.apps for the MCP Apps identifier and MIME. On 1.x the equivalent of intercept_tool_call did not exist, and this package reached into FastMCP's private handler registry to get one — so there is no shape that serves both majors, and the range is a single major rather than a span.

The floor is 2.1.0 because 2.0.0 validates an error result from a tool with structured output against its output schema and replaces it, dropping the challenge auth_error_result carries.

The upper bound is deliberate. mcp 2.0 reached fresh installs of this package through an uncapped >= floor and broke them, which is what the cap prevents from happening again at 3.0.

Releases publish on a nimblebrain-synapse-v* tag (distinct from the npm v* tags).

Metadata

Release files for nimblebrain-synapse 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nimblebrain-synapse 0.7.0
File Size Uploaded
nimblebrain_synapse-0.7.0.tar.gz 26.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nimblebrain-synapse 0.7.0
File Interpreter ABI Platform
nimblebrain_synapse-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 45.1 kB

Release files / nimblebrain_synapse-0.7.0.tar.gz

Download URL nimblebrain_synapse-0.7.0.tar.gz
Size 26.6 kB
Tags Source
SHA-256 checksum
How to use checksums
48c6cfc75c219076256ba9a559f85f371eae38914d81e93c986a6808165233dc
BLAKE2b-256 checksum
How to use checksums
867329297c02f2a525864ab7a24a3acd738d7ba80b4ef3ed1e3ef9914a86c138
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release files / nimblebrain_synapse-0.7.0-py3-none-any.whl

Download URL nimblebrain_synapse-0.7.0-py3-none-any.whl
Size 18.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b0eee9a4322a51df6237afdfc1b52c8783b455998f7da19e23a7fb4049d80cb6
BLAKE2b-256 checksum
How to use checksums
07bbecdf95c611c4576c63350a54061a5fd9635553da431cc1cede3fea0c1d98
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.0

2 release files

0.8.0

2 release files

0.7.1

2 release files

This release

0.7.0 This release

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page