← All articles
10 min read

How we shipped an MCP server for our REST API in a day

RegIntel is a REST API for structured regulatory data. As of this week, it is also an MCP server. Any Claude Desktop, Claude Code or other MCP-compatible client can now discover and call our five regulatory-data tools — without writing a line of integration code. This is the build log, including the bits we got wrong.

Why ship an MCP server when you already have a REST API?

The Model Context Protocol is, in one sentence, the standard wire format for LLM tool use. Without it, every API provider asks every LLM-app developer to do the same work: read the docs, write Pydantic-or-equivalent schemas, build a thin tool wrapper, document the calling conventions back to the model in a system prompt, handle auth, handle errors. With it, that work happens once — on the server side — and every MCP client gets the tools for free.

For an API like ours, that shifts the distribution model. Before MCP, the question was "how many developers will install our REST client?" After MCP, the question is "how many people use an MCP-compatible LLM client?" — which today already means Claude Desktop, Claude Code, Cursor, Zed, Continue, and a growing list. The ceiling is much higher, and the integration cost for the end user drops from an afternoon to roughly thirty seconds.

⚡ Strategic point

An MCP server is not a replacement for a REST API. It is a second front door to the same data, optimised for LLM clients instead of human developers. Both should exist; neither makes the other obsolete.

Tool surface: what to expose

RegIntel's REST API has nine customer-facing endpoints. Not all of them belong in an MCP server. We picked six: the ones an LLM agent would actually want to call, as opposed to the ones a human developer uses to manage their account.

The account endpoints — request a new key, check usage, create a Stripe checkout session — are deliberately out of scope. They are user-administration concerns, not data queries, and giving an LLM agent the ability to mint API keys on behalf of a user is a footgun nobody asked for.

Transport: stdio, not HTTP

MCP supports both stdio and HTTP/SSE transports. For a client-installed server like this one, stdio is the obvious choice. The server runs as a subprocess of the user's MCP client (Claude Desktop, Claude Code, etc.), communicating over standard input/output using newline-delimited JSON-RPC. The client launches it on demand, our REST API is called over HTTPS from the user's machine, and there is no separate hosted MCP service for us to operate.

HTTP transport would have meant standing up another long-running service, with its own auth model, scaling concerns, and uptime page. For a thin wrapper over an existing REST API, that is gratuitous complexity. Stdio means no new infrastructure.

The code

We used FastMCP from the official Python SDK (mcp[cli]>=1.2.0). The decorator-and-type-hints pattern is genuinely lovely — the SDK introspects the function signature and docstring and generates the JSON Schema for the tool automatically. A complete tool looks like this:

python — a real tool from regintel-mcp
from mcp.server.fastmcp import FastMCP
import httpx, os

mcp = FastMCP("regintel")
API_KEY = os.environ.get("REGINTEL_API_KEY", "")

@mcp.tool()
async def check_compliance(country: str, activity: str) -> str:
    """Check the regulatory status of an activity in a country.

    Returns one of: allowed, requires_license, restricted, prohibited.

    Args:
        country: Country code or name, e.g. "AU", "US", "EU".
        activity: One of crypto, payments, kyc, aml, lending, ...
    """
    async with httpx.AsyncClient(timeout=30.0) as client:
        r = await client.get(
            "https://api.regintelapi.com/compliance-check",
            headers={"x-api-key": API_KEY},
            params={"country": country, "activity": activity},
        )
    if r.status_code == 401:
        return "Error 401: check REGINTEL_API_KEY is set correctly."
    return r.text

Two design points worth calling out:

Errors as text, not exceptions. A failed REST call returns a plain string starting with "Error 401: ..." rather than raising. The reason: the LLM consuming the tool output sees a readable, actionable message and can pick a sensible next step. If we raised, the client would surface a stack trace as a tool failure, which is much less useful in context.

Never write to stdout. Under stdio transport, anything printed to stdout corrupts the JSON-RPC frames the client is parsing. The SDK warns about this loudly in the docs, but it is the kind of mistake that is easy to introduce later — a leftover print() for debugging, a logging call without a stderr handler — and very hard to diagnose. Our server's startup banner goes to sys.stderr explicitly.

Publishing: three registries, one annoying gotcha

To get full distribution we needed to publish in three places: PyPI (the package itself), the official MCP Registry at registry.modelcontextprotocol.io (the canonical discovery surface for MCP clients), and the community awesome list at punkpeye/awesome-mcp-servers (which a lot of developers browse directly). PyPI was straightforward. The MCP Registry had a gotcha.

The Registry uses io.github.<username>/<package> as the namespace when you authenticate with GitHub OAuth. That part is reasonable — it proves you control the GitHub identity. But the Registry also wants proof that you control the corresponding PyPI package. Its verification mechanism: it checks the PyPI README for a literal marker string of the form mcp-name: io.github.<username>/<package>.

We forgot. Our first publish attempt returned:

mcp-publisher — verification failure
Error: publish failed: server returned status 400
"PyPI package 'regintel-mcp' ownership validation failed. The server name
 'io.github.ad0750/regintel-mcp' must appear as
 'mcp-name: io.github.ad0750/regintel-mcp' in the package README"

The fix is a one-line addition to README.md (an HTML comment in markdown works fine: <!-- mcp-name: io.github.ad0750/regintel-mcp -->). The annoying bit is that PyPI is immutable per version — you cannot edit a README after upload. So the fix requires a version bump, a rebuild, a re-upload to PyPI, and only then a successful Registry publish. We went from 0.1.0 to 0.1.1 for what is, semantically, a no-op release.

⚠ Lesson

If you are publishing an MCP server to PyPI for the first time, add the mcp-name: marker to your README before the first PyPI upload. It costs nothing and saves a version bump.

Try it

If you have an MCP-compatible client, installing RegIntel takes about a minute. Get a free API key (100 credits, no card) at /get-key.html, install the package, and point your client at it.

bash — install
pip install regintel-mcp

For Claude Desktop, add this to claude_desktop_config.json:

json — Claude Desktop config
{
  "mcpServers": {
    "regintel": {
      "command": "regintel-mcp",
      "env": { "REGINTEL_API_KEY": "your-key-here" }
    }
  }
}

For Claude Code:

bash — Claude Code
claude mcp add regintel -e REGINTEL_API_KEY=your-key-here -- regintel-mcp

Restart your client. Six tools — list_jurisdictions, search_regulations, get_regulation, get_recent_updates, check_compliance, get_aasb_s2_obligations — should appear under the regintel server. Ask your model "is crypto trading allowed in Australia?" and watch it route the question to check_compliance(country="AU", activity="crypto") on its own.

What we'd do differently

Three things, looking back at the day's work:

  1. The README marker first. See the callout above. One line, saves a version bump.
  2. Trusted Publishing from day one. We used a classic PyPI API token for the first upload. A cleaner setup is OIDC-based Trusted Publishing from GitHub Actions — no long-lived secret on the local machine. We will migrate before 0.2.0.
  3. An examples/ directory. The README has install instructions and a tools table, but no recipes. A small set of example prompts ("find all crypto regulations in the EU added since 2026-01-01") would help model developers see the value quickly.

The source is on GitHub at ad0750/regintel-mcp — MIT licensed, issues and PRs welcome.

Try RegIntel via MCP today

Get a free API key with 100 credits — no credit card required.

Get a free API key →