r33drichards/mcp-js
요약
mcp-v8은 AI 에이전트가 V8 isolate 환경에서 JavaScript/TypeScript를 직접 실행할 수 있게 해주는 Rust 기반 MCP 서버입니다. 에이전트가 단일 도구로 복잡한 로직을 수행하며, 상태 유지 및 보안 정책을 통해 프로덕션 환경에 적합한 기능을 제공합니다.
핵심 포인트
- V8 isolate를 통한 안전한 JS/TS 코드 실행 환경 제공
- 힙 스냅샷을 활용하여 에이전트의 상태 유지(Stateful) 지원
- OPA/Rego 정책 기반의 강력한 보안 및 권한 제어
- Claude Code, Cursor 등 MCP 클라이언트와 연동 가능
- 토큰 사용량을 줄이는 효율적인 도구 호출 방식
mcp-v8은 Rust로 작성된 Model Context Protocol (MCP) 서버로, AI 에이전트가 샌드박스화된 V8 isolate 내에서 JavaScript 및 TypeScript를 실행할 수 있게 해줍니다. 수십 개의 좁은 도구들을 일일이 연결하는 대신, 에이전트에게 run_js라는 단 하나의 도구를 제공하면 에이전트가 직접 코드를 작성합니다. 루프(looping), 분기(branching), 데이터 변환(transforming data), 그리고 다른 도구 호출 등을 수행하며, 이는 종종 동일한 기능을 수행하는 도구 호출 체인(tool-call chains)보다 훨씬 적은 토큰을 사용합니다.
기본 상태 유지 (stateful) 모드에서는 V8 힙(heap)이 콘텐츠 주소 지정 스냅샷(content-addressed snapshot)으로 저장되므로, 에이전트가 여러 턴에 걸쳐 상태를 구축할 수 있습니다. 호스트 기능(네트워크, 파일 시스템, 서브프로세스, WebAssembly, 모듈 임포트 및 다른 MCP 서버 호출)은 모두 기본적으로 꺼져 있으며 (off by default), 명시적인 OPA/Rego 정책에 의해서만 잠금 해제됩니다.
단 하나의 도구, 무한한 능력. 에이전트는 고정된 도구 메뉴가 아닌 프로그램을 실행합니다.
지속 가능한 상태 (Durable state). 힙 스냅샷(Heap snapshots)을 통해 호출 간에 변수와 객체를 유지합니다.
기본적으로 안전함 (Secure by default). 정책을 통해 권한을 부여하기 전까지 fetch, 파일 시스템, 서브프로세스 및 외부 임포트는 거부됩니다.
프로덕션 준비 완료 (Production-ready). stdio / Streamable HTTP / SSE 전송 방식, REST 사이드카(sidecar), 페이지네이션(pagination)이 포함된 비동기 실행, JWKS 인증, 그리고 Raft 복제 클러스터링을 지원합니다.
전체 문서는 https://r33drichards.github.io/mcp-js/ (site-docs/에서 빌드됨)에서 확인할 수 있습니다. 여기에는 튜토리얼, 가이드, 개념 설명, 그리고 CLI 플래그, HTTP API 및 MCP 도구에 대한 완전한 참조가 포함되어 있습니다.
# Server
curl -fsSL https://raw.githubusercontent.com/r33drichards/mcp-js/main/install.sh | sudo bash
# Optional CLI client
...
/usr/local/bin에 설치됩니다. 지원되는 플랫폼: Linux x86_64/arm64 및 macOS Apple Silicon입니다. 또한 nix run github:r33drichards/mcp-js를 사용하거나, Docker를 사용하거나( docker-compose.*.yml 스택 참조), 소스에서 직접 빌드할 수 있습니다.
# Claude Code (stdio)
claude mcp add mcp-v8 -- mcp-v8 --directory-path /tmp/mcp-v8-heaps # stateful
claude mcp add mcp-v8 -- mcp-v8 --stateless # stateless
Claude Desktop / Cursor의 경우, 클라이언트의 mcpServers 설정에 추가하세요:
{ "mcpServers": { "js": { "command": "mcp-v8", "args": ["--stateless"] } } }
그 다음 에이전트에게 다음과 같이 요청하세요: "Run this JavaScript: console.log([1,2,3].map(x => x2))"*.
mcp-v8 --stateless --http-port 8080
# MCP endpoint: POST http://localhost:8080/mcp
# REST sidecar: POST http://localhost:8080/api/exec (JSON body, or a raw-body file upload)
/api/exec는 JSON 본문(body) 또는 raw-body 파일 업로드를 모두 수락합니다. 스크립트를 non-JSON Content-Type과 함께 요청 본문으로 전송하세요 (curl --data-binary @script.js -H 'Content-Type: application/javascript' .../api/exec).
run_js MCP 도구는 선택적 file 파라미터를 통해 서버 자체의 경로로부터 스크립트를 읽을 수도 있습니다. 이 기능은 기본적으로 비활성화되어 있으며, --allow-run-js-file 또는 run_js_file 정책을 통해 활성화할 수 있습니다.
더 자세한 내용은 Quick Start 튜토리얼과 전송(transports) 가이드를 참조하세요.
격리된 V8 엔진 내 JavaScript & TypeScript (deno_core를 통해 제공); TypeScript 타입은 SWC를 통해 제거됩니다 (타입 체크가 아닌 타입 제거).
Async/await & 타이머 — Promise와 이벤트 루프(event loop), 그리고 setTimeout / clearTimeout을 지원합니다.
콘솔 캡처 (Console capture) — console.log/info/warn/error/debug/trace를 지원하며, 스토리지로 스트리밍되어 라인(line) 또는 바이트(byte) 기반 페이지네이션으로 읽을 수 있습니다.
비동기 실행 모델 (Async execution model) — run_js는 실행 ID(execution ID)를 반환합니다. 상태를 폴링(poll)하고 출력을 스트리밍하며, 실행 중인 작업을 취소할 수 있습니다.
콘텐츠 주소 지정 힙 스냅샷 (Content-addressed heap snapshots) — 호출 간에 V8 상태를 유지/복원할 수 있습니다 (로컬 파일 시스템(FS), S3, 또는 S3 + write-through 캐시). 또는 stateless 모드로 실행할 수 있습니다.
WebAssembly — 표준 WebAssembly API와 더불어, 전역(globals)으로 노출되고 클라이언트에 runjs__wasm__<name> 스텁(stub) 도구로 광고되는 사전 로드된 모듈(--wasm-module)을 지원합니다.
ES 모듈 임포트 (ES module imports) — 선택적인 npm:, jsr:, 그리고 런타임에 가져오는 URL 임포트를 지원합니다 (정책에 의해 제어됨).
정책 제어 기능 (Policy-gated capabilities) — fetch, 파일 시스템 (fs), 그리고 서브프로세스(subprocess) 액세스를 지원하며, 각 작업은 Rego 정책에 따라 검사됩니다. 또한 fetch를 위한 헤더/OAuth 주입(injection)을 지원합니다.
.다른 MCP 서버 구성 (Compose other MCP servers)— 상위(upstream) MCP 서버를 연결하고 mcp.callTool(), mcp.listTools()를 통해 JS에서 호출할 수 있습니다.
.커스터마이징 가능한 인터페이스 (Customizable surface)— 서버의 instructions 및 run_js 설명을 재정의할 수 있습니다 (--instructions, --run-js-description).
.인증 및 클러스터링 (Auth & clustering)— JWKS 기반의 JWT 검증을 지원하며, 복제된 세션 메타데이터와 수평적 확장(horizontal scaling)을 제공하는 선택적 Raft 클러스터링을 지원합니다.
.다양한 전송 방식 (Multiple transports)— stdio, Streamable HTTP (MCP 2025-03-26+), 그리고 REST 사이드카와 OpenAPI 명세가 포함된 레거시 HTTP+SSE 전송 방식 (--sse-port, vendored rmcp 0.1.5에 의해 제공됨)을 지원합니다.
.태스크 (Tasks)— Streamable HTTP / stdio를 통한 네이티브 MCP 태스크 (SEP-1319): 태스크 기능이 활성화된 클라이언트는 run_js를 태스크(tasks/get, tasks/result, tasks/list, tasks/cancel)로 실행할 수 있으며, 이는 실행 시간이 긴 호출에 이상적입니다. (레거시 SSE 전송 방식은 태스크를 제공하지 않습니다.)
다음 전역 변수(globals)들은 run_js 내부에서 사용할 수 있습니다 (기능 전역 변수는 정책(policy)이 필요합니다):
| 전역 변수 | 용도 | 제한 조건 |
|---|---|---|
console, setTimeout | 출력 및 타이머 | — |
fetch(url, opts?) | HTTP 요청 (Fetch API) | fetch 정책 |
fs.* | 파일 I/O (readFile, writeFile, …) | filesystem 정책 |
child_process / Deno.Command | 서브프로세스 실행 | subprocess 정책 |
import (npm: / jsr: / URL) | 외부 ES 모듈 | --allow-external-modules + modules 정책 |
WebAssembly, __wasm_<name> | WASM 실행/인스턴스화 | — |
mcp.callTool/listTools/servers | 상위 MCP 서버 호출 | mcp_tools 정책 |
정책 모델에 대해서는 Concepts → Security policies를 참조하세요.
| 도구 (Tool) | 모드 (Mode) | 설명 (Description) |
|---|---|---|
run_js | both | 상태 유지 (Stateful): 실행을 큐에 추가 → {execution_id}. 상태 비유지 (Stateless): 실행 후 {output, error?} 반환. |
get_execution | stateful | 실행의 상태/결과를 폴링 (Poll). |
get_execution_output | stateful | 페이지가 나뉜 (paginated) 콘솔 출력 읽기 (라인 또는 바이트 단위). |
cancel_execution | stateful | 실행 중인 실행을 종료. |
list_executions | stateful | 실행 목록 및 상태 나열. |
list_sessions , list_session_snapshots | stateful | 이름이 지정된 세션 및 히스토리 탐색. |
get_heap_tags , set_heap_tags , delete_heap_tags , query_heaps_by_tags | stateful | 힙 스냅샷 (heap snapshots)에 태그를 지정하거나 검색. |
전체 파라미터: MCP 도구 참조 (MCP tools reference).
이 서버는 Streamable HTTP 및 stdio 전송 계층(transports) 모두에서 rmcp를 통해 MCP tasks 유틸리티(spec 2025-11-25/SEP-1319)를 네이티브로 구현합니다. initialize 결과는 tasks 기능을 광고하며, 클라이언트는 요청 파라미터(request params)에 task 객체를 추가함으로써 태스크 증강이 가능한(task-augmentable) run_js 도구를 태스크로 실행할 수 있습니다:
// → 차단(blocking)되는 대신 태스크와 함께 즉시 반환됨
{ "method": "tools/call",
"params": { "name": "run_js", "arguments": { "code": "…" }, "task": { "ttl": 300000 } } }
그 후 클라이언트는 tasks/get을 폴링하고, tasks/result(호출 시 반환되었을 것과 정확히 동일한 값을 반환)를 통해 최종적인 도구 결과를 가져오며, tasks/list로 작업을 열거하고, tasks/cancel로 실행을 중단합니다. task 필드가 없는 tools/call은 영향을 받지 않습니다.
mcp-v8은 스토리지 백엔드(storage backend), 전송(transport), 실행 제한(execution limits), 정책(policies), fetch-header 주입(injection), WASM 모듈, 클러스터링(clustering), JWKS 인증(auth), 그리고 프롬프트/도구 설명 재정의(prompt/tool-description overrides)에 이르기까지 모든 설정이 CLI 플래그를 통해 이루어집니다. 항상 최신 상태를 유지하는 전체 목록은 생성된 CLI 플래그 참조(CLI flags reference)에서 확인할 수 있습니다.
mcp-v8 --help # 모든 플래그
mcp-v8 --print-openapi # REST OpenAPI 명세 출력
progenitor를 통해 OpenAPI 명세로부터 생성된, REST API를 위한 완전한 타입이 지정된(fully-typed) 클라이언트:
mcp-v8 --stateless --http-port 3000 &
mcp-v8-cli exec "console.log('hello'); 1 + 1"
mcp-v8-cli exec --file ./script.js # 로컬 파일 실행 (코드 업로드)
...
[dependencies]
mcp-v8-client = { git = "https://github.com/r33drichards/mcp-js" }
use mcp_v8_client::Client;
let client = Client::new("http://localhost:3000");
let body = mcp_v8_client::types::ExecRequest {
...
OpenAPI 명세로부터 생성된, 완전히 타입이 지정된(fully-typed) TypeScript 클라이언트 ( openapi-typescript types + openapi-fetch runtime). clients/typescript에 위치합니다.
import { createMcpV8Client } from "@mcp-v8/client";
const client = createMcpV8Client("http://localhost:3000");
const { status, output, heap } = await client.runJs("console.log('hi'); 1 + 1");
API 변경 시 npm run generate로 타입 재생성 ( openapi.json 읽기).
이 저장소는 Nix flake입니다 (빌드가 오프라인 환경에서도 가능하도록 사전 가져온 V8 아카이브를 연결합니다):
nix build github:r33drichards/mcp-js # → ./result/bin/server
# 또는 개발 시:
nix develop # 그리고 나서: cargo build -p server
server/ 내부에서 일반 cargo build --release도 가능합니다. 단, 해당 툴체인이 deno_core/V8을 빌드할 수 있어야 합니다.
— await가 필요한 루프 사용 불가 (setInterval)
setTimeout은 사용할 수 있습니다.
DOM 또는 브라우저 API 없음 — window/document가 없습니다.
TypeScript는 타입 검사가 아닌 타입 제거(type-stripped) 방식 — 유효하지 않은 타입은 보고되지 않고 제거됩니다. JSX/TSX는 지원하지 않습니다 (명확한 오류로 파싱됨).
다양한 요청 속도에서 단일 노드 대 3노드 클러스터 비교.
pr에 railway gha 러너를 사용하여 실행함.
| 토폴로지 (Topology) | 목표 속도 (Target Rate) | 실제 반복/초 (Actual Iter/s) | HTTP 요청/초 (HTTP Req/s) | 실행 평균 (Exec Avg (ms)) | 실행 p95 (Exec p95 (ms)) | 실행 p99 (Exec p99 (ms)) | 성공률 (Success %) | 드롭됨 (Dropped) | 최대 VU (Max VUs) |
|---|---|---|---|---|---|---|---|---|---|
| cluster-stateful | 100/s | 99.5 | 99.5 | 44.9 | 196.88 | 416.99 | 100% | 31 | 41 |
| ... | |||||||||
| 토폴로지 (Topology) | 속도 (Rate) | P95 (ms) | |||||||
| --- | --- | --- | --- | ||||||
| cluster-stateful | 100/s | 196.88 | █████████████████████ | ||||||
| ... | |||||||||
| 목표 속도 (Target Rate): 설정된 상수 도착률 (constant-arrival-rate) (k6 시도 횟수/초) | |||||||||
| 실제 반복/초 (Actual Iter/s): 달성된 초당 반복 횟수 (각 반복 = 1회 POST /api/exec) | |||||||||
| HTTP 요청/초 (HTTP Req/s): 초당 총 HTTP 요청 수 (반복당 1회) | |||||||||
| 드롭됨 (Dropped): VU (Virtual Users)가 고갈되어 k6가 스케줄링할 수 없었던 반복 횟수 (서버 포화 상태를 나타냄) | |||||||||
| 토폴로지 (Topology): | |||||||||
single = 1개의 MCP-V8 노드; | |||||||||
cluster = Raft를 사용하는 3개의 MCP-V8 노드 |
AI 자동 생성 콘텐츠
본 콘텐츠는 GitHub AI Tools의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기