I will likely run out of topics to cover in the “LLMs as Agents” series, eventually. I wanted to explore creating a chat UI and what I could do with embeddings but I didn’t do that. For more context , checkout the previous posts: Minimalist Approach, Taking Action, Planning via Prompting, Tools & Benchmarking, and WebAgent Gets a Refactor. Take a look at the repository to see the code.
I’m back to cover the new hotness in the agent space: Model Context Protocol (MCP) from Anthropic
MCP is having its jQuery moment, and that’s not an insult. Even OpenAI is adopting MCP. I’ve learned about hooking tools up to LLM-based agents, and it was always clear that competing standards for how to connect LLM-based agents to tools would create fragmentation and hurt growth. MCP is an open protocol for building agents. The MCP system contains:
- MCP Hosts: Programs like Claude Desktop, IDEs, or other tools that contain a client
- MCP Clients: Protocol clients that maintain 1:1 connections with servers
- MCP Servers: Lightweight programs that each expose specific capabilities through the standardized Model Context Protocol
There is a ton of information available for you to learn about MCP. This post is not teaching you about MCP.
This post is about MCP-enabling the ToolProvider
and ToolAgent
framework I created! That’s right, I’m exposing ToolProviders
as MCP Servers and ToolAgent
as an MCP Client — and I’m doing it with zero dependencies on any MCP framework. I like to see how things work under the covers. Having spent time building Browsers, I also believe that any open protocol needs to have multiple implementations, even crappy ones like mine.
Getting Started
Before I started building an MCP Client and Server from scratch, I made a few decisions:
- I was going to use Copilot to help do the work. It just seemed silly to try this otherwise.
- I copy/pasted many sections of the MCP Specification into a single, long Markdown document. I had to give Copilot the right context. I also used the MCP Schema.
- I was going to implement the stdio transport. HTTP SSE and WebSockets (I think) are also available, but a local system was good enough to learn how this works.
- I was only going to support “Tools” in my Client and Server for now. You can also support “Resources” and “Prompts”.
ToolProvider MCP Server
Copilot handled all the heavy lifting here. I asked to create an MCP Server, using the MCP specification, that was implemented using ToolProviders
. ToolProviders
already supported everything an MCP Server needed to support tools, so Copilot just had to map from my basic JSON schema to the JSON schema used by MCP.
Copilot was able to create a basic stdio-based request/response system, and was able to add the JSON-RPC message passing. Copilot was able to glean some of the messages and the message flows from the MCP specification.
I install Claude Desktop to test it out. It didn’t work on the first try. We missed some nuance of the JSON-RPC message passing, and we had to update some message types based on the newer schema. It did not take long before we had my ToolProvider
tools running in Claude as an MCP Server.

ToolAgent MCP Client
Again, Copilot did most of the work. We already had a basic stdio-based system working, and we had several other examples of ToolAgent
being used in different ways: terminal chat, web-based chat, and some tests — so it didn’t take long to have a a basic system with MCPToolProvider
(since ToolAgent
connects to ToolProviders
, we have to map MCP Servers to ToolProviders
) and an ToolAgentMCPClient
that wraps ToolAgent
itself.
I also installed a 3rd party MCP Server (filesystem
) so I could test a Server that wasn’t something I wrote.
We had to tweak the Client stdio request/response mechanism to get things working reliably. Timing, buffering, and flushing are important! Because the Client is spawning Servers as processes, I had to add some code to make sure the Client was cleaning up the spawned Servers on shutdown or in failure situations.
I was able to get both ToolProvider
MCP Server and filesystem
MCP Server working in my ToolAgent
-based MCP Client.
Connecting to MCP server: filesystem
Connected to secure-filesystem-server v0.2.0
Available tools: 11
Connecting to MCP server: mock
Connected to ToolProvider MCP Server v0.1.0
Available tools: 19
Chat with the agent (type 'exit' to quit, 'tools' to list tools):
> What files do I have in my Desktop folder?
Thought: "I need to find out which directories I have access to before I can list the files in the Desktop folder.",
"type": "call_tool", "tool": "list_allowed_directories"
Thought: "I can now list the files in the Desktop folder since I know I have access to it.",
"type": "call_tool", "tool": "list_directory", "param": "/Users/mfinkle-mac/Desktop"
Thought: "I made a mistake in the previous JSON format. The parameter for list_directory should be an object.",
"type": "call_tool", "tool": "list_directory", "param": { "path": "/Users/mfinkle-mac/Desktop"}
Thought: "I have successfully listed the files and directories in the Desktop folder.",
Agent: Here are the files and directories in your Desktop folder:
.DS_Store
.localized
Reactions (directory)
Screen Recording 2024-06-24 at 5.12.08 PM.mov
Screen Recording 2024-07-19 at 10.30.18 AM.mov
Screen Recording 2024-11-04 at 12.53.58 PM.mov
Screen Recording 2025-03-17 at 11.56.51 AM.mov
Stuff (directory)
live-chat-sample.mov
You can see ToolAgent
processing the requests, but it’s using the filesystem
MCP Server to perform the actions.
The code for both the ToolProvider
MCP Server and the ToolAgent
MCP Client are in the LLMs Agents repo.
Learnings
MCP is a very handy mechanism to connect LLM-based agents to different types of tools and resources. We’ve seen “broker” or “middleware” systems like this in the industry before and there is usually a lot of benefit for the ecosystem while a dominate player exists. I remember using COM and ODBC.
There are a lot of MCP Servers being created everyday (curated list). Many are of questionable value, but that’s the beauty to the early adopter phase. We’ll start to see some examples that push the boundaries, and that will be interesting.
There are fewer MCP Clients, but that will certainly change. I wonder how people (or IT/Security) will feel about all of this data being moved around. It makes me want to start looking into local models (another topic I failed to start).
What’s Next
I want to refactor the ToolProvider
approach to defining tools. The MCP approach is cleaner and would make the mapping code (from ToolProvider
to MCP Server) simpler too. The Playwright team released a Playwright MCP Server, which when used in my ToolAgent
MCP Client could mean WebAgent
doesn’t need to exist anymore.
With the growing number of easy to connect MCP Servers, I want to start investigating ways to orchestrate all those tools into a meaningful and useful Agent.
Also:
- Local Models
- Chat UI (and non-Chat UI) experiences
- Embedding and Vector search