본문으로 건너뛰기

도구

샌드박스 내부의 에이전트에는 항상 자체 네이티브 도구가 있습니다. Bash, 파일 편집, 검색이 샌드박스 파일 시스템에서 직접 실행됩니다. 이는 에이전트가 작업 트리에서 로컬로 수행하는 모든 작업을 처리합니다.

에이전트가 자체적으로 할 수 없는 일은 여러분의 앱으로 되돌아가 데이터베이스, 시크릿, 도구를 정의할 때 캡처한 클로저에 접근하는 것입니다. 이를 위해 chat()이 제공하는 서버 도구를 샌드박스에 브리징합니다.

이 페이지에서는 오케스트레이터에 연결되는 여러분의 호스트 도구를 설명합니다. 호스트를 왕복하지 않고 에이전트가 직접 통신하는 서드파티 MCP 서버를 제공하려면 작업 공간에 선언해야 합니다. 프로비저닝을 참고하세요. 서버 도구 전반에 대해서는 기본 서버 도구 문서를 참고하세요.

네이티브 도구와 브리징된 도구 비교

미들웨어의 샌드박스와 함께 chat()tools를 전달하면 각 도구가 호스트 측 MCP 도구 프록시를 통해 샌드박스 내부 에이전트에 노출됩니다.

  1. 에이전트가 다른 MCP 도구와 마찬가지로 이름으로 도구를 호출합니다.
  2. 호출이 샌드박스 경계를 넘어 호스트로 프록시됩니다.
  3. 도구의 execute()호스트에서 실행되며 DB 핸들, 시크릿, 캡처한 모든 클로저를 유지합니다.
  4. 결과가 도구 호출 출력으로 샌드박스에 반환됩니다.

따라서 execute() 자체는 샌드박스로 전송되지 않고 호출과 결과만 경계를 넘습니다. 도구는 정의된 위치에서 계속 실행됩니다.

import { chat } from '@tanstack/ai'
import { grokBuildText } from '@tanstack/ai-grok-build'
import { withSandbox } from '@tanstack/ai-sandbox'
import { repoSandbox } from './sandbox'
import { messages, threadId } from './chat-context'
import { getTodos } from './tools'
import { db } from './db'

chat({
threadId,
adapter: grokBuildText('grok-build'),
messages,
// `execute()` closes over `db` and runs on the host, never in the sandbox.
tools: [
getTodos.server(async ({ userId }: { userId: string }) =>
db.todos.find({ userId }),
),
],
middleware: [withSandbox(repoSandbox)],
})

브리지는 실행마다 무작위로 생성되는 bearer 토큰으로 보호되므로 프록시 엔드포인트가 무방비로 노출되지 않습니다. 해당 토큰을 가진 이번 실행의 에이전트만 여러분의 도구를 호출할 수 있습니다.

브리지에 연결하기

브리지는 샌드박스가 콜백하는 HTTP 엔드포인트입니다. 브리징된 도구가 작동하려면 샌드박스가 오케스트레이터에 연결할 수 있어야 합니다. 두 경우에는 가능하지만 세 번째 경우에는 불가능합니다.

토폴로지샌드박스가 연결하는 호스트설정
로컬 프로세스localhost없음. 기본적으로 작동합니다.
Docker 컨테이너 (dockerSandbox)host.docker.internal없음. 기본적으로 작동합니다.
Docker Sandboxes (sbxSandbox)host.docker.internal게스트 URL은 host.docker.internal로 유지됩니다. sbx 프록시는 정책을 검사하기 전에 해당 호스트를 localhost로 다시 씁니다. 거부 또는 승인 요청 허용 목록에는 localhost가 포함되어야 합니다. 실제 허용 목록을 작성할 때 sbxSandbox()localhost를 추가합니다.
배포된 오케스트레이터(프로덕션)요청에서 도출한 공개 호스트없음. 기본적으로 작동합니다.
노트북에서 구동하는 원격 클라우드 샌드박스공개 URL이 없는 노트북withNgrokBridge로 브리지를 터널링합니다.

로컬 프로세스 / Docker 컨테이너

오케스트레이터는 샌드박스와 같은 머신에 있으며, 로컬 프로세스에서는 localhost, Docker 컨테이너에서는 host.docker.internal로 연결합니다. 브리징된 도구는 추가 구성 없이 작동합니다.

Docker 샌드박스 (sbxSandbox)

게스트는 계속 host.docker.internal로 연결합니다. 호스트 HTTP 프록시는 sbx policy를 확인하기 전에 해당 호스트를 localhost로 다시 씁니다. 거부 또는 승인 요청 허용 목록을 작성한다면 localhost를 허용하세요(또는 sbxSandbox()가 추가하도록 두세요). denyNetwork만으로는 해당 허용 목록을 작성하지 않습니다.

배포된 오케스트레이터(프로덕션)

배포된 오케스트레이터에는 이미 공개 URL이 있으므로 브리지가 기본적으로 연결됩니다. 프로비저너는 수신 요청에서 도출한 공개 호스트를 알립니다 (localhost 대신 사용하며, 모든 호출은 여전히 실행별 bearer 토큰으로 보호되므로 엔드포인트를 공개해도 안전합니다). 이는 엣지/Cloudflare 배포가 사용하는 것과 같은 경로입니다. Cloudflare를 참고하세요.

노트북에서 구동하는 원격 클라우드 샌드박스

로컬 개발에서 클라우드 제공자(Daytona, Vercel, Sprites)를 사용하면 샌드박스는 원격 VM입니다. 머신의 localhost로 연결할 수 없고, 노트북에는 공개 URL이 없으므로 브리지를 노출하기 전까지 브리징된 도구가 호스트에 연결할 수 없습니다.

@tanstack/ai-sandbox/ngrok 서브패스는 ngrok을 통해 루프백 브리지를 터널링하므로 원격 샌드박스가 연결할 수 있습니다. NGROK_AUTHTOKEN을 설정한 다음 withNgrokBridgewithSandbox(...)뒤에 추가하세요.

import { chat } from '@tanstack/ai'
import { grokBuildText } from '@tanstack/ai-grok-build'
import { withSandbox } from '@tanstack/ai-sandbox'
import { withNgrokBridge } from '@tanstack/ai-sandbox/ngrok'
import { repoSandbox } from './sandbox'
import { messages, threadId } from './chat-context'
import { getTodos } from './tools'
import { db } from './db'

chat({
threadId,
adapter: grokBuildText('grok-build'),
messages,
tools: [
getTodos.server(async ({ userId }: { userId: string }) =>
db.todos.find({ userId }),
),
],
// Cloud provider in local dev → tunnel the host bridge so the remote sandbox
// can reach it. Local process / Docker don't need this.
middleware: [withSandbox(repoSandbox), withNgrokBridge],
})

@ngrok/ngrok선택적 peer dependency이므로 서브패스와 함께 설치하세요(npm i @ngrok/ngrok). withNgrokBridge는 순전히 로컬 개발을 위한 편의 기능입니다. 프로덕션에서는 배포된 오케스트레이터에 이미 연결할 수 있으므로 이를 제외하고 배포합니다.