# GitHub Copilot SDK Integration with Existing Codebases: Implementation Guide

> Integrate the GitHub Copilot SDK into your existing codebase using JSON-RPC. Discover how to embed the agentic runtime with custom tools and real-time streaming in this implementation guide.

- Repository: [GitHub/copilot-sdk](https://github.com/github/copilot-sdk)
- Tags: how-to-guide
- Published: 2026-06-06

---

**The GitHub Copilot SDK provides language-specific client libraries that enable you to embed the Copilot agentic runtime into any existing application via JSON-RPC communication, supporting custom tools, lifecycle hooks, and real-time streaming.**

The `github/copilot-sdk` repository offers official client libraries for Node.js, Python, Go, Java, and Rust that abstract the complexity of spawning and communicating with the Copilot CLI. By implementing GitHub Copilot SDK integration with existing codebases, you can programmatically create AI sessions, register custom tools, and intercept lifecycle events while maintaining full control over permissions and context injection.

## Core Architecture

The SDK decouples your application from the CLI implementation through a standard JSON-RPC interface.

### JSON-RPC Communication

All SDKs communicate with the Copilot CLI via JSON-RPC over stdio or TCP. According to the `github/copilot-sdk` source code, the SDK manages the CLI process lifecycle—or connects to an external server—allowing applications written in any supported language to access the same agentic runtime used by the Copilot CLI.

### Session Object

The session object serves as the primary interface for interaction. In [`nodejs/docs/examples.md`](https://github.com/github/copilot-sdk/blob/main/nodejs/docs/examples.md), the `joinSession` function returns a session object, while [`python/README.md`](https://github.com/github/copilot-sdk/blob/main/python/README.md) documents `client.create_session`. These session objects expose methods to:

- `send` or `sendAndWait` user prompts
- Subscribe to real-time events via `session.on`
- Access the workspace directory through `session.workspacePath`

### Tools and Hooks

Tools are functions declared with a name, description, JSON-Schema parameters, and a handler. As implemented in `github/copilot-sdk`, tools can be built-in (such as `bash` or `edit`) or custom. Lifecycle hooks including `onUserPromptSubmitted`, `onPreToolUse`, and `onPostToolUse` let you rewrite prompts, approve or deny tool calls, or inject additional context before the model processes the data.

### Permissions

An optional `onPermissionRequest` handler can auto-approve, reject, or prompt the user before a tool executes. If omitted, permission requests emit as events for manual resolution.

### Streaming Events

Enabling `streaming: true` (Node.js) or `streaming=True` (Python) causes the CLI to emit `assistant.message_delta` events, allowing incremental UI updates for token-by-token responses.

## Language-Specific Integration Examples

### Node.js Extension Skeleton

The Node.js SDK, documented in [`nodejs/docs/examples.md`](https://github.com/github/copilot-sdk/blob/main/nodejs/docs/examples.md), provides a minimal extension skeleton for TypeScript and JavaScript applications:

```javascript
import { joinSession } from "@github/copilot-sdk/extension";

const session = await joinSession({
  hooks: {
    onUserPromptSubmitted: async (input) => ({
      additionalContext: "Always answer in bullet points.",
    }),
    onPreToolUse: async (input) => {
      if (input.toolName === "bash") {
        const cmd = String(input.toolArgs?.command || "");
        if (/rm\s+-rf/i.test(cmd)) {
          return { permissionDecision: "deny", permissionDecisionReason: "Destructive command" };
        }
      }
      return { permissionDecision: "allow" };
    },
  },
  tools: [
    {
      name: "lookup_issue",
      description: "Fetch issue details from a tracker",
      parameters: {
        type: "object",
        properties: { id: { type: "string", description: "Issue ID" } },
        required: ["id"],
      },
      handler: async (args) => {
        const issue = await fetch(`https://api.example.com/issues/${args.id}`);
        const data = await issue.json();
        return `Title: ${data.title}\nStatus: ${data.state}`;
      },
    },
  ],
});

```

This example demonstrates custom tool registration and pre-tool validation hooks to block destructive commands.

### Python Async Integration

The Python SDK in [`python/README.md`](https://github.com/github/copilot-sdk/blob/main/python/README.md) supports async/await patterns with streaming. The `CopilotClient` class creates sessions using `client.create_session`:

```python
import asyncio
from copilot import CopilotClient
from copilot.session import PermissionHandler

async def main():
    async with CopilotClient() as client:
        async with await client.create_session(
            on_permission_request=PermissionHandler.approve_all,
            model="gpt-5",
            streaming=True,
        ) as session:
            def on_event(event):
                match event.data:
                    case {"type": "assistant.message_delta", "delta_content": d}:
                        print(d, end="", flush=True)
                    case {"type": "assistant.message", "content": c}:
                        print("\n---\n", c)

            session.on(on_event)
            await session.send("Explain the Observer pattern in Rust.")
            await asyncio.Event().wait()

asyncio.run(main())

```

The `streaming=True` parameter enables token-by-token delivery via `assistant.message_delta` events.

### Go Implementation

The Go SDK, defined in [`go/types.go`](https://github.com/github/copilot-sdk/blob/main/go/types.go), uses a fluent API pattern with `copilot.NewClient()`:

```go
package main

import (
    "context"
    "fmt"
    "strings"
    "github.com/github/copilot-sdk/go"
)

func main() {
    client := copilot.NewClient()
    sess, err := client.CreateSession(context.Background(),
        copilot.WithModel("gpt-4"),
        copilot.WithPermissionHandler(copilot.ApproveAll),
    )
    if err != nil { panic(err) }
    defer sess.Close()

    sess.AddTool(copilot.Tool{
        Name: "uppercase",
        Description: "Return the uppercase version of a string",
        Parameters: map[string]any{
            "type": "object",
            "properties": map[string]any{
                "text": map[string]any{
                    "type": "string", "description": "Input text",
                },
            },
            "required": []string{"text"},
        },
        Handler: func(args map[string]any) (any, error) {
            return strings.ToUpper(args["text"].(string)), nil
        },
    })

    resp, err := sess.SendAndWait(context.Background(), "uppercase", map[string]any{"text": "hello"})
    fmt.Println("Result:", resp)
}

```

The `SendAndWait` method blocks until the LLM completes its response, while `AddTool` registers custom capabilities.

### Java Maven Integration

For JVM applications, [`java/README.md`](https://github.com/github/copilot-sdk/blob/main/java/README.md) documents the Maven-based SDK:

```java
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.Session;
import com.github.copilot.sdk.tools.Tool;

public class Demo {
    public static void main(String[] args) throws Exception {
        try (CopilotClient client = new CopilotClient()) {
            Session session = client.createSession()
                .withModel("gpt-4")
                .withPermissionHandler(Session.PermissionHandler.approveAll())
                .build();

            Tool echoTool = Tool.builder()
                .name("echo")
                .description("Echoes the supplied text")
                .parameters("{\"type\":\"object\",\"properties\":{\"msg\":{\"type\":\"string\"}},\"required\":[\"msg\"]}")
                .handler((invocation) -> invocation.getArguments().get("msg"))
                .build();

            session.registerTool(echoTool);
            session.send("Please echo the phrase: \"Hello Copilot\".");
            session.onAssistantMessage(msg -> System.out.println(msg.getContent()));
        }
    }
}

```

The builder pattern for `Tool` and `Session` configuration aligns with Java ecosystem conventions.

## Key Implementation Patterns

### Custom Tool Registration

Tools require four components: a unique name, description, JSON Schema parameters, and a handler function. According to [`docs/features/hooks.md`](https://github.com/github/copilot-sdk/blob/main/docs/features/hooks.md), tool handlers receive arguments and optional invocation metadata, returning results that the LLM can incorporate into its reasoning.

### Lifecycle Hooks

The `onUserPromptSubmitted` hook enables pre-processing to inject system prompts or additional context. The `onPreToolUse` and `onPostToolUse` hooks allow inspection and modification of tool invocations, enabling audit logging, argument validation, or result transformation before the LLM receives the output.

### Permission Handling

Security-critical applications should implement `onPermissionRequest` or `PermissionHandler` interfaces to gate tool execution. The SDK supports three decision modes: `allow`, `deny`, or async user prompting.

## Summary

- The GitHub Copilot SDK connects to the CLI via JSON-RPC, supporting Node.js, Python, Go, Java, and other languages
- **Session objects** manage conversation state and provide methods like `send`, `sendAndWait`, and `on` for event subscription
- **Custom tools** extend AI capabilities with domain-specific functions using JSON Schema parameter definitions
- **Lifecycle hooks** (`onUserPromptSubmitted`, `onPreToolUse`) enable prompt rewriting and tool call validation
- **Streaming mode** delivers incremental responses via `assistant.message_delta` events for responsive UIs
- Permission handlers provide fine-grained control over tool execution safety

## Frequently Asked Questions

### What languages does the GitHub Copilot SDK support?

The SDK provides official client libraries for Node.js/TypeScript, Python, Go, .NET, Java, and Rust. Each library communicates with the Copilot CLI via JSON-RPC, ensuring consistent behavior across language ecosystems while following idiomatic patterns for each runtime.

### How does the SDK communicate with the Copilot CLI?

The SDK spawns the bundled `copilot` binary and establishes communication over standard input/output or TCP using JSON-RPC 2.0. As implemented in `github/copilot-sdk`, this architecture decouples the application language from the CLI implementation, allowing any supported language to embed the same agentic runtime.

### Can I restrict which tools the AI can invoke?

Yes. Implement the `onPreToolUse` hook (Node.js/Python) or `WithPermissionHandler` option (Go) to inspect tool names and arguments before execution. Return `permissionDecision: "deny"` with a reason to block specific operations, such as destructive shell commands or unauthorized API calls.

### How do I enable real-time streaming responses?

Set `streaming: true` in the session configuration. This causes the CLI to emit `assistant.message_delta` events containing token chunks as they are generated. Subscribe to these events via `session.on` (Node.js/Python) or callbacks (Java/Go) to update your UI incrementally rather than waiting for the complete response.