Engineering
molecule-sdk-python 0.2.1: inbound attachments for external workspaces
SDK 0.2.1 adds inbound attachment downloads for poll-mode external workspaces.
We shipped molecule-sdk-python 0.2.1 — the main addition is inbound attachment support for poll-mode external workspaces.
Before 0.2.1 an external workspace built with the Python SDK could send attachments outbound but had no way to receive them. If another workspace or a canvas user sent a file, the poll loop got the A2A text envelope but the attachment bytes were unreachable. 0.2.1 closes that gap.
What changed
InboundMessage.attachments is now populated. The SDK requests enriched activity rows from the platform, which include attachment metadata alongside the text. The field is a list of dicts — each entry carries kind, uri, mime_type, and name, identical to the envelope that push-mode agents already received.
async def handle(msg: InboundMessage) -> str:
if msg.attachments:
for att in msg.attachments:
path = client.download_inbound_attachment(att)
# path is a local file under ~/.molecule/token_dir/attachments/
...
return "handled"
RemoteAgentClient.download_inbound_attachment(attachment, dest_dir=None, ack=True) fetches the bytes and returns the local Path. Two URI schemes are handled:
| URI scheme | What the SDK does |
|---|---|
platform-pending:<workspace>/<file_id> | Fetches from the pending-upload content endpoint; optionally acks the upload on success |
workspace:/workspace/… / file:///workspace/… / /workspace/… | Fetches via the platform’s chat download endpoint |
Downloads are capped at 100 MB and cached by URI under <token_dir>/attachments so re-processed messages don’t re-fetch. The workspace credential is attached automatically via the SDK’s auth layer.
The ack=True default is deliberate: once the SDK downloads a platform-pending upload, it acknowledges receipt so the platform can mark the file as delivered. Pass ack=False if your handler needs to fetch without triggering the ack state change.
The communication contract fixes that unblocked this
Two bugs in the underlying RemoteAgentClient had to be fixed first (SDK PR #28, before the attachment work in PR #29):
delegate route shape. The delegate call was putting the target workspace ID in the URL path and the source workspace in the body — the reverse of the platform contract. Any workspace delegate call from external poll-mode agents (also reachable as molecule workspace delegate from the CLI) was routing to the wrong endpoint. The fix puts the source workspace in the URL and the target in the JSON body, matching workspace-server’s handler. Regression test added.
register headers. The register call wasn’t sending the routing and auth headers that every other workspace call sends. On SaaS tenants with org-ID routing, this meant the registration landed on the wrong tenant. Fixed to send the full header set consistently; unchanged on self-host where the headers are absent and ignored.
Both fixes are in 0.2.1. If you ran RemoteAgentClient.delegate() before 0.2.1, those calls were silently misrouting; update to pick up the corrected route.
Supported runtime narrowing
0.2.1 also narrows the SDK’s validator and template documentation to the four runtimes the platform currently supports as managed options: claude-code, codex, hermes, and openclaw. The runtime: external value remains valid — it’s the path for external poll-mode workspaces like those built with this SDK. The change is docs and validation only; the underlying platform hasn’t added or removed runtime adapters.
Installing 0.2.1
The SDK is distributed via the Molecule AI internal package registry:
pip install molecule-ai-sdk==0.2.1 \
--extra-index-url https://packages.moleculesai.app/simple/
Docs on the internal install path are in the repository README.
Upgrade checklist
If you have an existing poll-mode external workspace:
- Upgrade to 0.2.1.
- If you use
RemoteAgentClient.delegate(), verify the target workspace receives the task correctly — the route shape was corrected and may change observable behavior. - To handle inbound attachments, check
msg.attachmentsin your handler and callclient.download_inbound_attachment(att)for each entry you want to process.
Source: git.moleculesai.app/molecule-ai/molecule-sdk-python — PRs #28, #29, #30.