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์์์ ์ญํ |
|---|---|---|---|
| stdin | fd 0 | ๋ถ๋ชจ โ ์์ | JSON-RPC ์์ฒญ ์์ |
| stdout | fd 1 | ์์ โ ๋ถ๋ชจ | JSON-RPC ์๋ต ์ ์ก |
| stderr | fd 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.js | console.log() | console.error() |
| Python | print() | print(..., file=sys.stderr) |
| Java | System.out.println() | logger.info() (stderr ์ค์ ) |
| Rust | println!() | eprintln!() |
STDIO vs Streamable HTTP
| ํญ๋ชฉ | STDIO | Streamable HTTP |
|---|---|---|
| ์คํ ์์น | ๋ก์ปฌ ์ ์ฉ | ๋ก์ปฌ + ์๊ฒฉ |
| ์ฐ๊ฒฐ ๋ฐฉ์ | ํ๋ก์ธ์ค spawn | HTTP POST + SSE |
| ํด๋ผ์ด์ธํธ ์ | 1:1 | 1:N |
| ๋คํธ์ํฌ ์ค๋ฒํค๋ | ์์ | ์์ |
| ์ธ์ฆ | ๋ถํ์ (๊ฐ์ ๋จธ์ ) | OAuth, API Key ๋ฑ |
| ์ค์ ๋ณต์ก๋ | ๋ฎ์ | ๋์ |
| ์ ํฉํ ๊ฒฝ์ฐ | ๊ฐ์ธ ๊ฐ๋ฐ ๋๊ตฌ, ๋ก์ปฌ DB | SaaS ์๋น์ค, ํ ๊ณต์ |