Cron jobs, CI, and AI agents drive the same engine the app uses.
kd exits 0 on success and non-zero on failure, so a script can test $? directly. The code field in the JSON names the same classification.
if kd pull report.pdf /out/report.pdf >/dev/null; then echo "pulled" else echo "pull failed" >&2 fi
Branch on the exit code when different failures need different handling. Exit 2 is a lost session and a reconnect may fix it. Exit 4 is a missing file:
kd pull report.pdf /out/report.pdf case $? in 0) echo "pulled" ;; 2) kd connect && kd pull report.pdf /out/report.pdf ;; 4) echo "not shared yet" >&2 ;; *) echo "pull failed" >&2 ;; esac
kd start runs in the foreground and is configured by environment variables set before it (list). Under systemd:
[Service] Environment=KD_SAVE_PATH=/srv/kd/received KD_NO_FUSE=1 ExecStart=/usr/local/bin/kd start Restart=on-failure
Ctrl-C, SIGTERM, and kd stop all shut it down cleanly: disconnect, unmount, exit.
With a saved contact on both sides, a nightly push is four commands:
#!/bin/sh set -e kd quick-connect "$PEER_FP" >/dev/null kd add /srv/backups/nightly.tar.zst >/dev/null # leave the session up, or: kd disconnect >/dev/null
The peer sees nightly.tar.zst offered and pulls it, or, in FUSE mode, reads it from the shared folder.
kd list | jq -r '.data.remote_files[]?.name' kd pull build-1234.zip /artifacts/build-1234.zip
A pull that gets interrupted keeps its partial data and resumes on the next identical pull. kd progress <name> reports 0-100 while it runs, and kd bench-pull measures the link with a throwaway transfer.
The daemon queues events; kd poll-event pops one per call, non-blocking:
while :; do ev=$(kd poll-event | jq -r '.data.event') [ -n "$ev" ] && echo "event: $ev" sleep 2 done
The queue holds 64 events and drops the oldest on overflow, so poll at least every few seconds while connected. The event names are listed in the kd reference.
quick-connect then needs no human step. Both sides must have saved each other once.reconnected: and gave_up: events.kdmcp wraps the same engine as an MCP server over stdio, with 17 tools: pair, list, send, pull, poll progress, wait for events, disconnect. An agent in Claude Code, Cursor or any client that reads an mcpServers object drives a full session with tool calls instead of shell commands.
Claude Desktop installs it in one click from the MCP registry, listed as io.github.KeibiSoft/keibidrop. You can also download keibidrop-mcp.mcpb from the latest release and open it. The bundle carries the server for macOS, Windows and Linux, and asks for the three folders below when it installs.
For Claude Code, Cursor and anything else that reads an mcpServers object, kdmcp installs with KeibiDrop and prints its own config:
kdmcp install # prints the config for this machine
kdmcp is also a single standalone binary on the release page if you want the MCP server without the desktop app.
Three environment variables shape it: KD_SHARE_ROOT is the only directory the server may send from, and leaving it unset turns sending off while receiving keeps working. KD_SAVE_PATH is where pulls land, and KD_MOUNT_PATH is where the peer's files appear when a FUSE provider is present. Pulls return a transfer id immediately and are polled with kd_transfer_status, so a large transfer never fights the client's tool-call timeout.
When kd_status reports fuse true, the peer's files are mounted at mount_path and behave like a local folder. From that point the agent barely needs KeibiDrop tools: everything that takes a path works on the mount, and only the bytes a tool actually reads cross the network, fetched in 16 MiB blocks as the reads arrive.
kd_status → {"connected": true, "fuse": true,
"mount_path": "/home/me/KeibiDrop/Mount"}
ls /home/me/KeibiDrop/Mount/dataset/
grep -rl "label=7" /home/me/KeibiDrop/Mount/dataset/manifests/
python3 -c 'print(open("/home/me/KeibiDrop/Mount/dataset/shard-000.bin","rb").read(1024)[:16])'
The listing arrives first and costs about 73 bytes per file, so the agent sees the whole tree before deciding what to open, and a file it never opens costs nothing beyond its listing entry. Reading 1 MB out of a 256 MB file moves the block around the read, not the file. Files the agent writes into the mount appear on the peer's side.
The order of preference for an agent: read from mount_path when fuse is true, and fall back to kd_pull_file when it is false or when the task needs a durable local copy. The pull is resumable either way.
The measured runs, the pairing flow and a 12-second recording are in the write-up. The same mount, driven by a person instead of an agent, is shown in this walkthrough.