One of the more interesting additions I’ve made recently to the Line of Succession website is support for the Model Context Protocol (MCP).
If you’ve spent any time around AI tooling recently, you’ve probably seen people talking about MCP. It’s often described as “USB for AI”, which is perhaps a little overblown, but the basic idea is sound. MCP provides a standard way for AI assistants to discover and use external tools and data sources.
In practical terms, it means that instead of building bespoke integrations for ChatGPT, Claude, Gemini and whatever comes next, you expose a standard MCP endpoint and let the AI clients do the rest.
For a data-driven site like Line of Succession, that seemed like an obvious experiment.
What is MCP?
The Model Context Protocol was originally developed by Anthropic and has rapidly become one of the emerging standards in the AI ecosystem.
An MCP server exposes:
- Information about itself
- A list of available tools
- Schemas describing how those tools should be called
- The results returned by those tools
An AI client can connect to the server, discover the available tools and invoke them when needed.
Instead of scraping web pages or attempting to infer information from HTML, the AI gets access to structured data.
That’s exactly the kind of thing Line of Succession is good at.
Why Add MCP?
The site already exposes information through a traditional web interface and a JSON API.
But those interfaces were designed for humans and developers respectively.
MCP gives AI systems a much cleaner integration point.
For example, an AI assistant can now answer questions like:
- Who was the British sovereign on 14 November 1948?
- What did the line of succession look like in 1980?
- Who was next in line when Queen Victoria died?
without having to scrape pages or understand the site’s internal URLs.
More importantly, it ensures that the information comes directly from the same database that powers the website.
The AI isn’t guessing.
It’s querying the source of truth.
As someone who runs a reference website, that’s a pretty attractive proposition.
The Initial Design
My first goal was to keep things simple.
Rather than exposing dozens of narrowly-focused tools, I started with just two:
sovereign_on_dateline_of_succession
Those two tools cover a surprisingly large proportion of the questions people are likely to ask.
The first returns the sovereign reigning on a given date. The second returns the line of succession for a specified date, with a configurable limit on the number of entries returned.
The implementation currently caps the list at thirty people. That’s enough for most use cases while preventing someone from accidentally asking for all six thousand people currently in the line of succession.
One thing I learned quite quickly is that MCP isn’t really about exposing huge amounts of data. It’s about exposing useful questions that can be answered from your data.
MCP Is Mostly JSON-RPC
One thing that surprised me when I first started reading the specification was how little protocol code is actually required.
At its core, MCP uses JSON-RPC.
A client sends requests like:
|
1 2 3 4 5 |
{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" } |
and the server responds with:
|
1 2 3 4 5 6 7 |
{ "jsonrpc": "2.0", "id": 1, "result": { ... } } |
Once I’d written helper methods for creating standard JSON-RPC responses, most of the complexity disappeared.
The MCP module contains methods like:
|
1 |
sub rpc_result ($self, $id, $result) |
and:
|
1 |
sub rpc_error ($self, $id, $code, $message) |
which means the Dancer route handlers remain pleasantly small.
The protocol logic lives in one place and the web application simply delegates to it.
Separating the MCP Logic
I didn’t want protocol-specific code scattered throughout the web application.
Instead, I created a dedicated module:
|
1 |
package Succession::MCP; |
This module is responsible for:
- Initialisation
- Tool discovery
- Tool execution
- JSON-RPC response generation
- Error handling
That keeps the Dancer routes thin and makes the MCP implementation easier to test independently.
It also means that if I ever decide to expose the same MCP server through a different transport mechanism, most of the work is already done.
Tool Calls Are Mostly Adapters
One pleasant surprise was how little new application logic I actually had to write.
The MCP server needs to expose tools, but those tools ultimately just answer questions about the succession database. The code to answer those questions already existed.
For example, the application’s model layer already contained methods such as:
sovereign_on_date()line_of_succession()
These methods power parts of the website itself, so they already encapsulate all of the business rules and database queries.
The MCP implementation simply acts as an adapter.
When a tool call arrives, the server extracts the arguments, validates them and passes them to the existing model methods:
|
1 2 3 4 5 |
sub _call_tool ($self, $tool_name, $args) { my $tool = $self->_tool_dispatch->{$tool_name}; return $tool->($args); } |
The tool implementations themselves are deliberately thin:
|
1 2 3 4 5 6 7 8 |
sub sovereign_on_date ($self, $args) { my $date = $args->{date}; my $sovereign = $self->model->sovereign_on_date($date); ... } |
That’s exactly how I wanted it to work.
The MCP layer doesn’t know how to calculate a line of succession or determine who was sovereign on a particular date. It simply knows how to expose those capabilities through the protocol.
This is one of the advantages of adding MCP to an existing application. If your business logic is already cleanly separated from your web interface, an MCP server often becomes surprisingly straightforward to implement.
In many ways, adding MCP feels less like building a new application and more like adding another interface alongside the website and API.
The YAML Epiphany
The most interesting design decision came a little later.
Initially, the tool definitions lived in Perl data structures.
That worked, but it quickly became obvious that I was duplicating information.
The MCP server needed tool descriptions.
The documentation page needed tool descriptions.
The schemas needed to be defined somewhere.
And every change required updating multiple places.
The obvious answer was to move all of the tool definitions into a YAML file.
The MCP module now loads its tool definitions at startup:
|
1 |
sub _build__tools ($self) { return LoadFile($self->tools_file); } |
The result is a single source of truth.
The same YAML file drives:
- The
tools/listresponse - Tool metadata
- JSON schemas
- Human-readable documentation
Adding a new tool now involves updating one file and writing the code that implements it.
Everything else follows automatically.
Here’s the current YAML file:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# data/mcp-tools.yml - name: sovereign_on_date description: Return the British sovereign on a given date. documentation: | Looks up the reigning British sovereign for the supplied date. Use this when answering questions such as “Who was sovereign on 6 February 1952?” inputSchema: type: object properties: date: type: string description: Date in YYYY-MM-DD format. required: - date - name: line_of_succession description: Return the line of succession on a given date. documentation: | Returns people in the line of succession. If no date is supplied, the current line of succession is returned. inputSchema: type: object properties: date: type: string description: Optional date in YYYY-MM-DD format. Omit for the current line of succession. limit: type: integer description: Maximum number of successors to return. minimum: 1 maximum: 100 required: [] |
Looking back, this is probably the part of the design I’m happiest with. It feels very Perl-ish: keep configuration as data and avoid duplicating information wherever possible.
Human Documentation Matters
One thing I noticed while exploring other MCP servers is that many of them are effectively invisible to humans.
You know an endpoint exists.
You know it speaks MCP.
But unless you inspect the protocol responses manually, you don’t really know what it does.
I decided to add a conventional web page at /mcp.
The page lists all available tools, their descriptions and their schemas.
The nice part is that there is no duplicated documentation.
The page is generated from the same YAML definitions used by the MCP server itself.
If I add a new tool tomorrow, both the machine-readable and human-readable views update automatically.
Structured Data and Text Responses
Another nice feature of MCP is that tool results can include both structured data and human-readable text.
For example, a tool response might contain:
|
1 2 3 4 5 6 7 8 9 |
{ "content": [ { "type": "text", "text": "The sovereign on 14 November 1948 was George VI." } ], "structuredContent": { ... } } |
The structured content is useful for software.
The text is useful for humans and language models.
Both are generated from the same underlying data.
That gives AI clients flexibility while ensuring consistency.
Getting Listed
Once everything was working, I submitted the server to the MCP directory at mcpservers.org.
That might seem like a small step, but discoverability is important.
An MCP server hidden on a random website isn’t much use if nobody knows it exists.
Directories like that are rapidly becoming the equivalent of API catalogues for the AI era.
Being listed means developers and AI enthusiasts can find the service without first discovering the website.
Was It Worth It?
Absolutely.
The amount of code required was surprisingly small. Most of the work wasn’t implementing the protocol; it was deciding how best to expose the data.
More importantly, it opens the site up to an entirely new audience: AI agents.
Historically, websites were built for humans and APIs were built for developers.
MCP introduces a third category: services designed specifically for AI systems.
For a structured-data site like Line of Succession, that’s a natural fit.
Will MCP still be the dominant standard in five years’ time? I have no idea. The AI industry changes too quickly to make confident predictions.
But right now it has significant momentum, broad industry support and a growing ecosystem of tools.
And if nothing else, it’s rather satisfying to ask an AI who was on the throne on a particular date and know that the answer came directly from my database rather than from whatever the model happened to remember.
