Call MCP Server(stdio) directly in the shell

May 19, 2025

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!