> ## Documentation Index
> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# mods API 사용하기

> Claude Code mod에서 mods API를 호출하여 명령어와 도구를 추가하고, 모델을 호출하고, 타이머에서 작업을 실행하고, 다른 세션에 메시지를 보내고, 파일 및 네트워크에 접근합니다.

mods API는 mod가 작동하기 위해 호출하는 메서드의 집합입니다. 명령어와 도구를 추가하고, 모델을 호출하고, 이벤트 사이에 작업을 실행하고, 파일 시스템, 프로세스 및 네트워크에 접근합니다. 모든 hook은 첫 번째 인수인 `$`로 이를 받으며, 메서드는 `$.ui` 및 `$.fs`와 같은 네임스페이스로 그룹화됩니다. [이벤트](/docs/ko/plugins/mods/events)는 hook이 실행되는 시점을 결정하며, mods API는 hook이 실행되면 호출하는 것입니다.

여기서 시작하기 전에 [첫 번째 mod를 빌드](/docs/ko/plugins/mods/create)하세요. 모든 메서드에 대해 [mods API 메서드](/docs/ko/plugins/mods/reference#mods-api-methods)를 참조하거나 [빌드용 타입](/docs/ko/plugins/mods/create#get-the-types-for-your-build)을 읽으세요.

<h2 id="add-a-command-or-a-tool">
  명령어 또는 도구 추가하기
</h2>

mod는 사용자가 실행할 명령어와 Claude가 호출할 도구를 추가할 수 있습니다. 둘 다 [`session.start`](/docs/ko/plugins/mods/reference#session) hook에 등록하세요. Claude Code는 첫 번째 프롬프트 전에 해당 hook을 기다리므로, 등록한 것은 첫 번째 턴부터 사용 가능합니다.

<h3 id="add-a-command">
  명령어 추가하기
</h3>

명령어는 사용자용입니다. 등록한 후 해당 이름에 대해 [`command.run`](/docs/ko/plugins/mods/reference#commands-and-configuration)을 처리하세요. 이 예제는 선택적 일 수를 받는 `/standup` 명령어를 추가합니다:

```javascript theme={null}
on('session.start', async ($, e, next) => {
  // /standup을 명령어 목록에 추가하고, 사용자가 볼 수 있는 설명을 포함합니다
  await $.command.register({ name: 'standup', description: 'Summarize what changed today', argumentHint: '[days]' })
  return next(e)
})

// matcher는 hook을 /standup으로 제한하므로 다른 명령어는 도달하지 않습니다
on('command.run', { command: 'standup' }, async ($, e) => {
  // e.args는 명령어 이름 뒤에 입력된 텍스트이거나 빈 문자열입니다
  return { text: 'Summary for the last ' + (e.args || '1') + ' day(s): ...' }
})
```

세션이 시작된 후, `/standup`은 설명과 함께 `/`를 입력할 때 보이는 목록에 나타납니다. `argumentHint`는 명령어를 입력한 후 공백을 입력할 때 프롬프트 뒤에 표시되며, `/standup [days]`와 같이 표시됩니다. `/standup 3`을 실행하면, 두 번째 hook은 `Summary for the last 3 day(s): ...`을 반환하고, 트랜스크립트는 플러그인 이름 뒤에 해당 텍스트를 표시합니다. hook은 `next`를 호출하지 않습니다. 명령어는 당신의 동작 외에 다른 동작이 없기 때문입니다.

반환하는 `text`는 트랜스크립트에 인쇄되고 Claude가 읽습니다. 아무것도 인쇄하지 않으려면, [pane](/docs/ko/plugins/mods/interface#pick-where-to-draw)만 여는 명령어의 경우 `{}`를 반환하세요. Claude가 작업 중일 때 명령어를 실행하도록 하려면 등록에 `immediate: true`를 추가하세요.

내장 명령어가 사용하지 않는 이름을 선택하세요. 세션에서 `/`를 입력하여 확인하세요. `$.command.register`는 사용 중인 이름에 대해 `"/focus" refused: it is the built-in /focus`와 같은 메시지와 함께 throw합니다. throw하는 hook은 건너뛰어지므로 해당 `session.start` hook의 나머지 부분도 실행되지 않습니다. 해당 hook에서 마지막에 명령어를 등록하거나 호출을 `try`와 `catch`로 래핑하세요.

<h3 id="add-a-tool">
  도구 추가하기
</h3>

도구는 Claude용입니다. 이름, Claude가 읽는 설명, 입력을 위한 JSON Schema로 등록하세요. Claude는 `mcp__`, 플러그인 이름, 두 개의 언더스코어, 등록한 이름으로 구성된 더 긴 이름 아래에서 이를 봅니다. [`tool.call`](/docs/ko/plugins/mods/events#guard-or-change-a-tool-call) hook에서 해당 전체 이름으로 필터링된 호출을 처리합니다. 이 예제는 `my-mod`라는 플러그인에서 `ticket`을 등록하므로 전체 이름은 `mcp__my-mod__ticket`입니다. Claude에게 이슈 추적기에서 티켓을 조회하는 도구를 제공합니다:

```javascript theme={null}
on('session.start', async ($, e, next) => {
  await $.tool.register({
    name: 'ticket',
    // Claude는 이 설명에서 도구를 호출할 시점을 결정합니다
    description: 'Look up a ticket by its id and return its title and status',
    // Claude가 보내야 하는 인수: id라는 필수 문자열 하나
    inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
  })
  return next(e)
})

// 전체 도구 이름은 mcp__, 플러그인 이름, 등록한 이름입니다
on('tool.call', { tool: 'mcp__my-mod__ticket' }, async ($, e) => {
  // 도구의 인수는 e의 필드이므로 id는 e.id입니다
  const response = await $.http.fetch('https://tickets.example.com/api/' + encodeURIComponent(e.id))
  // 어느 쪽이든 결과를 반환하므로 Claude는 조회가 실패했을 때를 알 수 있습니다
  return { result: response.ok ? response.text : 'Lookup failed with status ' + response.status }
})
```

티켓에 대해 물으면, Claude는 `mcp__my-mod__ticket`을 해당 id로 호출할 수 있습니다. 두 번째 hook은 티켓을 가져오고 응답 본문을 반환하며, Claude는 이를 도구의 결과로 읽습니다. 서버가 오류 상태로 응답하면, Claude는 `Lookup failed with status`와 숫자를 읽습니다.

<h2 id="call-a-model">
  모델 호출하기
</h2>

mod는 텍스트 정렬 또는 요약과 같은 작은 작업을 위해 대화 외부에서 모델에 질문할 수 있습니다. `$.model.complete`는 세션의 자격 증명으로 모델에 하나의 프롬프트를 보내고 회신으로 해결됩니다. 대화 기록이 없습니다.

이 hook은 [`command.run`](#add-a-command)으로 등록된 `/triage` 명령어에 답하여 작은 모델에 그 뒤에 입력된 텍스트에 레이블을 지정하도록 요청합니다:

```javascript theme={null}
on('command.run', { command: 'triage' }, async ($, e) => {
  const r = await $.model.complete({
    model: 'haiku',
    // 시스템 프롬프트는 작업을 설정하고, 프롬프트는 레이블을 지정할 텍스트를 전달합니다
    system: 'Reply with one word: bug, feature, or question.',
    prompt: e.args,
    // 한 단어는 적은 토큰이 필요하며, 호출은 15초 후 포기합니다
    maxTokens: 20,
    timeoutMs: 15000,
  })
  // r.text는 모델이 답변했을 때만 존재하므로 먼저 r.isAnswered를 확인하세요
  const label = r.isAnswered ? r.text.trim() : 'unknown'
  return { text: 'Label: ' + label }
})
```

`/triage the export button does nothing`을 실행하면, mod는 해당 텍스트를 모델로 보내고 `Label: bug`와 같은 답변을 인쇄합니다. Claude의 대화는 요청의 일부가 아닙니다. 모델이 답변하지 않으면 레이블은 `unknown`입니다.

Claude API 실패는 호출을 거부하지 않으므로 `r.isAnswered`를 확인하고, `false`일 때 `r.reason`을 읽으세요. 호출은 Claude Code가 보내지 않을 요청(예: 조직이 차단한 모델)에 대해서만 거부합니다. [빌드용 타입](/docs/ko/plugins/mods/create#get-the-types-for-your-build)은 `effort`와 같은 다른 옵션을 나열하고, [제한](/docs/ko/plugins/mods/reference#limits)은 `maxTokens` 기본값을 제공합니다.

`$.model.fork({ prompt })`는 대신 현재 대화에 대해 한 가지 질문을 하며, 동일한 모델과 시스템 프롬프트를 사용하므로 Claude API는 대부분을 프롬프트 캐시에서 제공합니다.

이러한 호출은 사용자의 플랜 또는 API 키를 사용합니다.

<h2 id="run-work-in-the-background">
  백그라운드에서 작업 실행하기
</h2>

한 이벤트를 초과하는 작업(예: 1분마다 무언가를 확인)은 `session.start`에서 시작하는 타이머에서 실행됩니다. hook 자체는 하나의 이벤트에 대해 실행되며 자체 실행 시간 제한은 10초입니다. `next` 또는 mods API 호출에 소비된 시간은 계산되지 않습니다. `$.clock.sleep` 제외. `$.clock.every` 및 `$.clock.after`는 `setInterval` 및 `setTimeout`을 대신하며, 지연은 밀리초 단위입니다: `$.clock.after(5000, fn)`은 지금부터 5초 후에 `fn`을 한 번 호출합니다. 각각은 `cancel()` 메서드가 있는 타이머를 반환하고, `await $.clock.now()`는 밀리초 단위의 시간을 제공합니다.

이 hook은 1분마다 pull request의 확인을 조회하고 프롬프트 아래에 결과를 표시합니다. `summarize`는 명령어의 JSON 출력을 몇 단어로 변환하는 자신의 함수입니다:

```javascript theme={null}
on('session.start', async ($, e, next) => {
  // 60,000밀리초마다 함수를 호출하며, 지금부터 1분 후에 시작합니다
  $.clock.every(60_000, async () => {
    const status = await $.process.run(['gh', 'pr', 'checks', '--json', 'state'])
    // 프롬프트 아래의 줄을 최신 요약으로 바꿉니다
    $.ui.status('checks: ' + summarize(status.stdout))
  })
  // 타이머를 기다리지 않고 반환하므로 세션이 바로 시작됩니다
  return next(e)
})
```

세션은 평소대로 시작됩니다. 1분 후, 프롬프트 아래에 `⚠`, mod의 이름, 그리고 `checks:`와 요약이 있는 줄이 나타납니다. 그 후 1분마다 교체됩니다. 타이머의 콜백은 모든 이벤트 외부에서 실행되므로 턴 사이에 계속 실행되고 턴을 시작하지 않습니다. 콜백이 throw하면, 오류는 [디버그 로그](/docs/ko/plugins/mods/troubleshoot#read-the-debug-log)로 이동하고 타이머는 다음 간격에 다시 실행됩니다.

<h3 id="show-something-without-starting-a-turn">
  턴을 시작하지 않고 무언가 표시하기
</h3>

백그라운드 작업은 턴을 시작하지 않고 사용자에게 무언가를 표시할 수 있습니다. 이러한 각 호출은 다른 위치에 텍스트를 배치합니다:

| 호출 | 사용자가 보는 것 |
| :- | :- |
| `$.ui.status(text)` | 변경할 때까지 프롬프트 아래에 남아있는 한 줄입니다. `⚠`와 mod의 이름으로 시작하며, `⚠ my-mod: checks: 3 passing`과 같습니다. |
| `$.ui.toast(text)` | 오른쪽 상단의 작은 상자이며, mod의 이름이 텍스트 위에 있고 몇 초 후 사라집니다 |
| `$.ui.log(text)` | Claude가 읽지 않는 트랜스크립트의 흐릿한 줄입니다. `●`과 mod의 이름으로 시작하며, `● my-mod: build finished`와 같습니다. |

<h3 id="start-a-turn-from-a-background-job">
  백그라운드 작업에서 턴 시작하기
</h3>

백그라운드 작업이 Claude의 주의가 필요한 것을 발견하면, `$.prompt.submit({ text })`로 프롬프트를 제출하여 턴을 시작할 수 있습니다. Claude는 발신자로 mod의 이름을 지정하는 문장 뒤의 텍스트를 읽습니다. 해당 문장 없이 사용자 자신의 말로 보내려면 `asUser: true`를 추가하세요. 호출은 세션이 유휴 상태가 될 때까지 기다린 후 새 턴을 시작합니다. 해당 턴이 시작될 때 해결되므로 Claude가 작업 중일 때 실행되는 핸들러에서 `await`하지 마세요.

<h3 id="stop-background-work">
  백그라운드 작업 중지하기
</h3>

백그라운드 작업은 두 가지 방법으로 중지됩니다. 모듈이 다시 로드되면 타이머가 중지됩니다. hook 내의 장기 실행 작업의 경우, [`next.signal`](/docs/ko/plugins/mods/reference#the-hook-function)은 hook이 처리하는 이벤트가 중단될 때(예: 사용자가 중단할 때) 중단되는 `AbortSignal`이므로 장기 실행 항목에 전달하세요.

<h2 id="send-and-receive-messages-between-sessions">
  세션 간 메시지 보내고 받기
</h2>

mod는 다른 세션 또는 이 세션의 subagent 중 하나에 일반 텍스트 메시지를 보낼 수 있으며, 도착하고 떠나는 메시지를 관찰할 수 있습니다. `$.session.send({ to, text })`는 하나를 보내며, SendMessage 도구가 만드는 것과 동일한 전달입니다. `to`는 세션의 경우 `{ sessionId }`, `$.agent.list()`의 subagent의 경우 `{ agentId }`, 또는 수신한 메시지가 온 문자열 주소입니다. 호출은 메시지가 큐에 들어가면 `{ isDelivered: true }`로 해결됩니다. 아무것도 전달되지 않으면 `{ isDelivered: false, reason }`으로 해결되며, `reason`은 이유를 설명합니다.

이 hook은 [`command.run`](#add-a-command)으로 등록된 `/ping` 명령어에 답하여 그 뒤에 입력한 id의 세션에 상태를 요청합니다:

```javascript theme={null}
on('command.run', { command: 'ping' }, async ($, e) => {
  // e.args는 /ping 뒤에 입력된 세션 id입니다
  const sent = await $.session.send({ to: { sessionId: e.args }, text: 'Status? One line.' })
  // 호출은 어느 쪽이든 해결되므로 isDelivered를 확인하여 무슨 일이 일어났는지 알아봅니다
  if (!sent.isDelivered) $.ui.toast('Not delivered: ' + sent.reason)
  // 빈 결과는 이 세션의 트랜스크립트에 아무것도 인쇄하지 않습니다
  return {}
})
```

메시지가 큐에 들어가면 세션에 아무것도 나타나지 않으며, 다른 세션의 Claude는 `Status? One line.`을 읽습니다. 아무것도 전달되지 않으면, 오른쪽 상단의 작은 상자가 이유를 제공하고 몇 초 후 사라집니다.

두 이벤트를 통해 mod는 메시지를 관찰할 수 있습니다. 두 이벤트 모두에서 `next(e)`를 반환하여 각 메시지를 변경되지 않은 상태로 전달합니다:

| 이벤트 | 발생 시점 | 유용한 필드 |
| :- | :- | :- |
| `session.receive` | 메시지가 이 세션에 도착하며, Claude가 읽기 전입니다 | `e.text`, 및 `e.origin.kind`(예: 다른 세션 또는 agent의 경우 `peer` 또는 `peer-send-message`, `task-notification`, 또는 `scheduled-trigger`). Claude에서 메시지를 유지하려면 `{ consumed: reason }`을 반환합니다. |
| `session.send` | 메시지가 SendMessage 도구 또는 mod에서 떠나려고 합니다 | `e.to`, `e.text`, 및 `e.origin.kind`(이는 `model` 또는 `plugin`입니다) |

[인바운드 메시지를 거부](/docs/ko/cross-session-messaging#control-inbound-messages)하도록 설정된 세션은 `session.receive`가 발생하기 전에 메시지를 거부하므로 hook은 이를 보지 않습니다. 승인을 위해 보류 중인 메시지는 먼저 hook에 도달하므로 mod는 아직 승인하지 않은 메시지를 읽을 수 있습니다. hook의 `next(e)`는 메시지가 전달되지 않으면 거부합니다.

수신한 메시지의 발신자 이름은 발신자가 작성한 것이므로 이를 기반으로 결정하지 마세요.

<h2 id="reach-files-processes-and-the-network">
  파일, 프로세스 및 네트워크에 접근하기
</h2>

mod는 Claude Code를 실행하는 사용자와 동일한 권한으로 파일 시스템, 프로세스 및 네트워크에 접근합니다. hooks 모듈 자체는 Node.js API, `setTimeout`과 같은 타이머 전역, 자체 네트워크 또는 파일 접근이 없습니다. `URL`, `TextEncoder`, `AbortController`, `crypto.subtle`과 같은 표준 JavaScript 및 웹 API를 사용할 수 있습니다. 아래의 각 네임스페이스는 한 종류의 접근을 다룹니다:

| 네임스페이스 | 수행하는 작업 |
| :- | :- |
| `$.fs` | `read(path)`, `write(path, text)`, `exists(path)`, `stat(path)`, 및 `list(path)`는 파일 및 디렉토리에서 작동합니다 |
| `$.process` | `run(['git', 'status'])`는 명령어를 시작하고 종료될 때 해결됩니다. `spawn`은 장기 실행 명령어의 출력을 스트리밍합니다. |
| `$.http` | `http` 또는 `https`를 통한 `fetch(url, init)`. 본문이 읽혀지면 `{ status, ok, headers, text }`로 해결됩니다. |
| `$.store` | 플러그인 자신의 JSON 키-값 저장소이며, 세션 간에 유지됩니다 |
| `$.env` | 환경 변수를 `get` 및 `set`합니다. 이름을 리터럴 문자열로 작성하세요. |
| `$.settings` | 설정 파일 및 관리 정책이 보유한 것을 `read`합니다 |
| `$.session` | `messages()`는 트랜스크립트를 `{ role, text, toolUses }` 목록으로 반환합니다. 또한 작업 디렉토리, 모델 등입니다. [`usage()`](/docs/ko/plugins/mods/reference#mods-api-methods)는 컨텍스트 윈도우 사용 및 플랜 제한을 반환합니다. |
| `$.mcp` | 연결된 MCP 서버에서 도구를 `call`합니다 |

파일 및 프로세스에는 자체 규칙이 몇 가지 있습니다:

* **경로**: 상대 경로는 세션의 작업 디렉토리 아래에 있습니다
* **`$.fs.list`**: 한 디렉토리의 항목을 `{ name, kind, size, isLink }`로 반환하며 하위 디렉토리로 내려가지 않습니다
* **`$.process.run`**: 인수 목록을 받으며 shell을 사용하지 않습니다. 종료 코드에 관계없이 `{ exitCode, stdout, stderr }`로 해결됩니다. 프로그램을 시작할 수 없거나 기본값인 30초의 타임아웃에서 여전히 실행 중이면 거부하므로 `try`와 `catch`로 래핑하세요.

이러한 호출 각각은 그 자체로 이벤트이며, `$.fs.read`의 경우 `fs.read`와 같이 `$.` 없이 네임스페이스 및 메서드로 이름이 지정됩니다. [체인의 앞에 있는](/docs/ko/plugins/mods/events#the-order-mods-run-in) mod는 호출을 관찰, 다시 작성 또는 거부할 수 있으며, 이것이 조직이 mod가 도달하는 것을 제한하는 방법입니다.

<h2 id="next-steps">
  다음 단계
</h2>

* [이벤트에 반응하기](/docs/ko/plugins/mods/events): hook 도구 호출, 프롬프트 및 턴
* [인터페이스에 그리기](/docs/ko/plugins/mods/interface): mod가 수집한 것을 pane 또는 프롬프트 위에 표시합니다
* [mod 테스트하기](/docs/ko/plugins/mods/test): 테스트에서 이러한 호출 중 하나를 stub합니다
* [Mods 참조](/docs/ko/plugins/mods/reference): 모든 이벤트, 모든 mods API 메서드 및 제한
