Skip to content

Quickstart

End-to-end: auth → connect → call a dispatch tool. You don’t need to run a server yourself for this walkthrough — point at the public reference deployment.

  • Python 3.10+
  • uv installed
  • A browser (for the OAuth login step)
  • A target MCP server URL — https://mcp.blender.bet/ for the reference deployment
  1. Install the fastmcp client library.

    Terminal window
    uv pip install --system fastmcp

    fastmcp.Client speaks the MCP-spec OAuth flow (discovery, DCR, PKCE) for you. If you’d rather drive the wire yourself, see the Authentication reference.

  2. Open a session. Save this as quickstart.py:

    import asyncio, json
    from fastmcp import Client
    SERVER = "https://mcp.blender.bet/"
    async def main():
    async with Client(SERVER) as client:
    # First run pops your browser to log in. Tokens persist
    # to ~/.config/fastmcp so you only do this once.
    result = await client.call_tool("blender_list_available_clients", {})
    print(json.loads(result.content[0].text))
    asyncio.run(main())
    Terminal window
    uv run python quickstart.py

    First run: a browser tab opens, you authenticate, the tab closes itself. Subsequent runs reuse the cached token.

    Expected output (no Blender connected yet):

    {
    "user_id": "<your-id>",
    "persistent": [],
    "ephemeral": [{"uuid": "fastmcp-...", "client_type": "llm", ...}]
    }

    You’ve successfully authenticated. The ephemeral list shows your own session.

  3. Connect a Blender peer so there’s something to dispatch to. In Blender:

    • Install addon.py from this repo: Edit → Preferences → Add-ons → Install
    • In the BlenderMCP panel (sidebar N): set Server URL to https://mcp.blender.bet/
    • Click Login — same browser-based PKCE flow
    • Click Connect

    Status pill should turn green with the client UUID displayed.

  4. Dispatch a tool. Edit quickstart.py:

    async with Client(SERVER) as client:
    result = await client.call_tool("blender_get_scene_info", {})
    envelope = json.loads(result.content[0].text)
    print(json.dumps(envelope, indent=2))
    Terminal window
    uv run python quickstart.py

    Expected output:

    {
    "status": "completed",
    "command": "get_scene_info",
    "target_uuid": "blender-demo01",
    "job_id": "j-7a3f2c1e9b04",
    "result": "{\"name\":\"Scene\",\"object_count\":3,...}",
    "error": ""
    }

    The server auto-picked blender-demo01 as the target because it’s the only Blender on your bus. With multiple peers, pass target_uuid in the arguments dict.

  5. Try a different tool. Run arbitrary Python in Blender:

    result = await client.call_tool("blender_execute_code", {
    "code": "import bpy; print(len(bpy.data.objects))"
    })
    print(json.loads(result.content[0].text)["result"]) # "3\n"

    The full dispatch tool catalog has 24 entries across scene inspection, code execution, viewport capture, console operations, msgbus, PolyHaven, Hyper3D Rodin, and Sketchfab.

You authenticated against the MCP server using OAuth 2.1 with PKCE (the library handled discovery → DCR → PKCE → token exchange). Your access token resolves to a user_id server-side, which scopes every subsequent call to your personal bus — your Blender peers can’t be seen by anyone else, and other users’ peers don’t show up in your list_available_clients. Each blender_* tool call traversed the bus to your Blender peer, executed there, and returned the result inline via JobWaiter — the bus round-trip is invisible from the caller’s perspective.