درس ۲۱ از ۳۳

STDIO transport

عنوان اصلی: STDIO transport

هدف یادگیری: تسلط بر STDIO transport: framing، lifecycle، چه زمان انتخابش کنیم.

مفاهیم کلیدی: راه‌اندازی subprocess، stdin/stdout، JSON delimit‌شده با newline، بدون newline داخلی، UTF-8، stderr فقط برای log.

STDIO transport پیش‌فرض برای serverهای local است. Client، server را به‌عنوان subprocess راه می‌اندازد؛ client، requestها را روی stdin server می‌نویسد و responseها را از stdout می‌خواند. پیام‌ها object منفرد JSON-RPC هستند، delimit‌شده با newline، و نباید newline داخلی داشته باشند. Server می‌تواند UTF-8 log روی stderr بنویسد؛ client می‌تواند آن‌ها را capture کند. مهم‌تر از همه، server نباید هیچ چیز روی stdout بنویسد که یک پیام معتبر MCP نیست — یک print("hello") سرگردان stream را خراب می‌کند.

ترتیب shutdown: client، stdin را می‌بندد، صبر می‌کند، سپس SIGTERM، سپس SIGKILL.

STDIO انتخاب درست است وقتی server روی همان ماشین host اجرا می‌شود، به filesystem محلی دسترسی دارد، و نیازی نیست از روی شبکه به آن رسید. بدون overhead است — بدون HTTP، auth، TLS.

هشدار امنیتی برجسته (STDIO)

⚠️ STDIO امنیت پایین‌تری نسبت به HTTP ندارد، اما برخلاف Streamable HTTP، اعتماد به‌صورت implicit از طریق فرایند والد است. اگر host یک server محلی غیرقابل‌اعتماد را راه می‌اندازد، آن server با همان user و دسترسی filesystem اجرا می‌شود. بنابراین: - Host باید قبل از launch server را verify کند (signed binary، hash تاییدشده، یا allow-list). - Server نباید payload متغیر environment که از client می‌رسد را blindly trust کند. - هرگز print() یا console.log() در کد server نزنید — stream را خراب می‌کند و می‌تواند داده حساس را به stdout بفرستد.

مثال عملی — Python (server به‌صورت پیش‌فرض روی STDIO)

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Local")

@mcp.tool()
def add(a: int, b: int) -> int:
    return a + b

if __name__ == "__main__":
    mcp.run()  # transport="stdio" is the default

مثال عملی — TypeScript

import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';

const server = new McpServer({ name: 'local', version: '1.0.0' });
await server.connect(new StdioServerTransport());

دیاگرام معماری (متنی): فرایند Client، فرایند Server را راه‌اندازی می‌کند. Client روی Server.stdin می‌نویسد (JSON با newline-frame). Server روی Client.stdout می‌نویسد (JSON با newline-frame). Server اختیاری log روی Client.stderr می‌نویسد.

اشتباهات رایج

  • صدا زدن print() (Python) یا console.log() (Node) در کد tool — stdout را خراب می‌کند. از logging SDK یا stderr استفاده کنید.
  • embed کردن newline داخل یک پیام JSON. serializer باید JSON تک‌خطی تولید کند.
  • فراموش کردن flush کردن stdout در لایه framing خودتان.