Platforms

Artifacts

Git storage behind the Cloudflare API. Read repos and history like on any forge. The forge part answers 501.
IDartifacts05 / 05api.cloudflare.com/client/v4
Platform / artifacts

Artifacts

Spoken by ArtifactsProvider. Same resources, same shapes, its own quirks kept inside.

Access

CreatecreateProvider("artifacts")

Importimport { ArtifactsProvider } from"@agntn/forges/artifacts"

EnvCLOUDFLARE_API_TOKEN, AGNTN_CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_ACCOUNT_ID

CLIcf auth login session

Header
Authorization: Bearer
Anonymous
none, every read needs a token
Threads
none, no pull requests to hang them on
Code search
unsupported, explicit error
Templates
none

Address an Artifacts namespace

ts
import { createProvider } from "@agntn/forges";

const artifacts = await createProvider("artifacts", {
  token: process.env.CLOUDFLARE_API_TOKEN,
  accountId: process.env.CLOUDFLARE_ACCOUNT_ID,
});

const repo = await artifacts.repos.get("default", "starter-repo");
const readme = await artifacts.repos.readContents("default", "starter-repo", "README.md");

Artifacts is Git without the forge around it. An account holds namespaces, a namespace holds repositories, and that's the whole model. So owner is the namespace. Everything else lines up with the shapes you already know.

Where does the token come from?

Leave both fields out and @agntn/credentials looks for you. The token comes from CLOUDFLARE_API_TOKEN, then the session cf auth login left behind. The account comes from AGNTN_CLOUDFLARE_ACCOUNT_ID or CLOUDFLARE_ACCOUNT_ID, or from that session when it reaches exactly one account. A token from the environment wants its account from there too.

That session token expires. So the provider asks the chain on every request, and a 401 on a session token gets one refresh and one retry. Your MCP server keeps reading after lunch. Nice.

There's no guest mode. { token: "" } means go look, same as leaving it out.

What it reads

Paths below hang off /accounts/:account/artifacts.

ResourceEndpoint
reposGET /namespaces/:namespace/repos, …/repos/:name
repository contentsGET …/repos/:name/file?ref=&path=, …/tree/:hash, after the ref resolves
commitsGET …/repos/:name/log?ref=&offset=&limit=, …/commit/:hash

Repositories page by cursor, and a cursor can't jump. Page 3 reads pages 1 and 2 on the way, 200 rows a request at most, plus one row to tell if there's more. The log pages by offset like a sane API.

A full SHA goes straight to …/commit/:hash. A branch or tag resolves through the log first. A file is one read at that commit. A directory walks the trees, one request per level.

What comes back

  • private is always true. Every read needs a token.
  • url is the REST address of the repository. cloneUrl is the Git remote.
  • Only a fork made inside Artifacts counts as one. A source of artifacts:<namespace>/<repo> sets isFork and parent.
  • Commit dates arrive as Unix seconds and leave as ISO 8601.
  • Directory entries have size: null. The tree doesn't send one.
  • commits.get returns files: [] with filesComplete: null. There's no diff in the API, so the honest answer is "don't know", not "nothing changed".

Gotchas

  • The log reads ref=HEAD as a ref that doesn't exist and answers with an empty list. forges leaves HEAD out, so the default branch answers.
  • An unknown ref isn't a 404 there either. Just an empty log. forges turns it into NotFoundError, so a typo doesn't pass for an empty repository.
  • commits.list takes ref and nothing else. path, since and until are a 501 before any request goes out.
  • The file route answers 404 for a directory too. That's why a path that isn't a file gets the tree walk.

What isn't there

Issues, pull requests, comments, review threads, users, releases, CI, code search and templates. None of it. Just Git, no drama.

Why put storage without a forge into a forge library then? Because reading is the same job. An agent that reads a file on GitHub reads one here with the same call. Everything else is a ForgesError with status 501 and a sentence, so the agent stops instead of retrying.

Where it lives

src/providers/artifacts.ts, with the credentials from @agntn/credentials/cloudflare.