Library / SDK
mary-ext/atcute avatar
mary-ext/atcute

atcute packages AT Protocol work in small TypeScript pieces

a collection of lightweight TypeScript packages for AT Protocol, the protocol powering Bluesky.

523 stars25 forksTypeScript0BSD

At a glance

What is it?
atcute is a collection of lightweight TypeScript packages for AT Protocol, the protocol behind Bluesky: an XRPC client, firehose and Jetstream readers, lexicon tooling, identity resolution and low level codecs. It favours picking one small package over adopting one large SDK, and its 0BSD licence keeps that choice easy to build on.
Who is it for?
atcute suits TypeScript developers who want to talk to an AT Protocol service directly, build a firehose consumer, or generate types from lexicons, and who prefer small scoped packages to a single SDK. It suits less well if you want a batteries-included Bluesky client with the feed logic already solved for you, since the package list here covers transport, encoding and schemas rather than product behaviour.
Can I use it commercially?
Yes. 0BSD is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 13 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A package collection instead of one Bluesky SDK

Most developers who meet AT Protocol arrive through Bluesky and carry an expectation of an SDK that does everything. atcute takes the opposite position. It is described as a collection of lightweight TypeScript packages for AT Protocol, the protocol powering Bluesky, and the structure of the repository reflects that: a packages directory with many small publishable units, each with its own name and scope.

The audience is therefore a developer with a specific job in mind rather than a beginner looking for a ready product. If you are writing a bot, a feed reader, a moderation tool or an archive exporter, you want to resolve identities, validate records and consume an event stream, and you would rather depend on the one package that does that job than on a framework that also carries a UI toolkit, a database layer and a scheduling system.

The project is published at codeberg.org/mary-ext/atcute, uses trunk as its default branch, and the root package.json marks the workspace as private with type module, which tells you the packages are ES modules. The repository also holds a patches directory, a native-shared directory and an AGENTS.md file, which are signs of a workspace that mixes pure TypeScript packages with some native build steps rather than a single flat library.

The client packages cover the transport shapes AT Protocol uses

Six client packages handle the ways a program talks to the network. The XRPC HTTP client package is the core one, and the quick start in the project documentation wires it to a public service endpoint with a simple fetch handler. Next to it sits a subscription client for firehose, the XRPC subscription client, and a Jetstream WebSocket client for the newer consumption path. A Tap WebSocket client covers that protocol, and a password-based auth session handler deals with the login flow that uses a password rather than OAuth.

A cache package provides a normalized cache store, which is the piece that matters most for anyone building a follower or feed view. Once you subscribe to a firehose, the hard part is not parsing events but keeping your local view consistent as posts, likes, reposts and profile changes arrive in any order. A normalized store gives you that state to query.

The split also tells you something about the design philosophy. Each package solves one problem and exposes it directly, so an application can take the HTTP client without pulling in a cache, or the Jetstream reader without the subscription client. Authentication is likewise separable: password session handling and the OAuth clients are their own packages rather than a mode inside the core client.

Lexicons are types first, and a generator writes them for you

AT Protocol describes every API endpoint and record type in a schema format called a lexicon, and atcute treats that schema as the source of truth. The core lexicons package holds the types and the schema validation, and a lexicon CLI generates TypeScript from lexicon schemas. A lexicon document package parses and authors lexicon documents, while resolver packages resolve lexicons from the network, with a Node.js flavour of the resolver and a DNS-based handle resolver for Node.js in the identity group.

Generated definition packages ship for a wide set of namespaces. The bluesky package covers app.bsky and chat.bsky definitions, and there are separate packages for com.atproto, tools.ozone, com.whtwnd, sh.tangled, site.standard, pub.leaflet, community.lexicon, blue.moji, blog.pckt and others, including a package for com.germnetwork and one for fyi.unravel.frontpage. Those last entries are a useful signal about how the ecosystem of lexicons has grown past the original two.

The practical consequence is that type-checking a query is a local operation. Because the definitions are compiled into TypeScript, an incorrect parameter name for a lexicon method is a compile error rather than a runtime surprise, and validation of records happens against the same generated types.

The utility packages implement the encoding layer directly

Underneath the clients sit a cluster of small codec packages that implement the storage and identity primitives of AT Protocol. There is a deterministic CBOR codec, a CAR archive codec, a content identifier codec, a multibase package for base32 and base64 encoding, a LEB128 varint codec, a timestamp identifier codec, and merkle search tree utilities for the MST structure used in repositories.

There is also a crypto package for signing and verification, a repository export reader for reading repo exports, and identity packages covering handle, DID and DID document types, plus a did:plc operation validation package that checks the operations used to create and update a did:plc identifier.

The repository topics list dag-cbor and ipld, which is where these pieces come from. The MST package in particular is worth noting, because the merkle search tree is what gives an AT Protocol repo its integrity properties, and having the utilities available as a standalone package means a tool that only needs to read or verify repos does not need the network layer at all.

One XRPC server framework with a WebSocket adapter per runtime

The server side mirrors the client philosophy with one framework and several runtime adapters. The xrpc-server package is an XRPC web framework, and separate packages adapt its WebSocket support to Bun, Cloudflare Workers, Deno and Node.js. Choosing your runtime therefore means choosing an adapter package rather than rewriting the server.

On the OAuth side there is a minimal OAuth client for single page applications and a separate OAuth client for Node.js. The documentation points web developers at the browser client for apps that need authenticated requests, and mentions the client docs for the general case.

The Bluesky-specific packages sit at the end of the list and are narrower in scope. There is a moderation interpretation package and a rich text facet builder for handling the rich text structures that carry mentions and links, plus further Bluesky packages that the truncated listing does not show in full. Everything else in the collection is protocol level, which is the dividing line you can use when deciding whether atcute is the right dependency.

A first request against a public service

The quick start is short enough to type out. Two packages are installed from npm, the client and the Bluesky definitions:

sh
npm install @atcute/client @atcute/bluesky

Then a client is created with a fetch handler pointed at the public API service, and a lexicon method is called by name with parameters:

ts
import { Client, simpleFetchHandler } from '@atcute/client';
import type {} from '@atcute/bluesky';

const client = new Client({
	handler: simpleFetchHandler({ service: 'https://public.api.bsky.app' }),
});

const { data } = await client.get('app.bsky.actor.getProfile', {
	params: { actor: 'bsky.app' },
});

console.log(data.displayName);
// -> Bluesky

Reading the snippet tells you the shape of the API. The method name is a string, which means the name can come from configuration or a loop rather than a compile-time constant, and the parameter object mirrors the lexicon. The import of the Bluesky definitions is a type-only import, so the definitions contribute types without pulling runtime code into your bundle.

Authenticated calls take one more step, and the documentation sends you to the client documentation for that or to the OAuth browser client for web apps.

0BSD licensing, a pnpm workspace, and what the docs leave out

The licence is 0BSD, which is a permissive choice with no conditions attached to redistribution. For a collection of small packages that other projects may vendor or fork, that removes the usual question of whether a dependency's terms will constrain your own distribution.

Development happens in a pnpm workspace. The root package.json holds private devDependencies and three scripts: formatting through oxfmt, linting through oxlint, and a test script that runs each workspace package's test runner with the --run flag. TypeScript is pinned at roughly 7.0, and there is a changesets setup in a .changeset directory, which is the mechanism that produces version bumps across many packages in one repository. A mise.toml file suggests the toolchain is managed through mise.

The repository publishes no GitHub releases, so individual package versions move without a repository level tag, and the last push was on 2026-10-10. Two gaps are worth naming. The README does not document a rollback procedure, so if a package version breaks your build you have to pin versions yourself. And the project homepage points at Codeberg rather than GitHub, which changes where you look for issues and where the default branch lives.

Editorial conclusion

atcute suits TypeScript developers who want to talk to an AT Protocol service directly, build a firehose consumer, or generate types from lexicons, and who prefer small scoped packages to a single SDK. It suits less well if you want a batteries-included Bluesky client with the feed logic already solved for you, since the package list here covers transport, encoding and schemas rather than product behaviour. Before you start, confirm that the package you need exists for your runtime, check the client docs for authenticated request handling, and note that the project is published from Codeberg with the default branch named trunk and no GitHub releases to track.

Frequently asked questions

How do I make an authenticated request with atcute?

The project documentation points you to the client documentation for authenticated requests, and for web apps it suggests the OAuth browser client, a minimal OAuth client for single page applications. There is also a separate OAuth client package for Node.js.

What is the difference between the atcute client and subscription packages?

The client package is an XRPC HTTP client, while firehose is an XRPC subscription client and jetstream is a Jetstream WebSocket client. The cache package provides a normalized cache store for keeping state consistent as those events arrive.

Does atcute work with Bun, Deno, Cloudflare Workers and Node.js?

For servers, the xrpc-server package is the framework and separate adapter packages add WebSocket support for Bun, Cloudflare Workers, Deno and Node.js. Identity and lexicon resolution also have Node.js specific packages.

How does atcute handle AT Protocol lexicons?

The lexicons package holds core types and schema validation, and lex-cli generates TypeScript from lexicon schemas, while resolver packages resolve lexicons from the network. Prebuilt definition packages ship for namespaces including app.bsky, chat.bsky, com.atproto and tools.ozone.

What licence is atcute released under and how are versions published?

The repository is licensed under 0BSD. It is a pnpm workspace with a changesets setup in the .changeset directory, and it publishes no GitHub releases, so individual package versions change without a repository level tag.

Official sources

  1. Issues
  2. License: 0BSD
  3. mary-ext/atcute on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mary-ext-atcute.svg)](https://hysenlabs.com/projects/mary-ext-atcute)

Was this useful?