본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 3장: 전송 계층 - stdio와 Streamable HTTP
2026년 1월 25일·AI / ML·

3장: 전송 계층 - stdio와 Streamable HTTP

MCP의 두 가지 핵심 전송 방식인 stdio와 Streamable HTTP의 동작 원리, 장단점, 선택 기준을 상세히 다룹니다.

17분550자8개 섹션
mcptypescriptpython
공유
mcp-guide3 / 10
12345678910
이전2장: 프로토콜 아키텍처 심층 분석다음4장: 서버 프리미티브 - 도구, 리소스, 프롬프트

전송 계층의 역할

MCP 프로토콜은 메시지의 형식(JSON-RPC 2.0)과 의미(도구 호출, 리소스 읽기 등)를 정의합니다. 하지만 이 메시지를 실제로 어떻게 주고받을 것인가는 별도의 관심사입니다. 이것이 전송 계층(Transport Layer)의 역할입니다.

MCP는 전송 계층을 프로토콜 본체와 분리하여 설계했습니다. 덕분에 동일한 MCP 서버 로직을 다양한 전송 방식 위에서 실행할 수 있습니다. 현재 MCP 사양에서 정의하는 전송 방식은 두 가지입니다.

  • stdio: 표준 입출력을 통한 로컬 프로세스 간 통신
  • Streamable HTTP: HTTP 기반의 원격 통신

이 장에서는 각 전송 방식의 동작 원리를 살펴보고, 상황에 맞는 선택 기준을 제시합니다.

stdio 전송

동작 원리

stdio 전송에서 MCP 서버는 호스트 애플리케이션의 자식 프로세스(child process)로 실행됩니다. 클라이언트와 서버 사이의 통신은 운영체제의 표준 입출력 스트림을 통해 이루어집니다.

  • 클라이언트는 서버 프로세스의 stdin에 JSON-RPC 메시지를 씁니다.
  • 서버는 stdout으로 응답 메시지를 씁니다.
  • stderr는 로그 출력에 사용되며, 프로토콜 메시지와 혼합되지 않습니다.
  • 각 메시지는 개행 문자(\n)로 구분됩니다.
  • 메시지 내부에는 개행 문자를 포함할 수 없습니다.

구현 예제

stdio 서버 실행
typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 
const server = new McpServer({
  name: "local-file-server",
  version: "1.0.0",
});
 
// 도구 등록
server.tool(
  "read_file",
  "파일의 내용을 읽습니다",
  { path: { type: "string", description: "파일 경로" } },
  async (params) => {
    const fs = await import("fs/promises");
    const content = await fs.readFile(params.path, "utf-8");
    return {
      content: [{ type: "text", text: content }],
    };
  }
);
 
// stdio 전송으로 서버 시작
const transport = new StdioServerTransport();
await server.connect(transport);

클라이언트 측에서는 서버를 자식 프로세스로 실행하고, stdin/stdout 스트림에 연결합니다.

stdio 클라이언트 연결
typescript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
 
const transport = new StdioClientTransport({
  command: "node",
  args: ["server.js"],
});
 
const client = new Client({
  name: "my-client",
  version: "1.0.0",
});
 
await client.connect(transport);
 
// 도구 목록 조회
const tools = await client.listTools();
console.log("사용 가능한 도구:", tools);

stdio의 장점과 한계

장점:

  • 설정이 단순합니다. 네트워크 구성, 포트, 인증 등이 필요 없습니다.
  • 운영체제 수준의 프로세스 격리를 활용합니다.
  • 지연 시간이 매우 낮습니다. 로컬 프로세스 간 통신이므로 네트워크 지연이 없습니다.
  • 방화벽이나 네트워크 정책의 영향을 받지 않습니다.

한계:

  • 같은 머신에서만 동작합니다. 원격 서버에는 사용할 수 없습니다.
  • 호스트 프로세스가 종료되면 서버도 함께 종료됩니다.
  • 여러 클라이언트가 하나의 서버를 공유할 수 없습니다.
Tip

stdio 전송은 로컬 개발 도구, IDE 플러그인, CLI 도구 등에 가장 적합합니다. Claude Desktop에서 MCP 서버를 연결할 때 가장 흔히 사용되는 방식이기도 합니다. 원격 서비스와의 통합이 필요한 경우에는 Streamable HTTP를 사용합니다.

Streamable HTTP 전송

배경: SSE에서 Streamable HTTP로

MCP 초기 버전(2024-11-05)에서는 원격 통신을 위해 HTTP+SSE(Server-Sent Events) 전송을 사용했습니다. SSE 방식에서는 클라이언트가 서버에 SSE 연결을 먼저 수립하고, 이 지속적 연결을 통해 서버에서 클라이언트로의 메시지를 수신했습니다. 클라이언트에서 서버로의 메시지는 별도의 HTTP POST 요청으로 전송했습니다.

이 방식에는 실무적인 한계가 있었습니다.

  • 지속적 연결 유지 필요: SSE 연결이 끊어지면 전체 세션을 재설정해야 합니다.
  • 로드 밸런싱 어려움: SSE 연결이 특정 서버 인스턴스에 고정되므로, 수평 확장이 복잡합니다.
  • 프록시 호환성 문제: 일부 프록시와 CDN이 장시간 SSE 연결을 지원하지 않습니다.

2025년 3월 사양(2025-03-26)에서 이 문제를 해결한 Streamable HTTP 전송이 도입되었으며, 기존 SSE 전송은 더 이상 사용하지 않는(deprecated) 상태가 되었습니다.

Streamable HTTP 동작 원리

Streamable HTTP에서 서버는 단일 HTTP 엔드포인트를 제공합니다. 클라이언트는 이 엔드포인트에 POST와 GET 요청을 보냅니다.

POST 요청: 클라이언트가 서버에 JSON-RPC 메시지를 전송합니다.

  • 서버는 단일 JSON 응답을 반환하거나, SSE 스트림으로 여러 메시지를 반환할 수 있습니다.
  • 알림(Notification)에 대해서는 빈 204 응답을 반환합니다.

GET 요청: 서버가 클라이언트에 보내야 하는 메시지(알림, 서버 발신 요청)를 수신하기 위한 SSE 스트림을 엽니다.

  • 이 스트림은 선택적이며, 서버가 클라이언트에 메시지를 보낼 필요가 없으면 GET을 지원하지 않아도 됩니다.

세션 관리

Streamable HTTP에서 세션 관리는 선택적입니다. 서버가 상태를 유지해야 하는 경우, 초기화 응답에 Mcp-Session-Id 헤더를 포함시킵니다.

HTTP/1.1 200 OK
Content-Type: application/json
Mcp-Session-Id: session-abc123

{"jsonrpc":"2.0","id":1,"result":{...}}

이후 클라이언트는 모든 요청에 이 세션 ID를 포함시킵니다.

POST /mcp HTTP/1.1
Content-Type: application/json
Mcp-Session-Id: session-abc123

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

상태가 없는(stateless) 서버는 세션 ID를 사용하지 않습니다. 이 경우 각 요청은 독립적으로 처리되며, 로드 밸런서 뒤에서 어떤 서버 인스턴스가 처리해도 무방합니다.

구현 예제

Streamable HTTP 서버
typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";
 
const app = express();
app.use(express.json());
 
app.all("/mcp", async (req, res) => {
  const transport = new StreamableHTTPServerTransport("/mcp");
 
  const server = new McpServer({
    name: "remote-server",
    version: "1.0.0",
  });
 
  server.tool(
    "search_documents",
    "문서를 검색합니다",
    { query: { type: "string", description: "검색어" } },
    async (params) => {
      const results = await searchIndex(params.query);
      return {
        content: [{ type: "text", text: JSON.stringify(results) }],
      };
    }
  );
 
  await server.connect(transport);
  await transport.handleRequest(req, res);
});
 
app.listen(3000, () => {
  console.log("MCP 서버가 http://localhost:3000/mcp에서 실행 중입니다");
});

Streamable HTTP의 장점

무상태 운영 가능: 세션 ID를 사용하지 않으면 완전히 무상태로 동작합니다. 로드 밸런서 뒤에 여러 서버 인스턴스를 배치하여 수평 확장이 가능합니다.

유연한 응답 방식: 단순한 요청에는 일반 JSON 응답을, 장시간 실행되는 작업에는 SSE 스트리밍을 선택적으로 사용할 수 있습니다.

표준 HTTP 인프라 활용: 기존의 프록시, CDN, 로드 밸런서, 모니터링 도구를 그대로 활용할 수 있습니다.

인증 통합 용이: OAuth 2.1, API 키 등 표준 HTTP 인증 메커니즘을 자연스럽게 적용할 수 있습니다.

전송 방식 선택 기준

프로젝트의 요구사항에 따라 적절한 전송 방식을 선택해야 합니다.

기준stdioStreamable HTTP
배포 위치로컬 전용로컬 또는 원격
설정 복잡도매우 낮음중간
인증불필요 (OS 수준)필요 (OAuth 등)
확장성단일 클라이언트다중 클라이언트
지연 시간극히 낮음네트워크 의존적
적합한 용도IDE, CLI, 로컬 도구클라우드 서비스, 팀 공유

판단 플로우차트

실무에서는 다음과 같은 패턴이 일반적입니다.

  • 개인 개발 환경: stdio 전송으로 Claude Desktop이나 IDE에 로컬 MCP 서버를 연결합니다.
  • 팀 공유 서버: Streamable HTTP로 사내 서비스(Jira, Confluence, 내부 API 등)에 대한 MCP 서버를 운영합니다.
  • SaaS 제공: Streamable HTTP로 외부 고객에게 MCP 서버를 제공합니다.

전송 방식의 전환

MCP SDK는 전송 계층을 추상화하므로, 서버 로직을 수정하지 않고 전송 방식만 교체할 수 있습니다.

동일한 서버를 다른 전송으로 실행
typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
 
// 서버 로직은 전송 방식과 독립적
function createServer(): McpServer {
  const server = new McpServer({
    name: "flexible-server",
    version: "1.0.0",
  });
 
  server.tool("greet", "인사합니다", {
    name: { type: "string" },
  }, async (params) => ({
    content: [{ type: "text", text: "안녕하세요, " + params.name + "님" }],
  }));
 
  return server;
}
 
// 명령줄 인자로 전송 방식 결정
const mode = process.argv[2];
 
if (mode === "http") {
  const transport = new StreamableHTTPServerTransport("/mcp");
  const server = createServer();
  await server.connect(transport);
  // HTTP 서버 설정...
} else {
  const transport = new StdioServerTransport();
  const server = createServer();
  await server.connect(transport);
}
Tip

개발 단계에서는 stdio로 빠르게 테스트하고, 배포 단계에서 Streamable HTTP로 전환하는 패턴이 효율적입니다. 서버 로직을 별도 함수로 분리해두면 전환이 수월합니다.

커스텀 전송 구현

MCP SDK가 제공하는 전송 방식 외에 직접 전송 계층을 구현할 수도 있습니다. 예를 들어 WebSocket 전송이나 메시지 큐 기반 전송을 구현할 수 있습니다.

커스텀 전송을 구현하려면 Transport 인터페이스를 만족시키면 됩니다.

Transport 인터페이스
typescript
interface Transport {
  // 메시지를 전송합니다
  send(message: JSONRPCMessage): Promise<void>;
 
  // 수신 메시지 콜백을 설정합니다
  onMessage: (message: JSONRPCMessage) => void;
 
  // 연결 종료 콜백을 설정합니다
  onClose: () => void;
 
  // 에러 콜백을 설정합니다
  onError: (error: Error) => void;
 
  // 연결을 시작합니다
  start(): Promise<void>;
 
  // 연결을 종료합니다
  close(): Promise<void>;
}

이 인터페이스만 구현하면, 기존 MCP 서버 코드를 수정하지 않고 새로운 전송 방식 위에서 실행할 수 있습니다.

정리

이 장에서 다룬 핵심 내용을 요약합니다.

  • MCP는 stdio와 Streamable HTTP 두 가지 전송 방식을 지원합니다.
  • stdio는 로컬 프로세스 간 통신에 적합하며, 설정이 단순하고 지연 시간이 낮습니다.
  • Streamable HTTP는 원격 통신에 적합하며, 무상태 운영, 수평 확장, 표준 HTTP 인프라 활용이 가능합니다.
  • 기존 SSE 전송은 2025년 3월 이후 더 이상 사용하지 않으며, Streamable HTTP로 대체되었습니다.
  • 전송 계층은 프로토콜 본체와 분리되어 있어, 서버 로직 수정 없이 전송 방식을 교체할 수 있습니다.

다음 장 미리보기

4장에서는 MCP 서버의 세 가지 프리미티브(도구, 리소스, 프롬프트)를 상세히 다룹니다. 각 프리미티브의 스키마 정의, 구현 패턴, 실전 활용 사례를 코드와 함께 살펴보겠습니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#mcp#typescript#python

관련 글

AI / ML

4장: 서버 프리미티브 - 도구, 리소스, 프롬프트

MCP 서버가 제공하는 세 가지 핵심 프리미티브의 스키마 정의, 구현 패턴, 실전 활용 사례를 상세히 다룹니다.

2026년 1월 27일·24분
AI / ML

2장: 프로토콜 아키텍처 심층 분석

MCP의 JSON-RPC 2.0 기반 메시지 형식, 연결 생명주기, 능력 협상 메커니즘을 상세히 분석합니다.

2026년 1월 23일·20분
AI / ML

5장: TypeScript로 MCP 서버 구축하기

TypeScript SDK를 사용하여 프로젝트 설정부터 도구, 리소스, 프롬프트 구현, 테스트까지 MCP 서버를 구축하는 전 과정을 다룹니다.

2026년 1월 29일·18분
이전 글2장: 프로토콜 아키텍처 심층 분석
다음 글4장: 서버 프리미티브 - 도구, 리소스, 프롬프트

댓글

목차

약 17분 남음
  • 전송 계층의 역할
  • stdio 전송
    • 동작 원리
    • 구현 예제
    • stdio의 장점과 한계
  • Streamable HTTP 전송
    • 배경: SSE에서 Streamable HTTP로
    • Streamable HTTP 동작 원리
    • 세션 관리
    • 구현 예제
    • Streamable HTTP의 장점
  • 전송 방식 선택 기준
    • 판단 플로우차트
  • 전송 방식의 전환
  • 커스텀 전송 구현
  • 정리
  • 다음 장 미리보기