{"meta":{"title":"사전 도구 사용 후크","intro":"onPreToolUse 도구가 실행되기 전에 후크가 호출됩니다. 이를 사용하여 다음을 수행합니다.","product":"GitHub Copilot","breadcrumbs":[{"href":"/ko/copilot","title":"GitHub Copilot"},{"href":"/ko/copilot/how-tos","title":"방법"},{"href":"/ko/copilot/how-tos/copilot-sdk","title":"코필로트 SDK"},{"href":"/ko/copilot/how-tos/copilot-sdk/hooks","title":"후크 사용"},{"href":"/ko/copilot/how-tos/copilot-sdk/hooks/pre-tool-use","title":"사전 도구 사용"}],"documentType":"article"},"body":"# 사전 도구 사용 후크\n\nonPreToolUse 도구가 실행되기 전에 후크가 호출됩니다. 이를 사용하여 다음을 수행합니다.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n* 도구 실행 승인 또는 거부\n* 도구 인수 수정\n* 도구에 대한 컨텍스트 추가\n* 대화에서 도구 출력 표시 안 함\n\n## 후크 서명\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nimport type { PreToolUseHookInput, HookInvocation, PreToolUseHookOutput } from \"@github/copilot-sdk\";\ntype PreToolUseHandler = (\n  input: PreToolUseHookInput,\n  invocation: HookInvocation\n) => Promise<PreToolUseHookOutput | null | undefined>;\n```\n\n```typescript\ntype PreToolUseHandler = (\n  input: PreToolUseHookInput,\n  invocation: HookInvocation\n) => Promise<PreToolUseHookOutput | null | undefined>;\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot.session import PreToolUseHookInput, PreToolUseHookOutput\nfrom typing import Callable, Awaitable\n\nPreToolUseHandler = Callable[\n    [PreToolUseHookInput, dict[str, str]],\n    Awaitable[PreToolUseHookOutput | None]\n]\n```\n\n```python\nPreToolUseHandler = Callable[\n    [PreToolUseHookInput, dict[str, str]],\n    Awaitable[PreToolUseHookOutput | None]\n]\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\npackage main\n\nimport copilot \"github.com/github/copilot-sdk/go\"\n\ntype PreToolUseHandler func(\n    input copilot.PreToolUseHookInput,\n    invocation copilot.HookInvocation,\n) (*copilot.PreToolUseHookOutput, error)\n\nfunc main() {}\n```\n\n```golang\ntype PreToolUseHandler func(\n    input PreToolUseHookInput,\n    invocation HookInvocation,\n) (*PreToolUseHookOutput, error)\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nusing GitHub.Copilot;\n\npublic delegate Task<PreToolUseHookOutput?> PreToolUseHandler(\n    PreToolUseHookInput input,\n    HookInvocation invocation);\n```\n\n```csharp\npublic delegate Task<PreToolUseHookOutput?> PreToolUseHandler(\n    PreToolUseHookInput input,\n    HookInvocation invocation);\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\nimport com.github.copilot.sdk.json.*;\n\nPreToolUseHandler preToolUseHandler;\n```\n\n</div>\n\n</div>\n\n## 입력\n\n| Field       | Type   | Description            |\n| ----------- | ------ | ---------------------- |\n| `timestamp` | number | 후크가 트리거될 때의 Unix 타임스탬프 |\n| `cwd`       | string | 현재 작업 디렉터리             |\n| `toolName`  | string | 호출되는 도구의 이름            |\n| `toolArgs`  | object | 도구에 전달된 인수             |\n\n## 출력\n\n`null` 또는 `undefined`을(를) 반환하여 변경 없이 도구를 실행할 수 있도록 합니다. 그렇지 않으면 다음 필드가 있는 개체를 반환합니다.\n\n| Field                      | Type    | Description                  |\n| -------------------------- | ------- | ---------------------------- |\n| `permissionDecision`       |         |                              |\n| `\"allow\"`                  |         |                              |\n| \\|                         |         |                              |\n| `\"deny\"`                   |         |                              |\n| \\|                         |         |                              |\n| `\"ask\"`                    |         |                              |\n| 도구 호출을 허용할지 여부             |         |                              |\n| `permissionDecisionReason` | string  | 사용자에게 표시되는 설명(거부/요청)         |\n| `modifiedArgs`             | object  | 도구에 전달할 수정된 인수               |\n| `additionalContext`        | string  | 대화에 삽입된 추가 컨텍스트              |\n| `suppressOutput`           | boolean | true이면 도구 출력이 대화에 표시되지 않습니다. |\n\n### 권한 결정\n\n| 의사 결정     | 동작                             |\n| --------- | ------------------------------ |\n| `\"allow\"` | 도구가 정상적으로 실행됩니다.               |\n| `\"deny\"`  | 도구가 차단되었습니다. 이유가 사용자에게 표시됨     |\n| `\"ask\"`   | 승인하라는 메시지가 사용자에게 표시됩니다(대화형 모드) |\n\n## 예제\n\n### 모든 도구 허용(로깅에만 해당)\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input, invocation) => {\n      console.log(`[${invocation.sessionId}] Calling ${input.toolName}`);\n      console.log(`  Args: ${JSON.stringify(input.toolArgs)}`);\n      return { permissionDecision: \"allow\" };\n    },\n  },\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot.session import PermissionHandler\n\nasync def on_pre_tool_use(input_data, invocation):\n    print(f\"[{invocation['session_id']}] Calling {input_data['toolName']}\")\n    print(f\"  Args: {input_data['toolArgs']}\")\n    return {\"permissionDecision\": \"allow\"}\n\nsession = await client.create_session(on_permission_request=PermissionHandler.approve_all, hooks={\"on_pre_tool_use\": on_pre_tool_use})\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    copilot \"github.com/github/copilot-sdk/go\"\n)\n\nfunc main() {\n    client := copilot.NewClient(nil)\n    session, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{\n        OnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n        Hooks: &copilot.SessionHooks{\n            OnPreToolUse: func(input copilot.PreToolUseHookInput, inv copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) {\n                fmt.Printf(\"[%s] Calling %s\\n\", inv.SessionID, input.ToolName)\n                fmt.Printf(\"  Args: %v\\n\", input.ToolArgs)\n                return &copilot.PreToolUseHookOutput{\n                    PermissionDecision: \"allow\",\n                }, nil\n            },\n        },\n    })\n    _ = session\n}\n```\n\n```golang\nsession, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{\n    Hooks: &copilot.SessionHooks{\n        OnPreToolUse: func(input copilot.PreToolUseHookInput, inv copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) {\n            fmt.Printf(\"[%s] Calling %s\\n\", inv.SessionID, input.ToolName)\n            fmt.Printf(\"  Args: %v\\n\", input.ToolArgs)\n            return &copilot.PreToolUseHookOutput{\n                PermissionDecision: \"allow\",\n            }, nil\n        },\n    },\n})\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nusing GitHub.Copilot;\n\npublic static class PreToolUseExample\n{\n    public static async Task Main()\n    {\n        await using var client = new CopilotClient();\n        var session = await client.CreateSessionAsync(new SessionConfig\n        {\n            Hooks = new SessionHooks\n            {\n                OnPreToolUse = (input, invocation) =>\n                {\n                    Console.WriteLine($\"[{invocation.SessionId}] Calling {input.ToolName}\");\n                    Console.WriteLine($\"  Args: {input.ToolArgs}\");\n                    return Task.FromResult<PreToolUseHookOutput?>(\n                        new PreToolUseHookOutput { PermissionDecision = \"allow\" }\n                    );\n                },\n            },\n        });\n    }\n}\n```\n\n```csharp\nvar session = await client.CreateSessionAsync(new SessionConfig\n{\n    Hooks = new SessionHooks\n    {\n        OnPreToolUse = (input, invocation) =>\n        {\n            Console.WriteLine($\"[{invocation.SessionId}] Calling {input.ToolName}\");\n            Console.WriteLine($\"  Args: {input.ToolArgs}\");\n            return Task.FromResult<PreToolUseHookOutput?>(\n                new PreToolUseHookOutput { PermissionDecision = \"allow\" }\n            );\n        },\n    },\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\nimport com.github.copilot.sdk.*;\nimport com.github.copilot.sdk.json.*;\nimport java.util.concurrent.CompletableFuture;\n\nvar hooks = new SessionHooks()\n    .setOnPreToolUse((input, invocation) -> {\n        System.out.println(\"[\" + invocation.getSessionId() + \"] Calling \" + input.getToolName());\n        System.out.println(\"  Args: \" + input.getToolArgs());\n        return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());\n    });\n\nvar session = client.createSession(\n    new SessionConfig()\n        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n        .setHooks(hooks)\n).get();\n```\n\n</div>\n\n</div>\n\n### 특정 도구 차단\n\n```typescript\nconst BLOCKED_TOOLS = [\"shell\", \"bash\", \"write_file\", \"delete_file\"];\n\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      if (BLOCKED_TOOLS.includes(input.toolName)) {\n        return {\n          permissionDecision: \"deny\",\n          permissionDecisionReason: `Tool '${input.toolName}' is not permitted in this environment`,\n        };\n      }\n      return { permissionDecision: \"allow\" };\n    },\n  },\n});\n```\n\n### 도구 인수 수정\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      // Add a default timeout to all shell commands\n      if (input.toolName === \"shell\" && input.toolArgs) {\n        const args = input.toolArgs as { command: string; timeout?: number };\n        return {\n          permissionDecision: \"allow\",\n          modifiedArgs: {\n            ...args,\n            timeout: args.timeout ?? 30000, // Default 30s timeout\n          },\n        };\n      }\n      return { permissionDecision: \"allow\" };\n    },\n  },\n});\n```\n\n### 특정 디렉터리에 대한 파일 액세스 제한\n\n```typescript\nconst ALLOWED_DIRECTORIES = [\"/home/user/projects\", \"/tmp\"];\n\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      if (input.toolName === \"read_file\" || input.toolName === \"write_file\") {\n        const args = input.toolArgs as { path: string };\n        const isAllowed = ALLOWED_DIRECTORIES.some(dir => \n          args.path.startsWith(dir)\n        );\n        \n        if (!isAllowed) {\n          return {\n            permissionDecision: \"deny\",\n            permissionDecisionReason: `Access to '${args.path}' is not permitted. Allowed directories: ${ALLOWED_DIRECTORIES.join(\", \")}`,\n          };\n        }\n      }\n      return { permissionDecision: \"allow\" };\n    },\n  },\n});\n```\n\n### 도구의 장황한 출력 억제\n\n```typescript\nconst VERBOSE_TOOLS = [\"list_directory\", \"search_files\"];\n\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      return {\n        permissionDecision: \"allow\",\n        suppressOutput: VERBOSE_TOOLS.includes(input.toolName),\n      };\n    },\n  },\n});\n```\n\n### 도구 기반 컨텍스트 추가\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      if (input.toolName === \"query_database\") {\n        return {\n          permissionDecision: \"allow\",\n          additionalContext: \"Remember: This database uses PostgreSQL syntax. Always use parameterized queries.\",\n        };\n      }\n      return { permissionDecision: \"allow\" };\n    },\n  },\n});\n```\n\n## 모범 사례\n\n1. **항상 결정을 반환** - `null`을 반환하면 도구가 허용되지만, `{ permissionDecision: \"allow\" }`를 명시적으로 사용하는 편이 더 명확합니다.\n\n2. **유용한 거부 이유 제공** - 거부할 때 사용자가 다음을 이해하는 이유를 설명합니다.\n\n   ```typescript\n   return {\n     permissionDecision: \"deny\",\n     permissionDecisionReason: \"Shell commands require approval. Please describe what you want to accomplish.\",\n   };\n   ```\n\n3. **인수 수정에 주의** - 수정된 인수가 도구에 필요한 스키마를 유지 관리해야 합니다.\n\n4. **성능 고려** - 각 도구 호출 전에 사전 도구 후크가 동기적으로 실행됩니다. 계속 빠르게 처리하세요.\n\n5. **신중하게 사용 `suppressOutput`** - 출력을 표시하지 않음은 모델에 대화 품질에 영향을 줄 수 있는 결과가 표시되지 않음을 의미합니다.\n\n## 참고하십시오\n\n* [후크 사용](/ko/copilot/how-tos/copilot-sdk/hooks)\n* [사후 도구 사용 후크](/ko/copilot/how-tos/copilot-sdk/hooks/post-tool-use)\n* [디버깅 가이드](/ko/copilot/how-tos/copilot-sdk/troubleshooting/debugging)"}