Skip to main content

Belgie: The generative AI/UI sandbox for python

Belgie is a sandboxed TypeScript environment for Python that lets you build React MCP Apps and have agents write code in a sandbox.

  • MCP Apps: Attach React widgets to Python MCP tools in one project.
  • AI agents: Sandboxed run_code so Pydantic AI and LangChain can run TypeScript and TSX.
  • Inline React widgets: Return self-contained HTML from run_code with @belgie/render.
  • Sandbox: Deno is bundled, so you do not need to install Node.js.

Installation

uv add belgie
uvx library-skills install  # optional: install the use-belgie skill for Cursor, Codex, Claude, etc.

For MCP Apps, install the MCP and CLI extras:

uv add "belgie[mcp,cli]"

MCP Apps

Skip the second package manager. Attach a React widget to a Python MCP tool. BelgieExtension starts Vite in the background for development and runs a one-time production build.

from datetime import UTC, datetime
from pathlib import Path

from mcp.server import MCPServer

from belgie.mcp import BelgieExtension

belgie = BelgieExtension(project=".")


@belgie.tool(
    widget=Path("src/widgets/get-time/widget.tsx"),
    name="get-time",
    title="Get Time",
    description="Get the current server time in ISO 8601 format.",
)
def get_time() -> dict[str, str]:
    return {"time": datetime.now(tz=UTC).isoformat()}


mcp = MCPServer(name="Get Time Server", extensions=[belgie])

The widget is a normal React entry. @belgie/mcp connects the MCP Apps host and surfaces the opening tool result:

import { Widget, useToolResult } from "@belgie/mcp";
import { getTime } from "@widgets/tools";

function AppView() {
  const { data, isLoading, execute } = useToolResult(getTime);
  return (
    <main>
      <p>{data?.time ?? (isLoading ? "Waiting..." : "No time returned.")}</p>
      <button onClick={() => void execute()}>Refresh</button>
    </main>
  );
}

export default function GetTime() {
  return (
    <Widget metadata={{ name: "Get Time", version: "1.0.0" }}>
      <AppView />
    </Widget>
  );
}

Declare JS deps under [tool.belgie.dependencies], then:

uv run belgie lock
uv run belgie install
# start your MCP server; Belgie starts Vite with widget HMR

Pass build=False to BelgieExtension when Vite is managed separately or production assets are already built.

Runnable projects:

  • mcp: Minimal MCP Apps widget.
  • shadcn: Same pattern with Tailwind CSS and shadcn/ui.
  • tanstack: TanStack Start SPA and MCP widget served together through FastAPI.

AI agents

When an agent needs an npm package, a browser-style API, or a JS-side transform, give it run_code. Belgie executes the TypeScript, JavaScript, or TSX in the embedded Deno sandbox. No separate Node install.

Pydantic AI

Install with uv add "belgie[pydantic-ai]", set OPENAI_API_KEY, then:

from pydantic_ai import Agent

from belgie.pydantic_ai import BelgieCapability

agent = Agent("openai:gpt-5", capabilities=[BelgieCapability()])

result = agent.run_sync(
    "Convert 'foo-bar' to camelCase using TypeScript and the camelcase npm package.",
)
print(result.output)

See examples/ai/pydantic-ai.

LangChain

Install with uv add "belgie[langchain]", set OPENAI_API_KEY, then:

from langchain.agents import create_agent

from belgie.langchain import BelgieMiddleware

agent = create_agent(
    model="openai:gpt-5",
    tools=[],
    middleware=[BelgieMiddleware()],
    system_prompt="You can execute JS/TS in a Deno sandbox with run_code.",
)

result = agent.invoke(
    {
        "messages": [
            (
                "user",
                "Convert 'foo-bar' to camelCase using TypeScript and the camelcase npm package.",
            ),
        ],
    },
)
print(result["messages"][-1].content)

See examples/ai/langchain.

Under the hood: Deno in Python

MCP Apps and agent run_code both use Belgie’s embedded Deno runtime. Call it directly when you need JS/TS from Python without MCP or an agent framework:

  • Scripts: Inline or file-based JS/TS with Runtime and Script, sync or async.
  • Inline dependencies: Import npm, JSR, and URL modules from source.
  • Environments: Lockfiles, custom cache/options, local packages, and Command for npm binaries (Vite, esbuild, etc.).
  • Data bridge: Pass JSON-safe dicts, lists, and primitives across the boundary.

Runtime permissions

RuntimePermissions gates Deno APIs and every host-backed module read, including static and dynamic imports, JSON modules, and Node require(). File entrypoints created with Script.from_file and command entrypoints must be covered by allow_read; inline and in-memory sources do not need a host read grant. Belgie-managed npm packages are available to the module loader without adding their node_modules or cache roots to the runtime's general read grants. Package imports therefore work in restricted runtimes, while Deno.readFile, arbitrary absolute file: URLs, and other direct host reads remain subject to the caller's allow_read and deny_read settings.

import asyncio

from belgie import Runtime, Script

script = Script[[str], str](
    """
import camelcase from "npm:camelcase@8.0.0";

export default function run(input: string): string {
  return camelcase(input);
}
"""
)


async def main() -> None:
    async with Runtime() as run:
        print(await run(script)("foo-bar"))  # prints: fooBar


asyncio.run(main())

Inline widget rendering

Pydantic AI and LangChain agents can return a complete inline React widget through the same run_code tool. The agent writes one TSX module and imports the standalone renderer:

import { render } from "npm:@belgie/render";

function Widget() {
  return <main>Hello from Belgie</main>;
}

export default function run() {
  return render({
    widget: <Widget />,
    plugins: [],
  });
}

render() requests HTML from a Belgie-owned renderer side-channel (not from the model-visible Deno worker). The agent Script stays workspace-restricted — no host /etc//proc, allow_sys, or allow_ffi — while Vite runs only in that host-mediated worker (workspace FFI/sys/write, no host path grants) and returns one self-contained HTML string with inline JavaScript, CSS, and assets. Package imports are supported. Relative host-file imports are intentionally unavailable, and Vite plugins run only during the server build, where their factories, hooks, and imports have the renderer worker's broader permissions. Treat them as reviewed application code and use plugins: [] for untrusted agents. Plugin-only imports are removed from the browser bundle. This API is independent from @belgie/mcp and its path-based widget.tsx development and production flow.

Examples

Small, runnable projects. Each focuses on one capability.

UI

  • mcp: MCP Apps extension with a React widget built through Belgie.
  • shadcn: MCP Apps widget styled with Tailwind CSS and shadcn/ui.
  • tanstack: TanStack Start SPA and MCP widget served together through FastAPI.

AI

  • pydantic-ai: Pydantic AI agent with BelgieCapability() for sandboxed JS/TS/TSX execution.
  • langchain: LangChain agent with BelgieMiddleware() for sandboxed JS/TS/TSX execution.

Basic

  • simple: Async Runtime with a TypeScript file on disk.
  • inline-deps: Direct npm:, jsr:, and URL imports in a script.
  • jsr-deps: JSR packages declared through an explicit Environment.
  • pyproject: Manage project package dependencies with belgie[cli] and [tool.belgie.dependencies].
  • environment: Sync and async Environment setup with path.
  • commands: npm package binaries via Runtime and Command.

For deeper integration guidance, optionally install the use-belgie skill with uvx library-skills install.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

belgie-0.39.0.tar.gz (867.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

belgie-0.39.0-cp312-abi3-win_amd64.whl (54.2 MB view details)

Uploaded CPython 3.12+Windows x86-64

belgie-0.39.0-cp312-abi3-manylinux_2_28_x86_64.whl (61.3 MB view details)

Uploaded CPython 3.12+manylinux: glibc 2.28+ x86-64

belgie-0.39.0-cp312-abi3-manylinux_2_28_aarch64.whl (64.9 MB view details)

Uploaded CPython 3.12+manylinux: glibc 2.28+ ARM64

belgie-0.39.0-cp312-abi3-macosx_11_0_arm64.whl (55.4 MB view details)

Uploaded CPython 3.12+macOS 11.0+ ARM64

belgie-0.39.0-cp312-abi3-macosx_10_12_x86_64.whl (57.4 MB view details)

Uploaded CPython 3.12+macOS 10.12+ x86-64

File details

Details for the file belgie-0.39.0.tar.gz.

File metadata

  • Download URL: belgie-0.39.0.tar.gz
  • Upload date:
  • Size: 867.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for belgie-0.39.0.tar.gz
Algorithm Hash digest
SHA256 f7a8a73ee31855a42ceec15aefda200271ebc988ff2107a282c2e778c4db422c
MD5 8d3d735d65619c1b68b158cf11d94a39
BLAKE2b-256 bf130b445d8d1b49504664deb8bf05edcb640c687b25efcd5473b8c9f8078a46

See more details on using hashes here.

File details

Details for the file belgie-0.39.0-cp312-abi3-win_amd64.whl.

File metadata

  • Download URL: belgie-0.39.0-cp312-abi3-win_amd64.whl
  • Upload date:
  • Size: 54.2 MB
  • Tags: CPython 3.12+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for belgie-0.39.0-cp312-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 820fc7c08b0a52543a5c17c6db5d89d8ce16fbb100a9015ad634323be6ae30a6
MD5 3e278b8435002ecdcb35212cca301201
BLAKE2b-256 716dbe7d301a09d9477f6f2bf2c264dad2370c270e573d4626b57fd90a5ebc6f

See more details on using hashes here.

File details

Details for the file belgie-0.39.0-cp312-abi3-manylinux_2_28_x86_64.whl.

File metadata

  • Download URL: belgie-0.39.0-cp312-abi3-manylinux_2_28_x86_64.whl
  • Upload date:
  • Size: 61.3 MB
  • Tags: CPython 3.12+, manylinux: glibc 2.28+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for belgie-0.39.0-cp312-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 8e33f9a4ebb8e350c1092da43bb3f364ebeba8b66378ac6cb465d118ae97a9f1
MD5 90ab20011b156157143ff01ff35c9c28
BLAKE2b-256 62cf47543bce1c72b36f919b80736a0bde4547a20dd6c1d15cb480a28c6362ed

See more details on using hashes here.

File details

Details for the file belgie-0.39.0-cp312-abi3-manylinux_2_28_aarch64.whl.

File metadata

  • Download URL: belgie-0.39.0-cp312-abi3-manylinux_2_28_aarch64.whl
  • Upload date:
  • Size: 64.9 MB
  • Tags: CPython 3.12+, manylinux: glibc 2.28+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for belgie-0.39.0-cp312-abi3-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 0a0781228d8290b82867f29152523b92d49c83dd18392577bba2408dd0dab6b6
MD5 0a25f3c99f0aaa731d37a7548f6a3d63
BLAKE2b-256 4f0cf430b67aba2d2fcdc4a8ff78e56673a1827b7db6cfc18a9e4790a393fd73

See more details on using hashes here.

File details

Details for the file belgie-0.39.0-cp312-abi3-macosx_11_0_arm64.whl.

File metadata

  • Download URL: belgie-0.39.0-cp312-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 55.4 MB
  • Tags: CPython 3.12+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for belgie-0.39.0-cp312-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 14d28efdb2c272964e615cc8564e9fe655b61530ce0d70d464b5b9086c2b4294
MD5 508d742b0958c2b3aa39e8b26618fb37
BLAKE2b-256 72553262f39c0e080508e510a10e8def63aa71adc9277cfcdf0a005f2c11a2e9

See more details on using hashes here.

File details

Details for the file belgie-0.39.0-cp312-abi3-macosx_10_12_x86_64.whl.

File metadata

  • Download URL: belgie-0.39.0-cp312-abi3-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 57.4 MB
  • Tags: CPython 3.12+, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for belgie-0.39.0-cp312-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 d995ec5d7c327b69796338dda59aba2390b0295a079e47a3c4083b4565fb77a4
MD5 19d86c8192cea17f33bcc51b02401d89
BLAKE2b-256 ac9667aa012434314c16883255960a34161757caef8e52f1696ca04fc760f1ea

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page