MCP Servers for Domains: Tools, Tokens, PHP Client

Updated: 5 min read Editorial team The PHP Experts
Short answer

An MCP server for domains gives AI assistants such as Claude or ChatGPT tools to check availability, prices and registry data instead of guessing. betadomain runs one at https://betadomain.de/api/mcp with five open read tools and three account tools that need a bearer token. Ordering always ends in a checkout link that a person completes.

Ask a language model whether a domain is free and it will often answer confidently, from training data that may be two years old. That is the problem MCP servers for domains solve. Instead of guessing, the model calls a tool that asks the registry and returns the answer it got.

What the Model Context Protocol actually is

MCP is an open protocol, first published by Anthropic, that lets an AI client discover and call tools exposed by a server. Under the hood it’s JSON-RPC 2.0. With the Streamable HTTP transport the client sends POST requests to a single URL; the methods you’ll meet first are initialize, tools/list and tools/call.

A domain-focused server maps those calls to real lookups. The model sees a tool called domain_check with a JSON schema for its input, decides to call it, and gets structured data back. Less hallucination, more facts.

The tools on betadomain’s MCP server

The endpoint is https://betadomain.de/api/mcp. A machine-readable server card lives at /.well-known/mcp.json; the human version is the developer and AI agent page.

ToolPurposeTokenPer-IP limit
domain_checkavailability and annual prices for one domainno30/min, 300/6 h
domain_searchone name across eight popular TLDsno30/min, 300/6 h
whois_lookupstatus, dates, nameservers, registrar, no personal datano15/min, 150/6 h
list_tldssupported TLDs with prices, filterableno30/min, 300/6 h
tld_priceprices for a single TLDno30/min, 300/6 h
list_my_domainsyour domains with expiry and auto-renewyesplus 20/min per token
start_orderprepares an order, returns a checkout linkyesplus 20/min per token
get_order_statusstatus of one of your ordersyesplus 20/min per token

whois_lookup has a tighter limit because each call hits an external registry. It follows the same RDAP-first logic described in RDAP in PHP.

Connecting the server to Claude or ChatGPT

In Claude, open Settings, then Connectors, add a custom connector and paste the endpoint URL. That’s enough for the read tools: ask “Which extensions are still free for myproject, and what does renewal cost?” and Claude will call domain_search. ChatGPT works the same way where your plan offers custom connectors or developer mode.

For the account tools, create a personal access token in the portal at /account/profile. Tokens start with bdk_live_, are displayed once and stored only as a SHA-256 hash. You can have up to 20 active tokens and revoke each one separately, which is useful when you give different agents different tokens.

Calling MCP tools from PHP

You don’t need an AI client to use the tools. Since it’s JSON-RPC over HTTP, cURL does the job. I ran this class against the live server:

<?php
declare(strict_types=1);

final class McpClient
{
    private int $id = 0;

    public function __construct(
        private string $endpoint = 'https://betadomain.de/api/mcp',
        private ?string $token = null,
    ) {}

    public function call(string $method, array $params = []): array
    {
        $payload = ['jsonrpc' => '2.0', 'id' => ++$this->id, 'method' => $method];
        if ($params !== []) {
            $payload['params'] = $params;
        }
        $headers = ['Content-Type: application/json', 'Accept: application/json, text/event-stream'];
        if ($this->token !== null) {
            $headers[] = 'Authorization: Bearer ' . $this->token;
        }

        $ch = curl_init($this->endpoint);
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => json_encode($payload, JSON_THROW_ON_ERROR),
            CURLOPT_HTTPHEADER     => $headers,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 15,
        ]);
        $body   = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
        if ($body === false) {
            throw new RuntimeException(curl_error($ch));
        }
        if ($status === 401) {
            throw new RuntimeException('Missing or invalid token (HTTP 401)');
        }

        $response = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
        if (isset($response['error'])) {
            throw new RuntimeException($response['error']['message'], $response['error']['code']);
        }
        return $response['result'];
    }

    public function tool(string $name, array $arguments = []): array
    {
        $result = $this->call('tools/call', ['name' => $name, 'arguments' => $arguments]);
        if ($result['isError'] ?? false) {
            throw new RuntimeException($result['content'][0]['text'] ?? 'Tool error');
        }
        // structuredContent is easier; content[0].text is the fallback
        return $result['structuredContent'] ?? json_decode($result['content'][0]['text'], true, 512, JSON_THROW_ON_ERROR);
    }
}

$mcp = new McpClient();
$init = $mcp->call('initialize', [
    'protocolVersion' => '2025-06-18',
    'capabilities'    => new stdClass(),
    'clientInfo'      => ['name' => 'php-demo', 'version' => '0.1'],
]);
echo $init['serverInfo']['name'], ' speaks ', $init['protocolVersion'], "\n";
print_r($mcp->tool('tld_price', ['tld' => '.io']));

The handshake returned server name betadomain and protocol version 2025-06-18. tld_price for .io returned registerEur, transferEur and renewEur, all the same value, currently €61.99, plus billing and a vatIncluded flag set to false because prices are net.

Call list_my_domains without a token and you get HTTP 401 with a WWW-Authenticate header pointing at /.well-known/oauth-protected-resource, not a JSON-RPC error in a 200 response. That’s how MCP clients learn where to authenticate, and it’s why the class checks the status code before decoding.

Where the agent has to stop

start_order takes a list of items like {"domain": "mycompany.com", "type": "register"}, plus an auth code for transfers. The server re-checks availability and price, never trusts values from the request, and returns a payment_url. Payment happens in Stripe Checkout in a browser; some registries also require identity verification. If buyer details are incomplete you get a plain not_ready_for_payment notice rather than a half-created order.

I think that’s the right design. An agent that misreads an instruction and registers twenty domains on your card is not a bug report anyone wants to file. If you genuinely need unattended registrations, a registrar API with a prepaid balance is the better fit; the domain registration API comparison lays out the options.

Models also mangle input sometimes, internationalised names in particular. The server converts IDNs itself, but for your own scripts the punycode guide is worth a read.

Building your own MCP server in PHP

To expose your own data, say your agency’s client domains, you need your own server. mcp/sdk is the official PHP SDK. For a small stateless server, a single endpoint that dispatches JSON-RPC methods to a few functions also works; the protocol stays manageable as long as you don’t need streaming.

The quickest way to learn MCP is still to use a working server. Connect Claude to betadomain, ask about a name and watch which tools it calls. Cross-check the result in the betadomain domain search, and if you’re naming an AI product, the .ai extension page shows the annual price of €203.99 up front.

Step by step

  1. Add a custom connectorIn Claude open Settings, then Connectors, add a custom connector and enter https://betadomain.de/api/mcp.
  2. Try the read toolsAsk something like "Is mycompany.com available at betadomain?". Read tools need no token.
  3. Create a tokenIn the customer portal at /account/profile, section MCP / API access, create a token. It starts with bdk_live_ and is shown once.
  4. Attach the tokenStore it as the connector's bearer token, sent as Authorization: Bearer <token>.

Found your domain? You can check and register it directly at compare domain prices – yearly price from €2.99.

FAQ

Do I need an account to use the MCP server?

Not for the read tools: domain_check, domain_search, whois_lookup, list_tlds and tld_price work anonymously with per-IP limits. list_my_domains, start_order and get_order_status require a personal access token created in the customer portal.

Can Claude buy a domain on its own through MCP?

No. start_order validates availability and price on the server and returns a Stripe checkout URL. You complete payment, plus identity verification where a registry requires it. Nothing is charged automatically.

Do I have to call initialize before tools/call?

The MCP specification defines a handshake and clients like Claude perform it automatically. betadomain's server is stateless and answers tools/call without a prior initialize, which is handy in scripts, but doing the handshake keeps your client portable.

Is there an official PHP SDK for MCP?

Yes, mcp/sdk on Packagist is the PHP SDK from the Model Context Protocol project. For a client calling an HTTP server, cURL and json_encode are enough, as shown here.