What you'll build
A toy protocol server that lists tools and dispatches calls.
By the end of this lab you will have a tiny JSON-RPC-style server that
tells a host what tool it offers, what arguments that tool expects, and
how to call it by name. Instead of memorizing CLI flags, the caller sends
a structured request and gets a structured response back.
This is the conceptual move that matters. A protocol adapter gives the
rest of the stack a stable surface: discover first, then invoke through
the protocol boundary. That is the same basic shape real hosts use with MCP.
Run it
cd ai_ecosystem_labs
printf '%s\n' '{"id":1,"method":"tools/list"}' | python3 03-protocol-adapter/protocol_server.py
Starting here? Quick setup
git clone https://github.com/BanditF/ai_ecosystem_labs
cd ai_ecosystem_labs
printf '%s\n' '{"id":1,"method":"tools/list"}' | python3 03-protocol-adapter/protocol_server.py
Requires Python 3.8+. No additional packages needed for this lab.
Time guide. Setup: ~2 min. Working through it: 20–35 min because there are a few more moving parts than the earlier CLI labs.
Why this piece exists
Hosts need a way to discover and call tools without knowing implementation trivia.
If every host has to remember that one tool wants --term, another wants
positional arguments, and a third prints half-human output, integration gets brittle fast.
A protocol adapter fixes that by putting one predictable request/response shape in front of the tool.
A good real-world analog is MCP. The host does not care that your tool happens to be Python,
or that it reads files with pathlib. It cares that it can discover the tool,
inspect its schema, and call it reliably. This toy server teaches that shape before the real SDK adds transport, auth, and the rest.
Expected output
What a successful exchange looks like.
A tools/list request returns the tool schema:
{
"id": 1,
"result": {
"tools": [
{
"name": "term_count",
"description": "Count how often a term appears across local text files.",
"input_schema": {
"type": "object",
"required": ["term", "files"],
"properties": {
"term": {"type": "string"},
"files": {"type": "array", "items": {"type": "string"}}
}
}
}
]
}
}
A tools/call request returns structured results for the named tool:
{
"id": 2,
"result": {
"ok": true,
"items": [
{"file": "sample_docs/agents.txt", "count": 2},
{"file": "sample_docs/protocols.txt", "count": 0}
],
"summary": {"total": 2},
"errors": []
}
}
If your output has this shape, the lab is working. Notice that both responses are JSON and both carry the request id back.