Jun 3, 2026 3 min read

Run Your First MCP Server: Filesystem, Step by Step

Run the official filesystem MCP server and drive it by hand with JSON-RPC. See initialize, tools/list, and tools/call on the wire.

Everyone is talking about MCP servers, but the fastest way to understand them is to run one and watch the raw messages go by. The Model Context Protocol is just a standard way for an AI client to discover and call tools on a server. No magic. In this post I install the official filesystem MCP server and drive it by hand with plain JSON, so you can see exactly what a "tool" looks like on the wire.

Tested with: Debian 13 (trixie), Node.js 22.22.2, npm 10.9.7, package @modelcontextprotocol/server-filesystem.

What an MCP server actually is

Think of an MCP server as a small program that exposes a menu of tools. An AI client (Claude Desktop, an agent loop, your own script) connects to it, asks "what can you do?", and the server replies with a list of tools and their input schemas. The client can then call any of those tools and get structured results back. The filesystem server, for example, exposes tools like read_text_file and list_directory, scoped to a directory you allow.

The most common transport is stdio: the client launches the server as a subprocess and they exchange newline-delimited JSON-RPC messages over stdin and stdout. That sounds heavy but it is genuinely just JSON in, JSON out, which is why we can fake a client with echo.

Install Node

MCP servers in the reference set are Node packages, so you need Node. I used the NodeSource setup for version 22:

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs

Confirm it:

$ node --version
v22.22.2
$ npm --version
10.9.7

Run the server by hand

You do not install the server globally. npx fetches and runs it on demand. Point it at a directory it is allowed to touch:

npx -y @modelcontextprotocol/server-filesystem /home/j/mcp-demo

On its own that just sits there waiting for JSON on stdin. So instead of typing into it, I piped a sequence of three messages in. First, the initialize handshake every MCP session begins with:

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
  "protocolVersion":"2024-11-05",
  "capabilities":{},
  "clientInfo":{"name":"llmbits-test","version":"1.0.0"}}}

The server replied with its own identity:

{"jsonrpc":"2.0","id":1,"result":{
  "protocolVersion":"2024-11-05",
  "capabilities":{"tools":{}},
  "serverInfo":{"name":"secure-filesystem-server","version":"0.2.0"}}}

Note the name: secure-filesystem-server. The "secure" part is real. It refuses to read or write outside the directory you passed in, which is the whole point of allowing a path explicitly.

Ask what it can do

After initialize, the client sends an initialized notification, then asks for the tool list:

{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}

The server returned 14 tools. These are the names that an AI model would see and choose from:

create_directory          read_file
directory_tree            read_media_file
edit_file                 read_multiple_files
get_file_info             read_text_file
list_allowed_directories  search_files
list_directory            write_file
list_directory_with_sizes move_file

Each one comes with a JSON schema describing its arguments, which is how the client knows that read_text_file needs a path. That schema is what lets a model fill in the arguments correctly without anyone hard-coding the call.

Call a tool

I had dropped a file at /home/j/mcp-demo/notes.txt containing hello from llmbits. Calling the read tool looks like this:

{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{
  "name":"read_text_file",
  "arguments":{"path":"/home/j/mcp-demo/notes.txt"}}}

And the result came back as structured content:

{"jsonrpc":"2.0","id":3,"result":{
  "content":[{"type":"text","text":"hello from llmbits"}]}}

That is the entire loop: initialize, list tools, call a tool, read the result. Every MCP integration you have read about is doing exactly this under the hood, just with a real client driving it instead of echo.

Wiring it into a real client

Once you have seen the raw messages, the config files in tools like Claude Desktop stop being mysterious. They are just telling the client how to launch this same subprocess:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/j/mcp-demo"]
    }
  }
}

The client runs that command, performs the initialize handshake for you, and surfaces the 14 tools to the model. Nothing more is happening than what we did by hand.

Takeaways

  • An MCP server is a program that exposes a typed menu of tools over JSON-RPC, usually via stdio.
  • You can run the official filesystem server with one npx command and need no global install.
  • Every session is the same three steps: initialize, tools/list, then tools/call.
  • The filesystem server is sandboxed to the directory you pass it, which is why you always specify an allowed path.
  • Client config files (Claude Desktop and friends) are just launching this subprocess for you. Seeing the raw messages once demystifies the whole thing.
J
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to LLMbits.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.