{"meta":{"title":"첫 번째 Copilot 기반 앱 빌드","intro":"이 자습서에서는 Copilot SDK를 사용하여 명령줄 도우미를 빌드합니다. 기본 사항으로 시작하고, 스트리밍 응답을 추가한 다음, 사용자 지정 도구를 추가하여 Copilot 코드를 호출할 수 있는 기능을 제공합니다.","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/getting-started","title":"시작하기"}],"documentType":"article"},"body":"# 첫 번째 Copilot 기반 앱 빌드\n\n이 자습서에서는 Copilot SDK를 사용하여 명령줄 도우미를 빌드합니다. 기본 사항으로 시작하고, 스트리밍 응답을 추가한 다음, 사용자 지정 도구를 추가하여 Copilot 코드를 호출할 수 있는 기능을 제공합니다.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n**빌드할 내용:**\n\n```text\nYou: What's the weather like in Seattle?\nCopilot: Let me check the weather for Seattle...\n         Currently 62°F and cloudy with a chance of rain.\n         Typical Seattle weather!\n\nYou: How about Tokyo?\nCopilot: In Tokyo it's 75°F and sunny. Great day to be outside!\n```\n\n## 사전 요구 사항\n\n시작하기 전에 다음을 확인합니다.\n\n* **GITHUB COPILOT CLI** 설치 및 인증(Node.js, Python 및 .NET SDK는 CLI를 자동으로 제공합니다. [기본 설정(번들 CLI)](/ko/copilot/how-tos/copilot-sdk/setup/bundled-cli)을 참조하세요. 애플리케이션 수준 CLI 번들 기능을 사용하지 않는 한 Go, Java 및 Rust에 필요합니다.)\n* 기본 설정 언어 런타임:\n  * **Node.js** 20 이상 또는 **Python** 3.11 이상 또는 **Go** 1.24 이상 또는 > **Rust** 1.94 이상 또는 **Java** 17 이상 또는 **.NET** 8.0 이상\n\nCLI가 작동하는지 확인합니다.\n\n```bash\ncopilot --version\n```\n\n## 1단계: SDK 설치\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먼저 새 디렉터리를 만들고 프로젝트를 초기화합니다.\n\n```bash\nmkdir copilot-demo && cd copilot-demo\nnpm init -y --init-type module\n```\n\n그런 다음, SDK 및 TypeScript 실행기를 설치합니다.\n\n```bash\nnpm install @github/copilot-sdk tsx\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```bash\npip install github-copilot-sdk\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먼저 새 디렉터리를 만들고 모듈을 초기화합니다.\n\n```bash\nmkdir copilot-demo && cd copilot-demo\ngo mod init copilot-demo\n```\n\n그런 다음, SDK를 설치합니다.\n\n```bash\ngo get github.com/github/copilot-sdk/go\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"rust\" data-label=\"Rust\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Rust</div>\n\n먼저 새 이진 상자를 만듭니다.\n\n```bash\ncargo new copilot-demo && cd copilot-demo\n```\n\n그런 다음, 예제에서 사용하는 SDK 및 직접 종속성을 설치합니다.\n\n```bash\ncargo add github-copilot-sdk --features derive\n# Used by #[tokio::main] and tokio::spawn\ncargo add tokio --features rt-multi-thread,macros\n# Used by custom-tool parameter derives later in this guide\ncargo add serde --features derive\ncargo add schemars\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먼저 새 콘솔 프로젝트를 만듭니다.\n\n```bash\ndotnet new console -n CopilotDemo && cd CopilotDemo\n```\n\n그런 다음, SDK를 추가합니다.\n\n```bash\ndotnet add package GitHub.Copilot.SDK\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먼저 새 디렉터리를 만들고 프로젝트를 초기화합니다.\n\n**Maven**—`pom.xml`에 추가:\n\n```xml\n<dependency>\n    <groupId>com.github</groupId>\n    <artifactId>copilot-sdk-java</artifactId>\n    <version>${copilot.sdk.version}</version>\n</dependency>\n```\n\n**Gradle**—`build.gradle`에 추가:\n\n```groovy\nimplementation 'com.github:copilot-sdk-java:${copilotSdkVersion}'\n```\n\n</div>\n\n</div>\n\n## 2단계: 첫 번째 메시지 보내기\n\n새 파일을 만들고 다음 코드를 추가합니다. SDK를 사용하는 가장 간단한 방법인 약 5줄의 코드입니다.\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`index.ts`을 만듭니다.\n\n```typescript\nimport { CopilotClient } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient();\nconst session = await client.createSession({ model: \"auto\" });\n\nconst response = await session.sendAndWait({ prompt: \"What is 2 + 2?\" });\nconsole.log(response?.data.content);\n\nawait client.stop();\nprocess.exit(0);\n```\n\n다음을 실행합니다.\n\n```bash\nnpx tsx index.ts\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`main.py`을 만듭니다.\n\n```python\nimport asyncio\nfrom copilot import CopilotClient\nfrom copilot.session import PermissionHandler\n\nasync def main():\n    client = CopilotClient()\n    await client.start()\n\n    session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model=\"auto\")\n    response = await session.send_and_wait(\"What is 2 + 2?\")\n    print(response.data.content)\n\n    await client.stop()\n\nasyncio.run(main())\n```\n\n다음을 실행합니다.\n\n```bash\npython main.py\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`main.go`을 만듭니다.\n\n```golang\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"os\"\n\n    copilot \"github.com/github/copilot-sdk/go\"\n)\n\nfunc main() {\n    ctx := context.Background()\n    client := copilot.NewClient(nil)\n    if err := client.Start(ctx); err != nil {\n        log.Fatal(err)\n    }\n    defer client.Stop()\n\n    session, err := client.CreateSession(ctx, &copilot.SessionConfig{Model: \"auto\"})\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    response, err := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: \"What is 2 + 2?\"})\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    if d, ok := response.Data.(*copilot.AssistantMessageData); ok {\n        fmt.Println(d.Content)\n    }\n    os.Exit(0)\n}\n```\n\n다음을 실행합니다.\n\n```bash\ngo run main.go\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"rust\" data-label=\"Rust\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Rust</div>\n\n`src/main.rs`을 만듭니다.\n\n```rust\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse github_copilot_sdk::handler::ApproveAllHandler;\nuse github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig};\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let client = Client::start(ClientOptions::default()).await?;\n    let session = client\n        .create_session(SessionConfig::default().with_permission_handler(Arc::new(ApproveAllHandler)))\n        .await?;\n\n    let response = session\n        .send_and_wait(\n            MessageOptions::new(\"What is 2 + 2?\").with_wait_timeout(Duration::from_secs(120)),\n        )\n        .await?;\n\n    if let Some(event) = response {\n        if let Some(content) = event.data.get(\"content\").and_then(|value| value.as_str()) {\n            println!(\"{content}\");\n        }\n    }\n\n    session.disconnect().await?;\n    client.stop().await?;\n    Ok(())\n}\n```\n\n다음을 실행합니다.\n\n```bash\ncargo run\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새 콘솔 프로젝트를 만들고 다음을 추가합니다 `Program.cs`.\n\n```csharp\nusing GitHub.Copilot;\n\nawait using var client = new CopilotClient();\nawait using var session = await client.CreateSessionAsync(new SessionConfig\n{\n    Model = \"auto\",\n    OnPermissionRequest = PermissionHandler.ApproveAll\n});\n\nvar response = await session.SendAndWaitAsync(new MessageOptions { Prompt = \"What is 2 + 2?\" });\nConsole.WriteLine(response?.Data.Content);\n```\n\n다음을 실행합니다.\n\n```bash\ndotnet run\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`HelloCopilot.java`을 만듭니다.\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\npublic class HelloCopilot {\n    public static void main(String[] args) throws Exception {\n        try (var client = new CopilotClient()) {\n            client.start().get();\n\n            var session = client.createSession(\n                new SessionConfig()\n                    .setModel(\"auto\")\n                    .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n            ).get();\n\n            var response = session.sendAndWait(\n                new MessageOptions().setPrompt(\"What is 2 + 2?\")\n            ).get();\n\n            System.out.println(response.getData().content());\n\n            client.stop().get();\n        }\n    }\n}\n```\n\n다음을 실행합니다.\n\n```bash\njavac -cp copilot-sdk.jar HelloCopilot.java && java -cp .:copilot-sdk.jar HelloCopilot\n```\n\n</div>\n\n</div>\n\n**다음과 같이 표시됩니다:**\n\n```text\n4\n```\n\n축하합니다! 방금 첫 번째 Copilot 지원 앱을 빌드했습니다.\n\n## 3단계: 스트리밍 응답 추가\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다음과 같이 `index.ts`를 업데이트합니다.\n\n```typescript\nimport { CopilotClient } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient();\nconst session = await client.createSession({\n    model: \"auto\",\n    streaming: true,\n});\n\n// Listen for response chunks\nsession.on(\"assistant.message_delta\", (event) => {\n    process.stdout.write(event.data.deltaContent);\n});\nsession.on(\"session.idle\", () => {\n    console.log(); // New line when done\n});\n\nawait session.sendAndWait({ prompt: \"Tell me a short joke\" });\n\nawait client.stop();\nprocess.exit(0);\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다음과 같이 `main.py`를 업데이트합니다.\n\n```python\nimport asyncio\nimport sys\nfrom copilot import CopilotClient\nfrom copilot.session import PermissionHandler\nfrom copilot.session_events import SessionEventType\n\nasync def main():\n    client = CopilotClient()\n    await client.start()\n\n    session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model=\"auto\", streaming=True)\n\n    # Listen for response chunks\n    def handle_event(event):\n        if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:\n            sys.stdout.write(event.data.delta_content)\n            sys.stdout.flush()\n        if event.type == SessionEventType.SESSION_IDLE:\n            print()  # New line when done\n\n    session.on(handle_event)\n\n    await session.send_and_wait(\"Tell me a short joke\")\n\n    await client.stop()\n\nasyncio.run(main())\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다음과 같이 `main.go`를 업데이트합니다.\n\n```golang\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"os\"\n\n    copilot \"github.com/github/copilot-sdk/go\"\n)\n\nfunc main() {\n    ctx := context.Background()\n    client := copilot.NewClient(nil)\n    if err := client.Start(ctx); err != nil {\n        log.Fatal(err)\n    }\n    defer client.Stop()\n\n    session, err := client.CreateSession(ctx, &copilot.SessionConfig{\n        Model:     \"auto\",\n        Streaming: copilot.Bool(true),\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    // Listen for response chunks\n    session.On(func(event copilot.SessionEvent) {\n        switch d := event.Data.(type) {\n        case *copilot.AssistantMessageDeltaData:\n            fmt.Print(d.DeltaContent)\n        case *copilot.SessionIdleData:\n            _ = d\n            fmt.Println()\n        }\n    })\n\n    _, err = session.SendAndWait(ctx, copilot.MessageOptions{Prompt: \"Tell me a short joke\"})\n    if err != nil {\n        log.Fatal(err)\n    }\n    os.Exit(0)\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"rust\" data-label=\"Rust\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Rust</div>\n\n다음과 같이 `src/main.rs`를 업데이트합니다.\n\n```rust\nuse std::io::{self, Write};\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse github_copilot_sdk::handler::ApproveAllHandler;\nuse github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig};\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let client = Client::start(ClientOptions::default()).await?;\n\n    let mut config = SessionConfig::default();\n    config.streaming = Some(true);\n    let session = client\n        .create_session(config.with_permission_handler(Arc::new(ApproveAllHandler)))\n        .await?;\n\n    // Listen for response chunks\n    let mut events = session.subscribe();\n    tokio::spawn(async move {\n        while let Ok(event) = events.recv().await {\n            match event.event_type.as_str() {\n                \"assistant.message_delta\" => {\n                    if let Some(text) =\n                        event.data.get(\"deltaContent\").and_then(|value| value.as_str())\n                    {\n                        print!(\"{text}\");\n                        io::stdout().flush().ok();\n                    }\n                }\n                \"assistant.message\" => println!(),\n                _ => {}\n            }\n        }\n    });\n\n    session\n        .send_and_wait(\n            MessageOptions::new(\"Tell me a short joke\")\n                .with_wait_timeout(Duration::from_secs(120)),\n        )\n        .await?;\n\n    session.disconnect().await?;\n    client.stop().await?;\n    Ok(())\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다음과 같이 `Program.cs`를 업데이트합니다.\n\n```csharp\nusing GitHub.Copilot;\n\nawait using var client = new CopilotClient();\nawait using var session = await client.CreateSessionAsync(new SessionConfig\n{\n    Model = \"auto\",\n    OnPermissionRequest = PermissionHandler.ApproveAll,\n    Streaming = true,\n});\n\n// Listen for response chunks\nsession.On<SessionEvent>(ev =>\n{\n    if (ev is AssistantMessageDeltaEvent deltaEvent)\n    {\n        Console.Write(deltaEvent.Data.DeltaContent);\n    }\n    if (ev is SessionIdleEvent)\n    {\n        Console.WriteLine();\n    }\n});\n\nawait session.SendAndWaitAsync(new MessageOptions { Prompt = \"Tell me a short joke\" });\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다음과 같이 `HelloCopilot.java`를 업데이트합니다.\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\npublic class HelloCopilot {\n    public static void main(String[] args) throws Exception {\n        try (var client = new CopilotClient()) {\n            client.start().get();\n\n            var session = client.createSession(\n                new SessionConfig()\n                    .setModel(\"auto\")\n                    .setStreaming(true)\n                    .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n            ).get();\n\n            // Listen for response chunks\n            session.on(AssistantMessageDeltaEvent.class, delta -> {\n                System.out.print(delta.getData().deltaContent());\n            });\n            session.on(SessionIdleEvent.class, idle -> {\n                System.out.println(); // New line when done\n            });\n\n            session.sendAndWait(\n                new MessageOptions().setPrompt(\"Tell me a short joke\")\n            ).get();\n\n            client.stop().get();\n        }\n    }\n}\n```\n\n</div>\n\n</div>\n\n코드를 다시 실행합니다. 응답이 단어별로 표시되는 것을 볼 수 있습니다.\n\n### 이벤트 구독 방법\n\nSDK는 세션 이벤트를 구독하는 메서드를 제공합니다.\n\n| Method                   | Description                                                  |\n| ------------------------ | ------------------------------------------------------------ |\n| `on(handler)`            | 모든 이벤트를 구독합니다. 는 구독 취소 함수를 반환합니다.                            |\n| `on(eventType, handler)` | 특정 이벤트 유형(Node.js/TypeScript만 해당)을 구독합니다. 는 구독 취소 함수를 반환합니다. |\n| `subscribe()`            | 모든 이벤트를 구독(Rust); `event_type` 기준으로 필터링                      |\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\n// Subscribe to all events\nconst unsubscribeAll = session.on((event) => {\n    console.log(\"Event:\", event.type);\n});\n\n// Subscribe to specific event type\nconst unsubscribeIdle = session.on(\"session.idle\", (event) => {\n    console.log(\"Session is idle\");\n});\n\n// Later, to unsubscribe:\nunsubscribeAll();\nunsubscribeIdle();\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\n# Subscribe to all events\nunsubscribe = session.on(lambda event: print(f\"Event: {event.type}\"))\n\n# Filter by event type in your handler\ndef handle_event(event):\n    if event.type == SessionEventType.SESSION_IDLE:\n        print(\"Session is idle\")\n    elif event.type == SessionEventType.ASSISTANT_MESSAGE:\n        print(f\"Message: {event.data.content}\")\n\nunsubscribe = session.on(handle_event)\n\n# Later, to unsubscribe:\nunsubscribe()\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\n// Subscribe to all events\nunsubscribe := session.On(func(event copilot.SessionEvent) {\n    fmt.Println(\"Event:\", event.Type)\n})\n\n// Filter by event type in your handler\nsession.On(func(event copilot.SessionEvent) {\n    switch d := event.Data.(type) {\n    case *copilot.SessionIdleData:\n        _ = d\n        fmt.Println(\"Session is idle\")\n    case *copilot.AssistantMessageData:\n        fmt.Println(\"Message:\", d.Content)\n    }\n})\n\n// Later, to unsubscribe:\nunsubscribe()\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"rust\" data-label=\"Rust\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Rust</div>\n\n```rust\nlet mut events = session.subscribe();\n\ntokio::spawn(async move {\n    while let Ok(event) = events.recv().await {\n        println!(\"Event: {}\", event.event_type);\n\n        match event.event_type.as_str() {\n            \"session.idle\" => println!(\"Session is idle\"),\n            \"assistant.message\" => {\n                if let Some(content) = event.data.get(\"content\").and_then(|value| value.as_str()) {\n                    println!(\"Message: {content}\");\n                }\n            }\n            _ => {}\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\n// Subscribe to all events\nvar unsubscribe = session.On<SessionEvent>(ev => Console.WriteLine($\"Event: {ev.Type}\"));\n\n// Filter by event type using pattern matching\nsession.On<SessionEvent>(ev =>\n{\n    switch (ev)\n    {\n        case SessionIdleEvent:\n            Console.WriteLine(\"Session is idle\");\n            break;\n        case AssistantMessageEvent msg:\n            Console.WriteLine($\"Message: {msg.Data.Content}\");\n            break;\n    }\n});\n\n// Later, to unsubscribe:\nunsubscribe.Dispose();\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<!-- docs-validate: skip -->\n\n```java\n// Subscribe to all events\nvar unsubscribe = session.on(event -> {\n    System.out.println(\"Event: \" + event.getType());\n});\n\n// Subscribe to a specific event type\nsession.on(AssistantMessageEvent.class, msg -> {\n    System.out.println(\"Message: \" + msg.getData().content());\n});\n\nsession.on(SessionIdleEvent.class, idle -> {\n    System.out.println(\"Session is idle\");\n});\n\n// Later, to unsubscribe:\nunsubscribe.close();\n```\n\n</div>\n\n</div>\n\n## 4단계: 사용자 지정 도구 추가\n\n이제 강력한 기능을 살펴보겠습니다. Copilot 사용자 지정 도구를 정의하여 코드를 호출할 수 있는 기능을 제공해 보겠습니다. 간단한 날씨 조회 도구를 만듭니다.\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다음과 같이 `index.ts`를 업데이트합니다.\n\n```typescript\nimport { CopilotClient, defineTool } from \"@github/copilot-sdk\";\n\n// Define a tool that Copilot can call\nconst getWeather = defineTool(\"get_weather\", {\n    description: \"Get the current weather for a city\",\n    parameters: {\n        type: \"object\",\n        properties: {\n            city: { type: \"string\", description: \"The city name\" },\n        },\n        required: [\"city\"],\n    },\n    handler: async (args: { city: string }) => {\n        const { city } = args;\n        // In a real app, you'd call a weather API here\n        const conditions = [\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"];\n        const temp = Math.floor(Math.random() * 30) + 50;\n        const condition = conditions[Math.floor(Math.random() * conditions.length)];\n        return { city, temperature: `${temp}°F`, condition };\n    },\n});\n\nconst client = new CopilotClient();\nconst session = await client.createSession({\n    model: \"auto\",\n    streaming: true,\n    tools: [getWeather],\n});\n\nsession.on(\"assistant.message_delta\", (event) => {\n    process.stdout.write(event.data.deltaContent);\n});\n\nsession.on(\"session.idle\", () => {\n    console.log(); // New line when done\n});\n\nawait session.sendAndWait({\n    prompt: \"What's the weather like in Seattle and Tokyo?\",\n});\n\nawait client.stop();\nprocess.exit(0);\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다음과 같이 `main.py`를 업데이트합니다.\n\n```python\nimport asyncio\nimport random\nimport sys\nfrom copilot import CopilotClient\nfrom copilot.session import PermissionHandler\nfrom copilot.tools import define_tool\nfrom copilot.session_events import SessionEventType\nfrom pydantic import BaseModel, Field\n\n# Define the parameters for the tool using Pydantic\nclass GetWeatherParams(BaseModel):\n    city: str = Field(description=\"The name of the city to get weather for\")\n\n# Define a tool that Copilot can call\n@define_tool(description=\"Get the current weather for a city\")\nasync def get_weather(params: GetWeatherParams) -> dict:\n    city = params.city\n    # In a real app, you'd call a weather API here\n    conditions = [\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"]\n    temp = random.randint(50, 80)\n    condition = random.choice(conditions)\n    return {\"city\": city, \"temperature\": f\"{temp}°F\", \"condition\": condition}\n\nasync def main():\n    client = CopilotClient()\n    await client.start()\n\n    session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model=\"auto\", streaming=True, tools=[get_weather])\n\n    def handle_event(event):\n        if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:\n            sys.stdout.write(event.data.delta_content)\n            sys.stdout.flush()\n        if event.type == SessionEventType.SESSION_IDLE:\n            print()\n\n    session.on(handle_event)\n\n    await session.send_and_wait(\"What's the weather like in Seattle and Tokyo?\")\n\n    await client.stop()\n\nasyncio.run(main())\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다음과 같이 `main.go`를 업데이트합니다.\n\n```golang\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"math/rand\"\n    \"os\"\n\n    copilot \"github.com/github/copilot-sdk/go\"\n)\n\n// Define the parameter type\ntype WeatherParams struct {\n    City string `json:\"city\" jsonschema:\"The city name\"`\n}\n\n// Define the return type\ntype WeatherResult struct {\n    City        string `json:\"city\"`\n    Temperature string `json:\"temperature\"`\n    Condition   string `json:\"condition\"`\n}\n\nfunc main() {\n    ctx := context.Background()\n\n    // Define a tool that Copilot can call\n    getWeather := copilot.DefineTool(\n        \"get_weather\",\n        \"Get the current weather for a city\",\n        func(params WeatherParams, inv copilot.ToolInvocation) (WeatherResult, error) {\n            // In a real app, you'd call a weather API here\n            conditions := []string{\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"}\n            temp := rand.Intn(30) + 50\n            condition := conditions[rand.Intn(len(conditions))]\n            return WeatherResult{\n                City:        params.City,\n                Temperature: fmt.Sprintf(\"%d°F\", temp),\n                Condition:   condition,\n            }, nil\n        },\n    )\n\n    client := copilot.NewClient(nil)\n    if err := client.Start(ctx); err != nil {\n        log.Fatal(err)\n    }\n    defer client.Stop()\n\n    session, err := client.CreateSession(ctx, &copilot.SessionConfig{\n        Model:     \"auto\",\n        Streaming: copilot.Bool(true),\n        Tools:     []copilot.Tool{getWeather},\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    session.On(func(event copilot.SessionEvent) {\n        switch d := event.Data.(type) {\n        case *copilot.AssistantMessageDeltaData:\n            fmt.Print(d.DeltaContent)\n        case *copilot.SessionIdleData:\n            _ = d\n            fmt.Println()\n        }\n    })\n\n    _, err = session.SendAndWait(ctx, copilot.MessageOptions{\n        Prompt: \"What's the weather like in Seattle and Tokyo?\",\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n    os.Exit(0)\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"rust\" data-label=\"Rust\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Rust</div>\n\n다음과 같이 `src/main.rs`를 업데이트합니다.\n\n```rust\nuse std::io::{self, Write};\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse github_copilot_sdk::handler::ApproveAllHandler;\nuse github_copilot_sdk::tool::{define_tool, JsonSchema};\nuse github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig, ToolResult};\nuse serde::Deserialize;\n\n#[derive(Deserialize, JsonSchema)]\nstruct GetWeatherParams {\n    city: String,\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    // Define a tool that Copilot can call\n    let tools = vec![define_tool(\n        \"get_weather\",\n        \"Get the current weather for a city\",\n        |_inv, params: GetWeatherParams| async move {\n            Ok(ToolResult::Text(format!(\n                \"{}: 62°F and sunny\",\n                params.city\n            )))\n        },\n    )];\n\n    let client = Client::start(ClientOptions::default()).await?;\n\n    let mut config = SessionConfig::default();\n    config.streaming = Some(true);\n    let session = client\n        .create_session(\n            config\n                .with_tools(tools)\n                .with_permission_handler(Arc::new(ApproveAllHandler)),\n        )\n        .await?;\n\n    let mut events = session.subscribe();\n    tokio::spawn(async move {\n        while let Ok(event) = events.recv().await {\n            match event.event_type.as_str() {\n                \"assistant.message_delta\" => {\n                    if let Some(text) =\n                        event.data.get(\"deltaContent\").and_then(|value| value.as_str())\n                    {\n                        print!(\"{text}\");\n                        io::stdout().flush().ok();\n                    }\n                }\n                \"assistant.message\" => println!(),\n                _ => {}\n            }\n        }\n    });\n\n    session\n        .send_and_wait(\n            MessageOptions::new(\"What's the weather like in Seattle and Tokyo?\")\n                .with_wait_timeout(Duration::from_secs(120)),\n        )\n        .await?;\n\n    session.disconnect().await?;\n    client.stop().await?;\n    Ok(())\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다음과 같이 `Program.cs`를 업데이트합니다.\n\n```csharp\nusing GitHub.Copilot;\nusing Microsoft.Extensions.AI;\nusing System.ComponentModel;\n\nawait using var client = new CopilotClient();\n\n// Define a tool that Copilot can call\nvar getWeather = CopilotTool.DefineTool(\n    ([Description(\"The city name\")] string city) =>\n    {\n        // In a real app, you'd call a weather API here\n        var conditions = new[] { \"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\" };\n        var temp = Random.Shared.Next(50, 80);\n        var condition = conditions[Random.Shared.Next(conditions.Length)];\n        return new { city, temperature = $\"{temp}°F\", condition };\n    },\n    factoryOptions: new AIFunctionFactoryOptions\n    {\n        Name = \"get_weather\",\n        Description = \"Get the current weather for a city\",\n    }\n);\n\nawait using var session = await client.CreateSessionAsync(new SessionConfig\n{\n    Model = \"auto\",\n    OnPermissionRequest = PermissionHandler.ApproveAll,\n    Streaming = true,\n    Tools = [getWeather],\n});\n\nsession.On<SessionEvent>(ev =>\n{\n    if (ev is AssistantMessageDeltaEvent deltaEvent)\n    {\n        Console.Write(deltaEvent.Data.DeltaContent);\n    }\n    if (ev is SessionIdleEvent)\n    {\n        Console.WriteLine();\n    }\n});\n\nawait session.SendAndWaitAsync(new MessageOptions\n{\n    Prompt = \"What's the weather like in Seattle and Tokyo?\",\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다음과 같이 `HelloCopilot.java`를 업데이트합니다.\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.concurrent.CompletableFuture;\n\npublic class HelloCopilot {\n    public static void main(String[] args) throws Exception {\n        var random = new Random();\n        var conditions = List.of(\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\");\n\n        // Define a tool that Copilot can call\n        var getWeather = ToolDefinition.create(\n            \"get_weather\",\n            \"Get the current weather for a city\",\n            Map.of(\n                \"type\", \"object\",\n                \"properties\", Map.of(\n                    \"city\", Map.of(\"type\", \"string\", \"description\", \"The city name\")\n                ),\n                \"required\", List.of(\"city\")\n            ),\n            invocation -> {\n                var city = (String) invocation.getArguments().get(\"city\");\n                var temp = random.nextInt(30) + 50;\n                var condition = conditions.get(random.nextInt(conditions.size()));\n                return CompletableFuture.completedFuture(Map.of(\n                    \"city\", city,\n                    \"temperature\", temp + \"°F\",\n                    \"condition\", condition\n                ));\n            }\n        );\n\n        try (var client = new CopilotClient()) {\n            client.start().get();\n\n            var session = client.createSession(\n                new SessionConfig()\n                    .setModel(\"auto\")\n                    .setStreaming(true)\n                    .setTools(List.of(getWeather))\n                    .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n            ).get();\n\n            session.on(AssistantMessageDeltaEvent.class, delta -> {\n                System.out.print(delta.getData().deltaContent());\n            });\n            session.on(SessionIdleEvent.class, idle -> {\n                System.out.println();\n            });\n\n            session.sendAndWait(\n                new MessageOptions().setPrompt(\"What's the weather like in Seattle and Tokyo?\")\n            ).get();\n\n            client.stop().get();\n        }\n    }\n}\n```\n\n</div>\n\n</div>\n\n실행하면 Copilot 도구를 호출하여 날씨 데이터를 가져와서 결과로 응답하는 것을 볼 수 있습니다.\n\n## 5단계: 대화형 도우미 빌드\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 { CopilotClient, defineTool } from \"@github/copilot-sdk\";\nimport * as readline from \"readline\";\n\nconst getWeather = defineTool(\"get_weather\", {\n    description: \"Get the current weather for a city\",\n    parameters: {\n        type: \"object\",\n        properties: {\n            city: { type: \"string\", description: \"The city name\" },\n        },\n        required: [\"city\"],\n    },\n    handler: async ({ city }) => {\n        const conditions = [\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"];\n        const temp = Math.floor(Math.random() * 30) + 50;\n        const condition = conditions[Math.floor(Math.random() * conditions.length)];\n        return { city, temperature: `${temp}°F`, condition };\n    },\n});\n\nconst client = new CopilotClient();\nconst session = await client.createSession({\n    model: \"auto\",\n    streaming: true,\n    tools: [getWeather],\n});\n\nsession.on(\"assistant.message_delta\", (event) => {\n    process.stdout.write(event.data.deltaContent);\n});\n\nconst rl = readline.createInterface({\n    input: process.stdin,\n    output: process.stdout,\n});\n\nconsole.log(\"🌤️  Weather Assistant (type 'exit' to quit)\");\nconsole.log(\"   Try: 'What's the weather in Paris?'\\n\");\n\nconst prompt = () => {\n    rl.question(\"You: \", async (input) => {\n        if (input.toLowerCase() === \"exit\") {\n            await client.stop();\n            rl.close();\n            return;\n        }\n\n        process.stdout.write(\"Assistant: \");\n        await session.sendAndWait({ prompt: input });\n        console.log(\"\\n\");\n        prompt();\n    });\n};\n\nprompt();\n```\n\n다음을 사용하여 실행합니다.\n\n```bash\nnpx tsx weather-assistant.ts\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`weather_assistant.py`을 만듭니다.\n\n```python\nimport asyncio\nimport random\nimport sys\nfrom copilot import CopilotClient\nfrom copilot.session import PermissionHandler\nfrom copilot.tools import define_tool\nfrom copilot.session_events import SessionEventType\nfrom pydantic import BaseModel, Field\n\nclass GetWeatherParams(BaseModel):\n    city: str = Field(description=\"The name of the city to get weather for\")\n\n@define_tool(description=\"Get the current weather for a city\")\nasync def get_weather(params: GetWeatherParams) -> dict:\n    city = params.city\n    conditions = [\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"]\n    temp = random.randint(50, 80)\n    condition = random.choice(conditions)\n    return {\"city\": city, \"temperature\": f\"{temp}°F\", \"condition\": condition}\n\nasync def main():\n    client = CopilotClient()\n    await client.start()\n\n    session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model=\"auto\", streaming=True, tools=[get_weather])\n\n    def handle_event(event):\n        if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:\n            sys.stdout.write(event.data.delta_content)\n            sys.stdout.flush()\n\n    session.on(handle_event)\n\n    print(\"🌤️  Weather Assistant (type 'exit' to quit)\")\n    print(\"   Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\\n\")\n\n    while True:\n        try:\n            user_input = input(\"You: \")\n        except EOFError:\n            break\n\n        if user_input.lower() == \"exit\":\n            break\n\n        sys.stdout.write(\"Assistant: \")\n        await session.send_and_wait(user_input)\n        print(\"\\n\")\n\n    await client.stop()\n\nasyncio.run(main())\n```\n\n다음을 사용하여 실행합니다.\n\n```bash\npython weather_assistant.py\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`weather-assistant.go`을 만듭니다.\n\n```golang\npackage main\n\nimport (\n    \"bufio\"\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"math/rand\"\n    \"os\"\n    \"strings\"\n\n    copilot \"github.com/github/copilot-sdk/go\"\n)\n\ntype WeatherParams struct {\n    City string `json:\"city\" jsonschema:\"The city name\"`\n}\n\ntype WeatherResult struct {\n    City        string `json:\"city\"`\n    Temperature string `json:\"temperature\"`\n    Condition   string `json:\"condition\"`\n}\n\nfunc main() {\n    ctx := context.Background()\n\n    getWeather := copilot.DefineTool(\n        \"get_weather\",\n        \"Get the current weather for a city\",\n        func(params WeatherParams, inv copilot.ToolInvocation) (WeatherResult, error) {\n            conditions := []string{\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"}\n            temp := rand.Intn(30) + 50\n            condition := conditions[rand.Intn(len(conditions))]\n            return WeatherResult{\n                City:        params.City,\n                Temperature: fmt.Sprintf(\"%d°F\", temp),\n                Condition:   condition,\n            }, nil\n        },\n    )\n\n    client := copilot.NewClient(nil)\n    if err := client.Start(ctx); err != nil {\n        log.Fatal(err)\n    }\n    defer client.Stop()\n\n    session, err := client.CreateSession(ctx, &copilot.SessionConfig{\n        Model:     \"auto\",\n        Streaming: copilot.Bool(true),\n        Tools:     []copilot.Tool{getWeather},\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    session.On(func(event copilot.SessionEvent) {\n        switch d := event.Data.(type) {\n        case *copilot.AssistantMessageDeltaData:\n            fmt.Print(d.DeltaContent)\n        case *copilot.SessionIdleData:\n            _ = d\n            fmt.Println()\n        }\n    })\n\n    fmt.Println(\"🌤️  Weather Assistant (type 'exit' to quit)\")\n    fmt.Println(\"   Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\\n\")\n\n    scanner := bufio.NewScanner(os.Stdin)\n    for {\n        fmt.Print(\"You: \")\n        if !scanner.Scan() {\n            break\n        }\n        input := scanner.Text()\n        if strings.ToLower(input) == \"exit\" {\n            break\n        }\n\n        fmt.Print(\"Assistant: \")\n        _, err = session.SendAndWait(ctx, copilot.MessageOptions{Prompt: input})\n        if err != nil {\n            fmt.Fprintf(os.Stderr, \"Error: %v\\n\", err)\n            break\n        }\n        fmt.Println()\n    }\n    if err := scanner.Err(); err != nil {\n        fmt.Fprintf(os.Stderr, \"Input error: %v\\n\", err)\n    }\n}\n```\n\n다음을 사용하여 실행합니다.\n\n```bash\ngo run weather-assistant.go\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"rust\" data-label=\"Rust\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Rust</div>\n\n`src/main.rs`을 만듭니다.\n\n```rust\nuse std::io::{self, BufRead, Write};\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse github_copilot_sdk::handler::ApproveAllHandler;\nuse github_copilot_sdk::tool::{define_tool, JsonSchema};\nuse github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig, ToolResult};\nuse serde::Deserialize;\n\n#[derive(Deserialize, JsonSchema)]\nstruct GetWeatherParams {\n    city: String,\n}\n\nfn read_line() -> Option<String> {\n    let stdin = io::stdin();\n    let mut line = String::new();\n    stdin.lock().read_line(&mut line).ok()?;\n    if line.is_empty() {\n        return None;\n    }\n    Some(line.trim_end_matches(&['\\n', '\\r'][..]).to_string())\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let tools = vec![define_tool(\n        \"get_weather\",\n        \"Get the current weather for a city\",\n        |_inv, params: GetWeatherParams| async move {\n            Ok(ToolResult::Text(format!(\n                \"{}: 62°F and sunny\",\n                params.city\n            )))\n        },\n    )];\n\n    let client = Client::start(ClientOptions::default()).await?;\n\n    let mut config = SessionConfig::default();\n    config.streaming = Some(true);\n    let session = client\n        .create_session(\n            config\n                .with_tools(tools)\n                .with_permission_handler(Arc::new(ApproveAllHandler)),\n        )\n        .await?;\n\n    let mut events = session.subscribe();\n    tokio::spawn(async move {\n        while let Ok(event) = events.recv().await {\n            match event.event_type.as_str() {\n                \"assistant.message_delta\" => {\n                    if let Some(text) =\n                        event.data.get(\"deltaContent\").and_then(|value| value.as_str())\n                    {\n                        print!(\"{text}\");\n                        io::stdout().flush().ok();\n                    }\n                }\n                \"assistant.message\" => println!(),\n                _ => {}\n            }\n        }\n    });\n\n    println!(\"Weather Assistant (type 'exit' to quit)\");\n    println!(\"Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\\n\");\n\n    loop {\n        print!(\"You: \");\n        io::stdout().flush().ok();\n\n        let Some(input) = read_line() else { break };\n        if input.eq_ignore_ascii_case(\"exit\") {\n            break;\n        }\n\n        print!(\"Assistant: \");\n        io::stdout().flush().ok();\n        session\n            .send_and_wait(MessageOptions::new(input).with_wait_timeout(Duration::from_secs(120)))\n            .await?;\n        println!();\n    }\n\n    session.disconnect().await?;\n    client.stop().await?;\n    Ok(())\n}\n```\n\n다음을 사용하여 실행합니다.\n\n```bash\ncargo run\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새 콘솔 프로젝트를 만들고 `Program.cs`을 업데이트합니다.\n\n```csharp\nusing GitHub.Copilot;\nusing Microsoft.Extensions.AI;\nusing System.ComponentModel;\n\n// Define the weather tool\nvar getWeather = CopilotTool.DefineTool(\n    ([Description(\"The city name\")] string city) =>\n    {\n        var conditions = new[] { \"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\" };\n        var temp = Random.Shared.Next(50, 80);\n        var condition = conditions[Random.Shared.Next(conditions.Length)];\n        return new { city, temperature = $\"{temp}°F\", condition };\n    },\n    factoryOptions: new AIFunctionFactoryOptions\n    {\n        Name = \"get_weather\",\n        Description = \"Get the current weather for a city\",\n    });\n\nawait using var client = new CopilotClient();\nawait using var session = await client.CreateSessionAsync(new SessionConfig\n{\n    Model = \"auto\",\n    OnPermissionRequest = PermissionHandler.ApproveAll,\n    Streaming = true,\n    Tools = [getWeather]\n});\n\n// Listen for response chunks\nsession.On<SessionEvent>(ev =>\n{\n    if (ev is AssistantMessageDeltaEvent deltaEvent)\n    {\n        Console.Write(deltaEvent.Data.DeltaContent);\n    }\n    if (ev is SessionIdleEvent)\n    {\n        Console.WriteLine();\n    }\n});\n\nConsole.WriteLine(\"🌤️  Weather Assistant (type 'exit' to quit)\");\nConsole.WriteLine(\"   Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\\n\");\n\nwhile (true)\n{\n    Console.Write(\"You: \");\n    var input = Console.ReadLine();\n\n    if (string.IsNullOrEmpty(input) || input.Equals(\"exit\", StringComparison.OrdinalIgnoreCase))\n    {\n        break;\n    }\n\n    Console.Write(\"Assistant: \");\n    await session.SendAndWaitAsync(new MessageOptions { Prompt = input });\n    Console.WriteLine(\"\\n\");\n}\n```\n\n다음을 사용하여 실행합니다.\n\n```bash\ndotnet run\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`WeatherAssistant.java`을 만듭니다.\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.Scanner;\nimport java.util.concurrent.CompletableFuture;\n\npublic class WeatherAssistant {\n    public static void main(String[] args) throws Exception {\n        var random = new Random();\n        var conditions = List.of(\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\");\n\n        var getWeather = ToolDefinition.create(\n            \"get_weather\",\n            \"Get the current weather for a city\",\n            Map.of(\n                \"type\", \"object\",\n                \"properties\", Map.of(\n                    \"city\", Map.of(\"type\", \"string\", \"description\", \"The city name\")\n                ),\n                \"required\", List.of(\"city\")\n            ),\n            invocation -> {\n                var city = (String) invocation.getArguments().get(\"city\");\n                var temp = random.nextInt(30) + 50;\n                var condition = conditions.get(random.nextInt(conditions.size()));\n                return CompletableFuture.completedFuture(Map.of(\n                    \"city\", city,\n                    \"temperature\", temp + \"°F\",\n                    \"condition\", condition\n                ));\n            }\n        );\n\n        try (var client = new CopilotClient()) {\n            client.start().get();\n\n            var session = client.createSession(\n                new SessionConfig()\n                    .setModel(\"auto\")\n                    .setStreaming(true)\n                    .setOnPermissionRequest(request ->\n                        CompletableFuture.completedFuture(PermissionDecision.allow())\n                    )\n                    .setTools(List.of(getWeather))\n            ).get();\n\n            session.on(AssistantMessageDeltaEvent.class, delta -> {\n                System.out.print(delta.getData().deltaContent());\n            });\n            session.on(SessionIdleEvent.class, idle -> {\n                System.out.println();\n            });\n\n            System.out.println(\"🌤️  Weather Assistant (type 'exit' to quit)\");\n            System.out.println(\"   Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\\n\");\n\n            var scanner = new Scanner(System.in);\n            while (true) {\n                System.out.print(\"You: \");\n                if (!scanner.hasNextLine()) break;\n                var input = scanner.nextLine();\n                if (input.equalsIgnoreCase(\"exit\")) break;\n\n                System.out.print(\"Assistant: \");\n                session.sendAndWait(\n                    new MessageOptions().setPrompt(input)\n                ).get();\n                System.out.println(\"\\n\");\n            }\n\n            client.stop().get();\n        }\n    }\n}\n```\n\n다음을 사용하여 실행합니다.\n\n```bash\njavac -cp copilot-sdk.jar WeatherAssistant.java && java -cp .:copilot-sdk.jar WeatherAssistant\n```\n\n</div>\n\n</div>\n\n**세션 예제:**\n\n```text\n🌤️  Weather Assistant (type 'exit' to quit)\n   Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\n\nYou: What's the weather in Seattle?\nAssistant: Let me check the weather for Seattle...\nIt's currently 62°F and cloudy in Seattle.\n\nYou: How about Tokyo and London?\nAssistant: I'll check both cities for you:\n- Tokyo: 75°F and sunny\n- London: 58°F and rainy\n\nYou: exit\n```\n\nCopilot 호출할 수 있는 사용자 지정 도구를 사용하여 도우미를 빌드했습니다.\n\n## 도구 작동 방식\n\n도구를 정의할 때 Copilot에 다음과 같이 알려주는 것입니다:\n\n1. **도구가 수행하는 작업** (설명)\n2. **필요한 매개 변수** (스키마)\n3. **실행할 코드** (처리기)\n\nCopilot 사용자의 질문에 따라 도구를 호출할 시기를 결정합니다. 이 작업을 수행하는 경우:\n\n1. Copilot 매개 변수를 사용하여 도구 호출 요청을 보냅니다.\n2. SDK는 처리기 함수를 실행합니다.\n3. 결과는 Copilot 다시 전송됩니다.\n4. Copilot 결과를 응답에 통합합니다.\n\n## 다음 단계는 무엇인가요?\n\n이제 기본 사항을 살펴보았으므로 다음과 같은 더 강력한 기능을 살펴볼 수 있습니다.\n\n### MCP 서버에 연결\n\nMCP(모델 컨텍스트 프로토콜) 서버는 미리 빌드된 도구를 제공합니다. GitHub MCP 서버에 연결하여 리포지토리, 문제 및 끌어오기 요청에 Copilot 액세스 권한을 부여합니다.\n\n```typescript\nconst session = await client.createSession({\n    mcpServers: {\n        github: {\n            type: \"http\",\n            url: \"https://api.githubcopilot.com/mcp/\",\n        },\n    },\n});\n```\n\n📖\n\\*\\*\n[GitHub Copilot SDK에서 MCP 서버 사용](/ko/copilot/how-tos/copilot-sdk/features/mcp)\\*\\* - 로컬 서버와 원격 서버, 모든 구성 옵션 및 문제 해결에 대해 알아봅니다.\n\n### 사용자 지정 에이전트 만들기\n\n특정 작업에 대한 특수한 AI 페르소나를 정의합니다.\n\n```typescript\nconst session = await client.createSession({\n    customAgents: [{\n        name: \"pr-reviewer\",\n        displayName: \"PR Reviewer\",\n        description: \"Reviews pull requests for best practices\",\n        prompt: \"You are an expert code reviewer. Focus on security, performance, and maintainability.\",\n    }],\n});\n```\n\n> \\[!TIP]\n> 세션 구성에서 이 에이전트를 처음부터 미리 선택하도록 설정할 `agent: \"pr-reviewer\"` 수도 있습니다. 자세한 내용은 [사용자 정의 에이전트 및 하위 에이전트 오케스트레이션](/ko/copilot/how-tos/copilot-sdk/features/custom-agents#selecting-an-agent-at-session-creation) 을 참조하세요.\n\n### 시스템 메시지 사용자 지정\n\n지침을 추가하여 AI의 동작 및 성격을 제어합니다.\n\n```typescript\nconst session = await client.createSession({\n    systemMessage: {\n        content: \"You are a helpful assistant for our engineering team. Always be concise.\",\n    },\n});\n```\n\n더 세밀하게 제어하려면 나머지 부분은 유지한 채 시스템 프롬프트의 개별 섹션을 재정의할 수 있도록 `mode: \"customize\"`을 사용하세요.\n\n```typescript\nconst session = await client.createSession({\n    systemMessage: {\n        mode: \"customize\",\n        sections: {\n            tone: { action: \"replace\", content: \"Respond in a warm, professional tone. Be thorough in explanations.\" },\n            code_change_rules: { action: \"remove\" },\n            guidelines: { action: \"append\", content: \"\\n* Always cite data sources\" },\n        },\n        content: \"Focus on financial analysis and reporting.\",\n    },\n});\n```\n\n사용 가능한 섹션 ID: `preamble`,, `identity`,`tone``tool_efficiency`, `environment_context`, `code_change_rules``guidelines`, `tool_instructions``custom_instructions``safety`, . `runtime_instructions``last_instructions`\n\n`identity` 및 `tool_instructions` 섹션 *그룹*입니다. 관련 하위 섹션의 컬렉션을 단위로 대상으로 합니다.\n`preamble`를 사용하면 동일 수준의 하위 섹션에 영향을 주지 않고 ID 프리앰블만 지정할 수 있습니다.\n\n각 재정의는 다음 5개 작업을 지원합니다: `replace`, `remove`, `prepend`, `append`, `preserve`.\n`preserve` 작업은 아무 작업도 수행하지 않는 no-op으로, 개별적으로 주소 지정 가능한 섹션을 그룹 수준의 `remove` 대상에서 제외합니다(예: `tone` 그룹을 제거할 때 `identity`는 유지). 알 수 없는 섹션 ID는 문제없이 처리됩니다. `replace`//`append``prepend` 재정의의 내용은 추가 지침에 덧붙여지며, `remove` 재정의는 별도 알림 없이 무시됩니다.\n\n[TypeScript](https://github.com/github/copilot-sdk/tree/main/nodejs/README.md) 예제는 언어별 SDK README를 참조하세요. [Python](https://github.com/github/copilot-sdk/tree/main/python/README.md), [Go](https://github.com/github/copilot-sdk/tree/main/go/README.md), [Rust](https://github.com/github/copilot-sdk/tree/main/rust/README.md), [Java](https://github.com/github/copilot-sdk/tree/main/java/README.md) 및 [C#](https://github.com/github/copilot-sdk/tree/main/dotnet/README.md).\n\n## 외부 CLI 서버에 연결\n\n기본적으로 SDK는 필요에 따라 CLI를 시작하고 중지하여 Copilot CLI 프로세스 수명 주기를 자동으로 관리합니다. 그러나 서버 모드에서 별도로 CLI를 실행하고 SDK를 연결할 수도 있습니다. 이 기능은 다음과 같은 경우에 유용할 수 있습니다.\n\n* **디버깅**: 로그를 검사하기 위해 SDK 다시 시작 간에 CLI를 계속 실행합니다.\n* **리소스 공유**: 여러 SDK 클라이언트가 동일한 CLI 서버에 연결할 수 있습니다.\n* **개발**: 사용자 지정 설정 또는 다른 환경에서 CLI 실행\n\n### 서버 모드에서 CLI 실행\n\n플래그를 사용하여 서버 모드에서 CLI를 `--headless` 시작하고 필요에 따라 포트를 지정합니다.\n\n```bash\ncopilot --headless --port 4321\n```\n\n포트를 지정하지 않으면 CLI에서 사용 가능한 임의 포트를 선택합니다.\n\n기본적으로 헤드리스 서버는 루프백(`127.0.0.1`)의 연결만 허용하므로 SDK는 동일한 컴퓨터에서 실행되어야 합니다. 다른 호스트(예: 컨테이너 또는 별도의 서버에서 CLI를 실행하는 경우)의 연결을 허용하려면 다음을 사용하여 루프백이 아닌 주소 `--host`에 바인딩합니다.\n\n```bash\n# Listen on all interfaces\ncopilot --headless --host 0.0.0.0 --port 4321\n```\n\n> \\[!WARNING]\n> 비 루프백 주소에 헤드리스 서버를 노출하면 해당 주소로 라우팅할 수 있는 모든 사용자가 연결할 수 있습니다. 네트워크 컨트롤(방화벽, 프라이빗 네트워크, 역방향 프록시) 및 사용자 환경에 적합한 인증과 페어링합니다.\n\n### 외부 서버에 SDK 연결\n\nCLI가 서버 모드에서 실행되면 \"cli url\" 옵션을 사용하여 연결하도록 SDK 클라이언트를 구성합니다.\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 { CopilotClient, approveAll } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient({\n    cliUrl: \"localhost:4321\"\n});\n\n// Use the client normally\nconst session = await client.createSession({ onPermissionRequest: approveAll });\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 import CopilotClient, RuntimeConnection\nfrom copilot.session import PermissionHandler\n\nclient = CopilotClient(connection=RuntimeConnection.for_uri(\"localhost:4321\"))\nawait client.start()\n\n# Use the client normally\nsession = await client.create_session(on_permission_request=PermissionHandler.approve_all)\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\nimport copilot \"github.com/github/copilot-sdk/go\"\n\nclient := copilot.NewClient(&copilot.ClientOptions{\n    Connection: copilot.URIConnection{URL: \"localhost:4321\"},\n})\n\nif err := client.Start(ctx); err != nil {\n    log.Fatal(err)\n}\ndefer client.Stop()\n\n// Use the client normally\nsession, err := client.CreateSession(ctx, &copilot.SessionConfig{\n    OnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n})\n// ...\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"rust\" data-label=\"Rust\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Rust</div>\n\n```rust\nuse std::sync::Arc;\n\nuse github_copilot_sdk::handler::ApproveAllHandler;\nuse github_copilot_sdk::{Client, ClientOptions, SessionConfig, Transport};\n\nlet mut options = ClientOptions::default();\noptions.transport = Transport::External {\n    host: \"localhost\".to_string(),\n    port: 4321,\n    connection_token: None,\n};\nlet client = Client::start(options).await?;\n\n// Use the client normally\nlet session = client\n    .create_session(SessionConfig::default().with_permission_handler(Arc::new(ApproveAllHandler)))\n    .await?;\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\nusing var client = new CopilotClient(new CopilotClientOptions\n{\n    Connection = RuntimeConnection.ForUri(\"localhost:4321\"),\n});\n\n// Use the client normally\nawait using var session = await client.CreateSessionAsync(new()\n{\n    OnPermissionRequest = PermissionHandler.ApproveAll\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.CopilotClient;\nimport com.github.copilot.rpc.*;\n\nvar client = new CopilotClient(\n    new CopilotClientOptions().setCliUrl(\"localhost:4321\")\n);\nclient.start().get();\n\n// Use the client normally\nvar session = client.createSession(\n    new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n).get();\n// ...\n```\n\n</div>\n\n</div>\n\n**참고:** /Go가 `cli_url` 제공되거나 Rust가 사용되는  / 경우`cliUrl``URIConnection``Transport::External`SDK는 CLI 프로세스를 생성하거나 관리하지 않습니다. 지정된 URL의 기존 서버에만 연결됩니다.\n\n## 원격 분석 및 관찰 가능성\n\nCopilot SDK는 분산 추적을 위해 [OpenTelemetry](https://opentelemetry.io/) 지원합니다.\n`telemetry` CLI 프로세스에서 추적 내보내기 및 SDK와 CLI 간의 자동 [W3C 추적 컨텍스트](https://www.w3.org/TR/trace-context/) 전파를 사용하도록 클라이언트에 구성을 제공합니다.\n\n### 원격 분석 사용\n\n클라이언트를 생성할 때 `telemetry`(또는 `Telemetry`) 구성 객체를 전달하세요. 이는 옵트인입니다. 별도의 \"사용\" 플래그가 필요하지 않습니다.\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<!-- docs-validate: skip -->\n\n```typescript\nimport { CopilotClient } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient({\n  telemetry: {\n    otlpEndpoint: \"http://localhost:4318\",\n  },\n});\n```\n\n선택적 피어 종속성: `@opentelemetry/api`\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<!-- docs-validate: skip -->\n\n```python\nfrom copilot import CopilotClient, CopilotClientOptions\n\nclient = CopilotClient(CopilotClientOptions(\n    telemetry={\n        \"otlp_endpoint\": \"http://localhost:4318\",\n    },\n))\n```\n\n원격 분석 추가 기능을 포함하여 설치: `pip install copilot-sdk[telemetry]` (`opentelemetry-api` 제공)\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<!-- docs-validate: skip -->\n\n```golang\nclient := copilot.NewClient(&copilot.ClientOptions{\n    Telemetry: &copilot.TelemetryConfig{\n        OTLPEndpoint: \"http://localhost:4318\",\n    },\n})\n```\n\n종속성: `go.opentelemetry.io/otel`\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"rust\" data-label=\"Rust\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Rust</div>\n\n<!-- docs-validate: skip -->\n\n```rust\nuse github_copilot_sdk::{Client, ClientOptions, OtelExporterType, TelemetryConfig};\n\nlet mut options = ClientOptions::default();\noptions.telemetry = Some(\n    TelemetryConfig::new()\n        .with_exporter_type(OtelExporterType::OtlpHttp)\n        .with_otlp_endpoint(\"http://localhost:4318\"),\n);\nlet client = Client::start(options).await?;\n```\n\n추가 종속성이 없습니다. SDK는 생성된 CLI 프로세스에 대한 원격 분석 환경 변수를 삽입합니다.\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<!-- docs-validate: skip -->\n\n```csharp\nvar client = new CopilotClient(new CopilotClientOptions\n{\n    Telemetry = new TelemetryConfig\n    {\n        OtlpEndpoint = \"http://localhost:4318\",\n    },\n});\n```\n\n추가 종속성이 없습니다. 기본 제공 `System.Diagnostics.Activity`을 사용합니다.\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<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\nvar client = new CopilotClient(new CopilotClientOptions()\n    .setTelemetry(new TelemetryConfig()\n        .setOtlpEndpoint(\"http://localhost:4318\")));\n```\n\n종속성: `io.opentelemetry:opentelemetry-api`\n\n</div>\n\n</div>\n\n### 'TelemetryConfig' 설정 옵션\n\n| Option                    | Node.js          | Python            | Go               | 러스트               | Java             | .NET             | Description                                                  |\n| ------------------------- | ---------------- | ----------------- | ---------------- | ----------------- | ---------------- | ---------------- | ------------------------------------------------------------ |\n| OTLP 엔드포인트                | `otlpEndpoint`   | `otlp_endpoint`   | `OTLPEndpoint`   | `otlp_endpoint`   | `otlpEndpoint`   | `OtlpEndpoint`   | OTLP HTTP 엔드포인트 URL                                          |\n| OTLP 프로토콜                 | `otlpProtocol`   | `otlp_protocol`   | `OTLPProtocol`   | `otlp_protocol`   | `otlpProtocol`   | `OtlpProtocol`   | 모든 신호에 대한 OTLP HTTP 프로토콜: `\"http/json\"` 또는 `\"http/protobuf\"` |\n| 파일 경로                     | `filePath`       | `file_path`       | `FilePath`       | `file_path`       | `filePath`       | `FilePath`       | JSON 줄 추적 출력에 대한 파일 경로                                       |\n| 내보내기 형식                   | `exporterType`   | `exporter_type`   | `ExporterType`   | `exporter_type`   | `exporterType`   | `ExporterType`   |                                                              |\n| `\"otlp-http\"` 또는 `\"file\"` |                  |                   |                  |                   |                  |                  |                                                              |\n| 원본 이름                     | `sourceName`     | `source_name`     | `SourceName`     | `source_name`     | `sourceName`     | `SourceName`     | 계측 범위 이름                                                     |\n| 콘텐츠 캡처                    | `captureContent` | `capture_content` | `CaptureContent` | `capture_content` | `captureContent` | `CaptureContent` | 메시지 콘텐츠를 캡처할지 여부                                             |\n\nOTLP 프로토콜 필드는 모든 신호에 대해 CLI의 `\"otlp-http\"` 내보내기를 구성합니다. CLI 기본값을 사용하도록 설정하지 않은 상태로 두거나 HTTP를 통해 protobuf를 내보내도록 `\"http/protobuf\"` 설정합니다.\n\n### 파일 내보내기\n\nOTLP 엔드포인트 대신 로컬 파일에 추적을 쓰려면 다음을 수행합니다.\n\n<!-- docs-validate: skip -->\n\n```typescript\nconst client = new CopilotClient({\n  telemetry: {\n    filePath: \"./traces.jsonl\",\n    exporterType: \"file\",\n  },\n});\n```\n\n### 추적 컨텍스트 전파\n\n추적 컨텍스트는 자동으로 전파되며 수동 계측이 필요하지 않습니다.\n\n* **SDK → CLI**: 현재 span/activity의 `traceparent` 및 `tracestate` 헤더가 `session.create`, `session.resume`, 그리고 `session.send` RPC 호출에 포함됩니다.\n* **CLI → SDK**: CLI가 도구 처리기를 호출하면 CLI 범위의 추적 컨텍스트가 전파되므로 도구 코드가 올바른 부모 범위에서 실행됩니다.\n\n📖\n\\*\\*\n[코필로트 SDK용 OpenTelemetry 계측](/ko/copilot/how-tos/copilot-sdk/observability/opentelemetry)\\*\\* - TelemetryConfig 옵션, 추적 컨텍스트 전파 및 언어별 종속성입니다.\n\n## 자세히 알아보기\n\n* [인증](/ko/copilot/how-tos/copilot-sdk/auth/authenticate) - GitHub OAuth, 환경 변수 및 BYOK\n* [BYOK(사용자 고유의 키 가져오기)](/ko/copilot/how-tos/copilot-sdk/auth/byok) - Microsoft Foundry, OpenAI 등에서 고유한 API 키를 사용합니다.\n* [Node.js SDK 참조](https://github.com/github/copilot-sdk/tree/main/nodejs/README.md)\n* [Python SDK 참조](https://github.com/github/copilot-sdk/tree/main/python/README.md)\n* [Go SDK 참조](https://github.com/github/copilot-sdk/tree/main/go/README.md)\n* [Rust SDK 참조](https://github.com/github/copilot-sdk/tree/main/rust/README.md)\n* [.NET SDK 참조](https://github.com/github/copilot-sdk/tree/main/dotnet/README.md)\n* [Java SDK 참조](https://github.com/github/copilot-sdk/tree/main/java/README.md)\n* [GitHub Copilot SDK에서 MCP 서버 사용](/ko/copilot/how-tos/copilot-sdk/features/mcp) - 모델 컨텍스트 프로토콜을 통해 외부 도구 통합\n* [GitHub MCP 서버 설명서](https://github.com/github/github-mcp-server)\n* [MCP 서버 디렉터리](https://github.com/modelcontextprotocol/servers) - 더 많은 MCP 서버 탐색\n* [코필로트 SDK용 OpenTelemetry 계측](/ko/copilot/how-tos/copilot-sdk/observability/opentelemetry) - TelemetryConfig, 추적 컨텍스트 전파 및 언어별 종속성\n\n**잘 했어요!** GitHub Copilot SDK의 핵심 개념을 알아보았습니다.\n\n* ✅ 클라이언트 및 세션 만들기\n* ✅ 메시지 보내기 및 응답 받기\n* ✅ 실시간 출력을 위한 스트리밍\n* ✅ Copilot 호출할 수 있는 사용자 지정 도구 정의\n\n이제 멋진 것을 만들어 보세요!\n🚀"}