I final wrote in regards to the Mannequin Context Protocol (MCP) in December 2024, shortly earlier than the subject’s exponential progress into what it’s immediately. I recall pondering on the time that one of many key issues I felt needed to occur to MCP to make the know-how a recreation changer was the flexibility for MCP purchasers to entry non-local MCP servers. That’s already occurring, after all, however what if you need a chunk of this motion? How do you go about writing a distant MCP server, crafting helpful instruments for it, testing it, then deploying it to the cloud, for instance, in order that anybody can entry the instruments it exposes from any supported shopper wherever on the planet?
I’ll present you how you can do all of these issues on this article.
A fast recap on what an MCP server is
There are dozens of definitions for what an MCP server is. For my part, which might be a little bit of an oversimplification, an MCP server permits MCP-enabled purchasers, resembling Cursor and Claude code, to name helpful capabilities that the MCP server comprises.
How is that completely different from you simply writing a bunch of helpful instruments and calling them in your code?
Effectively, the hot button is that you’re writing these instruments. What in regards to the potential universe of instruments that exist that another person has written? I’m certain you’ve heard the expression “… there’s an app for that”. Within the not-too-distant future, that may develop into “… there’s an MCP server for that“. Okay, not as snappy however simply as groundbreaking.
Till now, the overwhelming majority of MCP servers have been written with the STDIO transport kind in thoughts. Because of this the onus is on you to host the server in your native system. That may generally be difficult and vulnerable to error. Furthermore, solely you possibly can entry that server. And that’s the place distant (or Streamable HTTP) MCP servers come into their very own. Hosted remotely, you solely have to know the URL of the server and the names of the instruments it gives, and also you’re up and working with it in seconds.
So, if you happen to’ve written one thing that others would possibly discover really useful, why not make a distant MCP server of it, host it within the cloud and let others use it too?
Okay, let’s do that.
My setup
I’ll be growing the code for the MCP server and its instruments utilizing Home windows and Microsoft Visible Studio Code. I’ll be utilizing Git Bash for my command line because it comes with some useful utilities that I’ll use, resembling curl and sed. You’ll additionally want to put in Node.js and the uv Python package deal utility. If you wish to deploy the completed MCP server to the cloud, you’ll additionally have to retailer your code on GitHub, so that you’ll want an account for that.
The very first thing you must do is initialise a brand new mission on your code, and so forth. Use the uv device with the init flag for this. Subsequent, we add an surroundings, swap to it and add all of the exterior libraries that our code will use.
$ uv init remote-mcp
Initialized mission `remote-mcp` at `/house/tom/tasks/remote-mcp`
$ cd remote-mcp
$ ls -al
complete 28
drwxr-xr-x 3 tom tom 4096 Jun 23 17:42 .
drwxr-xr-x 14 tom tom 4096 Jun 23 17:42 ..
drwxr-xr-x 7 tom tom 4096 Jun 23 17:42 .git
-rw-r--r-- 1 tom tom 109 Jun 23 17:42 .gitignore
-rw-r--r-- 1 tom tom 5 Jun 23 17:42 .python-version
-rw-r--r-- 1 tom tom 0 Jun 23 17:42 README.md
-rw-r--r-- 1 tom tom 88 Jun 23 17:42 primary.py
-rw-r--r-- 1 tom tom 156 Jun 23 17:42 pyproject.toml
$ uv venv && supply .venv/bin/activate
# Now, set up the libraries we are going to use.
(remote-mcp) $ uv add fastapi 'uvicorn[standard]' mcp-server requests yfinance python-dotenv
What we’ll develop
We’ll develop an MCP server and two distinct instruments for our MCP server to utilise. The primary will likely be a Nobel Prize checker. You present a 12 months, e.g, 1935, and a topic, e.g, Physics, and the MCP server will return details about who received the prize that 12 months in that topic. The second device will return the utmost recorded temperature for a metropolis prior to now week
First off, we’ll code our two instruments and take a look at them regionally. Subsequent, we are going to incorporate the instruments into an MCP server working regionally and take a look at that setup. If it really works as anticipated, we will deploy the MCP server and its instruments to a distant cloud server and confirm that it continues to operate appropriately.
Code Instance 1— Getting Nobel prize data
The providers of the Nobel Prize web site are licensed beneath the Inventive Commons zero license. You may see the main points utilizing the hyperlink beneath:
https://www.nobelprize.org/about/terms-of-use-for-api-nobelprize-org-and-data-nobelprize-org
Right here is the bottom operate we’ll use. Open up your code editor and save this content material in a file referred to as prize_tool.py.
import requests
import os
import io
import csv
# from mcp.server.fastmcp import FastMCP
strive:
from mcp.server.fastmcp import FastMCP
besides ModuleNotFoundError:
# Attempt importing from a neighborhood path if working regionally
import sys
sys.path.append(os.path.abspath(os.path.be part of(os.path.dirname(__file__), '..')))
from fastmcp import FastMCP
mcp = FastMCP(identify="nobelChecker",stateless_http=True)
@mcp.device()
def nobel_checker(12 months, topic):
"""
Finds the Nobel Prize winner(s) for a given 12 months and topic utilizing the Nobel Prize API.
Args:
12 months (int): The 12 months of the prize.
topic (str): The class of the prize (e.g., 'physics', 'chemistry', 'peace').
Returns:
record: An inventory of strings, the place every string is the complete identify of a winner.
Returns an empty record if no prize was awarded or if an error occurred.
"""
BASE_URL = "http://api.nobelprize.org/v1/prize.csv"
# Put together the parameters for the request, changing topic to lowercase
# to match the API's expectation.
params = {
'12 months': 12 months,
'class': topic.decrease()
}
strive:
# Make the request utilizing the protected 'params' argument
response = requests.get(BASE_URL, params=params)
# It will increase an exception for dangerous standing codes (like 404 or 500)
response.raise_for_status()
# If the API returns no knowledge (e.g., no prize that 12 months), the textual content will
# usually simply be the header row. We examine if there's multiple line.
if len(response.textual content.splitlines()) <= 1:
return [] # No winners discovered
# Use io.StringIO to deal with the response textual content (a string) like a file
csv_file = io.StringIO(response.textual content)
# Use DictReader to simply entry columns by identify
reader = csv.DictReader(csv_file)
winners = []
for row in reader:
full_name = f"{row['firstname']} {row['surname']}"
winners.append(full_name)
return winners
besides requests.exceptions.RequestException as e:
print(f"An error occurred in the course of the API request: {e}")
return [] # Return an empty record on community or HTTP errors
if __name__ == "__main__":
knowledge = nobel_checker(1921,"Physics")
print(knowledge)
This script defines a small “nobel-checker” MCP (Mannequin Context Protocol) device that may be run both regionally or inside a FastMCP server. After making an attempt to import FastMCP
from the mcp.server
package deal, and falling again to a sibling fastmcp
module if that import fails. It then constructs an MCP occasion named nobelChecker with the stateless_http=True flag, which means that FastMCP will robotically expose a plain HTTP endpoint for one-shot calls. The adorned operate nobel_checker turns into an MCP device. When invoked, it constructs a question to the Relaxation API utilizing the equipped 12 months and material, and returns the identify(s) of the prize winner for that 12 months and topic (or a useful message if not).
If we run the above code regionally, we acquire output just like the next, which signifies that the operate is working appropriately and performing its supposed job.
['Albert Einstein']
Code Instance 2— Getting metropolis temperature data
For our second base operate, we’ll write a device that returns the best temperature for a metropolis over the past week. The climate knowledge is supplied by Open-Meteo.com. On their license web page (“https://open-meteo.com/en/license), it states,
“API knowledge are provided beneath Attribution 4.0 International (CC BY 4.0)
You might be free to share: copy and redistribute the fabric in any medium or format and adapt: remix, rework, and construct upon the fabric. “
I’ve given the proper attribution and hyperlink to their license, which fulfills the phrases of their license.
Create the Python file temp_tool.py and enter this code.
# temp_tool.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP(identify="stockChecker", stateless_http=True)
import requests
from datetime import datetime, timedelta
# This helper operate could be reused. It is not tied to a selected API supplier.
def get_coords_for_city(city_name):
"""
Converts a metropolis identify to latitude and longitude utilizing a free, open geocoding service.
"""
# Utilizing Open-Meteo's geocoding, which can be free and requires no key.
GEO_URL = "https://geocoding-api.open-meteo.com/v1/search"
params = {'identify': city_name, 'rely': 1, 'language': 'en', 'format': 'json'}
strive:
response = requests.get(GEO_URL, params=params)
response.raise_for_status()
knowledge = response.json()
if not knowledge.get('outcomes'):
print(f"Error: Metropolis '{city_name}' not discovered.")
return None, None
# Extract the very first outcome
location = knowledge['results'][0]
return location['latitude'], location['longitude']
besides requests.exceptions.RequestException as e:
print(f"API request error throughout geocoding: {e}")
return None, None
@mcp.device()
def get_historical_weekly_high(city_name):
"""
Will get the best temperature for a metropolis over the earlier 7 days utilizing the
commercially-friendly Open-Meteo API.
Args:
city_name (str): The identify of the town (e.g., "New York", "London").
Returns:
float: The very best temperature in Fahrenheit from the interval, or None if an error happens.
"""
# 1. Get the coordinates for the town
lat, lon = get_coords_for_city(city_name)
if lat is None or lon is None:
return None # Exit if metropolis wasn't discovered
# 2. Calculate the date vary for the final week
end_date = datetime.now() - timedelta(days=1)
start_date = datetime.now() - timedelta(days=7)
start_date_str = start_date.strftime('%Y-%m-%d')
end_date_str = end_date.strftime('%Y-%m-%d')
# 3. Put together the API request for the Historic API
HISTORICAL_URL = "https://archive-api.open-meteo.com/v1/era5"
params = {
'latitude': lat,
'longitude': lon,
'start_date': start_date_str,
'end_date': end_date_str,
'each day': 'temperature_2m_max', # The precise variable for each day max temp
'temperature_unit': 'fahrenheit' # This API handles items appropriately
}
strive:
print(f"Fetching historic weekly max temp for {city_name.title()}...")
response = requests.get(HISTORICAL_URL, params=params)
response.raise_for_status()
knowledge = response.json()
daily_data = knowledge.get('each day', {})
max_temps = daily_data.get('temperature_2m_max', [])
if not max_temps:
print("Couldn't discover historic temperature knowledge within the response.")
return None
# 4. Discover the one highest temperature from the record of each day highs
highest_temp = max(max_temps)
return spherical(highest_temp, 1)
besides requests.exceptions.RequestException as e:
print(f"API request error throughout historic fetch: {e}")
return None
if __name__ == "__main__":
knowledge = get_historical_weekly_high("New York")
print(knowledge)
This operate takes a metropolis identify and returns the best recorded temperature within the metropolis for the final week.
Here’s a typical output when working regionally.
Fetching historic weekly max temp for New York...
104.3
Creating our MCP server
Now that we’ve proven our capabilities are working, let’s incorporate them into an MCP server and get that working regionally. Right here is the server code you’ll want.
# mcp_server.py
import contextlib
from fastapi import FastAPI
from temp_tool import mcp as temp_mcp
from prize_tool import mcp as prize_mcp
import os
from dotenv import load_dotenv
load_dotenv()
# Create a mixed lifespan to handle each session managers
@contextlib.asynccontextmanager
async def lifespan(app: FastAPI):
async with contextlib.AsyncExitStack() as stack:
await stack.enter_async_context(temp_mcp.session_manager.run())
await stack.enter_async_context(prize_mcp.session_manager.run())
yield
app = FastAPI(lifespan=lifespan)
app.mount("/temp", temp_mcp.streamable_http_app())
app.mount("/prize", prize_mcp.streamable_http_app())
PORT = int(os.getenv("PORT", "10000"))
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=PORT)
The one adjustments to our unique prize_tool and temp_tool codebases are to delete the three strains on the backside of every, that are used for testing. Take away these from each.
if __name__ == "__main__":
knowledge = nobel_checker(1921,"Physics")
print(knowledge)
and ...
if __name__ == "__main__":
knowledge = get_historical_weekly_high("New York")
print(knowledge)
Working the MCP server regionally
To run our server, kind the next command right into a command-line terminal.
$ uvicorn mcp_server:app --reload --port 10000
$ # You too can use python mcp_server.py --reload --port 10000
$ #
INFO: Will look ahead to adjustments in these directories: ['C:Usersthomaprojectsremote-mcpremote-mcp']
INFO: Uvicorn working on http://127.0.0.1:10000 (Press CTRL+C to stop)
INFO: Began reloader course of [3308] utilizing WatchFiles
INFO: Began server course of [38428]
INFO: Ready for utility startup.
[06/25/25 08:36:22] INFO StreamableHTTP session supervisor began streamable_http_manager.py:109
INFO StreamableHTTP session supervisor began streamable_http_manager.py:109
INFO: Utility startup full.
Testing our MCP server regionally
We are able to use a Gitbash command terminal and curl for this. Be certain that your server is up and working first. Let’s strive our temperature checker device first. The output can all the time be post-processed to carry out precisely the content material you need in a extra user-friendly format.
$ curl -sN -H 'Content material-Kind: utility/json' -H 'Settle for: utility/json, textual content/event-stream' -d '{"jsonrpc":"2.0","id":1,"technique":"instruments/name","params":{"identify":"get_historical_weekly_high","arguments":{"city_name":"New York"}}}' http://localhost:10000/temp/mcp/ | sed -n '/^knowledge:/{s/^knowledge: //;p}'
{"jsonrpc":"2.0","id":1,"outcome":{"content material":[{"type":"text","text":"104.3"}],"isError":false}}
This exhibits the max temp in NY over the past week was 104.3 Fahrenheit.
And now we will take a look at the prize checker device.
$ curl -sN -H 'Content material-Kind: utility/json' -H 'Settle for: utility/json, textual content/event-stream' -d '{"jsonrpc":"2.0","id":1,"technique":"instruments/name","params":{"identify":"nobel_checker","arguments":{"12 months":1921,"class":"Physics"}}}' http://localhost:10000/prize/mcp/ | sed -n '/^knowledge:/{s/^knowledge: //;p}'
{"jsonrpc":"2.0","id":1,"outcome":{"content material":[{"type":"text","text":"Albert Einstein"}],"isError":false}}
Albert Einstein did certainly win the Nobel Prize for Physics in 1921.
Deploying our MCP server remotely
Now that we’re glad with our code and that the MCP server is working as anticipated regionally, the subsequent stage is to deploy it remotely, permitting anybody on the planet to make use of it. There are a couple of choices to do that, however maybe the simplest (and initially the most affordable) is to make use of a service like Render.
Render is a contemporary cloud internet hosting platform — like a less complicated different to AWS, Heroku, or Vercel — that allows you to deploy full-stack apps, APIs, databases, background staff, and extra, with minimal DevOps overhead. Extra to the purpose is that it’s free to get began and is greater than sufficient for our wants. So head over to their website and enroll.
Earlier than deploying with Render, you will need to commit and ship your code to a GitHub (or a GitLab/Bitbucket) repository. After that, on the Render web site, select to create a New net server,
The primary time, Render will ask for entry to your GitHub (or Bitbucket/GitLab) account.

After that, you could present the instructions to construct your deployment and begin your server. For instance ….

Again on the Settings display, click on the Guide Deploy -> Deploy newest commit menu merchandise, and a log of the construct and deployment course of will likely be displayed. After a couple of minutes, you must see the next messages indicating your deployment was profitable.
...
...
==> Construct profitable 🎉
==> Deploying...
==> Working 'uv run mcp_server.py'
...
...
...
==> Out there at your major URL https://remote-mcp-syp1.onrender.com==> Out there at your major URL https://remote-mcp-syp1.onrender.com
...
Detected service working on port 10000
...
...
The important tackle you want is the one marked as the first URL. In our case, that is https://remote-mcp-syp1.onrender.com
Testing our distant MCP server
We are able to do that in the identical approach we examined the native working, i.e utilizing curl. First, examine on max temperature, this time Chicago. Be aware the change of URL to our new distant one.
$ curl --ssl-no-revoke -sN -H "Content material-Kind: utility/json" -H "Settle for: utility/json, textual content/event-stream" -d '{"jsonrpc":"2.0","id":1,"technique":"instruments/name","params":{"identify":"get_historical_weekly_high","arguments":{"city_name":"Chicago"}}}' https://remote-mcp-syp1.onrender.com/temp/mcp/|sed -n '/^knowledge:/{s/^knowledge: //;p}'
And our output?
{"jsonrpc":"2.0","id":1,"outcome":{"content material":[{"type":"text","text":"95.4"}],"isError":false}}
The sharp-eyed amongst you might have observed that we have now included an additional flag ( — ssl-no-revoke) within the above curl command in comparison with the one we used regionally. That is merely as a result of a quirk in the best way curl works beneath Home windows. In case you’re utilizing WSL2 for Home windows or Linux, you don’t want this further flag.
Subsequent, we take a look at our distant Nobel prize checker. This time for Chemistry in 2024.
$ $ curl --ssl-no-revoke -sN
-H 'Content material-Kind: utility/json'
-H 'Settle for: utility/json, textual content/event-stream'
-d '{"jsonrpc":"2.0","id":1,"technique":"instruments/name","params":{"identify":"nobel_checker","arguments":{"12 months":2024,"topic":"Chemistry"}}}'
'https://remote-mcp-syp1.onrender.com/prize/mcp/' | sed -n '/^knowledge:/{s/^knowledge: //;p}'
And the output?
{"jsonrpc":"2.0","id":1,"outcome":{"content material":[{"type":"text","text":"David Baker"},{"type":"text","text":"Demis Hassabis"},{"type":"text","text":"John Jumper"}],"isError":false}}
If you wish to strive accessing the MCP server through code as an alternative of utilizing curl, right here’s some instance Python that illustrates calling the distant nobel_checker device.
import requests
import json
import ssl
from urllib3.exceptions import InsecureRequestWarning
from urllib3 import disable_warnings
# Disable SSL warnings (equal to --ssl-no-revoke)
disable_warnings(InsecureRequestWarning)
def call_mcp_server(url, technique, tool_name, arguments, request_id=1):
"""
Name a distant MCP server
Args:
url (str): The MCP server endpoint URL
technique (str): The JSON-RPC technique (e.g., "instruments/name")
tool_name (str): Identify of the device to name
arguments (dict): Arguments to cross to the device
request_id (int): JSON-RPC request ID
Returns:
dict: Response from the MCP server
"""
# Put together headers
headers = {
"Content material-Kind": "utility/json",
"Settle for": "utility/json, textual content/event-stream"
}
# Put together JSON-RPC payload
payload = {
"jsonrpc": "2.0",
"id": request_id,
"technique": technique,
"params": {
"identify": tool_name,
"arguments": arguments
}
}
strive:
# Make the request with SSL verification disabled
response = requests.publish(
url,
headers=headers,
json=payload,
confirm=False, # Equal to --ssl-no-revoke
stream=True # Help for streaming responses
)
# Verify if the request was profitable
response.raise_for_status()
# Attempt to parse as JSON first
strive:
return response.json()
besides json.JSONDecodeError:
# If not JSON, return the textual content content material
return {"textual content": response.textual content}
besides requests.exceptions.RequestException as e:
return {"error": f"Request failed: {str(e)}"}
# Instance utilization
if __name__ == "__main__":
outcome = call_mcp_server(
url="https://remote-mcp-syp1.onrender.com/prize/mcp/",
technique="instruments/name",
tool_name="prize_checker",
arguments={"12 months": 2024, "topic": "Chemistry"}
)
print("MCP Instrument Name Response:")
print(json.dumps(outcome, indent=2))
The output is.
MCP Instrument Name Response:
{
"textual content": "occasion: messagerndata: {"jsonrpc":"2.0","id":1,"outcome":{"content material":[{"type":"text","text":"David Baker"},{"type":"text","text":"Demis Hassabis"},{"type":"text","text":"John Jumper"}],"isError":false}}rnrn"
}
Abstract
This text introduces how you can write, take a look at, and deploy your distant, Streamable HTTP Mannequin Context Protocol (MCP) server within the cloud, enabling any MCP shopper to entry capabilities (instruments) remotely.
I confirmed you how you can code some helpful stand-alone capabilities — a Nobel prize checker and a metropolis temperature data device. After testing these regionally utilizing the curl command to make sure they labored as anticipated, we transformed them into MCP instruments and coded an MCP server. After deploying and efficiently testing the MCP server regionally, we checked out how you can deploy our server to the cloud.
For that goal, I demonstrated how you can use Render, a cloud internet hosting platform, and walked you thru the steps of signing up and deploying (free of charge) our MCP server app. We then used curl to check the distant server, confirming it was working as anticipated.
Lastly, I additionally supplied some Python code you should use to check the MCP server.
Be at liberty to check out my MCP server on Render for your self. Be aware that as a result of it’s on the free tier, the server spins down after a interval of inactivity, which can end in a 30–60 second delay in retrieving outcomes.