AgentDomains

When an AI agent builds a website or an API, it needs a domain to serve it on. AgentDomains hands out free ones under makes.fyi (the default) and agentdomains.co, claimable from a single CLI command. Point a domain at an IP or a CNAME, delegate it to your own nameservers, or add TXT records to get HTTPS.

API base: https://api.agentdomains.co · CLI: github.com/tashfeenahmed/AgentDomains · source-available (FSL-1.1).

Install

# with the Go toolchain
go install github.com/tashfeenahmed/AgentDomains/cmd/agentdomains@latest

# verify
agentdomains --help

Or download a prebuilt binary — macOS, Linux and Windows, amd64 and arm64 — from the latest release. Each archive carries the binary, README and LICENSE, and every release ships a SHA256SUMS you can check with shasum -a 256 -c SHA256SUMS --ignore-missing. Note the module path keeps its capitals (AgentDomains) even though the command is lowercase.

Quickstart

agentdomains signup                                   # instant account + API key
agentdomains claim myagent --email you@example.com --type A --content 203.0.113.10
dig +short myagent.makes.fyi                       # 203.0.113.10 ✨
Your first registration needs --email — we send a confirmation link and start a 30-day clock. Confirm within 30 days or the name (and any others on the account) is deleted automatically. After the first time, the email is remembered on the account, so later claim/forward/proxy calls don't repeat it.

Choosing a domain

Names are claimed under makes.fyi by default. Pass --domain agentdomains.co (CLI) or a "domain" field (API) to claim under the other one. The same label can exist under each domain independently; scope get/delete with --domain when a label is claimed under both.

agentdomains claim myagent --domain agentdomains.co   # myagent.agentdomains.co

Accounts & confirmation

Signup creates an anonymous provisional account and API key. The first time you register a name you pass --email; we email a confirmation link and the account stays provisional on a 30-day clock. Confirming the link (a human clicks it) makes the account — and all its names — permanent:

agentdomains claim myagent --email you@example.com   # registers + emails a link
agentdomains whoami                                  # state: verified once confirmed

You can also (re)send the link any time with agentdomains email you@example.com. If the account is never confirmed within 30 days, it and all its names are removed automatically by a background job and the names are released. One account can hold up to ten names at once.

Labels are lowercased when you register them: claiming MyApp gives you myapp.makes.fyi, and that is the label every later command expects.

DNS records

Claim a label, optionally with a record, then add more any time:

agentdomains claim shop --type CNAME --content myshop.vercel.app
agentdomains record shop --type A --content 203.0.113.10 --host www
agentdomains get shop                 # show all records, each with its id
agentdomains get shop --json          # machine-readable
agentdomains unrecord shop <record-id>   # remove ONE record, keep the name
TypeUse
A / AAAAPoint at an IPv4 / IPv6 address.
CNAMEAlias to another hostname (e.g. a PaaS).
TXTVerification / ACME challenges (see SSL below).

Every record has an id, printed beside it by get and returned by claim and record. Pass that id to unrecord to drop a single record while keeping the name — deleting the whole name to undo one wrong IP is no longer the only option.

A claim and its first record stand or fall together. If claim --type … --content … is given a record the API can't create, the label is not claimed: fix the record and run the same command again. Re-claiming a name you already hold is not an error either — the CLI says "you already own it" and exits 0, and the API answers 409 with "owned": true.

Getting HTTPS / SSL

AgentDomains handles DNS, so you bring your own certificate. Two paths:

HTTP-01 (simplest)

Point your domain at your server, then let Let's Encrypt verify over HTTP:

agentdomains claim mybot --type A --content "$(curl -s ifconfig.me)"
sudo certbot certonly --standalone -d mybot.makes.fyi

DNS-01 (no inbound server needed)

Add the ACME challenge token as a TXT record:

agentdomains txt mybot "<token-from-acme-client>" --host _acme-challenge

URL forwarding

Forward a name to any URL with a real HTTP redirect, served at the edge with valid HTTPS. forward claims the label first if you don't own it yet, so it's a single step:

agentdomains forward me https://my-portfolio.example.com
# me.makes.fyi -> 302 redirect to https://my-portfolio.example.com

agentdomains forward me https://dest.com --permanent        # 301 instead of 302
agentdomains forward me https://dest.com --no-preserve-path # land on target root
agentdomains unforward me                                   # remove the forward

The request path and query are preserved by default (me.makes.fyi/blog?x=1…/blog?x=1).

A forward takes the hostname over. Any A/AAAA/CNAME sitting on the label itself is deleted as part of the call — at the DNS provider and here — and handed back as replaced_records, which the CLI prints. So claiming a name with a record and then forwarding it is a perfectly good sequence; nothing has to be cleared out of the way first. Records on a sub-label (www.me.makes.fyi) are separate hostnames and survive, and TXT records are untouched. If the forward then fails to come up, the replaced records are put back with new ids. Going the other way is still refused: while a forward stands, adding an address record to the same label answers a 409 telling you to unforward first.

Forwarding is an HTTP redirect, not hosting. The visitor's browser ends up on the target URL. To serve your own content while keeping your name in the address bar, use reverse proxy below.

Reverse proxy

Point a name at a backend you run somewhere else and serve it over HTTPS at your name, with our edge certificate — nothing to configure on the backend. Unlike a forward, the address bar stays on your name and your backend's own content is served. proxy claims the label first if you don't own it yet:

agentdomains proxy shop myapp.fly.dev
# shop.makes.fyi -> serves https://myapp.fly.dev, over our cert, at shop.makes.fyi

agentdomains unproxy shop                                   # remove the proxy

We terminate HTTPS at the edge and fetch your backend by its own hostname, so it accepts the request and you never need a certificate for your AgentDomains name. Like a forward, a proxy replaces the label's own A/AAAA/CNAME records, reporting them as replaced_records, and leaves sub-label and TXT records alone. A proxy and a forward remain mutually exclusive on one label.

Heads-up for login-gated apps. The proxy serves your backend, but it can't rewrite hostnames your app hardcodes. Apps that bake their own domain into OAuth/SSO redirects (e.g. a Keycloak login) may bounce users back to the backend's native hostname mid-login until you add your AgentDomains name in the app's own settings. Static sites and apps that use relative URLs work with no setup.

Nameserver delegation

Want full control of the subtree, with your own records, MX, and sub-domains? Delegate it to your nameservers:

agentdomains ns mybot ns1.yourdns.com ns2.yourdns.com

From then on, mybot.makes.fyi and everything under it is served by your nameservers.

Using it from agents

Every command accepts --json for clean machine output, and reads credentials from the environment so no interactive setup is needed:

export AGENTDOMAINS_API_KEY=adom_...           # skip local config
agentdomains claim $NAME --email you@example.com --type A --content $IP --json | jq -r .fqdn

You can also call the HTTP API directly (below), which is handy inside sandboxes.

If your agent speaks MCP, skip the shell entirely: the MCP server exposes every operation as a typed tool, over stdio (npx -y agentdomains-mcp) or the hosted endpoint at mcp.agentdomains.co.

CLI commands

CommandDescription
agentdomains signupCreate an account; saves the key to ~/.agentdomains/config.json
agentdomains whoamiAccount state, usage, and the per-account cap on names held at once
agentdomains email <addr>Attach an email for human validation
agentdomains claim <label>Register <label>.makes.fyi (--email required on first use; --domain agentdomains.co; optional --type/--content/--host)
agentdomains listList your domains
agentdomains get <label>Show one domain and its records
agentdomains record <label>Add a record: --type --content [--host]; prints the new record's id
agentdomains unrecord <label> <record-id>Remove one record, keeping the name (ids come from get)
agentdomains forward <label> <url>Forward to a URL (--permanent, --no-preserve-path); claims the label if needed
agentdomains unforward <label>Remove a forward
agentdomains proxy <label> <host>Serve a backend at the name over HTTPS (our cert); claims the label if needed
agentdomains unproxy <label>Remove a reverse proxy
agentdomains ns <label> <ns>…Delegate to your nameservers
agentdomains txt <label> <value>Add a TXT record (--host _acme-challenge)
agentdomains delete <label>Delete a domain and its records
agentdomains account deleteClose the account and invalidate its key; --force deletes the names it still holds
agentdomains versionPrint the CLI version

Global flags: --json, --api-url, --domain. Env: AGENTDOMAINS_API_KEY, AGENTDOMAINS_API_URL.

MCP server

AgentDomains ships a Model Context Protocol server, so an agent can register and manage names as typed tool calls instead of shelling out. Run it locally over stdio, or point at the hosted endpoint — same tools either way.

Local (stdio)

AGENTDOMAINS_API_KEY=adom_… npx -y agentdomains-mcp

In Claude Code:

claude mcp add agentdomains --env AGENTDOMAINS_API_KEY=adom_… -- npx -y agentdomains-mcp

In Claude Desktop, add to claude_desktop_config.json:

{
  "mcpServers": {
    "agentdomains": {
      "command": "npx",
      "args": ["-y", "agentdomains-mcp"],
      "env": { "AGENTDOMAINS_API_KEY": "adom_…" }
    }
  }
}

No env var needed if you already use the CLI — the server reads ~/.agentdomains/config.json. AGENTDOMAINS_API_URL overrides the API base. The key is never logged or echoed in tool output.

Hosted (remote)

Streamable HTTP at https://mcp.agentdomains.co, authenticated with the same Bearer key. Nothing to install:

claude mcp add --transport http agentdomains https://mcp.agentdomains.co \
  --header "Authorization: Bearer adom_…"

# or by hand
curl -s https://mcp.agentdomains.co \
  -H "Authorization: Bearer adom_…" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Or as a config-file entry, for clients that take one:

{
  "mcpServers": {
    "agentdomains": {
      "type": "http",
      "url": "https://mcp.agentdomains.co",
      "headers": { "Authorization": "Bearer adom_…" }
    }
  }
}

Tools

ToolEndpointDescription
check_availabilityGET /v1/availableIs a label free? No key required
signupPOST /v1/signupCreate an account; the key is shown once. No key required
whoamiGET /v1/whoamiAccount state, email, quota, usage
attach_emailPOST /v1/account/emailAttach an email and send its verification link
claim_domainPOST /v1/subdomainsRegister a name, optionally with a first record
list_domainsGET /v1/subdomainsList every name on the account
get_domainGET /v1/subdomains/{label}One name, its records and forward/proxy state
delete_domainDELETE /v1/subdomains/{label}destructive Delete a name and all its records
add_dns_recordPOST /v1/subdomains/{label}/recordsAdd an A, AAAA, CNAME, or TXT record
delete_recordDELETE /v1/subdomains/{label}/records/{id}destructive Remove one record, keeping the name
add_acme_challengePOST /v1/subdomains/{label}/recordsTXT at _acme-challenge for Let's Encrypt DNS-01
set_forwardPUT /v1/subdomains/{label}/forwardRedirect the name to a URL; replaces the address records on it
remove_forwardDELETE /v1/subdomains/{label}/forwardStop forwarding
set_proxyPUT /v1/subdomains/{label}/proxyServe a backend through the edge, TLS terminated for you; replaces the address records on it
remove_proxyDELETE /v1/subdomains/{label}/proxyStop proxying
delegate_nameserversPUT /v1/subdomains/{label}/nsDelegate the name to your own nameservers
delete_accountDELETE /v1/account[?force=true]destructive Close the account and kill its key; refuses while names are held unless force

Every label-scoped tool takes an optional domain (makes.fyi or agentdomains.co). Seventeen tools in all.

There is no "give me SSL" tool, because that is not one operation. Either set_proxy (the edge terminates TLS with its own certificate — nothing to install on your origin), or add_acme_challenge (publishes the DNS-01 record so your own ACME client can issue a cert). A plain public web server needs neither: Let's Encrypt HTTP-01 just works. See Getting HTTPS / SSL.

Client compatibility

Any MCP client works. Common ones:

ClientHow
Claude Codeclaude mcp add, stdio or --transport http
Claude DesktopmcpServers block in claude_desktop_config.json
OpenAI Agents SDKMCPServerStdio, or MCPServerStreamableHttp for the hosted URL
LangChain / LangGraphlangchain-mcp-adaptersMultiServerMCPClient
CrewAIMCPServerAdapter with the same stdio command

Source: github.com/tashfeenahmed/AgentDomains-mcp

HTTP API

Base URL https://api.agentdomains.co. Authenticate with Authorization: Bearer <api_key>.

Method & pathDescription
POST /v1/signupCreate a provisional account, returns api_key
GET /v1/whoamiAccount info
POST /v1/account/email{"email":"…"}, sends a validation link
POST /v1/subdomains{"label","email?","domain?","type?","content?","host?"}email required on first registration
GET /v1/subdomainsList
GET /v1/subdomains/{label}Show one
POST /v1/subdomains/{label}/recordsAdd a record
DELETE /v1/subdomains/{label}/records/{id}Remove one record, keeping the name
PUT /v1/subdomains/{label}/ns{"nameservers":[…]}
PUT /v1/subdomains/{label}/forward{"target","permanent?","preserve_path?","cloak?"}
DELETE /v1/subdomains/{label}/forwardRemove a forward
PUT /v1/subdomains/{label}/proxy{"origin","domain?"} — reverse-proxy the name to a backend host
DELETE /v1/subdomains/{label}/proxyRemove a reverse proxy
DELETE /v1/subdomains/{label}Delete
DELETE /v1/account[?force=true]Close the account; 409 listing held names unless force=true

Errors are JSON too, and carry the field that tells them apart: 409 {"owned":true} means the name is already yours (carry on), 400 means the request was wrong, and an upstream failure is 503 {"upstream":"cloudflare","retry":…}retry:true for an outage worth coming back to, retry:false for one that needs us to fix something first. A 429 carries Retry-After and repeats it in the body as retry_after.

# example: claim with curl
KEY=$(curl -s -X POST https://api.agentdomains.co/v1/signup | jq -r .api_key)
curl -s -X POST https://api.agentdomains.co/v1/subdomains \
  -H "Authorization: Bearer $KEY" \
  -d '{"label":"myagent","email":"you@example.com","type":"A","content":"203.0.113.10"}'

agent·domains · agentdomains.co · Pricing · GitHub