Agent Client Protocol 하네스

Agent Client Protocol 하네스 (Agent Client Protocol Harness)

Agent Client Protocol(ACP) 하네스 어댑터는 ACP 버전 1과 호환되는 모든 하네스에 HarnessAgent를 연결해요. 일반 어댑터는 브리지, ACP 클라이언트, 호스트 도구 릴레이, 이벤트 번역, 승인, 수명주기 동작을 소유해요. 인라인 프로파일은 하나의 ACP 런타임을 설치·구성하는 방법을 설명해요.

ACP 하네스 어댑터 외에 필요한 것은 NPM에서 설치하거나 신뢰할 수 있는 Bash 명령으로 설치할 수 있는 ACP 호환 구현 하나뿐이에요. 그러면 몇 줄의 코드로 ACP 하네스 어댑터를 구성할 수 있어요.

참고: 하네스 패키지는 실험적이에요. 이 초기 API가 더 다듬어지면서 릴리스 사이에 파괴적인 변경이 예상돼요.

출처: 문서

본문

셋업 (Setup)

npm install @ai-sdk/harness @ai-sdk/harness-acp @ai-sdk/sandbox-vercel

기본 사용법 (Basic Usage)

아래 완전한 구현 중 하나 같은 ACP 하네스 프로파일을 정의하고 HarnessAgent에 전달하세요:

import { HarnessAgent } from '@ai-sdk/harness/agent';
import { createVercelNetworkSandboxSession } from '@ai-sdk/sandbox-vercel';
import { codexACPHarness } from './codex-acp-harness';

const agent = new HarnessAgent({
  harness: codexACPHarness,
});

const sandboxSession = await createVercelNetworkSandboxSession({
  runtime: 'node24',
  ports: [4000],
  template: await agent.getSandboxTemplate(),
});
const session = await agent.createSession({ sandboxSession });
try {
  const result = await agent.generate({
    session,
    prompt: 'Check the test failures and fix the production code.',
  });
  console.log(result.text);
} finally {
  await session.destroy();
  await sandboxSession.destroy();
}

어댑터 설정 (Adapter Settings)

createACP는 다음을 받아요:

  • harnessId: 이 프로파일의 안정적인 kebab-case 식별자.
  • version: ACP 프로토콜 버전. 기본값이며 현재 'v1'만 지원해요.
  • source: ACP 구현을 획득하는 방법 — 단순 NPM 패키지, 잠긴(locked) NPM 설치, 또는 신뢰할 수 있는 Bash 설치 명령.
  • executable: 획득한 구현에서 실행할 단순 명령 이름.
  • args: 실행 파일에 전달하는 선택 인자.
  • forwardEnv: 샌드박스로 전달할 비자격 증명(credential 아님) 호스트 환경 변수 이름.
  • credentialEnv: 호스트 자격 증명 환경 변수 이름. credentialBrokering과 함께 구성하세요.
  • credentialBrokering: 유효한 호스트 런타임 환경을 env로, 샌드박스로 전달된 환경을 sandboxEnv로 받고, 추가 아웃바운드 요청 변환을 반환하는 함수. 정확한 요청 헤더 일치에는 sandboxEnv의 자격 증명을, 변환된 요청 헤더에는 env의 실제 자격 증명을 사용하세요.
  • credentialForwarding: 하네스 어댑터가 각 자격 증명을 샌드박스 프로세스로 전달하기 직전에 커스터마이즈하는 선택 동기/비동기 콜백. 그렇지 않으면 전달될 자격 증명 값(실제 자격 증명 또는 마스킹된 값)과 이를 노출하는 데 쓰는 환경 변수 이름을 받아요. 이 콜백은 샌드박스 프로세스로 전달되는 값만 제어해요. 하네스 어댑터가 호스트 프로세스에서 어떤 자격 증명을 발견·읽거나 접근할 수 있는지는 제한하지 않아요.
  • env: 부트스트랩 시 기록되는 영구 환경 값.
  • builtinTools: 정적 타이핑과 정확한 이름 일치를 위한 선택 네이티브 도구 정의.
  • mcpServers: 서버 이름별 MCP 서버 정의.
  • hostToolMcpTransport: ACP 구현에 호스트 도구를 노출하는 하네스 소유 MCP 서버에 사용하는 전송. 기본값은 'stdio'. 클라이언트의 HTTP 또는 SSE MCP 서버만 받아들이는 구현의 경우 'http'로 설정하세요. 이는 구현이 agentCapabilities.mcpCapabilities.http를 알려야 해요.
  • authentication: 광고되는 ACP 인증 방법, 메타데이터, 클라이언트 역량.
  • auth: 다운스트림 provider 인증 모드:
    • auto(기본값): Gateway 자격 증명과 providerAuthentication 구성이 있으면 AI Gateway를 사용하고, 그렇지 않으면 직접 인증 사용.
    • direct: Gateway 자격 증명이 있어도 항상 직접 인증 사용.
    • ai-gateway: 항상 AI Gateway를 사용하고 Gateway 자격 증명이 없으면 throw. providerAuthentication 구성이 필요해요.
    • 인증 환경: 호스트 프로세스 환경 대신 제공된 record에서 자동 감지.
  • providerAuthentication: 선언적 런타임 특정 Gateway 환경.
  • modelMapping: HarnessAgent에 제공된 model에서 구현이 사용하는 ACP 연산으로의 필수 정적 매핑. session-config-option을 구성 옵션 ID와 함께 path로 사용하거나, session-model을 session/set_model 요청 속성과 함께 path로 사용하세요.
  • skillsDirectory: 구현의 유효한 $HOME에 상대적인 네이티브 스킬 디렉터리. 기본값은 .agents/skills. 다른 위치를 쓰는 런타임은 재정의하세요.
  • instructionMapping: HarnessAgent 지침에서 구현의 네이티브 시스템/개발자 프롬프트로의 선택 매핑. ACP 세션 요청의 _meta 필드 아래 경로는 session-meta를, JSON 런치 환경 변수 내 경로는 launch-env-json을, 구현의 유효한 $HOME 아래 파일에 지침을 쓰려면 상대 path와 함께 filesystem을 사용하세요. 생략하면 지침이 첫 번째 사용자 프롬프트 앞에 붙어요.
  • outputSchemaMapping: 구조화 출력 JSON Schema에서 ACP session/prompt 요청의 _meta 필드 아래 경로로의 선택 구현별 매핑. ACP는 구조화 출력을 표준화하지 않으므로, 선택한 구현이 그 프라이빗 확장을 문서화하지 않는 한 생략하세요.
  • askUserQuestions: 구현별 ACP 클라이언트 질문 요청과 Harness askUserQuestions 도구 사이의 선택 번역. ACP 구현이 클라이언트 응답을 기다리는 네이티브 요청을 노출할 때만 구성하세요.
  • permissionModeMapping: 세 가지 Harness 권한 모드에서 광고된 ACP 세션 모드 또는 구성 옵션으로의 매핑. ACP 구현이 그 모드를 지원하지 않으면 항목을 null로 설정하세요. 생략하면 어댑터는 permissionMode를 도구 종류별로 ACP 권한 요청에 적용해요.
  • session.meta: 세션 생성과 복원을 위한 직렬화 가능 구현별 메타데이터.
  • port: 노출된 브리지 포트 재정의.
  • startupTimeoutMs: 브리지 시작 타임아웃. 기본 120초.
  • reconnect: 브리지 WebSocket 연결이 끊긴 후 재연결 타이밍. maxElapsedMs는 연결 설정과 백오프 지연을 포함한 전체 재시도 창을 제어하며 기본 30초. initialDelayMs 기본 50밀리초, maxDelayMs 기본 2초. 이 재시도는 지수 백오프를 사용하며 startupTimeoutMs와 별개예요. 샌드박스, 브리지 프로세스, 브리지 엔드포인트가 영구적으로 사용 불가하면 복구할 수 없어요.
  • mintBridgeToken: 샌드박스 id를 받고 브리지 인증 토큰을 반환하는 동기 함수. 기본적으로 어댑터는 무작위 32바이트 토큰을 생성해요. 커스텀 구현은 충분히 비밀인 토큰을 반환해야 해요.
  • clientApp: name과 version이 있는 선택 클라이언트 귀속(attribution). 기본값은 설치된 ai-sdk/harness-acp/<version> 식별자이며 Gateway 환경 플레이스홀더에서 사용할 수 있어요.

런타임별 패키지 이름, 환경 변수, 모드, 세션 메타데이터는 일반 어댑터가 아니라 인라인 프로파일에 속해요.

구조화 출력 (Structured Output)

ACP 버전 1은 구조화 출력 역량을 정의하지 않아요. 프라이빗 프롬프트 메타데이터 확장이 있는 구현용 프로파일은 명시적으로 옵트인할 수 있어요:

const harness = createACP({
  harnessId: 'example-acp',
  source: {
    type: 'npm-simple',
    packageName: '@example/acp-agent',
  },
  executable: 'example-acp',
  modelMapping: {
    type: 'session-config-option',
    path: 'model',
  },
  outputSchemaMapping: {
    type: 'session-prompt-meta',
    path: ['outputSchema'],
  },
});

output으로 구성된 HarnessAgent의 경우 이는 생성된 JSON Schema를 session/prompt.params._meta.outputSchema에 써요. 이 매핑이 없는 프로파일은 임의의 ACP 구현이 스키마를 이해한다고 가정하는 대신 HarnessCapabilityUnsupportedError를 throw해요. Codex ACP는 지원되지 않고, Grok Build 하네스는 검증된 매핑을 포함해요.

구현 소스 (Implementation source)

단순 NPM 소스는 이름으로 단일 패키지를 설치해요:

const source = {
  type: 'npm-simple',
  packageName: '@agentclientprotocol/codex-acp',
  packageVersion: '1.1.4',
} as const;

packageVersion은 선택이고 제공 시 정확한 버전이어야 해요. 생략하면 패키지의 latest dist-tag를 설치해요. 생략된 버전은 하네스 식별자에도 남지 않으므로, 새로운 업스트림 릴리스가 기존 수명주기 상태를 무효화하지 않아요.

단순 소스는 요청된 ACP 패키지만 고정하고, 그 전이 의존성은 샌드박스가 부트스트랩할 때 해결돼요. 전체 설치를 고정하려면 잠긴(locked) 소스를 사용하고 package.json과 그 pnpm-lock.yaml 내용을 제공하세요:

const source = {
  type: 'npm-locked',
  packageJson: packageJsonContents,
  pnpmLockYaml: pnpmLockYamlContents,
  pnpmWorkspaceYaml: pnpmWorkspaceYamlContents,
} as const;

잠긴 소스는 pnpm install --frozen-lockfile로 제공된 파일을 설치해요. pnpmWorkspaceYaml은 선택이고, 잠긴 설치가 필수 의존성 빌드 스크립트에 대한 정확한 버전 allowBuilds 정책 같은 워크스페이스 수준 pnpm 구성이 필요할 때 제공하세요.

매니페스트, 잠금 파일, 선택 워크스페이스 구성은 유지되는 부트스트랩 아티팩트이며 수명주기 식별자에 참여해요. 이 문자열에 자격 증명을 포함하지 마세요.

설치 명령 소스는 하네스의 결정적 부트스트랩 디렉터리 안에서 신뢰할 수 있는 Bash 명령을 실행해요:

const cursorSource = {
  type: 'install-command',
  command: 'curl https://cursor.com/install -fsS | bash',
} as const;

const fxSource = {
  type: 'install-command',
  command: 'curl -fsSL https://fx.sh/setup.sh | bash',
} as const;

명령은 set -euo pipefail로, 작업 디렉터리를 .harness-bootstrap/<harnessId>/implementation으로, HOME을 그 바로 아래 home 디렉터리로 설정해 실행돼요. 모든 영구 설치 파일은 그 HOME 아래에 있어야 해요. 명령은 executable을 $HOME/.local/bin에 설치해야 하며, 어댑터는 셸 없이 그 결정적 경로를 실행하고 샌드박스에 이미 있는 명령으로 폴백하지 않아요.

설치 명령은 자동 버전 고정을 제공하지 않아요. 성공한 부트스트랩은 캐시로 유지되고, 정확한 명령이 부트스트랩·수명주기 식별자에 참여해요. 부트스트랩 레시피가 유지되므로 명령을 실행 가능한 구성으로 취급하고 자격 증명을 심지 마세요.

인증 (Authentication)

providerAuthentication이 기본 ACP 구현이 Gateway 구성을 받는 방법을 정의할 때 AI Gateway 인증이 지원돼요. 어댑터는 다음 환경 변수를 읽어요:

  • VERCEL_OIDC_TOKEN
  • AI_GATEWAY_API_KEY
  • AI_GATEWAY_BASE_URL

기본 auth: 'auto'로, 어댑터는 AI_GATEWAY_API_KEY 또는 VERCEL_OIDC_TOKEN이 있을 때 AI Gateway를 사용하고 그렇지 않으면 직접 인증을 사용해요.

런타임에 자격 증명을 해결하는 애플리케이션은 process.env를 변경하지 않고 전달할 수 있어요:

const harness = createACP({
  // ...implementation settings
  auth: {
    AI_GATEWAY_API_KEY: await resolveGatewayToken(),
    AI_GATEWAY_BASE_URL: 'https://ai-gateway.vercel.sh',
  },
});

제공된 record는 인증 발견을 위해 호스트 환경을 대체해요. credentialEnv는 이 record에서 읽고 forwardEnv는 비인증 런타임 구성을 호스트에서 계속 읽어요. 자격 증명 값은 부트스트랩·수명주기 식별자에서 제외돼요.

직접 인증을 위해 credentialEnv를 기본 ACP 구현이 읽는 호스트 환경 변수 이름으로 구성하고 credentialBrokering으로 그 값을 아웃바운드 요청 헤더로 매핑하세요. 네트워크 샌드박스가 가산적 요청 변환을 지원하면 ACP 프로세스는 sandboxEnv의 값(기본적으로 임시 플레이스홀더)을 봐요. 실제 값은 요청이 샌드박스를 떠난 뒤, 그리고 요청이 정확한 기대 값을 포함할 때만 주입돼요. 그 역량이 없는 샌드박스는 실제 값을 전달하는 레거시 동작을 유지해요. forwardEnv는 비자격 증명 런타임 구성에만 사용하세요.

Gateway 환경 (Gateway environment)

providerAuthentication.gateway.env를 기본 ACP 구현이 provider 엔드포인트, 자격 증명, 귀속에 사용하는 환경 변수로 설정하세요. gateway-api-key, gateway-base-url, gateway-authorization, client-app, client-app-name, client-app-version 같은 프로파일 값은 어댑터가 Gateway 인증을 선택한 후에만 해결하는 플레이스홀더예요. 세 가지 client-app 플레이스홀더는 각각 결합된 name/version 식별자, 그 이름, 그 버전으로 해결돼요. 구조화된 런치 환경 값은 플레이스홀더가 해결된 후 JSON으로 직렬화돼요. 해결된 Gateway 환경은 credentialBrokering에 전달된 환경에서 같은 이름의 직접 값을 재정의하므로, 하나의 콜백이 두 인증 모드 모두에서 동작해요.

샌드박스 (Sandbox)

모든 ACP 하네스는 노출된 포트가 하나 이상 있는 네트워크 샌드박스(예: @ai-sdk/sandbox-vercel)가 필요해요:

const sandboxSession = await createVercelNetworkSandboxSession({
  runtime: 'node24',
  ports: [4000],
  template: await agent.getSandboxTemplate(),
});

기본적으로 어댑터는 첫 번째 노출된 포트를 사용해요. 다른 노출된 포트를 선택하려면 createACP에서 port를 설정하세요. 둘 다 없으면 세션 시작이 하나를 구성하라는 지침과 함께 HarnessCapabilityUnsupportedError를 throw해요.

브리지 패키지, ACP 설치, 재생 상태는 세션 프로젝트 밖의 어댑터 소유 디렉터리에 있어요. 스킬은 구현의 $HOME 아래 구성된 네이티브 디렉터리에 기록돼요. 세션 워크스페이스는 빈 상태로 시작해요.

도구와 승인 (Tools and Approvals)

선택 builtinTools 정의는 에이전트가 도구 키 또는 nativeName과 일치하는 프로그램적 이름을 제공할 때만 네이티브 ACP 도구를 분류해요. 일치하지 않는 네이티브 및 타사 MCP 호출은 유효한 provider 실행 동적 도구로 남아요.

호스트가 정의한 AI SDK 도구는 하네스 소유 MCP 서버를 통해 노출되고 호스트에서 실행돼요. 그 재귀 JSON Schema는 보존돼요. execute가 없는 도구는 continueStream을 통해 호출자가 제공한 결과를 위해 일시 정지해요.

ACP 구현이 네이티브 권한 제어를 노출하면 세 가지 Harness 권한 모드를 모두 매핑하고 지원되지 않는 모드는 null로 설정하세요. null 매핑을 선택하면 HarnessCapabilityUnsupportedError가 throw돼요. 어댑터는 구체 모드나 구성 옵션을 세션 응답에 대해 검증해요. 매핑이 구성되면 여전히 발생하는 모든 ACP 권한 요청은 Harness 승인 요청이 돼요. 매핑이 없으면 어댑터는 permissionMode가 허용한 도구 종류에 allow_once를 자동 선택하고 나머지는 Harness 승인을 요청해요. 거부는 reject_once를 선택하고, 영구 선택은 결코 추론되지 않아요. 호스트 도구 승인은 독립적으로 유지돼요.

완전한 ACP 하네스 구현 (Complete ACP Harness Implementations)

별도로 명시하지 않으면 이 프로파일들은 아래 표시된 런타임별 환경 변수를 통한 직접 인증과 AI_GATEWAY_API_KEY 또는 VERCEL_OIDC_TOKEN을 통한 AI Gateway 인증을 지원해요.

Claude Code ACP

Claude Code는 /v1 없이 Anthropic 호환 Gateway 루트 URL을 받아요. 이 프로파일은 런타임의 지원되는 런치 환경을 사용해요:

import { createACP, type ACPPermissionModeMapping } from '@ai-sdk/harness-acp';
import { createCredentialRequestTransformation } from '@ai-sdk/harness/utils';

export const claudeCodeACPHarness = createACP({
  harnessId: 'acp-claude-code',
  // Define the runtime's built-in tool names and input schemas to expose
  // provider-executed calls as typed HarnessAgent tools.
  // builtinTools: { ... },
  source: {
    type: 'npm-simple',
    packageName: '@agentclientprotocol/claude-agent-acp',
    packageVersion: '0.61.0',
  },
  executable: 'claude-agent-acp',
  modelMapping: {
    type: 'session-config-option',
    path: 'model',
  },
  skillsDirectory: '.claude/skills',
  credentialEnv: ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN'],
  credentialBrokering: ({ env, sandboxEnv }) => {
    const apiKey = env.ANTHROPIC_API_KEY;
    const authToken = env.ANTHROPIC_AUTH_TOKEN;
    const sandboxApiKey = sandboxEnv?.ANTHROPIC_API_KEY;
    const sandboxAuthToken = sandboxEnv?.ANTHROPIC_AUTH_TOKEN;
    if (!apiKey && !authToken) return [];

    const transformations = [];
    if (apiKey && sandboxApiKey) {
      transformations.push(
        createCredentialRequestTransformation({
          matchUrl: env.ANTHROPIC_BASE_URL ?? 'https://api.anthropic.com',
          matchHeaders: { 'x-api-key': sandboxApiKey },
          transformHeaders: { 'x-api-key': apiKey },
        }),
      );
    }
    if (authToken && sandboxAuthToken) {
      transformations.push(
        createCredentialRequestTransformation({
          matchUrl: env.ANTHROPIC_BASE_URL ?? 'https://api.anthropic.com',
          matchHeaders: {
            Authorization: `Bearer ${sandboxAuthToken}`,
          },
          transformHeaders: { Authorization: `Bearer ${authToken}` },
        }),
      );
    }
    return transformations;
  },
  env: {
    IS_SANDBOX: '1',
  },
  instructionMapping: {
    type: 'session-meta',
    path: ['systemPrompt', 'append'],
  },
  permissionModeMapping: {
    'allow-reads': { type: 'session-mode', modeId: 'default' },
    'allow-edits': { type: 'session-mode', modeId: 'acceptEdits' },
    'allow-all': { type: 'session-mode', modeId: 'bypassPermissions' },
  } as const satisfies ACPPermissionModeMapping,
  providerAuthentication: {
    gateway: {
      env: {
        ANTHROPIC_API_KEY: { $source: 'gateway-api-key' },
        ANTHROPIC_AUTH_TOKEN: { $source: 'gateway-api-key' },
        ANTHROPIC_BASE_URL: { $source: 'gateway-base-url' },
        CLAUDE_AGENT_SDK_CLIENT_APP: { $source: 'client-app' },
      },
    },
  },
});

Gateway 자격 증명이 없으면 이 프로파일은 직접 ANTHROPIC_API_KEY 또는 ANTHROPIC_AUTH_TOKEN 인증을 사용해요. Gateway 환경은 Claude가 지원하는 CLAUDE_AGENT_SDK_CLIENT_APP 설정을 통해 버전 지정 귀속을 제공해요. IS_SANDBOX는 샌드박스 프로세스가 root로 실행될 때 Claude Code ACP가 bypassPermissions를 알릴 수 있게 해요.

Codex ACP

Codex ACP는 API 키와 JSON 세션 구성을 런치 환경으로 받아요. 그 OpenAI 호환 엔드포인트는 /v1로 끝나야 해요:

import { createACP, type ACPPermissionModeMapping } from '@ai-sdk/harness-acp';
import { createCredentialRequestTransformation } from '@ai-sdk/harness/utils';
import { secureJsonParse } from '@ai-sdk/provider-utils';

export const codexACPHarness = createACP({
  harnessId: 'acp-codex',
  // Define the runtime's built-in tool names and input schemas to expose
  // provider-executed calls as typed HarnessAgent tools.
  // builtinTools: { ... },
  source: {
    type: 'npm-simple',
    packageName: '@agentclientprotocol/codex-acp',
    packageVersion: '1.1.4',
  },
  executable: 'codex-acp',
  modelMapping: {
    type: 'session-config-option',
    path: 'model',
  },
  forwardEnv: ['CODEX_CONFIG'],
  credentialEnv: ['CODEX_API_KEY', 'OPENAI_API_KEY'],
  credentialBrokering: ({ env, sandboxEnv }) => {
    const environmentVariableName = env.CODEX_API_KEY
      ? 'CODEX_API_KEY'
      : 'OPENAI_API_KEY';
    const credential = env[environmentVariableName];
    const sandboxCredential = sandboxEnv?.[environmentVariableName];
    if (!credential || !sandboxCredential) return [];
    const config =
      env.CODEX_CONFIG == null
        ? undefined
        : (secureJsonParse(env.CODEX_CONFIG) as {
            model_provider?: string;
            model_providers?: Record<string, { base_url?: string }>;
          });
    const baseUrl =
      config?.model_providers?.[config.model_provider ?? '']?.base_url ??
      'https://api.openai.com/v1';
    return [
      createCredentialRequestTransformation({
        matchUrl: baseUrl,
        matchHeaders: {
          Authorization: `Bearer ${sandboxCredential}`,
        },
        transformHeaders: { Authorization: `Bearer ${credential}` },
      }),
    ];
  },
  instructionMapping: {
    type: 'launch-env-json',
    variable: 'CODEX_CONFIG',
    path: ['developer_instructions'],
  },
  permissionModeMapping: {
    'allow-reads': null,
    'allow-edits': null,
    'allow-all': { type: 'session-mode', modeId: 'agent-full-access' },
  } as const satisfies ACPPermissionModeMapping,
  authentication: {
    methodId: 'api-key',
  },
  providerAuthentication: {
    gateway: {
      env: {
        CODEX_API_KEY: { $source: 'gateway-api-key' },
        CODEX_CONFIG: {
          model: 'openai/gpt-5.6-sol',
          model_provider: 'ai_gateway',
          model_providers: {
            ai_gateway: {
              name: 'AI Gateway',
              base_url: {
                $source: 'gateway-base-url',
                ensureSuffix: '/v1',
              },
              env_key: 'CODEX_API_KEY',
              wire_api: 'responses',
              supports_websockets: false,
              http_headers: {
                'User-Agent': { $source: 'client-app' },
                'x-client-app': { $source: 'client-app' },
              },
            },
          },
          model_supports_reasoning_summaries: true,
          preferred_auth_method: 'apikey',
        },
      },
    },
  },
});

Codex ACP는 permissionMode: 'allow-all'만 지원해요. 그 제한적인 세션 모드는 Codex의 내부 샌드박스를 활성화하는데, 이는 이미 HarnessAgent에 제공된 샌드박스 안에서 실행되면 안 돼요.

client-app 플레이스홀더는 버전 지정 ai-sdk/harness-acp/<version> 식별자로 해결되고 User-Agent와 x-client-app 둘 다로 전송돼요. Gateway 자격 증명이 없으면 이 프로파일은 직접 CODEX_API_KEY 또는 OPENAI_API_KEY 인증을 사용해요. Gateway env 값은 Gateway가 선택될 때만 ACP 프로세스에 추가되므로, 직접 런타임 구성은 변하지 않아요.

Cursor ACP

Cursor는 agent acp로 ACP를 노출하고 NPM 패키지가 아닌 Bash 설치 프로그램으로 CLI를 설치해요:

import { createACP } from '@ai-sdk/harness-acp';

export const cursorACPHarness = createACP({
  harnessId: 'cursor-acp',
  source: {
    type: 'install-command',
    command: 'curl https://cursor.com/install -fsS | bash',
  },
  executable: 'agent',
  args: ['--disable-auto-update', 'acp'],
  modelMapping: {
    type: 'session-config-option',
    path: 'model',
  },
  clientCapabilities: {
    _meta: { parameterizedModelPicker: true },
  },
  credentialEnv: ['CURSOR_API_KEY'],
  credentialBrokering: ({ env, sandboxEnv }) => {
    if (!env.CURSOR_API_KEY || !sandboxEnv?.CURSOR_API_KEY) return [];
    return [
      {
        match: {
          host: 'api2.cursor.sh',
          path: { exact: '/auth/exchange_user_api_key' },
          method: ['POST'],
          headers: [
            {
              key: { exact: 'Authorization' },
              value: { exact: `Bearer ${sandboxEnv.CURSOR_API_KEY}` },
            },
          ],
        },
        transform: {
          headers: {
            Authorization: `Bearer ${env.CURSOR_API_KEY}`,
          },
        },
      },
    ];
  },
});

업데이트 플래그는 캐시된 설치가 런타임에 스스로 변경하는 것을 방지해요. 자격 증명 브로커링은 Cursor의 초기 API 키 교환으로 제한되므로, 이후 요청은 그 교환이 반환한 단기 Cursor 액세스 토큰을 유지해요.

Cursor는 AI Gateway를 지원해요. AI Gateway 자격 증명으로 Cursor의 OpenAI API 키를 구성하고 Override OpenAI Base URL을 https://ai-gateway.vercel.sh/cursor/v1로 설정하세요. 일반 Cursor CLI는 CURSOR_API_KEY로 Cursor에 계속 인증하며, 그 로그인은 Cursor 계정에 구성된 모델 provider 및 라우팅과 별개예요.

Grok Build ACP

Grok Build는 grok agent stdio를 통해 ACP를 직접 노출해요. 권한 동작에 대해 ACP 세션 모드를 알리지 않으므로 이 프로파일은 permissionModeMapping을 생략해요:

import { createACP } from '@ai-sdk/harness-acp';
import { createCredentialRequestTransformation } from '@ai-sdk/harness/utils';

export const grokBuildACPHarness = createACP({
  harnessId: 'acp-grok-build',
  // Define the runtime's built-in tool names and input schemas to expose
  // provider-executed calls as typed HarnessAgent tools.
  // builtinTools: { ... },
  source: {
    type: 'npm-simple',
    packageName: '@xai-official/grok',
    packageVersion: '0.2.111',
  },
  executable: 'grok',
  args: ['agent', 'stdio'],
  modelMapping: {
    type: 'session-model',
    path: 'modelId',
  },
  credentialEnv: ['XAI_API_KEY'],
  credentialBrokering: ({ env, sandboxEnv }) => {
    if (!env.XAI_API_KEY || !sandboxEnv?.XAI_API_KEY) return [];
    return [
      createCredentialRequestTransformation({
        matchUrl: env.GROK_XAI_API_BASE_URL ?? 'https://api.x.ai/v1',
        matchHeaders: {
          Authorization: `Bearer ${sandboxEnv.XAI_API_KEY}`,
        },
        transformHeaders: {
          Authorization: `Bearer ${env.XAI_API_KEY}`,
        },
      }),
    ];
  },
  instructionMapping: {
    type: 'filesystem',
    path: '.grok/AGENTS.md',
  },
  providerAuthentication: {
    gateway: {
      env: {
        GROK_CLIENT_NAME: { $source: 'client-app-name' },
        GROK_CLIENT_VERSION: { $source: 'client-app-version' },
        XAI_API_KEY: { $source: 'gateway-api-key' },
        GROK_XAI_API_BASE_URL: {
          $source: 'gateway-base-url',
          ensureSuffix: '/v1',
        },
        GROK_MODELS_BASE_URL: {
          $source: 'gateway-base-url',
          ensureSuffix: '/v1',
        },
      },
    },
  },
});

Gateway 자격 증명이 없으면 이 프로파일은 직접 XAI_API_KEY 인증을 사용해요. Gateway 환경은 클라이언트 이름과 버전 귀속을 제공해요. Grok Build는 내장된 안전 연산을 내부적으로 처리해요. ACP를 통해 전송되는 권한 요청에 대해 어댑터는 구성된 Harness permissionMode를 도구 종류별로 적용하고 나머지는 명시적 승인을 요청해요.

알려진 제한 (Known Limitations)

  • ACP v1은 모델 스텝 경계나 스텝별 사용량을 노출하지 않아요. 어댑터는 경계를 추론하고 알 수 없는 스텝별 사용량을 보고하며, 터미널 프롬프트 사용량이 턴 합계를 제공해요.
  • 표준 ACP v1에는 휴대 가능한 수동 압축 또는 미드 턴 스티어링 API가 없어요.
  • ACP v1에는 휴대 가능한 내장 도구 필터링 API가 없어요. 호스트 실행 도구를 필터링하려면 여전히 activeTools와 inactiveTools를 쓸 수 있지만, ACP 내장 도구를 필터링하면 throw돼요.
  • 변경된 호스트 도구 카탈로그는 ACP 구현이 MCP 도구 목록을 새로고쳐야 해요. 오래된 도구를 유지하는 구현은 명시적으로 실패해요.

더 알아보기 (Learn more)