MCP server and agents
The mxgate MCP server: a hosted server with OAuth sign-in, or a local one with DNS setup. The tools, and what an agent cannot do without you.
The mxgate MCP server gives an AI agent the full API as tools. The agent can set up a domain, set aliases, send mail and read the log. There are two servers: a hosted one that you connect with a sign-in in the browser, and a local one that can also write your DNS records.
The hosted server
The address is:
https://api.mxgate.io/mcpAdd it to an app that takes a remote MCP server, for example Claude or ChatGPT (as a custom connector) or an IDE. For Claude Code:
claude mcp add --transport http mxgate https://api.mxgate.io/mcpThe app opens a page of mxgate in your browser. You sign in, you see which app asks, and you press "Allow". No key is copied. The app gets a token that works until you remove the app: the dashboard lists the connected apps on its MCP page, and DELETE /v1/oauth/apps/{id} does the same.
An app that has no sign-in can send the API key in a header:
claude mcp add --transport http mxgate https://api.mxgate.io/mcp \
--header "Authorization: Bearer $MXGATE_API_KEY"The hosted server has 22 of the 27 tools. It does not have the five that write a file on your machine or need your DNS token: signup, recover_key, confirm_key_recovery, rotate_key and setup_domain. With the hosted server, the agent gives you the DNS records and you publish them, or you run npx mxgate setup on your machine. A connected app cannot make an API key and cannot connect other apps.
The server speaks MCP over Streamable HTTP and keeps no session. Each request needs a key or a token, also the handshake: an app then sees from the first answer that the server needs a sign-in.
OAuth for an MCP client
A client that follows the MCP authorization specification finds all of this by itself. A request with no token gets 401 with a WWW-Authenticate header that names the metadata.
| Step | Address |
|---|---|
| Metadata of the resource | GET https://api.mxgate.io/.well-known/oauth-protected-resource |
| Metadata of the authorization server | GET https://api.mxgate.io/.well-known/oauth-authorization-server |
| Name the client: a client ID that is a URL, or a registration (RFC 7591) | The URL of a client metadata document, or POST https://api.mxgate.io/oauth/register |
| Send the person to the consent page | https://mxgate.io/oauth/authorize |
| Trade the code for a token | POST https://api.mxgate.io/oauth/token |
| Revoke the token (RFC 7009) | POST https://api.mxgate.io/oauth/revoke |
A client has two ways to name itself. The first: its client ID is the https URL of a client metadata document, a JSON file with client_id (the same URL), client_name and redirect_uris. mxgate reads the file and needs no registration; the consent page then shows the host of the URL as the checked name of the app. Claude uses this. The second: the client registers itself at /oauth/register and gets a client ID that starts with mxc_; the consent page says that mxgate did not check that name.
Each client is a public client: there is no client secret. PKCE with the method S256 is necessary. A redirect address is https, or http on localhost, and must be the same text as in the document or at the registration; only the port of a localhost address is free. A code works once, for 10 minutes. The only grant is authorization_code; the token has no end time and there is no refresh token. The token starts with mxo_ and is a Bearer credential for the hosted server and for the HTTP API. One app has one token for each account: a new connection replaces the older token.
The local server
The local server has all 27 tools. It runs on your machine, so it can keep the key in a file and use your DNS token.
Install
For Claude Code:
claude mcp add mxgate -- npx -y mxgate mcpFor each MCP client, add this to its configuration:
{"mcpServers":{"mxgate":{"command":"npx","args":["-y","mxgate","mcp"]}}}The server runs on your machine and talks to the client on stdio. It needs Node.js 20 or later.
Where the key comes from
The server reads the API key from one of these places:
- The environment variable
MXGATE_API_KEY, if it is set. - The file
~/.config/mxgate/config.json. IfXDG_CONFIG_HOMEis set, the file is$XDG_CONFIG_HOME/mxgate/config.json.
mxgate signup and the signup tool write the key to that file, so an agent can make the account with no key at the start. The server reads the file again for each call. The CLI and the MCP server use the same file.
The tools
| Tool | Task | What it does |
|---|---|---|
signup | Account | Makes an account and saves the key on your machine. |
get_account | Account | The plan, the limits, the usage, the status, and the state of the subscription. |
recover_key | Account | Mails a recovery token to the account address. It needs no key. |
confirm_key_recovery | Account | Changes the recovery token into a new key and saves it. |
rotate_key | Account | Replaces the key. The older keys stop working. |
upgrade_plan | Paid plan | Returns a payment link for a person. |
billing_portal | Paid plan | Returns a link for a person: card, invoices, cancel. |
setup_domain | Domains | Adds the domain, writes the DNS records at the DNS host, waits for the check, and sets the catch-all. |
add_domain | Domains | Adds a domain and returns the DNS records to publish. |
list_domains | Domains | Each domain with its status, records and last check. |
get_domain | Domains | One domain with its records and last check. |
verify_domain | Domains | Checks the DNS records now. |
remove_domain | Domains | Removes a domain and its aliases. |
check_dns | Domains | Reads MX, SPF and DMARC of any domain. It needs no account. |
list_aliases | Aliases | The aliases of a domain, and why an alias is not active. |
set_alias | Aliases | Makes an alias or replaces its destinations. *@example.com is the catch-all. |
remove_alias | Aliases | Removes an alias. |
list_destinations | Aliases | The mailboxes that the account can forward to, confirmed or not. |
add_destination | Aliases | Mails a confirmation link to a mailbox. |
get_send_as | Send | If the domain can send, what is missing, the SMTP host and ports, the logins. |
create_smtp_credential | Send | Makes an SMTP login. The password is returned once. |
remove_smtp_credential | Send | Removes an SMTP login. |
send_email | Send | Sends one plain-text message from an address at your domain. |
get_logs | Log | What happened to each message, and why. |
get_notifications | Notifications | The digest and webhook settings, and the last 20 notifications. |
set_notifications | Notifications | Changes the digest, the events and the webhook. |
test_webhook | Notifications | Sends a signed test call to the webhook. |
Each tool has hints for the client: the read tools are marked read-only, and the remove tools, rotate_key and setup_domain are marked destructive. The CLI has a command for each of these tasks: npx mxgate --help.
What setup_domain needs
setup_domain writes DNS records from your machine. It reads the token of the DNS host from the environment of the MCP server:
- Cloudflare:
CLOUDFLARE_API_TOKEN - Vercel:
VERCEL_TOKEN, andVERCEL_TEAM_IDwhen the domain is in a team - Porkbun:
PORKBUN_API_KEYandPORKBUN_SECRET_API_KEY
The token is never sent to mxgate. Most MCP clients set the environment of a server in its configuration:
{"mcpServers":{"mxgate":{"command":"npx","args":["-y","mxgate","mcp"],"env":{"CLOUDFLARE_API_TOKEN":"..."}}}}The result has a status and a message that says what to do next:
| Status | Meaning |
|---|---|
done | The records are written, the domain is verified, and its MX points to mxgate. |
waiting_for_dns | The records are written, but the check does not see them yet. Call verify_domain later. |
planned | A dry run (dry_run: true). Nothing changed at the DNS host. |
conflict | The domain has MX records for another server. The agent must ask you, then call again with replace_mx: true. |
needs_credentials | The token for the DNS host is not in the environment. |
unsupported_dns_host | mxgate cannot write DNS at this host. The result has the records to add by hand. |
See DNS setup for each step.
Errors that an agent can correct
A tool that fails returns JSON with error, status, message and fix. error is a code to switch on. message is a sentence for a person. fix is the next step, for example the record to add or the call to make. Some errors also have retry_after_seconds or upgrade_url. See Errors.
What an agent cannot do
- Pay.
upgrade_planpays nothing. It returns a link, and the person who owns the account pays by card on a Stripe page. The plan is on about a minute after the payment, andget_accountshows it. - Confirm a destination. The owner of the mailbox opens the mailed link and presses "Confirm forwarding".
- Read the recovery token.
recover_keymails the token to the account address. The owner gives it to the agent. - Move your mail without consent.
replace_mxdeletes the MX records of another mail server. The agent must ask you first.
The API on one page
/llms.txt has the flow, the endpoints, the errors and the limits on one page of plain text. An agent with no MCP client can read it and call the API directly. The API reference has each endpoint in full.
An example prompt
Use mxgate to set up email for example.com. Forward all mail to you@gmail.com.
Do a dry run first and show me the plan. Do not replace an existing mail
server before I say yes. When the setup is done, show me the last rows of
the log.Notifications and webhooks
mxgate tells you when mail fails: an hourly email digest and a signed webhook. The payload, the signature check in Node.js, retries and the test call.
SDKs
The TypeScript and Python clients for the mxgate API: install, a first call, errors with a fix, and the check of a webhook signature.