Assumes DAIMON Desktop is already running locally.

daimon-sdk Python docs

Use daimon_sdk to talk to your local processd-mcp service with typed APIs, structured errors, and SDK-only file transfer helpers.

quickstart.py
import asyncio

from daimon_sdk import DaimonClient


async def main() -> None:
    async with DaimonClient("http://127.0.0.1:8080/mcp") as client:
        runtime = await client.runtime.get_context()
        print(runtime.base_workdir)

        files = await client.files.glob("**/*.py", path=runtime.base_workdir)
        print(files.filenames[:5])

        uploaded = await client.files.upload_bytes(
            "artifacts/hello.bin",
            b"hello from daimon-sdk",
        )
        print(uploaded.file_path, uploaded.bytes_written)


asyncio.run(main())

Install

Start by installing the SDK

The SDK is published as a normal Python package. For local development, install the editable extras and keep the client code in your own app.

pip install daimon-sdk
What this gives you

A typed async client, MCP transport handling, structured tool errors, session helpers, and the SDK-only HTTP file transfer endpoints.

Quick Start

Connect to your local DAIMON runtime

Use the local MCP endpoint managed by DAIMON Desktop. The same pattern works whether you are reading files, calling tools, or using the SDK-only file transfer API.

import asyncio

from daimon_sdk import DaimonClient


async def main() -> None:
    async with DaimonClient("http://127.0.0.1:8080/mcp") as client:
        runtime = await client.runtime.get_context()
        print(runtime.base_workdir)

        result = await client.files.glob("**/*.py", path=runtime.base_workdir)
        print(result.filenames[:5])


asyncio.run(main())
Endpoint

Use the local MCP endpoint exposed by DAIMON Desktop. If your service is configured with a token, pass it as access_token when constructing DaimonClient.

Authentication

Pass the access token only when the server requires it

The SDK reuses the same access token for MCP calls and the SDK-only file endpoints, so one client configuration covers both surfaces.

from daimon_sdk import DaimonClient

async with DaimonClient(
    "http://127.0.0.1:8080/mcp",
    access_token="your-token",
) as client:
    runtime = await client.runtime.get_context()
    print(runtime.capabilities)
Behavior

If the server runs without PROCESSD_TOKEN, you can omit access_token. If the server is protected, the SDK sends X-Access-Token on both MCP and /sdk/file requests.

Files

Read, write, edit, glob, and grep

The files namespace covers the MCP file tools and returns typed result objects instead of raw JSON payloads.

runtime = await client.runtime.get_context()

read = await client.files.read("/tmp/demo.txt")
print(read.file.content)

written = await client.files.write("/tmp/demo.txt", "hello\n")
print(written.file_path)

glob = await client.files.glob("**/*.rs", path=runtime.base_workdir)
print(glob.search_path, glob.num_files)

grep = await client.files.grep("TODO", path=runtime.base_workdir)
print(grep.num_matches)
Typed results

Read returns ReadResult, write returns WriteResult, glob returns GlobResult, and grep returns GrepResult. Every result keeps the raw payload for debugging.

Error shape

MCP tool failures are mapped to SDK exceptions. You should handle DaimonToolError rather than parsing structured_content yourself.

File Transfer

Upload and download raw bytes through the SDK-only endpoint

These methods target /sdk/file directly. They are meant for SDK and GUI integrations, not for the LLM tool surface.

blob = await client.files.upload_bytes(
    "artifacts/report.pdf",
    pdf_bytes,
)
print(blob.file_path, blob.created)

downloaded = await client.files.download_bytes("artifacts/report.pdf")
print(len(downloaded))

local_path = await client.files.download_file(
    "artifacts/report.pdf",
    "/tmp/report.pdf",
)
Returned metadata

Upload returns FileTransferResult with file_path, bytes_written, created_parent_directories, created, and updated. Download returns bytes or writes to a local path.

Exec

Run commands with typed Bash results

Use the exec namespace when you want simple command execution, background tasks, or a persistent shell-like interaction.

result = await client.exec.bash("printf 'hello\n'")
print(result.stdout)

background = await client.exec.bash(
    "sleep 1; echo done",
    run_in_background=True,
)
print(background.background_task_id)
Why this matters

bash() gives you a structured BashResult with stdout, stderr, background task metadata, and sandbox flags.

Sessions

Keep interactive command sessions open

Start a tty session once, then write, poll, and wait until the process exits.

session = await client.exec.start_session("/bin/cat", tty=True)

echoed = await session.write("hello session\n")
print(echoed.output)

final = await session.wait_for_exit()
print(final.exit_code)
Session handle

SessionHandle wraps the low-level session_id and provides write(), poll(), wait_for_exit(), and close() so callers do not manage raw tool calls.

Web

Fetch pages and inspect the typed response

Use web.fetch when the agent needs a controlled HTTP client with content-type aware parsing and persisted output metadata.

page = await client.web.fetch("https://example.com")
print(page.status_code)
print(page.content[:200])
Common use

The SDK returns WebFetchResult, which includes the URL, result type, byte count, content, redirect information, and persisted output path when available.

Raw API

Drop down to call_tool only when you need the escape hatch

Most app code should stay on the typed surface. raw.call_tool exists for debugging, experimental surfaces, and migration work.

payload = await client.raw.call_tool(
    "GetRuntimeContext",
    {},
)
print(payload["baseWorkdir"])
Recommendation

If you already know the capability you need, prefer the typed namespaces first. raw.call_tool is useful, but it should stay the exception.

Reference

Quick API summary

This is the surface area the current Python SDK exposes. It maps directly onto processd-mcp tools plus the SDK-only file transfer endpoints.

DaimonClient(base_url, access_token=None, timeout_s=30.0)
client.runtime.get_context()
client.files.read()/write()/edit()/glob()/grep()
client.files.upload_bytes()/download_bytes()/upload_file()/download_file()
client.exec.bash()/start_session()/write_stdin()
SessionHandle.write()/poll()/wait_for_exit()/close()
client.web.fetch()
client.raw.call_tool()