DEV Community

Yong Yu
Yong Yu

Posted on Originally published at yongboyu.hashnode.dev

Treat Your MCP Server as a Tool Boundary (stdio, schemas, and safe local calls)

Treat Your MCP Server as a Tool Boundary (stdio, schemas, and safe local calls)

Attributed Chinese → English compile

Source: MCP Server 实战:从协议到本地工具调用

Original author: AJie (AJie's Blog) · Published: 2026-06-03

This is a faithful English rewrite for learning. It is not original research by the compiler. Always link the Chinese original; do not present this compile as the source work.


Most first MCP tutorials jump straight to “register a tool, wire stdio, celebrate the demo.” AJie’s post is useful because it starts one layer earlier: an MCP Server is a capability boundary, not a dumping ground for whatever the model might want to run on your machine.

If you are wiring agents, MCP, or RAG into a product, that framing matters. Models do not “use your laptop”; they generate call intents against schemas. Your job is to expose narrow, typed, recoverable actions—and keep stdout clean so the JSON-RPC pipe does not die under log noise. Over-broad tools and silent protocol corruption show up as “the agent is flaky,” when the real failure is the tool surface.

What a good tool boundary looks like

Dimension Healthy MCP tool Fragile MCP tool
Input Explicit fields, types, constraints Free-form natural language for the tool to “figure out”
Output Stable structure the model can continue from Raw logs / untyped blobs
Scope One action (or one family) “Do anything” shell god-mode
Failure Explainable error the model can act on Opaque stack traces

Instead of exposing a generic shell, split a local project-assistant into narrow tools:

Tool Input Output Why
list_project_files root, file types path list Scope the workspace
read_project_file relative path, line range snippet Pull only needed context
summarize_markdown_posts directory, limit title / date / summary table Inventory content
find_internal_links path in-page links Structure checks
check_frontmatter path missing / bad fields Pre-publish lint

Less flexible than bash. Much more operable in production: no accidental deletes, and outputs stay consumable across turns.

Design I/O so the model guesses less

Tool descriptions are routing context for the model, not human README fluff.

Weak:

Read a file from local project.

Stronger (paraphrased from the source’s guidance):

Read a text file under the configured project root. Use when you need a specific source, markdown, or config file. Path must be relative to project root. Optional start / limit restrict the returned line range.

Suggested parameters for read_project_file:
Truncation and pagination beat dumping tens of thousands of lines into the context window. MCP Servers should default to summaries, slices, or pages.

The stdio call chain (local MCP’s default path)

Local MCP almost always means:

  1. The user configures a launch command in the host (Claude Desktop, Claude Code, Cursor, or your own host).
  2. The client starts a local server process.
  3. The server waits on stdio for initialize.
  4. The client lists tools.
  5. The model decides whether to call.
  6. The client forwards tools/call.
  7. The server runs the local capability and returns JSON-RPC over stdout.

Troubleshooting map (from the source)

Symptom Likely cause First check
Client sees no tools Process fail / bad path / init fail Run the launch command alone
Tools listed, call fails Schema mismatch / tool exception Log args + structured error
Hang after call No response / stdout polluted by logs Separate stdout vs stderr
Works in terminal, fails in host Env / cwd / permissions differ Diff the client launch environment
Flaky External cmds / file state Boundary checks + clear errors

Hard rule for stdio: never write casual debug logs to stdout. Stdout is the protocol channel. Use stderr or a log file.

Official references the source## Ship a minimum closed loop before real tools

AJie recommends a tiny ping_project tool first: accept a string; return project name, cwd, and the echoed input. You are not proving business value—you are proving:

  • The client can spawn the server
  • Initialize completes
  • The tool appears in the list
  • Args round-trip
  • The response renders in the host
  • stderr does not corrupt the protocol

Then add one real tool at a time and call it from the host after each addition. When something breaks, the last delta is obvious.

Many “works locally, fails in Claude Code” issues are path, env, or cwd mismatches—not SDK bugs.

Safety tiers before you expose writes

Tier Examples Default policy
Read-only List dir, read snippet, status query Ship first
Constrained write Drafts /## Debug order that actually works
  1. Run the server start command alone.
  2. Verify initialize with the ping tool.
  3. Capture model-supplied args versus the schema.
  4. Cap output length.
  5. Return structured errors (ok, error, message, path, hint).
  6. Only then attach real files, APIs, or commands.

Example failure shape (paraphrased): instead of bare ENOENT, return file_not_found plus a hint to call list_project_files first. That turns a retry loop into a recovery path.

Rollout checklist for a real project

  1. Pick one narrow scenario (one sentence).
  2. Split two or three read-only tools with clear I/O.
  3. Prove the stdio minimum loop in the target host.
  4. Connect real data with truncated, structured returns.
  5. Add recoverable error objects.
  6. Guard context budget (paginate or summarize).
  7. Only then consider writes with directory and confirmation gates.

Why this matters if you are shipping agents / MCP into a product

Product agents fail less often on “model IQ” than on tool surface area: over-broad tools, polluted stdio, and unrecoverable errors. MCP gives you a standard discovery and call pipe; you still have to design the boundary as carefully as a public API. Pair this with RAG the same way—retrieve evidence as structured resources, execute side effects as narrow tools, and keep both behind server-side policy. That is the difference between a demo that works once and a capability you can operate.


Compiler (not original author)

English compile by YongBo Yu.

Site: https://yongbo-yu.vercel.app · GitHub: https://github.com/YongBoYu1

Original post remains © AJie / source site. Read the Chinese original before publishing any derivative. reports in a sandbox dir | Allowlisted paths + types |
| High risk | Delete, arbitrary shell, push, publish | Off by default or human confirm |

Do not accept arbitrary absolute paths. Do not ship “run any shell command” as a general tool. Prefer allowlisted roots such as reports/ or drafts/.

points at:

Suggested structured return:

  • path, start, end, content, truncated

| Safety | Path / command / network limits | Default-open to the whole host |

AJie’s practical rule: if the model must understand intent and the tool should do deterministic work, make an MCP tool. If the work itself still needs open-ended judgment (“rewrite this article”), leave that to the model and only give it evidence tools.

Top comments (0)