I final wrote concerning the Mannequin Context Protocol (MCP) in December 2024, shortly earlier than the subject’s exponential progress into what it’s right now. I recall considering on the time that one of many key issues I felt needed to occur to MCP to make the expertise a recreation changer was the flexibility for MCP shoppers to entry non-local MCP servers. That’s already taking place, in fact, however what if you’d like a bit 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 consumer anyplace on the earth?
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 shoppers, corresponding to Cursor and Claude code, to name helpful capabilities that the MCP server comprises.
How is that totally different from you simply writing a bunch of helpful instruments and calling them in your code?
Nicely, the secret’s that you’re writing these instruments. What concerning 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 turn 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 tough 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 supplies, and also you’re up and operating with it in seconds.
So, in the event you’ve written one thing that others may discover actually 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 helpful utilities that I’ll use, corresponding to 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 it is best to do is initialise a brand new challenge to your code, and so on. Use the uv instrument with the init flag for this. Subsequent, we add an surroundings, change to it and add all of the exterior libraries that our code will use.
$ uv init remote-mcp
Initialized challenge `remote-mcp` at `/dwelling/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 predominant.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'll use.
(remote-mcp) $ uv add fastapi 'uvicorn[standard]' mcp-server requests yfinance python-dotenv
What we’ll develop
We are going to develop an MCP server and two distinct instruments for our MCP server to utilise. The primary can be a Nobel Prize checker. You present a Yr, 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 instrument will return the utmost recorded temperature for a metropolis previously week
First off, we’ll code our two instruments and check them regionally. Subsequent, we’ll incorporate the instruments into an MCP server operating regionally and check that setup. If it really works as anticipated, we are able to 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 info
The providers of the Nobel Prize web site are licensed below the Inventive Commons zero license. You may see the small print 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 known as prize_tool.py.
import requests
import os
import io
import csv
# from mcp.server.fastmcp import FastMCP
attempt:
from mcp.server.fastmcp import FastMCP
besides ModuleNotFoundError:
# Strive importing from a neighborhood path if operating 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.instrument()
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()
}
attempt:
# Make the request utilizing the secure 'params' argument
response = requests.get(BASE_URL, params=params)
# This may 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
# typically simply be the header row. We test 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 through 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) instrument 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 mechanically expose a plain HTTP endpoint for one-shot calls. The embellished operate nobel_checker turns into an MCP instrument. When invoked, it constructs a question to the Relaxation API utilizing the provided 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 meant activity.
['Albert Einstein']
Code Instance 2— Getting metropolis temperature info
For our second base operate, we’ll write a instrument that returns the best temperature for a metropolis during the last week. The climate knowledge is offered by Open-Meteo.com. On their license web page (“https://open-meteo.com/en/license), it states,
“API knowledge are provided below Attribution 4.0 International (CC BY 4.0)
You’re 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 right 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 may be reused. It isn't 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, 'depend': 1, 'language': 'en', 'format': 'json'}
attempt:
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 consequence
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.instrument()
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 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 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 models appropriately
}
attempt:
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 operating 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 operating 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 authentic prize_tool and temp_tool codebases are to delete the three traces 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)
Operating 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 may as well use python mcp_server.py --reload --port 10000
$ #
INFO: Will look ahead to adjustments in these directories: ['C:Usersthomaprojectsremote-mcpremote-mcp']
INFO: Uvicorn operating on http://127.0.0.1:10000 (Press CTRL+C to give up)
INFO: Began reloader course of [3308] utilizing WatchFiles
INFO: Began server course of [38428]
INFO: Ready for software 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: Software startup full.
Testing our MCP server regionally
We will use a Gitbash command terminal and curl for this. Be certain that your server is up and operating first. Let’s attempt our temperature checker instrument first. The output can at all times be post-processed to convey out precisely the content material you need in a extra user-friendly format.
$ curl -sN -H 'Content material-Sort: software/json' -H 'Settle for: software/json, textual content/event-stream' -d '{"jsonrpc":"2.0","id":1,"methodology":"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,"consequence":{"content material":[{"type":"text","text":"104.3"}],"isError":false}}
This reveals the max temp in NY during the last week was 104.3 Fahrenheit.
And now we are able to check the prize checker instrument.
$ curl -sN -H 'Content material-Sort: software/json' -H 'Settle for: software/json, textual content/event-stream' -d '{"jsonrpc":"2.0","id":1,"methodology":"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,"consequence":{"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 happy with our code and that the MCP server is working as anticipated regionally, the following stage is to deploy it remotely, permitting anybody on the earth to make use of it. There are just a few choices to do that, however maybe the best (and initially the most affordable) is to make use of a service like Render.
Render is a contemporary cloud internet hosting platform — like an easier different to AWS, Heroku, or Vercel — that permits 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 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 should present the instructions to construct your deployment and begin your server. For instance ….

Again on the Settings display screen, click on the Handbook Deploy -> Deploy newest commit menu merchandise, and a log of the construct and deployment course of can be displayed. After a couple of minutes, it is best to see the next messages indicating your deployment was profitable.
...
...
==> Construct profitable 🎉
==> Deploying...
==> Operating 'uv run mcp_server.py'
...
...
...
==> Obtainable at your major URL https://remote-mcp-syp1.onrender.com==> Obtainable at your major URL https://remote-mcp-syp1.onrender.com
...
Detected service operating on port 10000
...
...
The very important handle 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 will do that in the identical manner we examined the native operating, i.e utilizing curl. First, test on max temperature, this time Chicago. Observe the change of URL to our new distant one.
$ curl --ssl-no-revoke -sN -H "Content material-Sort: software/json" -H "Settle for: software/json, textual content/event-stream" -d '{"jsonrpc":"2.0","id":1,"methodology":"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,"consequence":{"content material":[{"type":"text","text":"95.4"}],"isError":false}}
The sharp-eyed amongst you’ll have seen that we’ve got 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 below Home windows. In case you’re utilizing WSL2 for Home windows or Linux, you don’t want this additional flag.
Subsequent, we check our distant Nobel prize checker. This time for Chemistry in 2024.
$ $ curl --ssl-no-revoke -sN
-H 'Content material-Sort: software/json'
-H 'Settle for: software/json, textual content/event-stream'
-d '{"jsonrpc":"2.0","id":1,"methodology":"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,"consequence":{"content material":[{"type":"text","text":"David Baker"},{"type":"text","text":"Demis Hassabis"},{"type":"text","text":"John Jumper"}],"isError":false}}
If you wish to attempt accessing the MCP server through code as a substitute of utilizing curl, right here’s some instance Python that illustrates calling the distant nobel_checker instrument.
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, methodology, tool_name, arguments, request_id=1):
"""
Name a distant MCP server
Args:
url (str): The MCP server endpoint URL
methodology (str): The JSON-RPC methodology (e.g., "instruments/name")
tool_name (str): Identify of the instrument to name
arguments (dict): Arguments to cross to the instrument
request_id (int): JSON-RPC request ID
Returns:
dict: Response from the MCP server
"""
# Put together headers
headers = {
"Content material-Sort": "software/json",
"Settle for": "software/json, textual content/event-stream"
}
# Put together JSON-RPC payload
payload = {
"jsonrpc": "2.0",
"id": request_id,
"methodology": methodology,
"params": {
"identify": tool_name,
"arguments": arguments
}
}
attempt:
# Make the request with SSL verification disabled
response = requests.publish(
url,
headers=headers,
json=payload,
confirm=False, # Equal to --ssl-no-revoke
stream=True # Assist for streaming responses
)
# Test if the request was profitable
response.raise_for_status()
# Attempt to parse as JSON first
attempt:
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__":
consequence = call_mcp_server(
url="https://remote-mcp-syp1.onrender.com/prize/mcp/",
methodology="instruments/name",
tool_name="prize_checker",
arguments={"12 months": 2024, "topic": "Chemistry"}
)
print("MCP Device Name Response:")
print(json.dumps(consequence, indent=2))
The output is.
MCP Device Name Response:
{
"textual content": "occasion: messagerndata: {"jsonrpc":"2.0","id":1,"consequence":{"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, check, and deploy your distant, Streamable HTTP Mannequin Context Protocol (MCP) server within the cloud, enabling any MCP consumer 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 info instrument. 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 function, I demonstrated how you can use Render, a cloud internet hosting platform, and walked you thru the steps of signing up and deploying (without spending a dime) our MCP server app. We then used curl to check the distant server, confirming it was working as anticipated.
Lastly, I additionally offered 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. Observe 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.