STDIO Transport๋Š” ๊ฐ™์€ ๋จธ์‹ ์—์„œ ์‹คํ–‰๋˜๋Š” ๋‘ ํ”„๋กœ์„ธ์Šค ๊ฐ„ ํ†ต์‹  ๋ฐฉ์‹์œผ๋กœ, ์šด์˜์ฒด์ œ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํ‘œ์ค€ ์ž…์ถœ๋ ฅ ์ŠคํŠธ๋ฆผ(stdin, stdout, stderr)์„ ํ†ต์‹  ์ฑ„๋„๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

MCP Server์—์„œ๋Š” ๋กœ์ปฌ ์„œ๋ฒ„์˜ ๊ธฐ๋ณธ ์ „์†ก ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.

ํ•ด๋‹น ๊ฐœ๋…์ด ํ•„์š”ํ•œ ์ด์œ 

  • MCP Server๋ฅผ ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•  ๋•Œ ๋„คํŠธ์›Œํฌ ์„ค์ • ์—†์ด ๋ฐ”๋กœ ํ†ต์‹  ๊ฐ€๋Šฅ
  • ๋ณ„๋„ ํฌํŠธ ๋ฐ”์ธ๋”ฉ, HTTP ์„œ๋ฒ„ ๊ตฌ์„ฑ์ด ๋ถˆํ•„์š”
  • Claude Code, Claude Desktop ๋“ฑ ๋Œ€๋ถ€๋ถ„์˜ MCP Host๊ฐ€ ๋กœ์ปฌ ์„œ๋ฒ„์— STDIO๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉ

AS-IS

sequenceDiagram
    autonumber
    participant App as AI ์•ฑ
    participant Server as ์™ธ๋ถ€ ์„œ๋ฒ„

    App->>Server: HTTP ์š”์ฒญ (ํฌํŠธ ์„ค์ • ํ•„์š”)
    Server-->>App: HTTP ์‘๋‹ต
    Note over App,Server: ํฌํŠธ ์ถฉ๋Œ, ๋ฐฉํ™”๋ฒฝ, CORS ๋“ฑ<br/>๋„คํŠธ์›Œํฌ ์ด์Šˆ ๋ฐœ์ƒ ๊ฐ€๋Šฅ

TO-BE

sequenceDiagram
    autonumber
    participant Host as Claude Code (๋ถ€๋ชจ)
    participant Server as MCP Server (์ž์‹)

    Host->>Server: spawn ํ”„๋กœ์„ธ์Šค
    Host->>Server: stdin์— JSON-RPC write
    Server-->>Host: stdout์— JSON-RPC write
    Server-->>Host: stderr์— ๋””๋ฒ„๊ทธ ๋กœ๊ทธ
    Note over Host,Server: ๋„คํŠธ์›Œํฌ ๋ถˆํ•„์š”<br/>OS ์ˆ˜์ค€์˜ ํ”„๋กœ์„ธ์Šค ํ†ต์‹ 

3๊ฐœ์˜ ํ‘œ์ค€ ์ŠคํŠธ๋ฆผ

์ŠคํŠธ๋ฆผํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฐฉํ–ฅMCP์—์„œ์˜ ์—ญํ• 
stdinfd 0๋ถ€๋ชจ โ†’ ์ž์‹JSON-RPC ์š”์ฒญ ์ˆ˜์‹ 
stdoutfd 1์ž์‹ โ†’ ๋ถ€๋ชจJSON-RPC ์‘๋‹ต ์ „์†ก
stderrfd 2์ž์‹ โ†’ ๋ถ€๋ชจ๋””๋ฒ„๊ทธ ๋กœ๊ทธ ์ถœ๋ ฅ
graph LR
    subgraph "๋ถ€๋ชจ ํ”„๋กœ์„ธ์Šค (Claude Code)"
        W["stdin.write()"]
        R["stdout.read()"]
        L["stderr.read()"]
    end

    subgraph "์ž์‹ ํ”„๋กœ์„ธ์Šค (MCP Server)"
        SI["process.stdin"]
        SO["process.stdout"]
        SE["process.stderr"]
    end

    W -->|"JSON-RPC ์š”์ฒญ"| SI
    SO -->|"JSON-RPC ์‘๋‹ต"| R
    SE -->|"๋กœ๊ทธ ๋ฉ”์‹œ์ง€"| L

๋™์ž‘ ์›๋ฆฌ

1. ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ (spawn)

MCP Host๊ฐ€ ์„ค์ • ํŒŒ์ผ์˜ command์™€ args๋กœ ์ž์‹ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค:

{
  "mcpServers": {
    "todo-server": {
      "command": "node",
      "args": ["/path/to/build/index.js"]
    }
  }
}

Host ๋‚ด๋ถ€์—์„œ ์ผ์–ด๋‚˜๋Š” ์ผ (๊ฐœ๋…์  ์ฝ”๋“œ):

const child = child_process.spawn("node", ["/path/to/build/index.js"]);
// child.stdin  โ†’ ๋ถ€๋ชจ๊ฐ€ write (์š”์ฒญ ์ „์†ก)
// child.stdout โ†’ ๋ถ€๋ชจ๊ฐ€ read  (์‘๋‹ต ์ˆ˜์‹ )
// child.stderr โ†’ ๋ถ€๋ชจ๊ฐ€ read  (๋กœ๊ทธ ์ˆ˜์‹ )

2. ๋ฉ”์‹œ์ง€ ๊ตํ™˜

stdin/stdout์„ ํ†ตํ•ด ์ค„๋ฐ”๊ฟˆ์œผ๋กœ ๊ตฌ๋ถ„๋œ JSON-RPC 2.0 ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š”๋‹ค:

โ†’ stdin:  {"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}\n
โ† stdout: {"jsonrpc":"2.0","id":1,"result":{...}}\n

3. ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ

MCP Host๊ฐ€ ์—ฐ๊ฒฐ์„ ๋Š์œผ๋ฉด ์ž์‹ ํ”„๋กœ์„ธ์Šค์˜ stdin์ด ๋‹ซํžˆ๊ณ , ์„œ๋ฒ„๋Š” ์ •์ƒ ์ข…๋ฃŒ๋œ๋‹ค.

console.log() ๊ธˆ์ง€ ๊ทœ์น™

STDIO ์„œ๋ฒ„์—์„œ ๊ฐ€์žฅ ํ”ํ•œ ์‹ค์ˆ˜์ด์ž ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ทœ์น™:

// stdout์€ JSON-RPC ์ „์šฉ ์ฑ„๋„
 
console.log("hello");    // stdout โ†’ JSON-RPC ํŒŒ์‹ฑ ์‹คํŒจ โ†’ ์„œ๋ฒ„ ํฌ๋ž˜์‹œ
console.error("hello");  // stderr โ†’ ์•ˆ์ „ (๋กœ๊ทธ์šฉ)

์™œ ํฌ๋ž˜์‹œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”๊ฐ€:

sequenceDiagram
    autonumber
    participant Host as MCP Host
    participant Server as MCP Server

    Server->>Host: stdout: "hello" (console.log)
    Host->>Host: JSON.parse("hello") ์‹œ๋„
    Host->>Host: SyntaxError ๋ฐœ์ƒ
    Host->>Host: ์—ฐ๊ฒฐ ์ข…๋ฃŒ (ํ”„๋กœํ† ์ฝœ ์œ„๋ฐ˜)

์–ธ์–ด๋ณ„ ์•ˆ์ „ํ•œ ๋กœ๊น…:

์–ธ์–ด๊ธˆ์ง€์•ˆ์ „
TypeScript/Node.jsconsole.log()console.error()
Pythonprint()print(..., file=sys.stderr)
JavaSystem.out.println()logger.info() (stderr ์„ค์ •)
Rustprintln!()eprintln!()

STDIO vs Streamable HTTP

ํ•ญ๋ชฉSTDIOStreamable HTTP
์‹คํ–‰ ์œ„์น˜๋กœ์ปฌ ์ „์šฉ๋กœ์ปฌ + ์›๊ฒฉ
์—ฐ๊ฒฐ ๋ฐฉ์‹ํ”„๋กœ์„ธ์Šค spawnHTTP POST + SSE
ํด๋ผ์ด์–ธํŠธ ์ˆ˜1:11:N
๋„คํŠธ์›Œํฌ ์˜ค๋ฒ„ํ—ค๋“œ์—†์Œ์žˆ์Œ
์ธ์ฆ๋ถˆํ•„์š” (๊ฐ™์€ ๋จธ์‹ )OAuth, API Key ๋“ฑ
์„ค์ • ๋ณต์žก๋„๋‚ฎ์Œ๋†’์Œ
์ ํ•ฉํ•œ ๊ฒฝ์šฐ๊ฐœ์ธ ๊ฐœ๋ฐœ ๋„๊ตฌ, ๋กœ์ปฌ DBSaaS ์„œ๋น„์Šค, ํŒ€ ๊ณต์œ 

์ฐธ๊ณ  ๋ฌธ์„œ