atproto—crates

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.

Every binary in the workspaceListing 1
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.

Identity & cryptography — atproto-identity
BinaryFeaturesDoes
atpdidclap, hickory-dnsIdentity, 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.
Content addressing & records — atproto-dasl, atproto-record, atproto-repo
BinaryFeaturesDoes
atpcidclapCompute 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-daslclapencode JSON to DAG-CBOR hex, decode it back; --non-strict accepts non-canonical input.
atptidclapGenerate 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-carclapRead-only CAR v1 inspection: ls, roots, get, verify, stats. --max-blocks caps what it reads.
atproto-repo-mstclapThe Merkle Search Tree inside an archive: ls, get, tree, height, info, collections, under --max-blocks and --max-memory.
Attestation, lexicons & rich text — atproto-attestation, atproto-lexicon, atproto-extras
BinaryFeaturesDoes
atproto-attestation-signclap, tokioinline 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-verifyclap, tokioVerify every attestation on a record, or one, against a repository DID. A record with no signatures exits 1.
atproto-lexicon-resolveclap, hickory-dnsResolve an NSID through DNS and XRPC to its schema; --recursive follows every reference, --show-deps draws the graph.
atproto-extras-parse-facetsclap, hickory-dnsMentions, 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.
Clients & auth — atpxrpc, atproto-oauth, atpmcp
BinaryFeaturesDoes
atpxrpcclap (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-tokenclapMint 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.
Event streams — atproto-jetstream, atproto-tap
BinaryFeaturesDoes
atproto-jetstream-consumerclapStream Jetstream v2 events, filtered server-side by --collections, --dids and --kinds; --cursor to resume, --compress for dictionary zstd, --limit to stop.
atproto-tap-clientclapStream 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.
Servers — atproto-pds, atproto-entryway
BinaryFeaturesDoes
pdsclap + extrasThe Personal Data Server Git only. Storage, mail and metrics are further features; see the operator guide.
atproto-pds-adminclapOperator 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.
entrywayclap + extrasThe 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.

IdentityListing 2
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
Content addressingListing 3
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
RepositoriesListing 4
atproto-repo-car verify repo.car
atproto-repo-car stats repo.car
atproto-repo-mst collections repo.car
atproto-repo-mst tree repo.car
Attestation and lexiconsListing 5
# 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
Clients and authListing 6
# 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
Event streamsListing 7
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:...
ServersListing 8
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}.

Register it with an MCP clientListing 9
{
  "mcpServers": {
    "atpmcp": {
      "command": "/path/to/target/release/atpmcp"
    }
  }
}
cargo build -p atpmcp --release
atpmcp login alice.bsky.social    # the account store atpxrpc uses