GitHub Copilot SDK Integration with Existing Codebases: Implementation Guide

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, the joinSession function returns a session object, while 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, provides a minimal extension skeleton for TypeScript and JavaScript applications:

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 supports async/await patterns with streaming. The CopilotClient class creates sessions using client.create_session:

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, uses a fluent API pattern with copilot.NewClient():

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 documents the Maven-based SDK:

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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →