Tl.
Command-line tools
Eighteen binaries across fourteen crates. Every one but atpmcp sits behind the clap feature, which is off by default, so a library never pulls in an argument parser you did not ask for.
§ 01
Building them
One line builds the whole set, servers included. Put target/release on your path, or run each one ad hoc through cargo run.
cargo build --release --bins -F clap,hickory-dns,atproto-attestation/tokio
# Or one tool, installed from a checkout of this tree:
cargo install --path crates/atproto-identity --features clap,hickory-dns
cargo build --features clap --bins is not that line. It silently skips the two attestation binaries, whose required-features it does not satisfy, and says nothing about having done so. A bare tokio in the list does not work either: atproto-attestation is the only crate with a tokio feature, and Cargo refuses it on the others.
§ 02
Reference
Every binary in the workspace, with the crate it belongs to and the features it needs. The features column is the binary’s required-features; without them Cargo does not build it.
| Binary | Features | Does |
|---|---|---|
| atpdid | clap, hickory-dns | Identity, whole. resolve takes handles or DIDs (--document for the full DID document). key generates P-256, K-256 or Ed25519 keys and inspects, signs and verifies. plc verifies an audit log, reports every fork with the rotation key that won and why the others lost, and creates, updates, signs and submits operations. A private-key argument names where to read the key — -, a file, or an environment variable — so it never has to sit in ps or shell history. |
| Binary | Features | Does |
|---|---|---|
| atpcid | clap | Compute the CID of a JSON value, stdin or files. Input starting with { is a record; --record forces it, hashing through the data model so a blob or CID link becomes a tag-42 link, and --raw gives the bafkrei… a blob needs. |
| atproto-dasl | clap | encode JSON to DAG-CBOR hex, decode it back; --non-strict accepts non-canonical input. |
| atptid | clap | Generate TIDs (-n for a batch), parse one back to its timestamp, validate with -q, or mint one from a microsecond timestamp with -t. |
| atproto-repo-car | clap | Read-only CAR v1 inspection: ls, roots, get, verify, stats. --max-blocks caps what it reads. |
| atproto-repo-mst | clap | The Merkle Search Tree inside an archive: ls, get, tree, height, info, collections, under --max-blocks and --max-memory. |
| Binary | Features | Does |
|---|---|---|
| atproto-attestation-sign | clap, tokio | inline embeds a signature bound to the repository the record will live in; remote builds a separate proof record and a strongRef to it. |
| atproto-attestation-verify | clap, tokio | Verify every attestation on a record, or one, against a repository DID. A record with no signatures exits 1. |
| atproto-lexicon-resolve | clap, hickory-dns | Resolve an NSID through DNS and XRPC to its schema; --recursive follows every reference, --show-deps draws the graph. |
| atproto-extras-parse-facets | clap, hickory-dns | Mentions, URLs and hashtags out of text, as a facet array with byte offsets. A mention needs a DID, so it is emitted only with --resolve-mentions. |
| Binary | Features | Does |
|---|---|---|
| atpxrpc | clap (default) | The XRPC client. login stores a session and any method then runs against it; --body makes a procedure, --bytes streams stdin for uploadBlob. proxy goes through the PDS with an atproto-proxy header, check-auth confirms a session, and dpop makes one call with an OAuth access token and a DPoP key. Also a library — atpmcp shares its account store. |
| atproto-oauth-service-token | clap | Mint a service-auth JWT from an issuer to an audience. Pass lxm=: a verifier refuses a token bound to no method. |
| atpmcp | — | MCP server over stdio, revision 2026-07-28 only. Twelve tools across CIDs, identity, records, lexicons, XRPC, TIDs, facets and blobs; blobs also readable as resources. § 04. |
| Binary | Features | Does |
|---|---|---|
| atproto-jetstream-consumer | clap | Stream Jetstream v2 events, filtered server-side by --collections, --dids and --kinds; --cursor to resume, --compress for dictionary zstd, --limit to stop. |
| atproto-tap-client | clap | Stream verified Tap events as JSON (read), manage tracked repositories (repos add, remove, add-follows), and resolve or info a DID. The admin password comes from ATPROTO_TAP_ADMIN_PASSWORD. |
| Binary | Features | Does |
|---|---|---|
| pds | clap + extras | The Personal Data Server Git only. Storage, mail and metrics are further features; see the operator guide. |
| atproto-pds-admin | clap | Operator CLI over a running PDS’s admin API Git only: invite, account, takedown, highport reconcile and release, and entryway export and adopt for handing accounts to an entryway. |
| entryway | clap + extras | The entryway Git only: serve, plus check, keys, pds, ops, reconcile, handles and accounts import for the operator. Each operator command opens the database, or calls a deployed entryway’s admin API with --admin-url. See the entryway guide. |
§ 03
Worked invocations
Drawn from the workspace README and each binary’s own --help. After Listing 2, the cargo run prefix is left off.
cargo run --features clap,hickory-dns \
--bin atpdid -- resolve alice.bsky.social
atpdid key generate p256
atpdid key inspect did:key:...
atpdid key sign signing.key data.json
atpdid key verify did:key:... data.json <signature>
# Every fork in a PLC audit log, and why each loser lost.
atpdid plc verify did:plc:...
atpdid plc forks --verbose did:plc:...
# A signed update, with the rotation key read from a file.
atpdid plc update did:plc:... \
--signing-key rotation.key --also-known-as at://new.example.com
cargo run --package atproto-dasl --features clap \
--bin atpcid -- '{"text":"hello"}'
# JSON in, DAG-CBOR hex out — and back again.
echo '{"text":"hello"}' | atproto-dasl encode
echo 'a164746578746568656c6c6f' | atproto-dasl decode
# A record's own CID: hashed through the data model, so a blob's
# {"$link": ...} is a tag-42 link rather than a one-key map.
cat record.json | atpcid --record --
# A blob needs the raw CID, not the DAG-CBOR one.
atpcid --raw --files avatar.png
atptid -n 5
atptid 3mgn37sytoc2c
atproto-repo-car verify repo.car
atproto-repo-car stats repo.car
atproto-repo-mst collections repo.car
atproto-repo-mst tree repo.car
# inline takes four positionals, and the repository DID is one of them:
# the signature is bound to the repository the record will live in.
atproto-attestation-sign inline \
record.json did:plc:repo123 signing.key metadata.json
atproto-attestation-verify signed_record.json did:plc:repo123
atproto-lexicon-resolve --recursive --show-deps app.bsky.feed.post
# One stored session; every later call runs against it.
atpxrpc login alice.bsky.social # prompts for the app password
atpxrpc com.atproto.repo.listRecords \
repo=alice.bsky.social collection=app.bsky.feed.post
# A file on stdin, for uploadBlob.
atpxrpc --bytes --content-type image/png \
com.atproto.repo.uploadBlob < avatar.png
# Through the PDS to another service.
atpxrpc proxy did:web:api.bsky.app#bsky_appview \
app.bsky.actor.getProfile actor=alice.bsky.social
# A one-off call with an OAuth token and a DPoP key; nothing stored.
ATPROTO_ACCESS_TOKEN=... ATPROTO_DPOP_KEY=did:key:... \
atpxrpc dpop alice.bsky.social com.atproto.repo.describeRepo repo=alice.bsky.social
atproto-oauth-service-token did:plc:issuer SIGNING_KEY did:web:audience \
lxm=com.atproto.repo.createRecord
atproto-jetstream-consumer jetstream.us-east.bsky.network \
--collections app.bsky.feed.post --kinds commit
ATPROTO_TAP_ADMIN_PASSWORD=... atproto-tap-client localhost:2480 read --live-only
atproto-tap-client localhost:2480 repos add did:plc:...
PDS_ADMIN_PASSWORD=... atproto-pds-admin invite create --count 5
# Validate an entryway's configuration and keys before serving.
entryway check
entryway serve
# The same operator commands against a deployment with no shell.
ENTRYWAY_ADMIN_PASSWORD=... entryway ops list --failed \
--admin-url https://account.example.com
§ 04
The MCP server
atpmcp speaks MCP revision 2026-07-28 over stdio, and only that revision: it has no initialize handshake, so a client on an earlier revision is answered with an error naming the version it needs. Such a client should stay on atpmcp 0.14.5.
Twelve tools. Nine compute or fetch and change nothing:create_record_cid, validate_lexicon_schema, generate_tid, resolve_handle_to_did, resolve_identity, parse_facets, get_record, get_lexicon, validate_xrpc. download_blob fetches a blob, verifies it against its CID and saves it to a cache. Two write: upload_blob, and invoke_xrpc, the one marked destructive because it calls whatever method it is given. Blobs are also resources at atpmcp://public/blob/{did}/{cid}.
{
"mcpServers": {
"atpmcp": {
"command": "/path/to/target/release/atpmcp"
}
}
}
cargo build -p atpmcp --release
atpmcp login alice.bsky.social # the account store atpxrpc uses