Skip to content

Latest commit

ย 

History

15 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

ํ† ์Šค์ฆ๊ถŒ MCP (TossInvest MCP)

English

ํ† ์Šค์ฆ๊ถŒ MCP๋Š” ํ† ์Šค์ฆ๊ถŒ ๊ณต์‹ Open API๋ฅผ MCP(Model Context Protocol) ํ˜ธํ™˜ AI ์—์ด์ „ํŠธ์™€ ์—ฐ๊ฒฐํ•˜๋Š” ์˜คํ”ˆ์†Œ์Šค ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค. VS Code, Hermes ๊ฐ™์€ MCP ํด๋ผ์ด์–ธํŠธ์—์„œ ์ฃผ์‹ ์‹œ์„ธ, ๋ณด์œ  ์ข…๋ชฉ, ์ฃผ๋ฌธ ๊ฐ€๋Šฅ ๊ธˆ์•ก๊ณผ ์ฃผ๋ฌธ ๋‚ด์—ญ์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ํ”„๋กœ์ ํŠธ๋Š” ํ† ์Šค์ฆ๊ถŒ์˜ ๊ณต์‹ ์ œํ’ˆ์ด ์•„๋‹ˆ๋ฉฐ ํˆฌ์ž ์กฐ์–ธ์„ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ† ์Šค์ฆ๊ถŒ MCP๋ž€?

TossInvest MCP๋Š” ํ† ์Šค์ฆ๊ถŒ Open API์˜ ๊ธฐ๋Šฅ์„ AI ์—์ด์ „ํŠธ๊ฐ€ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋Š” MCP ๋„๊ตฌ๋กœ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Docker ์ปจํ…Œ์ด๋„ˆ๋Š” API ํ‚ค ์—†์ด ๊ณ„์† ์‹คํ–‰ํ•˜๊ณ , ๊ฐ ์‚ฌ์šฉ์ž๊ฐ€ ์ž์‹ ์˜ client_id์™€ client_secret์„ MCP ์—ฐ๊ฒฐ์˜ ๋น„๊ณต๊ฐœ ์š”์ฒญ ํ—ค๋”๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

  • ๊ธฐ๋ณธ ์‹คํ–‰์€ ์กฐํšŒ ์ „์šฉ์ด๋ฉฐ ์ฃผ๋ฌธ ๋„๊ตฌ๊ฐ€ ๋“ฑ๋ก๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ๊ตญ๋‚ด์™ธ ์ข…๋ชฉ ์ •๋ณด, ์‹œ์„ธ, ํ˜ธ๊ฐ€, ์ฒด๊ฒฐ, ๊ณ„์ขŒ์™€ ๋ณด์œ  ์ฃผ์‹์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • API ํ‚ค์™€ ๊ณ„์ขŒ ์ •๋ณด๋Š” MCP ๋„๊ตฌ ์ธ์ž๋‚˜ schema์— ๋…ธ์ถœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ๊ฑฐ๋ž˜ ๊ธฐ๋Šฅ์€ --dangerously-enable-trading๊ณผ ๋ณ„๋„ ์‚ฌ๋žŒ ์Šน์ธ์„ ๋ชจ๋‘ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค.

๋ณด์•ˆ ๋ชจ๋ธ

  • Toss API ํ‚ค์™€ ๊ณ„์ขŒ sequence๋Š” MCP ์„œ๋ฒ„์˜ .env๋‚˜ ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์— ์ €์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์ž๊ฒฉ ์ฆ๋ช…์€ MCP ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ ํ—ค๋”๋กœ ์ „๋‹ฌํ•˜๊ณ  ์„œ๋ฒ„๋Š” ์š”์ฒญ๋ณ„ ์ธ์ฆ ์ปจํ…์ŠคํŠธ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  • ์ž๊ฒฉ ์ฆ๋ช… ํ—ค๋”๋Š” MCP ๋„๊ตฌ schema์™€ ๋„๊ตฌ ์ธ์ž์— ๋‚˜ํƒ€๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์š”์ฒญ๋ณ„ OAuth token, HTTP client, ์ฃผ๋ฌธ preview๋Š” ์ž๊ฒฉ ์ฆ๋ช… fingerprint๋ณ„๋กœ ๊ฒฉ๋ฆฌ๋ฉ๋‹ˆ๋‹ค.
  • ๊ณต๊ฐœ ์ฃผ์†Œ์˜ ํ‰๋ฌธ HTTP ์š”์ฒญ์€ 426 https-required๋กœ ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.
  • ํ‰๋ฌธ HTTP๋Š” loopback์œผ๋กœ ๊ฒŒ์‹œํ•œ ๋กœ์ปฌ ์„œ๋ฒ„์™€ ๋‚ด๋ถ€ healthcheck์—์„œ๋งŒ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • MCP ์‘๋‹ต์€ Cache-Control: no-store์ด๋ฉฐ HTTPS ์‘๋‹ต์—๋Š” HSTS๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  • Uvicorn access log๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋•๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์˜ค๋ฅ˜์™€ upstream ๋ฐ์ดํ„ฐ์˜ ์ธ์ฆยท๊ณ„์ขŒ ํ•„๋“œ๋Š” redactionํ•ฉ๋‹ˆ๋‹ค.
  • ์ฃผ๋ฌธ ๋„๊ตฌ๋Š” --dangerously-enable-trading ์—†์ด๋Š” ๋“ฑ๋ก๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ๋ชจ๋“  ์ฃผ๋ฌธ ์ƒ์„ฑยท์ •์ •ยท์ทจ์†Œ๋Š” 2๋ถ„์งœ๋ฆฌ preview, ๋ณ„๋„ ์Šน์ธ ํŽ˜์ด์ง€, ์‹คํ–‰ ์ง์ „ ์žฌ๊ฒ€์ฆ์„ ํ†ต๊ณผํ•ด์•ผ ํ•˜๋ฉฐ ์ž๋™ ์žฌ์‹œ๋„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ตฌ์กฐ

flowchart LR
    U["์‚ฌ์šฉ์ž"] --> H["MCP ํด๋ผ์ด์–ธํŠธ"]
    H -->|"๋„๊ตฌ ์ธ์ž<br/>(๋น„๋ฐ€๊ฐ’ ์—†์Œ)"| M["TossInvest MCP"]
    H -->|"๋น„๊ณต๊ฐœ ์š”์ฒญ ํ—ค๋”<br/>HTTPS ๋˜๋Š” loopback HTTP"| M
    M --> C["์š”์ฒญ๋ณ„ ์ธ์ฆ ์ปจํ…์ŠคํŠธ"]
    C --> T["ํ† ์Šค์ฆ๊ถŒ Open API"]
    U -->|"๋ณ„๋„ ์‚ฌ๋žŒ ์Šน์ธ token"| A["์Šน์ธ ํŽ˜์ด์ง€"]
    A --> M
Loading

๋น ๋ฅธ ์‹œ์ž‘

ํ•„์š”ํ•œ ๊ฒƒ์€ Python 3.12 ๋˜๋Š” Docker, ๊ทธ๋ฆฌ๊ณ  ํ† ์Šค์ฆ๊ถŒ Open API client_id์™€ client_secret์ž…๋‹ˆ๋‹ค.

์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์ด๋™ํ•˜๋Š” main์ด๋‚˜ latest ๋Œ€์‹  ํ˜„์žฌ ์•ˆ์ • ๋ฒ„์ „์ธ v0.1.1์„ ๊ณ ์ •ํ•˜์„ธ์š”.

git clone --branch v0.1.1 --depth 1 https://github.com/cha2hyun/tossinvest-mcp.git
cd tossinvest-mcp
cp .env.example .env
docker compose up -d --build
curl http://127.0.0.1:8000/healthz

GitHub Release์—๋Š” Python wheel, source distribution๊ณผ SHA-256 checksum์ด ์ฒจ๋ถ€๋ฉ๋‹ˆ๋‹ค. ๋นŒ๋“œ ์—†์ด ๊ฒŒ์‹œ๋œ multi-architecture GHCR ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋ฒ„์ „์„ ๋ช…์‹œํ•ด ์‹คํ–‰ํ•˜์„ธ์š”.

TOSSINVEST_IMAGE=ghcr.io/cha2hyun/tossinvest-mcp:0.1.1 docker compose pull
TOSSINVEST_IMAGE=ghcr.io/cha2hyun/tossinvest-mcp:0.1.1 docker compose up -d --no-build

latest๋Š” ์ƒˆ ๋ฆด๋ฆฌ์ฆˆ๋งˆ๋‹ค ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. ์žฌํ˜„ ๊ฐ€๋Šฅํ•œ ๋ฐฐํฌ์—๋Š” 0.1.1์ฒ˜๋Ÿผ ์ „์ฒด ๋ฒ„์ „ ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. ๋ฆด๋ฆฌ์ฆˆ ์ ˆ์ฐจ๋Š” RELEASING.md์— ์ •๋ฆฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์„œ๋ฒ„ .env์—๋Š” ํฌํŠธ์™€ ์บ์‹œ ๊ฐ™์€ ๋น„๋ฐ€์ด ์•„๋‹Œ ์šด์˜ ์„ค์ •๋งŒ ๋‘ก๋‹ˆ๋‹ค. ํ† ์Šค ํ‚ค์™€ ๊ณ„์ขŒ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜์ง€ ๋งˆ์„ธ์š”.

๊ธฐ๋ณธ ๋กœ์ปฌ ์ฃผ์†Œ:

์šฉ๋„ ์ฃผ์†Œ
MCP http://127.0.0.1:8000/mcp
์ƒํƒœ http://127.0.0.1:8000/healthz
์ค€๋น„ ์ƒํƒœ http://127.0.0.1:8000/readyz

์š”์ฒญ๋ณ„ ์ธ์ฆ ๋ชจ๋“œ์˜ /readyz๋Š” ์™ธ๋ถ€ API๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ์ž๊ฒฉ ์ฆ๋ช…์„ ๊ฒ€์‚ฌํ•˜์ง€ ์•Š๊ณ  ์„œ๋ฒ„๊ฐ€ ์š”์ฒญ์„ ๋ฐ›์„ ์ค€๋น„๊ฐ€ ๋˜์—ˆ๋Š”์ง€๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„ ํ™˜๊ฒฝ๋ณ€์ˆ˜

๋ณ€์ˆ˜ ๊ธฐ๋ณธ๊ฐ’ ์„ค๋ช…
MCP_PUBLISHED_HOST 127.0.0.1 Compose๊ฐ€ ํ˜ธ์ŠคํŠธ์— ๋ฐ”์ธ๋”ฉํ•  ์ฃผ์†Œ
MCP_PUBLISHED_PORT 8000 Compose๊ฐ€ ๊ณต๊ฐœํ•  ํฌํŠธ
MCP_TENANT_CACHE_SIZE 100 ๋ฉ”๋ชจ๋ฆฌ์— ์œ ์ง€ํ•  ์š”์ฒญ๋ณ„ ์ธ์ฆ ์ปจํ…์ŠคํŠธ ์ตœ๋Œ€ ์ˆ˜
MCP_TENANT_CACHE_TTL 3600 ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์ธ์ฆ ์ปจํ…์ŠคํŠธ ๋ณด์กด ์‹œ๊ฐ„(์ดˆ)
MCP_TRUSTED_PROXY_IPS 127.0.0.1 forwarded scheme์„ ์‹ ๋ขฐํ•  reverse proxy IP ๋ชฉ๋ก
MCP_ALLOWED_ORIGINS ๋นˆ ๊ฐ’ ๋ธŒ๋ผ์šฐ์ € MCP ์š”์ฒญ์— ํ—ˆ์šฉํ•  Origin ๋ชฉ๋ก
TOSSINVEST_APPROVAL_BASE_URL http://127.0.0.1:8000 preview๊ฐ€ ๋ฐ˜ํ™˜ํ•  ์Šน์ธ ํŽ˜์ด์ง€ origin
TOSSINVEST_BASE_URL ๊ณต์‹ API ์ฃผ์†Œ ํ…Œ์ŠคํŠธยทํ˜ธํ™˜ ํ”„๋ก์‹œ์šฉ upstream ์ฃผ์†Œ
TOSSINVEST_REQUEST_TIMEOUT 15 upstream timeout(์ดˆ)
LOG_LEVEL INFO ์„œ๋ฒ„ ๋กœ๊ทธ ๋ ˆ๋ฒจ

์ง์ ‘ ์‹คํ–‰ํ•  ๋•Œ๋งŒ MCP_HOST์™€ MCP_PORT๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋‘ ๊ฐ’์€ Compose ๋‚ด๋ถ€์—์„œ ๊ฐ๊ฐ 0.0.0.0, 8000์œผ๋กœ ๊ณ ์ •๋ฉ๋‹ˆ๋‹ค.

/mcp ์š”์ฒญ์ด 403 {"error":"origin-not-allowed"}๋กœ ์‹คํŒจํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋‚˜ ์›น๋ทฐ ๊ธฐ๋ฐ˜ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๋‚ธ Origin์ด ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ํด๋ผ์ด์–ธํŠธ์˜ ์ •ํ™•ํ•œ origin์„ ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„ํ•ด MCP_ALLOWED_ORIGINS์— ๋„ฃ๊ณ  ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋‹ค์‹œ ์ƒ์„ฑํ•˜์„ธ์š”. ์˜ˆ: MCP_ALLOWED_ORIGINS=http://127.0.0.1:6274,http://localhost:6274.

MCP ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ

ํ† ์Šค ์ž๊ฒฉ ์ฆ๋ช…์€ MCP ์„œ๋ฒ„๊ฐ€ ์•„๋‹ˆ๋ผ ํด๋ผ์ด์–ธํŠธ์˜ ๋น„๋ฐ€ ์ €์žฅ์†Œ์— ๋‘ก๋‹ˆ๋‹ค. Hermes ์˜ˆ์‹œ๋Š” ~/.hermes/.env์ž…๋‹ˆ๋‹ค.

TOSSINVEST_CLIENT_ID=๋ฐœ๊ธ‰๋ฐ›์€_client_id
TOSSINVEST_CLIENT_SECRET=๋ฐœ๊ธ‰๋ฐ›์€_client_secret

~/.hermes/config.yaml:

mcp_servers:
  tossinvest:
    url: "http://127.0.0.1:8000/mcp"
    headers:
      X-Tossinvest-Client-Id: "${TOSSINVEST_CLIENT_ID}"
      X-Tossinvest-Client-Secret: "${TOSSINVEST_CLIENT_SECRET}"
    enabled: true
    timeout: 120
    connect_timeout: 30
    supports_parallel_tool_calls: false

์ด ํ—ค๋”๋Š” MCP ์—ฐ๊ฒฐ ๊ณ„์ธต์ด ์ž๋™์œผ๋กœ ๋ถ™์ด๋ฉฐ ๋ชจ๋ธ์˜ ๋„๊ตฌ ์ธ์ž์—๋Š” ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ์ž๊ฒฉ ์ฆ๋ช… ๊ฐ’์„ ํ”„๋กฌํ”„ํŠธ, ์ฑ„ํŒ…, Skill, tool argument์— ๋ณต์‚ฌํ•˜์ง€ ๋งˆ์„ธ์š”.

์กฐํšŒ ์ „์šฉ allowlist ์ „์ฒด ์˜ˆ์‹œ๋Š” examples/hermes-config.yaml, ํด๋ผ์ด์–ธํŠธ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์˜ˆ์‹œ๋Š” examples/hermes.env.example์— ์žˆ์Šต๋‹ˆ๋‹ค.

๊ณ„์ขŒ ์ž๋™ ์„ ํƒ

์„œ๋ฒ„๋Š” client_id์™€ client_secret์œผ๋กœ OAuth token์„ ๋ฐœ๊ธ‰ํ•œ ๋’ค ๊ณ„์ขŒ ๋ชฉ๋ก API๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๊ณ„์ขŒ๊ฐ€ ํ•˜๋‚˜๋ฉด ์‹ค์ œ accountSeq๋ฅผ ๋‚ด๋ถ€ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ž๋™ ์„ ํƒํ•˜๋ฏ€๋กœ ์‚ฌ์šฉ์ž๊ฐ€ sequence๋ฅผ ์•Œ๊ฑฐ๋‚˜ ์„ค์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๊ณ„์ขŒ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ๋ฉด ์„œ๋ฒ„๋Š” ์ž„์˜๋กœ ์ฃผ๋ฌธ ๊ณ„์ขŒ๋ฅผ ์„ ํƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. list_accounts๊ฐ€ ๊ณ„์ขŒ๋ฒˆํ˜ธ์™€ sequence๋ฅผ ์ œ๊ฑฐํ•œ ๋‹ค์Œ account_index, ๊ณ„์ขŒ ์œ ํ˜•, ์„ ํƒ ์ƒํƒœ๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•  ๊ณ„์ขŒ์˜ 1๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” index๋ฅผ MCP ์—ฐ๊ฒฐ ํ—ค๋”์— ์ถ”๊ฐ€ํ•˜์„ธ์š”.

X-Tossinvest-Account-Index: "${TOSSINVEST_ACCOUNT_INDEX}"

index๋Š” ์‹ค์ œ ๊ณ„์ขŒ๊ฐ’์ด ์•„๋‹ˆ๋ฉฐ ๋ชจ๋ธ ๋„๊ตฌ ์ธ์ž์—๋„ ๋“ค์–ด๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

VS Code

๋ช…๋ น ํŒ”๋ ˆํŠธ์˜ MCP: Open User Configuration์—์„œ ์—ฌ๋Š” mcp.json์—๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ client_id์™€ client_secret ๋‘ ์ž…๋ ฅ๋งŒ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. VS Code๊ฐ€ ${input:...} ๊ฐ’์„ ์ฒ˜์Œ ์—ฐ๊ฒฐํ•  ๋•Œ ๋ฌผ์–ด๋ณด๊ณ  ๋ณด์•ˆ ์ €์žฅ์†Œ์— ๋ณด๊ด€ํ•ฉ๋‹ˆ๋‹ค.

{
  "inputs": [
    {
      "type": "promptString",
      "id": "tossinvest-client-id",
      "description": "TossInvest Client ID",
      "password": true
    },
    {
      "type": "promptString",
      "id": "tossinvest-client-secret",
      "description": "TossInvest Client Secret",
      "password": true
    }
  ],
  "servers": {
    "tossinvest": {
      "type": "http",
      "url": "http://127.0.0.1:8000/mcp",
      "headers": {
        "X-Tossinvest-Client-Id": "${input:tossinvest-client-id}",
        "X-Tossinvest-Client-Secret": "${input:tossinvest-client-secret}"
      }
    }
  }
}

์‹ค์ œ ID์™€ secret์„ JSON์— ์ง์ ‘ ์ ์ง€ ๋งˆ์„ธ์š”. ${input:...}๋กœ ์ž…๋ ฅํ•œ ๊ฐ’์€ ์—ฐ๊ฒฐ ํ—ค๋”์—๋งŒ ์‚ฌ์šฉ๋˜๋ฉฐ ๋ชจ๋ธ์ด ์ƒ์„ฑํ•˜๋Š” ๋„๊ตฌ JSON ์ธ์ž์—๋Š” ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ณต๊ฐœ ์„œ๋ฒ„๋ผ๋ฉด url์€ ๋ฐ˜๋“œ์‹œ https://์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ „์ฒด ์กฐํšŒ ์˜ˆ์ œ๋Š” examples/vscode-mcp.json, ๊ฑฐ๋ž˜ ๋ชจ๋“œ ์˜ˆ์ œ๋Š” examples/vscode-mcp-trading.json์— ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์ค‘ ๊ณ„์ขŒ์—์„œ๋งŒ ๋‹ค์Œ input๊ณผ header๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

{
  "type": "promptString",
  "id": "tossinvest-account-index",
  "description": "TossInvest account index from list_accounts",
  "password": false
}
{
  "X-Tossinvest-Account-Index": "${input:tossinvest-account-index}"
}

์ œ๊ณต ๋„๊ตฌ

๊ธฐ๋ณธ ์กฐํšŒ ๋ชจ๋“œ๋Š” ๋‹ค์Œ 28๊ฐœ ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๊ตฌ๋ถ„ ๋„๊ตฌ
์ข…๋ชฉยท์‹œ์„ธ get_stock_info, list_stocks, get_stock_warnings, get_prices, get_orderbook, get_recent_trades, get_price_limits, get_candles
์ข…๋ชฉ ๋™ํ–ฅ get_stock_investor_trading, get_stock_program_trades, get_stock_short_selling, get_stock_credit_trades, get_stock_securities_lending
์‹œ์žฅ ์ •๋ณด get_exchange_rate, get_market_calendar, get_rankings, get_market_indicator_prices, get_market_indicator_candles, get_market_indicator_investor_trading
๊ณ„์ขŒยท์ž์‚ฐ list_accounts, get_holdings, get_buying_power, get_sellable_quantity, get_commissions
์ฃผ๋ฌธ ์กฐํšŒ list_orders, get_order, list_conditional_orders, get_conditional_order

๊ณต์‹ OpenAPI์˜ ์กฐ๊ฑด์ฃผ๋ฌธ ์ƒ์„ฑยท์ˆ˜์ •ยท์ทจ์†Œ๋Š” ์ผ๋ฐ˜ ์ฃผ๋ฌธ์— ์ ์šฉ๋œ preview, ๋ณ„๋„ ์‚ฌ๋žŒ ์Šน์ธ, ์‹คํ–‰ ์ง์ „ ์žฌ๊ฒ€์ฆ์ด ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์•„ MCP ๋„๊ตฌ๋กœ ๋…ธ์ถœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ฑฐ๋ž˜ ๋ชจ๋“œ

๊ฑฐ๋ž˜ ๋ชจ๋“œ๋Š” ์•„๋ž˜ ์„ธ ์กฐ๊ฑด์„ ๋ชจ๋‘ ์ถฉ์กฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  1. ์„œ๋ฒ„๋ฅผ --dangerously-enable-trading์œผ๋กœ ์‹œ์ž‘
  2. MCP ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ฃผ๋ฌธ ํ•œ๋„์™€ ์Šน์ธ token digest ํ—ค๋”๋ฅผ ์ „๋‹ฌ
  3. ์‚ฌ์šฉ์ž๊ฐ€ ๋ณ„๋„ ์Šน์ธ ํŽ˜์ด์ง€์—์„œ ์›๋ฌธ ์Šน์ธ token์œผ๋กœ preview๋ฅผ ์Šน์ธ

์›๋ฌธ ์Šน์ธ token์€ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ด€๋ฆฌ์ž์ฒ˜๋Ÿผ MCP ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๋ฐ–์˜ ์ €์žฅ์†Œ์— ๋ณด๊ด€ํ•ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ์—๋Š” SHA-256 digest๋งŒ ๋‘ก๋‹ˆ๋‹ค.

openssl rand -hex 32
read -rsp "Approval token: " APPROVAL_TOKEN
echo
printf '%s' "$APPROVAL_TOKEN" | openssl dgst -sha256 -r | awk '{print $1}'
unset APPROVAL_TOKEN

~/.hermes/.env์— ์ถ”๊ฐ€:

TOSSINVEST_MAX_ORDER_KRW=1000000
TOSSINVEST_MAX_ORDER_USD=500
TOSSINVEST_APPROVAL_TOKEN_SHA256=64์ž๋ฆฌ_sha256_digest

๊ฑฐ๋ž˜์šฉ ํ—ค๋”์™€ allowlist๋Š” examples/hermes-trading-config.yaml์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

docker compose \
  -f compose.yaml \
  -f compose.trading.yaml \
  up -d --build --force-recreate

๋“ฑ๋ก๋˜๋Š” ์ถ”๊ฐ€ ๋„๊ตฌ:

์ž‘์—… preview ์‹คํ–‰
์ƒ์„ฑ preview_order place_order
์ •์ • preview_order_modification modify_order
์ทจ์†Œ preview_order_cancellation cancel_order

preview์—๋Š” ์Šน์ธ secret์ด ์—†์œผ๋ฉฐ, ์Šน์ธ๋˜์ง€ ์•Š์€ ์‹คํ–‰์€ approval-required๋กœ ๊ฑฐ๋ถ€๋ฉ๋‹ˆ๋‹ค. ์Šน์ธ ํ›„์—๋„ ๊ฐ€๊ฒฉยทํ™˜์œจยท์ž”๊ณ ยทํŒ๋งค ๊ฐ€๋Šฅ ์ˆ˜๋Ÿ‰ยท์ฃผ๋ฌธ ์ƒํƒœยทํ•œ๋„๋ฅผ ๋‹ค์‹œ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜๋กœ order-state-unknown์ด ๋ฐ˜ํ™˜๋˜๋ฉด ๊ฐ™์€ ์ฃผ๋ฌธ์„ ์žฌ์‹œ๋„ํ•˜์ง€ ๋ง๊ณ  ์ฃผ๋ฌธ ๋‚ด์—ญ์„ ๋จผ์ € ํ™•์ธํ•˜์„ธ์š”.

๊ณต๊ฐœ ๋ฐฐํฌ

๊ณต๊ฐœ ์„œ๋ฒ„๋Š” ๋ฐ˜๋“œ์‹œ TLS reverse proxy ๋’ค์— ๋‘์„ธ์š”.

  • ์™ธ๋ถ€ MCP URL๊ณผ TOSSINVEST_APPROVAL_BASE_URL ๋ชจ๋‘ https:// ์‚ฌ์šฉ
  • reverse proxy๊ฐ€ ์›๋ž˜ scheme์„ ์ „๋‹ฌํ•˜๊ณ  ๊ทธ ์ฃผ์†Œ๋งŒ MCP_TRUSTED_PROXY_IPS์— ์„ค์ •
  • proxy access/error log์—์„œ X-Tossinvest-*, Authorization, form body๋ฅผ ์‚ญ์ œ ๋˜๋Š” redaction
  • request/response body dump, debug trace, APM header capture ๋น„ํ™œ์„ฑํ™”
  • MCP streaming ์‘๋‹ต buffering ๋น„ํ™œ์„ฑํ™”
  • /healthz, /readyz, /approvals/* ์ ‘๊ทผ ๋ฒ”์œ„ ์ œํ•œ
  • ๋ฐฉํ™”๋ฒฝ, VPN ๋˜๋Š” ๋ณ„๋„ gateway ์ธ์ฆ ์ถ”๊ฐ€
  • ๊ณต๊ฐœ ํฌํŠธ๊ฐ€ ํ•„์š”ํ•  ๋•Œ๋งŒ MCP_PUBLISHED_HOST=0.0.0.0 ์‚ฌ์šฉ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ HTTPS๊ฐ€ ์•„๋‹Œ ๊ณต๊ฐœ ์š”์ฒญ์„ ๊ฑฐ๋ถ€ํ•˜์ง€๋งŒ, TLS ์ข…๋ฃŒ์™€ proxy ๋กœ๊ทธ ์ •์ฑ…์€ reverse proxy ์šด์˜์ž๊ฐ€ ์ฑ…์ž„์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Docker ๋ณด์•ˆ

๊ธฐ๋ณธ Compose๋Š” loopback bind, non-root ์‚ฌ์šฉ์ž, read-only root filesystem, capability ์ œ๊ฑฐ, no-new-privileges, ์ œํ•œ๋œ /tmp, ๋‹จ์ผ worker๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ธ์ฆ ์ปจํ…์ŠคํŠธ์™€ preview๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ์žˆ์œผ๋ฏ€๋กœ ์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ์‹œ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ตฌ์กฐ๋Š” ๋‹จ์ผ instance ์ „์ œ์ž…๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ๊ณผ ๊ฒ€์ฆ

uv sync --all-extras
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run mypy src scripts tests
uv run pip-audit --strict .
uv run python scripts/check_docs.py
uv run python scripts/validate_skills.py
uv run python scripts/update_openapi.py --check
uv build
docker build .
./scripts/run_e2e.sh

run_e2e.sh๋Š” ํ”„๋กœ๋•์…˜ ์ด๋ฏธ์ง€๋ฅผ ๊ธฐ๋™ํ•˜๊ณ  ๋กœ์ปฌ ๊ฐ€์งœ upstream์„ ํ†ตํ•ด MCP HTTP, ์š”์ฒญ๋ณ„ ์ž๊ฒฉ ์ฆ๋ช… ๊ฒฉ๋ฆฌ, OAuth, ์กฐํšŒ ํ˜ธ์ถœ๊ณผ ์˜ค๋ฅ˜ redaction์„ ๋๊นŒ์ง€ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ์™ธ๋ถ€ API๋‚˜ ์‹ค์ œ ์ž๊ฒฉ ์ฆ๋ช…์€ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ณด์•ˆ ๋ฌธ์ œ๋Š” ๊ณต๊ฐœ Issue ๋Œ€์‹  SECURITY.md์˜ ์ ˆ์ฐจ๋กœ ์‹ ๊ณ ํ•˜์„ธ์š”.

About

๐Ÿค– ํ† ์Šค์ฆ๊ถŒ ๊ณต์‹ open api ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” mcp ์„œ๋ฒ„

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages