Making Your Own MCP Server in Midio
Last updated
Was this helpful?
Last updated
Was this helpful?
We’ve recently added a new package (mcp-server
) to the Midio package registry, which lets you easily create your own MCP servers with little effort. What’s especially cool is that the entire package is built in pure Midio, which means you can peek under the hood and learn exactly how things work.
While there are still a few rough edges to smooth out, it’s already surprisingly easy to set up and customize your own MCP server. Let’s walk through how it works and how you can create your own MCP server.
In the end, we'll connect it to Cursor to see it in action.
The MCP protocol supports several different transport variants, but Midio currently supports only the most widely adopted one: Server-Sent Events (SSE).
This approach requires defining two endpoints to establish a two-way communication channel with the client.
The first establishes a long-lived SSE stream from the server to the client.
The second one is for all subsequent requests from the client.
Next up, we'll discuss the lifecycle of our MCP server.
When a client connects to our MCP server, it will first connect to our GET endpoint to set up the long-lived stream. We do this on the server side by responding with the Start SSE Response
, which establishes our response as a stream. We then use the Send POST Endpoint Response from the mcp-server
package to tell the client where to send any subsequent POST requests. We embed a unique ID as a query parameter in the URL, which ensures each client session is isolated and supports concurrent usage.
Once the connection is established and the client has the POST endpoint, we enter the message loop:
Clients send requests to the provided POST endpoint.
The server listens and responds via the SSE stream.
Additionally, the server regularly pings the client to see if it is still there.
Midio has a powerful primitive called Merge on Key that makes this kind of setup quite simple. This node allows a process to merge with another process by using a shared key—in this case, the clientId
. It essentially routes the client’s POST request back to the correct SSE stream process.
Lastly, instead of just ending the process when we time out, like in the example above, we want to try pinging the client to see if it is still there. We can do this like so:
If we time out waiting for a request from the client, and we haven't already tried to ping it, we send a ping message using the Send Ping Message
function. This prompts the client to send us a response, which is handled like any other request. If we receive a response, we reset the didPing
variable. If we don't receive a response, the next time the If
function is called after a timeout, we will not ping again, log the event and terminate the process.
Let’s create a minimal MCP server for managing a todo list from Cursor. Cursor only supports using MCP tools, so we’ll implement everything as tools inside Midio.
The Handle MCP Request
function expects a list of tools, which can be any Midio function. We'll create three functions that handle our basic requirements:
These functions are included in the mcp-server
package’s Example
module, so you don't have to create them yourself.
Get Todos
Add Todo
Complete Todo
Once defined, we bundle the three functions together and pass them into the tools
input of Handle MCP Request
.
We hook it up to our Handle MCP Request
function like so:
At this point, your todo MCP server is ready to go.
Lets connect to our server from Cursor.
Copy the URL of the main endpoint, and add an entry to your Cursor MCP settings (File → Preferences → Cursor Settings → MCP → press the "Add new global MCP server" button).
Cursor should automatically try connecting to your server. If not, make sure it is enabled and try clicking the refresh icon.
It’s still early days for this feature, and as you can see, it does require a little bit of setup, but we think it's pretty cool that everything can be implemented directly in Midio.
There are still some MCP features that we haven't added support for, like logging, completion, progress, change notifications and a few more, but we plan on expanding the feature set over time, and based on demand.
Each function uses a shared to persist todos across requests. To ensure client data remains isolated, we use Midio’s partial function application feature to pre-apply the clientId
to each function. This keeps todos for each client separated.
Add the following to your config ().
Get Todo List Tools
function returns our functions as a list.