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.
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-sdkA 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())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)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)Read returns ReadResult, write returns WriteResult, glob returns GlobResult, and grep returns GrepResult. Every result keeps the raw payload for debugging.
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",
)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)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)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])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"])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.