mandate0
Developer guides

Python client

Use standard-library HTTP or the official MCP Python SDK.

Set M0_BASE_URL and a sandbox M0_API_KEY as in the quickstart. Keep both examples server-side. There is no verified standalone Mandate0 Python SDK package in this checkout.

HTTP client

This read-only example uses Python's standard library and refuses redirects:

import json
import os
import urllib.error
import urllib.request
from urllib.parse import urljoin


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None


base_url = os.environ["M0_BASE_URL"]
api_key = os.environ["M0_API_KEY"]
request = urllib.request.Request(
    urljoin(base_url, "/api/v1/wallets"),
    headers={"Authorization": f"Bearer {api_key}", "Accept": "application/json"},
)
try:
    with urllib.request.build_opener(NoRedirect).open(request, timeout=30) as response:
        print(json.load(response))
except urllib.error.HTTPError as error:
    raise SystemExit(f"Wallet request failed: HTTP {error.code}") from None

Use an HTTPS platform origin, except for an intentional loopback development server. An empty wallet list is valid in a new sandbox account.

MCP client

This recipe targets the official MCP Python SDK 2.x interface and Python 3.11+. It uses the SDK's HTTP transport with an explicitly configured HTTP client:

python -m pip install 'mcp>=2,<3' httpx2

Save as client.py and run with python client.py:

import asyncio
import os
from urllib.parse import urljoin

import httpx2
from mcp import Client
from mcp.client.streamable_http import streamable_http_client


async def main():
    endpoint = urljoin(os.environ["M0_BASE_URL"], "/api/mcp")
    async with httpx2.AsyncClient(
        headers={"Authorization": f"Bearer {os.environ['M0_API_KEY']}"},
        timeout=httpx2.Timeout(30.0, read=300.0),
        follow_redirects=False,
    ) as http_client:
        transport = streamable_http_client(endpoint, http_client=http_client)
        async with Client(transport) as client:
            listing = await client.list_tools()
            print([tool.name for tool in listing.tools])
            result = await client.call_tool("m0_list_merchants", {"limit": 5})
            if result.is_error:
                raise RuntimeError("Merchant listing was refused")
            print(result.content)


asyncio.run(main())

Do not combine SDK 1.x examples with 2.x imports. In 2.x, transport headers and timeouts belong on httpx2.AsyncClient, rather than streamable_http_client keyword arguments. See the official transport guide and migration guide.

The example uses a static key. It does not implement OAuth login or token refresh. It performs a read-only tool call; payment and write operations require their own schema, authority and approval checks.

On this page