Call MCP Server(stdio) directly in the shell
stdio
is one of the transport mechanisms MCP(Model Context Protocol) natively supports. Actually stdio
is the only transport mechanism Claude Desktop supports currently(as in May, 2025). This article will show you how to call a MCP server in the shell, without mcp dev
or any third party tools, only with echo >
or copying/pasting JSONRPC message directly.
First, let’s write a very simple MCP server(get_time.py) to get the current time:
import datetime
import logging
from mcp.server.fastmcp import FastMCP
logging.basicConfig(level=logging.ERROR)
server = FastMCP()
@server.tool()
def get_time():
"""
Get the current time in the format YYYY-MM-DD HH:MM:SS
"""
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if __name__ == '__main__':
server.run(transport='stdio')
The MCP server can be started by python get_time.py
or mcp get_time.py:server run
. I will show you how to interact with the server with the latest 2025-03-26 specification.
The initialization process
Before access any meaningful calling, an initialization process should be handled.
First, paste(consider the server process is waiting for input) a initialize
message(single line of json) to the MCP input window
{"jsonrpc": "2.0","method": "initialize","params": {"protocolVersion": "2025-03-26","capabilities": {},"clientInfo": {"name": "test", "version": "0.1.0"}},"id": 1}
And the MCP server will print the response to the stdout:
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-03-26","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"FastMCP","version":"1.9.0"}}}
Then you paste a initialized
notification to the MCP server you have finished initialization:
{"jsonrpc": "2.0","method": "notifications/initialized"}
The server will print out nothing, and the initialization process is over, we can finally do some meaningful method calls.
Call methods
Here are a few examples of calling methods.
ping
The ping calling message and its MCP server response is very simple
{"jsonrpc":"2.0","method":"ping","id":2}
{"jsonrpc":"2.0","id":2,"result":{}}
Get tools list
Use tools/list
to get a list of tools, paste the following message
{"jsonrpc":"2.0","id":3,"method":"tools/list","params": {}}
The server response will be
{"jsonrpc":"2.0","id":3,"result":{"tools":[{"name":"get_time","description":"\nGet the current time in the format YYYY-MM-DD HH:MM:SS\n","inputSchema":{"properties":{},"title":"get_timeArguments","type":"object"}}]}}
Call a tools
We can call the tools by pasting
{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"get_time", "arguments":{}}}
The result will be output on stdout
{"jsonrpc":"2.0","id":4,"result":{"content":[{"type":"text","text":"2025-05-19 14:38:20"}],"isError":false}}
Note that the message id can also be a string(e.g. UUID), for example
{"jsonrpc":"2.0","id":"3147e387-e601-47d7-9e8a-0b69d43ca109","method":"tools/call","params":{"name":"get_time", "arguments":{}}}
{"jsonrpc":"2.0","id":"3147e387-e601-47d7-9e8a-0b69d43ca109","result":{"content":[{"type":"text","text":"2025-05-19 14:44:31"}],"isError":false}}
You may explore more methods based on official message specification.
Use a FIFO to transfer the message
The copy/paste approach is not very convenient, so we can use a FIFO to transfer the message.
Create 2 FIFOs, one for input and one for output
mkfifo /tmp/in /tmp/out
Then we can start the MCP server as
python get_time.py < /tmp/in > /tmpout
We will write the initialization message and tools calling message in a .jsonl
file:
{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test", "version":"0.1.0"}},"id":1}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_time", "arguments":{}}}
Monitoring the /tmp/out
with tail -f /tmp/out
, we can send our messages by
cat message.jsonl > /tmp/in
We shall find that the MCP server process ends normally and we get the following output from the /tmp/out
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-03-26","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"FastMCP","version":"1.9.0"}}}
{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"2025-05-19 18:06:00"}],"isError":false}}
That’s all, have fun with your MCP server in the shell!