본문으로 건너뛰기

Claude Code

Claude Code 어댑터는 Claude Code(@anthropic-ai/claude-agent-sdk를 통해)를 채팅 백엔드로 실행합니다. HTTP provider 어댑터와 달리 이는 하네스 어댑터입니다. Claude Code가 자체 에이전트 루프를 실행하고 자체 도구(bash, 파일 읽기 및 편집, glob/grep 검색, 웹 검색)를 서버에서 로컬로 실행합니다. 각 chat() 호출은 하나의 전체 하네스 턴을 실행하며, 하네스의 도구 활동은 UI에서 렌더링할 수 있도록 이미 해결된 도구 호출 이벤트로 스트리밍됩니다.

서버 전용입니다. 하네스는 Claude Code 런타임을 subprocess로 생성하므로 이 어댑터는 Node.js 서버 환경에서만 작동하며 브라우저에서는 절대 작동하지 않습니다. 실행되는 머신에서 Claude에게 셸을 제공하는 것과 같으므로 권한을 적절히 구성합니다.

설치

npm install @tanstack/ai-claude-code

실행 가능한 데모는 examples/sandbox-cloudflare에 있습니다. UI에서 Claude Code, Codex 또는 Grok Build를 선택할 수 있으며, 세션 재개, 하네스 도구 타임라인, 도구 브리징이 Workers에서 실행되는 TanStack Start 앱에 연결되어 있습니다. 동일한 연결을 일반 Node에서 영속적이고 새로 고침 후에도 유지되는 실행으로 구성하는 방법(이 어댑터를 Docker에서 사용)은 examples/sandbox-web를 참조합니다.

인증

노트북에는 이미 claude login가 있을 수 있습니다. CI runner에는 다음만 있습니다 ANTHROPIC_API_KEY. 기본 authMode'api-key'입니다. 'host'을 설정합니다 claude login가 필요할 때 사용합니다. 하네스 인증을 참조합니다.

import { claudeCodeText } from "@tanstack/ai-claude-code"

claudeCodeText("claude-opus-4-8")
claudeCodeText("claude-opus-4-8", { authMode: "host" })
  • 'api-key' (기본값): ANTHROPIC_API_KEY을 주입합니다(또는 apiKey을 전달합니다).
  • 'host': claude login을 사용합니다. ANTHROPIC_API_KEY은 주입하지 않습니다.

기본 사용법

import { chat } from "@tanstack/ai";
import { claudeCodeText } from "@tanstack/ai-claude-code";

const stream = chat({
adapter: claudeCodeText("claude-opus-4-8", {
cwd: "/path/to/project",
permissionMode: "acceptEdits",
}),
messages: [{ role: "user", content: "Fix the failing test in utils.test.ts" }],
});

구성

옵션설명
cwd하네스 세션의 작업 디렉터리입니다. 기본값은 process.cwd()입니다.
permissionModeClaude Code 권한 모드('default', 'acceptEdits', 'bypassPermissions', 'plan', 'dontAsk', 'auto')입니다. 아래 권한 참고 사항을 참조합니다.
allowedTools하네스가 프롬프트 없이 사용할 수 있는 기본 제공 도구입니다(예: ['Read', 'Grep', 'Bash(npm test:*)']).
disallowedTools하네스에서 기본 제공 도구를 완전히 제거합니다.
maxTurns실행당 하네스 내부 최대 턴 수입니다.
systemPromptMode'append' (기본값)은 Claude Code의 사전 설정 시스템 프롬프트를 주입하고 사용자의 systemPrompts을 추가합니다. 'replace'은 사용자의 프롬프트를 전체 프롬프트로 전송합니다.
mcpServers하네스에 수정 없이 전달되는 추가 MCP 서버입니다.
authMode'api-key' (기본값)은 ANTHROPIC_API_KEY을 주입합니다. 'host'claude login을 사용합니다. modelOptions에서도 유효합니다. 하네스 인증을 참조합니다.
apiKey하네스 subprocess를 위한 Anthropic API 키입니다.
envharness 하위 프로세스를 위한 추가 환경 변수입니다.
pathToClaudeCodeExecutableSDK에 번들로 포함된 실행 파일 대신 특정 Claude Code 실행 파일을 사용합니다.
streamPartials실제 토큰 수준의 텍스트 델타를 내보냅니다(기본값 true).
canUseTool사용자 지정 권한 핸들러이며, 어댑터의 기본 핸들러를 대체합니다.
settingSources로드할 Claude Code 설정 계층입니다. 기본값은 ['project']입니다. workspace의 CLAUDE.md, .claude/skills, .mcp.json가 적용되며 host의 ~/.claude(플러그인, 훅, 스킬)는 로드되지 않습니다. CLI와 동일한 동작을 사용하려면 ['user', 'project', 'local']을 전달하고, 설정을 로드하지 않으려면 []을 전달합니다.

헤드리스 서버의 권한입니다. 명시적인 permissionMode 또는 canUseTool이 없으면 어댑터가 안전한 기본 핸들러를 설치합니다. 브리지된 TanStack 도구는 항상 실행되고, 일반적으로 사람에게 프롬프트를 표시해야 하는 기본 제공 도구 호출은 요청을 중단시키는 대신 안내와 함께 거부됩니다. harness에서 파일을 편집하거나 명령을 실행하도록 허용하려면 permissionMode: 'acceptEdits' / 'bypassPermissions'을 설정하거나 allowedTools을 열거합니다.

상태 유지 세션

Claude Code 세션은 상태를 유지합니다. 즉, 하네스는 각 턴 사이에 전체 작업 컨텍스트(읽은 파일, 실행한 명령, 도출한 결론)를 유지합니다. 어댑터는 모든 실행의 세션 ID를 claude-code.session-id라는 사용자 지정 스트림 이벤트로 노출합니다. 세션을 재개하려면 modelOptions.sessionId를 통해 이 ID를 다시 전달합니다. 세션을 재개할 때는 최신 사용자 메시지만 전송됩니다. 하네스가 이전 컨텍스트를 이미 보유하고 있기 때문입니다.

claude-code.session-id 이벤트 페이로드에는 SDK 초기화 중 로드된 스킬의 배열인 skills도 포함됩니다(보고된 스킬이 없으면 빈 배열).

서버 엔드포인트:

import {
chat,
chatParamsFromRequest,
toServerSentEventsResponse,
} from "@tanstack/ai";
import { claudeCodeText } from "@tanstack/ai-claude-code";

export async function POST(request: Request) {
const params = await chatParamsFromRequest(request);

// Extra fields the client puts in the connection `body` arrive here.
const sessionId =
typeof params.forwardedProps.sessionId === "string"
? params.forwardedProps.sessionId
: undefined;

const stream = chat({
adapter: claudeCodeText("claude-opus-4-8", {
cwd: "/path/to/project",
permissionMode: "acceptEdits",
}),
messages: params.messages,
modelOptions: { sessionId },
});

return toServerSentEventsResponse(stream);
}

클라이언트 (React) — 사용자 지정 이벤트에서 세션 id를 캡처하고 이후 요청에서 이를 다시 전송합니다:

import { useState } from "react";
import { useChat } from "@tanstack/ai-react";
import { fetchServerSentEvents } from "@tanstack/ai-client";

function CodingAssistant() {
const [sessionId, setSessionId] = useState<string | undefined>(undefined);

const { messages, sendMessage } = useChat({
connection: fetchServerSentEvents("/api/chat", () => ({
body: { sessionId },
})),
onCustomEvent: (name, value) => {
if (
name === "claude-code.session-id" &&
typeof value === "object" &&
value !== null &&
"sessionId" in value &&
typeof value.sessionId === "string"
) {
setSessionId(value.sessionId);
}
},
});

// ... render messages; harness tool activity (Bash, Edit, Read, ...)
// arrives as regular tool-call parts with their results attached.
}

세션은 해당 세션을 실행한 머신(~/.claude/projects/)에 저장되므로, 재개는 동일한 서버 인스턴스에서만 작동합니다. 계속 진행하는 대신 세션을 분기하려면 modelOptions: { forkSession: true }sessionId과 함께 전달합니다.

도구

이 어댑터를 통해 흐르는 도구는 두 종류입니다:

  1. 내장 하네스 도구(Bash, Read, Write, Edit, Glob, Grep, WebSearch, ...)는 Claude Code 자체에서 실행됩니다. 이러한 도구의 활동은 결과가 이미 연결된 도구 호출 이벤트로 다시 스트리밍되므로 useChat UI에서 추가 연결 없이 렌더링할 수 있습니다. 하지만 사용자의 코드가 이러한 도구를 실행하지는 않습니다.

  2. 사용자의 TanStack 도구는 프로세스 내 MCP 서버로 하네스에 브리지됩니다. toolDefinition().server()로 평소처럼 정의하면 모델에는 mcp__tanstack__<name>으로 표시되고, 어댑터가 반환되는 과정에서 접두사를 제거하므로 이벤트가 등록한 이름과 일치합니다.

import { z } from "zod";
import { chat, toolDefinition } from "@tanstack/ai";
import { claudeCodeText } from "@tanstack/ai-claude-code";

const lookupTicket = toolDefinition({
name: "lookup_ticket",
description: "Look up an issue ticket by id",
inputSchema: z.object({ ticketId: z.string() }),
}).server(async ({ ticketId }) => {
return { ticketId, status: "open", title: "Crash on startup" };
});

const stream = chat({
adapter: claudeCodeText("claude-opus-4-8"),
messages: [{ role: "user", content: "What's the status of ticket T-123?" }],
tools: [lookupTicket],
});

클라이언트 측 도구와 승인이 필요한 도구는 지원되지 않습니다. 하네스는 실행 중인 하위 프로세스 내부에서 도구를 실행하며, 이 프로세스는 브라우저 왕복이나 사람의 승인을 기다리기 위해 HTTP 요청 사이에서 일시 중지할 수 없습니다. 서버 execute() 구현이 없는 도구나 needsApproval로 표시된 도구를 전달하면 설명이 포함된 오류와 함께 즉시 실패합니다. 이러한 도구는 일반 provider 어댑터를 사용해 하네스 외부에서 실행합니다.

구조화된 출력

outputSchemachat() 로 전달합니다. Claude Code 는 한 번의 하네스 턴을 실행하고 네이티브 도구를 사용하여 구조화된 객체를 반환합니다. 스키마 JSON 은 --json-schema 에 인라인 JSON 으로 전달됩니다 (CLI 는 파일 경로를 거부합니다). 도구 활동과 본문은 평소처럼 스트리밍됩니다. 객체는 structured-output.complete 로 도착하며, Claude 가 자체 StructuredOutput 도구를 통해 전달할 때도 포함됩니다.

기본적으로 어댑터는 프로젝트 설정 (--setting-sources project) 만 로드합니다. 워크스페이스 투영 (지시사항, 스킬, MCP 구성) 은 프로젝트 범위에 속하므로 적용됩니다. 호스트의 ~/.claude 는 로드되지 않으므로 로컬 프로세스 실행은 개인 스킬과 후크를 가져오지 않습니다. 복제된 저장소의 .claude/settings.json 와 후크는 실행에 적용됩니다. settingSources 를 설정하여 이를 변경합니다. 어댑터는 --bare 를 전달하지 않으며, 해당 플래그는 호스트 claude login 를 무시하기 때문입니다.

로컬 프로세스에서는 Claude 가 호스트 claude login 를 사용합니다. Docker 에서는 ANTHROPIC_API_KEY 를 프로세스 환경 변수로 전달합니다. 컨테이너에는 호스트 로그인 기능이 없습니다.

import { chat } from "@tanstack/ai"
import { claudeCodeText } from "@tanstack/ai-claude-code"
import { defineSandbox, withSandbox } from "@tanstack/ai-sandbox"
import { dockerSandbox } from "@tanstack/ai-sandbox-docker"
import { z } from "zod"

const Report = z.object({
summary: z.string(),
filesChanged: z.array(z.string()),
})

const sandbox = defineSandbox({
id: "repo-report",
provider: dockerSandbox({ image: "node:22" }),
})

const report = await chat({
adapter: claudeCodeText("claude-opus-4-8"),
messages: [{ role: "user", content: "Review this repo." }],
outputSchema: Report,
middleware: [withSandbox(sandbox)],
})

report.summary

클라이언트에서는 useChat 에 동일한 스키마를 전달하고 final 를 읽습니다. partial 는 끝까지 비어 있습니다.

import { fetchServerSentEvents, useChat } from "@tanstack/ai-react"
import { z } from "zod"

const Report = z.object({
summary: z.string(),
filesChanged: z.array(z.string()),
})

function ReportView() {
const { final, isLoading } = useChat({
connection: fetchServerSentEvents("/api/repo-report"),
outputSchema: Report,
})

if (isLoading) return <p>The agent is inspecting the repo.</p>
if (!final) return null
return <p>{final.summary}</p>
}

프롬프트에서 JSON 만 추출하고 샌드박스가 필요하지 않은 경우 @tanstack/ai-anthropic 를 사용하세요. 해당 경로는 더 빠릅니다.

클라이언트를 포함한 전체 개요: Harness Agents.

제한 사항

  • 서버 전용(Node)입니다. 하네스는 하위 프로세스를 생성하며, Windows 지원은 테스트되지 않았습니다.
  • 하네스가 에이전트 루프를 소유합니다. TanStack의 에이전트 루프 전략과 반복별 미들웨어는 하네스 턴 내부에 적용되지 않으며, maxTurns이 이에 해당하는 제어 수단입니다.
  • 샘플링 제어가 없습니다. temperature 스타일 옵션은 여기서 존재하지 않습니다.
  • 세션은 머신 로컬입니다. 재개하려면 동일한 서버 인스턴스에 연결해야 합니다.
  • 콜드 스타트가 발생합니다. 각 호출은 하네스 턴을 생성하므로, HTTP 어댑터보다 첫 토큰 지연 시간이 더 길 수 있습니다.