mcp-sdk: an Introduction to creating an MCP service with Clojure
Arne recently launched mcp-sdk, a flexible, pure Clojure SDK for building Model Context Protocol (MCP) servers.
MCP is set of standards that allows large language models (LLMs), like Anthropic's Claude and Microsoft Copilot, to communicate with external systems like data, APIs, custom built tools, or specific prompts. Companies like Google, Notion, and Figma use MCP services to let users integrate their apps directly into their own AI workflows. You can visit the official MCP documentation for a more detailed explanation.
If you're new to building MCPs in general, I'm going to walk through the example MCP weather service that we have in our project. I'll point out some notable code, then show to run the service on your own machine and connect to it from Claude desktop.
Building our own Weather Tool
We have an example weather service tool that lets us use an an LLM like Claude to ask for alerts and the weather forecast from any state or location in the US. Since we need to call the National Weather Service API for our data, we'll be configuring an MCP tool. I'll point out some notable code snippets.
Configuring the weather alerts tool (code)
(state/add-tool
{:name "weather_alert"
:title "Weather Alert Tool"
:description "Given a two letter state code, finds the weather alerts for that state."
:schema {"type" "object"
"properties" {"state" {"type" "string"
"description" "Two letter state code"}}
"required" ["state"]}
:tool-fn (fn [_req {:keys [state]}]
(let [alerts (get-alerts state)]
{:content [{:type :text
:text (str/join "\n" (vec alerts))}]
:isError false}))})The NWS alert API takes a two letter state code, which we're passing in as a parameter called state. In the example above, we defined an input schema (called inputSchema in the official docs) with a data type and description.
tool-fn is the function that gets called when our MCP tool is invoked. We can pull the state from our input params that we defined in schema and use it to make a simple clojure to get-alerts, which handles the HTTP request and data parsing. We need to make sure this function returns data in the correct schema - here, we're simply returning plain text, defined as TextContent, as defined here.
To run our service, we need to run co.gaiwan.mcp/run-http
(mcp/run-http! {:port 3999})Claude Desktop
You can simply connect to your new running MCP server using Claude connectors; however, if you're using a free Claude plan, you'll have to use a tool like mcp-remote in order to connect to the MCP tool we created. You can install it using npm install -g mcp-remote
Open Claude Desktop, open your settings, and then click on the "Developer" option. You can access your claude_desktop_config.json file by clicking "Edit Config."

Add this snippet to your claude_desktop_config.json
{
"mcpServers": {
"clojure-weather-tool": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:3999/mcp",
"--allow-http"
]
}
}
}
Save your file, restart Claude, and you should now see your service when you click on the "Search and Tools" button. Test it out yourself!


Try asking Claude: "What is the current forecast for New York City?"
I hope this was a useful introduction to learning how to create your own MCP server in Clojure! Have any more questions or need some developers to help build out your MCP tools? Feel free to contact us!
Comments ()