• Transport-agnostic๋Š” ํ”„๋กœํ† ์ฝœ์ด ํŠน์ • ์ „์†ก ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ๊ฒฐํ•ฉ๋˜์ง€ ์•Š์€ ์„ค๊ณ„ ์›์น™
  • ๋ฉ”์‹œ์ง€ ์ •์˜ยท๊ตํ™˜ ๊ทœ์น™์„ **์ „์†ก ๊ณ„์ธต(transport layer)**๊ณผ ๋ถ„๋ฆฌํ•˜๋Š” ์ถ”์ƒํ™” ์ „๋žต
  • ๊ฐ™์€ ํ”„๋กœํ† ์ฝœ์„ SSEยทWebSocketยทstdioยทHTTPยทgRPC ๋“ฑ ์–ด๋–ค ์ฑ„๋„์—์„œ๋„ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์ด์‹์„ฑ
  • โ€œagnosticโ€์˜ IT ์šฉ๋ฒ• โ€” underlying detail์— ์˜์กดํ•˜์ง€ ์•Š์Œ

์ด ์›์น™์ด ํ•„์š”ํ•œ ์ด์œ 

  • transport ๋ณ€ํ™”์— ๊ฐ•๊ฑด โ€” ์ƒˆ ์ „์†ก ์ˆ˜๋‹จ(์˜ˆ: HTTP/3, QUIC, IPC)์ด ๋‚˜ํƒ€๋‚˜๋„ ๋ฉ”์‹œ์ง€ ์ •์˜๋Š” ๊ทธ๋Œ€๋กœ
  • ๋‹ค์ค‘ ํ™˜๊ฒฝ ๋Œ€์‘ โ€” ๊ฐ™์€ ํ”„๋กœํ† ์ฝœ์ด ๋ธŒ๋ผ์šฐ์ €(SSE), ์„œ๋ฒ„(WebSocket), ๋ฐ์Šคํฌํ†ฑ(stdio)์—์„œ ๋™์ž‘
  • ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ โ€” ๋ฉ”์‹œ์ง€ ์˜๋ฏธ๋Š” transport ์—†์ด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ
  • ๋‹ค์–ธ์–ด SDK โ€” transport ์ฒ˜๋ฆฌ๋Š” ์–ธ์–ด๋ณ„ ๊ด€์šฉ ๋ฐฉ์‹์„ ๋”ฐ๋ฅด๋˜, ๋ฉ”์‹œ์ง€ ์Šคํ‚ค๋งˆ๋Š” ๊ณต์œ 
  • OSI ๊ณ„์ธต ๋ถ„๋ฆฌ ์›์น™์˜ ์ ์šฉ โ€” application ์˜๋ฏธ์™€ delivery ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ๋‹ค๋ฅธ ๊ด€์‹ฌ์‚ฌ

AS-IS โ€” transport์— ๊ฒฐํ•ฉ๋œ ์„ค๊ณ„

flowchart TB
    subgraph "๋‹จ์ผ ํŒจํ‚ค์ง€"
        Msg["๋ฉ”์‹œ์ง€ ํƒ€์ž…"]
        HTTP["HTTP ํด๋ผ์ด์–ธํŠธ"]
        SSE["SSE ํŒŒ์„œ"]
        Logic["๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง"]
    end

    Msg --> HTTP
    Msg --> SSE
    HTTP --> Logic
    SSE --> Logic

    style HTTP fill:#FFCDD2
    style SSE fill:#FFCDD2

๋ฌธ์ œ:

  • ๋ฉ”์‹œ์ง€ ์ •์˜๋ฅผ import ํ•˜๋Š” ์ˆœ๊ฐ„ HTTP ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊นŒ์ง€ ๋Œ๋ ค์˜ด
  • ์ƒˆ transport ์ถ”๊ฐ€ ์‹œ ๋ฉ”์‹œ์ง€ ํƒ€์ž…๊นŒ์ง€ ์ˆ˜์ • ์••๋ ฅ
  • ๋‹ค๋ฅธ ์–ธ์–ด SDK๋Š” ๋ฉ”์‹œ์ง€ ์ •์˜๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์ž‘์„ฑํ•ด์•ผ ํ•จ

TO-BE โ€” transport ๊ฒฝ๊ณ„ ๋ถ„๋ฆฌ (ํŒจํ‚ค์ง€ ๋ถ„ํ• )

flowchart TB
    subgraph CorePkg["@myproto/core (transport ๋ชจ๋ฆ„)"]
        Schema["๋ฉ”์‹œ์ง€ ์Šคํ‚ค๋งˆ<br/>์ด๋ฒคํŠธ ํƒ€์ž… ์ •์˜"]
    end

    subgraph EncoderPkg["@myproto/encoder (HTTP ๋ชจ๋ฆ„)"]
        Encoder["JSON/Binary ์ง๋ ฌํ™”<br/>media type ๋ผ์šฐํŒ…"]
    end

    subgraph T1Pkg["@myproto/http-client"]
        T1["HTTP/SSE Transport"]
    end

    subgraph T2Pkg["@myproto/ws-client"]
        T2["WebSocket Transport"]
    end

    subgraph T3Pkg["@myproto/stdio-client"]
        T3["stdio Transport"]
    end

    CorePkg --> EncoderPkg
    EncoderPkg --> T1Pkg
    EncoderPkg --> T2Pkg
    EncoderPkg --> T3Pkg

    style CorePkg fill:#E3F2FD
    style EncoderPkg fill:#F3E5F5
    style T1Pkg fill:#E0F7E9
    style T2Pkg fill:#E0F7E9
    style T3Pkg fill:#E0F7E9

ํ•ต์‹ฌ: ์˜์กด์ด ํ•œ ๋ฐฉํ–ฅ(์•„๋ž˜๋กœ)์œผ๋กœ๋งŒ ํ๋ฅธ๋‹ค. core๋Š” transport๋ฅผ ๋ชจ๋ฅด๊ณ , transport๋Š” core๋ฅผ importํ•  ๋ฟ ๊ทธ ๋ฐ˜๋Œ€๋Š” ์—†๋‹ค. ์ƒˆ transport๋Š” ์˜†์— ํŒจํ‚ค์ง€๋ฅผ ํ•˜๋‚˜ ๋” ์ถ”๊ฐ€ํ•˜๋ฉด ๋.

ํ•ต์‹ฌ ์ •์˜ ๋น„๊ต

๊ถŒ์œ„ ์žˆ๋Š” ์‚ฌ์šฉ์ฒ˜์—์„œ ์–ด๋–ป๊ฒŒ ์ •์˜ํ•˜๋Š”์ง€ ๋น„๊ต:

์ถœ์ฒ˜์ •์˜
MCP (๊ณต์‹ ์ŠคํŽ™)โ€œThe protocol is transport-agnostic and can be implemented over any communication channel that supports bidirectional message exchange.โ€
AG-UI (๊ณต์‹ docs)โ€œAG-UI doesnโ€™t mandate how events are delivered, supporting various transport mechanisms including SSE, WebSockets, webhooks, etc.โ€
gRPC (๊ณต์‹ ์†Œ๊ฐœ)โ€œtransport-agnostic RPC frameworkโ€ โ€” HTTP/2๊ฐ€ ๊ธฐ๋ณธ์ด์ง€๋งŒ ์ถ”์ƒ์ƒ์œผ๋กœ transport ๋…๋ฆฝ
NVM ExpressNVMe ๋ช…๋ น ์ง‘ํ•ฉ๊ณผ transport(PCIeยทTCPยทRDMA)๋ฅผ ๋ถ„๋ฆฌ โ€” ๊ฐ™์€ ๋ช…๋ น์ด ๋‹ค๋ฅธ transport ์œ„์—์„œ ๋™์ž‘

๊ณตํ†ต ํŒจํ„ด: โ€œX (๋ฉ”์‹œ์ง€/์ด๋ฒคํŠธ/๋ช…๋ น)์€ ์ •์˜๋˜์–ด ์žˆ๊ณ , ์–ด๋–ป๊ฒŒ ์ „๋‹ฌํ• ์ง€๋Š” ๋ณ„๊ฐœโ€.

๋ฌด์—‡์ด transport์ธ๊ฐ€

OSI 7๊ณ„์ธต ์ค‘ transport๋Š” 4๊ณ„์ธต(TCP/UDP)์ด์ง€๋งŒ, โ€œtransport-agnosticโ€์˜ transport๋Š” ๋” ๋„“์€ ์˜๋ฏธ๋‹ค โ€” ๋ฉ”์‹œ์ง€๋ฅผ ์–‘ ๋์  ์‚ฌ์ด์— ์‹ค์–ด ๋‚˜๋ฅด๋Š” ๋ชจ๋“  ์ฑ„๋„.

๋ถ„๋ฅ˜์˜ˆ์‹œ
๋„คํŠธ์›Œํฌ ๊ธฐ๋ฐ˜HTTP/1.1, HTTP/2, HTTP/3, WebSocket, gRPC, QUIC
์ŠคํŠธ๋ฆฌ๋ฐ ์˜๋ฏธServer-Sent Events (SSE), Long-polling
๋กœ์ปฌ IPCstdio, Unix socket, Named pipe
๋ฉ”์‹œ์ง€ ํKafka, RabbitMQ, NATS
RPC ์ฑ„๋„gRPC, Apache Thrift

transport-agnostic ํ”„๋กœํ† ์ฝœ์€ ์ด ์ค‘ ์–ด๋–ค ๊ฒƒ์„ ๊ณจ๋ผ ์จ๋„ ๋ฉ”์‹œ์ง€ ์˜๋ฏธ๊ฐ€ ๋ณด์กด๋˜์–ด์•ผ ํ•œ๋‹ค.

์ž๋งค ์šฉ์–ด โ€” ๋ฌด์—‡๊ณผ ๋ฌด์—‡์ด ๋…๋ฆฝ์ธ๊ฐ€

โ€œagnosticโ€์€ ํ•ญ์ƒ ๋ฌด์—‡์— ๋Œ€ํ•œ agnostic์ธ์ง€๊ฐ€ ํ•ต์‹ฌ์ด๋‹ค. ๋‹ค๋ฅธ ์ถ•์˜ ๋…๋ฆฝ์„ฑ๊ณผ ๋น„๊ต:

์šฉ์–ด๋…๋ฆฝ ๋Œ€์ƒ์˜ˆ์‹œ
Transport-agnostic์ „์†ก ์ฑ„๋„gRPC, MCP, AG-UI
Protocol-agnosticapplication ํ”„๋กœํ† ์ฝœ ์ž์ฒดPASTE ์—ฐ๊ตฌ, ๋‹ค์ค‘ ํ”„๋กœํ† ์ฝœ ๊ฒŒ์ดํŠธ์›จ์ด
Wire-format-agnostic์ง๋ ฌํ™” ํ˜•์‹ (JSON/Protobuf/Avro)OpenAPI, Apache Avro
Language-agnosticํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ดProtocol Buffers .proto
Cloud-agnosticํด๋ผ์šฐ๋“œ ํ”„๋กœ๋ฐ”์ด๋”Terraform, Kubernetes
Vendor-agnosticํŠน์ • ๋ฒค๋” ์ œํ’ˆOpenTelemetry vs APM ๋ฒค๋”

transport-agnostic์€ ์ด ์ค‘ delivery ์ฑ„๋„๋งŒ ์ถ”์ƒํ™”ํ•œ๋‹ค โ€” ๋ฉ”์‹œ์ง€ ํ˜•์‹์ด๋‚˜ ์˜๋ฏธ๋Š” ์—ฌ์ „ํžˆ ๊ฐ•ํ•˜๊ฒŒ ์ •์˜๋˜์–ด ์žˆ๋‹ค.

์‹ค์ œ ์ ์šฉ ์‚ฌ๋ก€ โ€” ์ ์šฉ ์ „/ํ›„ ๋น„๊ต

์ค‘์š”ํ•œ ์ „์ œ: transport-agnostic์€ ์–ด๋–ค ๋‹จ์œ„๋กœ ๋ถ„๋ฆฌํ–ˆ๋Š”๊ฐ€๊ฐ€ ์•„๋‹ˆ๋ผ ๋ถ„๋ฆฌ๋˜์—ˆ๋Š”๊ฐ€๊ฐ€ ํ•ต์‹ฌ์ด๋‹ค. ํŒŒ์ผ/๋ชจ๋“ˆ ๋ถ„๋ฆฌ๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํžˆ ์ ์šฉ๋œ ์ƒํƒœ์ด๋ฉฐ, ํŒจํ‚ค์ง€ยท์ €์žฅ์†Œ ๋ถ„๋ฆฌ๋Š” ๊ฐ•์ œ๋ ฅ์ด ๋” ๊ฐ•ํ•œ ํ˜•ํƒœ์ผ ๋ฟ์ด๋‹ค. ์•„๋ž˜ ๋‘ ์‚ฌ๋ก€๋Š” ๊ฐ™์€ ์›์น™์„ ๋‹ค๋ฅธ ๊ฐ•๋„๋กœ ์ ์šฉํ•œ ์˜ˆ์‹œ๋‹ค.

1. MCP ์Šคํƒ€์ผ โ€” ํ•œ ํŒจํ‚ค์ง€ ์•ˆ์—์„œ ํŒŒ์ผ/ํด๋ž˜์Šค ๋ถ„๋ฆฌ

๋‹จ์œ„: ํŒŒ์ผยทํด๋ž˜์Šค (๋‹จ์ผ ํŒจํ‚ค์ง€ mcp-sdk ์•ˆ์—์„œ ๋ถ„๋ฆฌ)

์ ์šฉ ์ „ โ€” ํ•œ ํŒŒ์ผ์— ๋ชจ๋“  ์ฑ…์ž„์ด ๋ญ‰์ณ ์žˆ์Œ

mcp-sdk/                              โ† ํŒจํ‚ค์ง€ 1๊ฐœ
โ””โ”€โ”€ mcp_client.py                     โ† ํŒŒ์ผ 1๊ฐœ
# ๐Ÿ“ฆ mcp-sdk / ๐Ÿ“„ mcp_client.py
import json, subprocess
 
class MCPClient:
    def __init__(self, server_command):
        self.process = subprocess.Popen(server_command, ...)
 
    def call_tool(self, name, args):
        msg = {"jsonrpc": "2.0", "method": "tools/call", "params": {...}}
        # โŒ stdio ํ˜ธ์ถœ์ด ๋ฉ”์‹œ์ง€ ๋นŒ๋“œ์™€ ๋’ค์—‰ํ‚ด
        self.process.stdin.write(json.dumps(msg).encode() + b"\n")
        line = self.process.stdout.readline()
        return json.loads(line)

๋ฌธ์ œ: HTTP ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜๋ ค๋ฉด MCPClient ์ „์ฒด๋ฅผ ๋ณต์ œํ•ด์•ผ ํ•จ. JSON-RPC ๋นŒ๋“œ ๋กœ์ง์ด stdio ํ˜ธ์ถœ๊ณผ ํ•œ ๋ฉ”์„œ๋“œ์— ์„ž์—ฌ ์žˆ๋‹ค.

์ ์šฉ ํ›„ โ€” ๊ฐ™์€ ํŒจํ‚ค์ง€ ์•ˆ์—์„œ ํŒŒ์ผ๋กœ ์ฑ…์ž„ ๋ถ„๋ฆฌ

mcp-sdk/                              โ† ์—ฌ์ „ํžˆ ํŒจํ‚ค์ง€ 1๊ฐœ
โ”œโ”€โ”€ mcp/
โ”‚   โ”œโ”€โ”€ protocol.py                   โ† transport ๋ชจ๋ฆ„
โ”‚   โ”œโ”€โ”€ transport.py                  โ† ์ถ”์ƒ ์ธํ„ฐํŽ˜์ด์Šค
โ”‚   โ”œโ”€โ”€ client.py                     โ† transport ์ฃผ์ž…๋ฐ›์Œ
โ”‚   โ””โ”€โ”€ transports/
โ”‚       โ”œโ”€โ”€ stdio.py                  โ† Transport ๊ตฌํ˜„์ฒด
โ”‚       โ””โ”€โ”€ http.py                   โ† Transport ๊ตฌํ˜„์ฒด
# ๐Ÿ“ฆ mcp-sdk / ๐Ÿ“„ mcp/protocol.py โ€” transport ๋ชจ๋ฆ„
@dataclass
class JSONRPCRequest:
    method: str
    params: dict
    id: int
 
def build_tool_call(name: str, args: dict) -> JSONRPCRequest: ...
def parse_response(raw: bytes) -> dict: ...
# ๐Ÿ“ฆ mcp-sdk / ๐Ÿ“„ mcp/transport.py โ€” ์ถ”์ƒ ์ธํ„ฐํŽ˜์ด์Šค
class Transport(Protocol):
    async def send(self, msg: bytes) -> None: ...
    async def receive(self) -> bytes: ...
# ๐Ÿ“ฆ mcp-sdk / ๐Ÿ“„ mcp/transports/stdio.py
from mcp.transport import Transport
 
class StdioTransport(Transport):
    def __init__(self, cmd): self.proc = subprocess.Popen(cmd, ...)
    async def send(self, msg): self.proc.stdin.write(msg + b"\n")
    async def receive(self): return self.proc.stdout.readline()
# ๐Ÿ“ฆ mcp-sdk / ๐Ÿ“„ mcp/transports/http.py
from mcp.transport import Transport
 
class StreamableHTTPTransport(Transport):
    def __init__(self, url): self.url = url
    async def send(self, msg): await httpx.post(self.url, content=msg)
    async def receive(self): ...  # SSE ํŒŒ์‹ฑ
# ๐Ÿ“ฆ mcp-sdk / ๐Ÿ“„ mcp/client.py โ€” transport ์ฃผ์ž…
from mcp.protocol import build_tool_call, parse_response
from mcp.transport import Transport
 
class MCPClient:
    def __init__(self, transport: Transport):
        self.transport = transport
 
    async def call_tool(self, name, args):
        req = build_tool_call(name, args)         # protocol layer
        await self.transport.send(serialize(req)) # transport layer
        return parse_response(await self.transport.receive())
# ์‚ฌ์šฉ์ž ์ฝ”๋“œ โ€” transport๋งŒ ๊ฐˆ์•„๋ผ์›€
from mcp.client import MCPClient
from mcp.transports.stdio import StdioTransport
from mcp.transports.http import StreamableHTTPTransport
 
client = MCPClient(StdioTransport(["uvx", "mcp-server-git"]))
client = MCPClient(StreamableHTTPTransport("https://example.com/mcp"))

ํ•ต์‹ฌ: ๋ชจ๋‘ ๊ฐ™์€ mcp-sdk ํŒจํ‚ค์ง€ ์•ˆ์˜ ํŒŒ์ผ ๋ถ„๋ฆฌ๋‹ค. ํŒจํ‚ค์ง€๋ฅผ ์ชผ๊ฐœ์ง€ ์•Š์•˜์–ด๋„ transport-agnostic์€ ์ด๋ฏธ ์ ์šฉ๋๋‹ค. ์ƒˆ transport ์ถ”๊ฐ€ = mcp/transports/ ๋””๋ ‰ํ† ๋ฆฌ์— ํŒŒ์ผ ํ•˜๋‚˜ ์ถ”๊ฐ€.

2. AG-UI ์Šคํƒ€์ผ โ€” ์—ฌ๋Ÿฌ ํŒจํ‚ค์ง€๋กœ ๋ถ„๋ฆฌ

๋‹จ์œ„: npm ํŒจํ‚ค์ง€ (@ag-ui/core, @ag-ui/proto, @ag-ui/encoder, @ag-ui/client)

MCP ์‚ฌ๋ก€๊ฐ€ ํ•œ ํŒจํ‚ค์ง€ ์•ˆ์—์„œ ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ–ˆ๋‹ค๋ฉด, AG-UI๋Š” ํ•œ ๋‹จ๊ณ„ ๋” ๊ฐ•ํ•˜๊ฒŒ ๋ณ„๋„ npm ํŒจํ‚ค์ง€๋กœ ๋ถ„๋ฆฌํ•ด์„œ ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„ ์ž์ฒด๋กœ ์ž˜๋ชป๋œ import๋ฅผ ์ฐจ๋‹จํ•œ๋‹ค.

์ ์šฉ ์ „ โ€” ๋‹จ์ผ ํŒจํ‚ค์ง€์— ๋ชจ๋“  ์ฝ”๋“œ

ag-ui/                                     โ† ๊ฐ€์ƒ์˜ ๋‹จ์ผ ํŒจํ‚ค์ง€
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ src/
    โ”œโ”€โ”€ events.ts                          โ† ํƒ€์ž… ์ •์˜
    โ”œโ”€โ”€ proto.ts                           โ† Protobuf ์ธ์ฝ”๋”ฉ
    โ”œโ”€โ”€ encoder.ts                         โ† SSE/binary ๋ผ์šฐํŒ…
    โ””โ”€โ”€ http-agent.ts                      โ† HTTP + RxJS
// ๐Ÿ“ฆ ag-ui / ๐Ÿ“„ package.json
{
  "name": "ag-ui",
  "dependencies": {
    "zod": "^3",
    "rxjs": "^7",
    "@bufbuild/protobuf": "^1",
    "fast-json-patch": "^3"
  }
}
// ๐Ÿ“ฆ ag-ui / ๐Ÿ“„ ์‚ฌ์šฉ์ž ์ฝ”๋“œ
import { Event, HttpAgent } from "ag-ui";
// โŒ Event ํƒ€์ž… ํ•˜๋‚˜๋งŒ import ํ•ด๋„ RxJSยทprotobuf ๋ชจ๋‘ ๋ฒˆ๋“ค์— ํฌํ•จ

๋ฌธ์ œ:

  • ๋ธŒ๋ผ์šฐ์ €์—์„œ ํƒ€์ž…๋งŒ ์“ฐ๋ ค ํ•ด๋„ ๋ชจ๋“  ์˜์กด์„ฑ ํŠธ๋ฆฌ ๋™๋ด‰
  • ๋‹ค๋ฅธ ์–ธ์–ด(Python) SDK๊ฐ€ .proto๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋ ค ํ•ด๋„ TS ์ฝ”๋“œ๊ฐ€ ๋”ฐ๋ผ์˜ด
  • WebSocket transport๋ฅผ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ํŒจํ‚ค์ง€ ์ „์ฒด ์˜์กด์„ฑ์— ์˜ํ–ฅ

์ ์šฉ ํ›„ โ€” 4๊ฐœ ํŒจํ‚ค์ง€๋กœ ๋ถ„ํ•  (AG-UI ์‹ค์ œ ๊ตฌ์กฐ)

sdks/typescript/packages/
โ”œโ”€โ”€ core/                                  โ† ๐Ÿ“ฆ @ag-ui/core
โ”‚   โ”œโ”€โ”€ package.json
โ”‚   โ””โ”€โ”€ src/
โ”‚       โ”œโ”€โ”€ events.ts                      โ† ์ด๋ฒคํŠธ ํƒ€์ž…
โ”‚       โ”œโ”€โ”€ types.ts                       โ† ๋ฉ”์‹œ์ง€ ํƒ€์ž…
โ”‚       โ””โ”€โ”€ capabilities.ts                โ† capability ํ˜‘์ƒ
โ”‚
โ”œโ”€โ”€ proto/                                 โ† ๐Ÿ“ฆ @ag-ui/proto
โ”‚   โ”œโ”€โ”€ package.json
โ”‚   โ””โ”€โ”€ src/
โ”‚       โ”œโ”€โ”€ proto/*.proto                  โ† ์–ธ์–ด ์ค‘๋ฆฝ ์Šคํ‚ค๋งˆ
โ”‚       โ””โ”€โ”€ proto.ts                       โ† encode/decode
โ”‚
โ”œโ”€โ”€ encoder/                               โ† ๐Ÿ“ฆ @ag-ui/encoder
โ”‚   โ”œโ”€โ”€ package.json
โ”‚   โ””โ”€โ”€ src/
โ”‚       โ”œโ”€โ”€ encoder.ts                     โ† SSE/binary ๋ผ์šฐํ„ฐ
โ”‚       โ””โ”€โ”€ media-type.ts
โ”‚
โ””โ”€โ”€ client/                                โ† ๐Ÿ“ฆ @ag-ui/client
    โ”œโ”€โ”€ package.json
    โ””โ”€โ”€ src/
        โ”œโ”€โ”€ agent/HttpAgent.ts             โ† HTTP transport
        โ””โ”€โ”€ run/                           โ† RxJS streaming
// ๐Ÿ“ฆ @ag-ui/core / ๐Ÿ“„ package.json
{
  "name": "@ag-ui/core",
  "dependencies": { "zod": "^3" }          // โœ… zod๋งŒ โ€” transportยทHTTPยทRxJS ์—†์Œ
}
// ๐Ÿ“ฆ @ag-ui/proto / ๐Ÿ“„ package.json
{
  "name": "@ag-ui/proto",
  "dependencies": {
    "@ag-ui/core": "*",
    "@bufbuild/protobuf": "^1"             // โœ… binary ์ธ์ฝ”๋”ฉ๋งŒ
  }
}
// ๐Ÿ“ฆ @ag-ui/encoder / ๐Ÿ“„ package.json
{
  "name": "@ag-ui/encoder",
  "dependencies": {
    "@ag-ui/core": "*",
    "@ag-ui/proto": "*"                    // โœ… HTTPยทRxJS ์—ฌ์ „ํžˆ ์—†์Œ
  }
}
// ๐Ÿ“ฆ @ag-ui/client / ๐Ÿ“„ package.json
{
  "name": "@ag-ui/client",
  "dependencies": {
    "@ag-ui/core": "*",
    "@ag-ui/encoder": "*",
    "rxjs": "^7",                          // โœ… HTTPยทRxJS๋Š” ์ด ํŒจํ‚ค์ง€์—์„œ๋งŒ
    "fast-json-patch": "^3"
  }
}
// ๐Ÿ“ฆ ์‚ฌ์šฉ์ž ์•ฑ / ๐Ÿ“„ client.ts
import type { Event } from "@ag-ui/core";
// โ†’ zod๋งŒ ๋Œ๋ ค์˜ด. RxJSยทprotobuf ํŠธ๋ฆฌ์‰์ดํ‚น์œผ๋กœ ์ œ๊ฑฐ๋จ
 
import { HttpAgent } from "@ag-ui/client";
// โ†’ HTTP๊ฐ€ ํ•„์š”ํ•  ๋•Œ๋งŒ ๋ช…์‹œ์ ์œผ๋กœ import

์ƒˆ transport ์ถ”๊ฐ€ ์‹œ๋‚˜๋ฆฌ์˜ค:

+ packages/ws-client/                      โ† ๐Ÿ“ฆ @ag-ui/ws-client (์ƒˆ ํŒจํ‚ค์ง€)
+ โ”œโ”€โ”€ package.json: dependencies = {
+ โ”‚     "@ag-ui/core": "*",                โ† ๋ฉ”์‹œ์ง€ ์ •์˜ ์žฌ์‚ฌ์šฉ
+ โ”‚     "@ag-ui/encoder": "*"              โ† ์ธ์ฝ”๋” ์žฌ์‚ฌ์šฉ
+ โ”‚   }                                    โ† @ag-ui/client๋Š” importํ•˜์ง€ ์•Š์Œ
+ โ””โ”€โ”€ src/agent/WsAgent.ts

๊ธฐ์กด @ag-ui/client ํŒจํ‚ค์ง€๋Š” ํ•œ ์ค„๋„ ์ˆ˜์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค.

MCP ์Šคํƒ€์ผ๊ณผ์˜ ์ฐจ์ด:

์ถ•MCP ์Šคํƒ€์ผ (ํŒŒ์ผ ๋ถ„๋ฆฌ)AG-UI ์Šคํƒ€์ผ (ํŒจํ‚ค์ง€ ๋ถ„๋ฆฌ)
๋ถ„๋ฆฌ ๋‹จ์œ„ํ•œ ํŒจํ‚ค์ง€ ์•ˆ์˜ ํŒŒ์ผยทํด๋ž˜์Šค๋ณ„๋„ npm ํŒจํ‚ค์ง€
๊ฐ•์ œ๋ ฅ์•ฝํ•จ (์ž˜๋ชป import ํ•ด๋„ ๋™์ž‘)๊ฐ•ํ•จ (์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„๋กœ ์ฐจ๋‹จ)
๋ฒˆ๋“ค ์ตœ์ ํ™”ํŠธ๋ฆฌ์‰์ดํ‚น์— ์˜์กดํŒจํ‚ค์ง€ ๋‹จ์œ„๋กœ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌ
๋‹ค์–ธ์–ด ๊ณต์œ ์–ด๋ ค์›€ (์Šคํ‚ค๋งˆ ์ถ”์ถœ ํ•„์š”)์‰ฌ์›€ (.proto๊ฐ€ ์ด๋ฏธ ๋ณ„๋„ ํŒจํ‚ค์ง€)
์ ํ•ฉ ์ƒํ™ฉ๋‹จ์ผ ์–ธ์–ด SDK, ์ž‘์€ ๊ทœ๋ชจ๋‹ค์–ธ์–ด + ๋ธŒ๋ผ์šฐ์ € ๋ฒˆ๋“ค ์ตœ์ ํ™” ํ•„์š”

3. libp2p โ€” multi-address๋กœ transport ์ •๋ณด๋ฅผ ์ฃผ์†Œ์— ์บก์Аํ™”

/ip4/192.168.1.1/tcp/8080/ws/p2p/QmPeer... ๊ฐ™์ด transport์™€ ํ”„๋กœํ† ์ฝœ์„ ์ฃผ์†Œ ์ž์ฒด์— ๋ช…์‹œํ•˜๋Š” multi-address ํ˜•์‹. โ€œ์ด peer๋Š” WebSocket์œผ๋กœ ์—ฐ๊ฒฐํ•ด๋ผโ€๊ฐ€ ์ฃผ์†Œ ํ•œ ์ค„์— ํ‘œํ˜„๋œ๋‹ค โ€” transport ์„ ํƒ ์ž์ฒด๋ฅผ ๋ฐ์ดํ„ฐ๋กœ ๋‹ค๋ฃฌ๋‹ค.

ํŒจํ„ด ์ •๋ฆฌ โ€” ์–ด๋А ์ˆ˜์ค€๊นŒ์ง€ ๊ฐˆ ๊ฒƒ์ธ๊ฐ€

์ˆ˜์ค€๋ถ„๋ฆฌ ๋ฐฉ์‹์–ธ์ œ ์ถฉ๋ถ„ํ•œ๊ฐ€
ํด๋ž˜์Šค ๋ถ„๋ฆฌTransport ์ถ”์ƒ ํด๋ž˜์Šค/์ธํ„ฐํŽ˜์ด์Šค + ๊ตฌํ˜„์ฒด๋‹จ์ผ ์–ธ์–ด, ๋‹จ์ผ ํŒจํ‚ค์ง€๋กœ ์ถฉ๋ถ„ํ•œ ์ž‘์€ SDK
๋ชจ๋“ˆ ๋ถ„๋ฆฌ๊ฐ™์€ ํŒจํ‚ค์ง€ ์•ˆ์—์„œ ๋””๋ ‰ํ† ๋ฆฌ(transports/)๋กœ ๊ฒฉ๋ฆฌ์˜์กด์„ฑ ๋ถ€ํ’€๋ฆผ์ด ์•„์ง ๋ฌธ์ œ๋Š” ์•„๋‹Œ ๋‹จ๊ณ„
ํŒจํ‚ค์ง€ ๋ถ„๋ฆฌnpm/PyPI ํŒจํ‚ค์ง€๋ฅผ 4-layer๋กœ ์ชผ๊ฐฌ๋ธŒ๋ผ์šฐ์ € ๋ฒˆ๋“คยท๋‹ค์–ธ์–ด SDKยท์„œ๋“œํŒŒํ‹ฐ transport ํ™•์žฅ ํ•„์š”
์ €์žฅ์†Œ ๋ถ„๋ฆฌ.proto/์Šคํ‚ค๋งˆ๋ฅผ ๋ณ„๋„ repo๋กœ์—ฌ๋Ÿฌ ํŒ€/ํšŒ์‚ฌ๊ฐ€ ๊ฐ™์€ ํ”„๋กœํ† ์ฝœ์„ ๊ตฌํ˜„ํ•  ๋•Œ

๋Œ€๋ถ€๋ถ„ ํด๋ž˜์Šค ๋ถ„๋ฆฌ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๊ณ , ์˜์กด์„ฑ์ด๋‚˜ ๋‹ค์–ธ์–ด ์š”๊ตฌ๊ฐ€ ์ƒ๊ธฐ๋ฉด ํŒจํ‚ค์ง€ ๋ถ„๋ฆฌ๋กœ ๊ฒฉ์ƒํ•˜๋Š” ๊ฒƒ์ด ํ˜„์‹ค์ ์ด๋‹ค.

ํ”ํžˆ ํ•˜๋Š” ์˜คํ•ด

์˜คํ•ด 1. โ€œtransport-agnosticโ€ = โ€œ์–ด๋–ค transport๋“  ์‹ค์‹œ๊ฐ„ streaming ๊ฐ€๋Šฅโ€

์•„๋‹ˆ๋‹ค. transport๋งˆ๋‹ค ๋Šฅ๋ ฅ์ด ๋‹ค๋ฅด๋ฏ€๋กœ (์˜ˆ: ๋‹จ๋ฐฉํ–ฅ SSE vs ์–‘๋ฐฉํ–ฅ WebSocket), ํ”„๋กœํ† ์ฝœ์€ ์ตœ์†Œ๊ณตํ†ต๋ถ„๋ชจ์— ๋งž์ถฐ ์„ค๊ณ„๋˜๊ฑฐ๋‚˜, capability negotiation์œผ๋กœ ๋Šฅ๋ ฅ์„ ํ˜‘์ƒํ•œ๋‹ค. AG-UI์˜ TransportCapabilities๊ฐ€ ๊ทธ ์˜ˆ์‹œ๋‹ค.

์˜คํ•ด 2. โ€œtransport-agnosticโ€ = โ€œ์–ด๋–ค transport๋“  ๋™๋“ฑํ•œ ์„ฑ๋Šฅโ€

์•„๋‹ˆ๋‹ค. transport๋งˆ๋‹ค latencyยทthroughputยทํ—ค๋” ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋‹ค๋ฅด๋‹ค. agnostic์€ ์˜๋ฏธ ๋ณด์กด์ด์ง€ ์„ฑ๋Šฅ ๋™๋“ฑ์ด ์•„๋‹ˆ๋‹ค.

์˜คํ•ด 3. โ€œtransport-agnosticโ€์ด๋ฉด โ€œwire format๋„ ์ž์œ ๋กญ๊ฒŒ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹คโ€

wire format: ๋ฉ”์‹œ์ง€๊ฐ€ ์ฑ„๋„์„ ๋”ฐ๋ผ ํ๋ฅผ ๋•Œ์˜ ๋ฐ”์ดํŠธ ํ‘œํ˜„ ๊ทœ์•ฝ โ€” ์ง๋ ฌํ™” ํ˜•์‹(JSON, Protobuf, MessagePack, Avro ๋“ฑ). โ€œtransport๋Š” ์–ด๋””๋กœ ํ๋ฅด๋‚˜โ€๋ผ๋ฉด, โ€œwire format์€ ๋ฌด์—‡์ด ํ๋ฅด๋‚˜โ€.

๋ณ„๊ฐœ ์ฐจ์›์ด๋‹ค. transport-agnostic์ด๋ฉด์„œ wire format์€ ๊ณ ์ •์ธ ํ”„๋กœํ† ์ฝœ๋„ ๋งŽ๋‹ค (gRPC๋Š” transport๋Š” ์œ ์—ฐํ•˜์ง€๋งŒ wire format์€ Protobuf ๊ฐ•์ œ).

์„ค๊ณ„ ์‹œ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

์ƒˆ ํ”„๋กœํ† ์ฝœ์„ transport-agnostic์œผ๋กœ ์„ค๊ณ„ํ•˜๋ ค ํ•  ๋•Œ:

  • ๋ฉ”์‹œ์ง€ ์ •์˜์— transport ์–ดํœ˜๊ฐ€ ์„ž์ด์ง€ ์•Š์•˜๋Š”๊ฐ€ (HTTP status code, WebSocket frame ๊ฐ™์€ ๋‹จ์–ด๊ฐ€ ๋ฉ”์‹œ์ง€ ํ•„๋“œ์— ๋“ฑ์žฅํ•˜๋ฉด ์œ„ํ—˜)
  • streaming ์˜๋ฏธ๊ฐ€ transport์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๊ฐ€ (์ด๋ฒคํŠธ ์‹œํ€€์Šค๋ฅผ transport์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ์ •์˜)
  • bidirectional์ด ํ•„์š”ํ•˜๋ฉด ๋ช…์‹œํ–ˆ๋Š”๊ฐ€ (๋‹จ๋ฐฉํ–ฅ transport์—์„œ fallback ์ „๋žต)
  • capability negotiation์ด ์žˆ๋Š”๊ฐ€ (transport๋ณ„ ๋Šฅ๋ ฅ ์ฐจ์ด๋ฅผ ๋Ÿฐํƒ€์ž„์— ํ˜‘์ƒ)
  • ํŒจํ‚ค์ง€/๋ชจ๋“ˆ ๊ฒฝ๊ณ„๋กœ ๊ฐ•์ œํ–ˆ๋Š”๊ฐ€ (๋ง์ด ์•„๋‹Œ ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„๋กœ)

์ฐธ๊ณ  ๋ฌธ์„œ