Phase 13/13 — Contribution Guide

Contribution & Extension Guide

Comprehensive reference for contributors, extension authors, and AI agents working with the copilot-sdk mono-repo.

1. Getting Started as a Contributor

1.1 Repository Architecture

SDK Architecture
flowchart TD
  A["Your Application"] -->|"JSON-RPC"| B["SDK Client"]
  B -->|"JSON-RPC"| C["Copilot CLI\n(server mode)"]
          
copilot-sdk/
  nodejs/          # Node.js / TypeScript SDK
  python/          # Python SDK
  go/              # Go SDK
  dotnet/          # .NET SDK
  scripts/codegen/ # Cross-language type generation
  test/            # Shared test infrastructure
    harness/       # Replaying CAPI proxy for E2E
    snapshots/     # YAML snapshot exchanges
    scenarios/     # E2E scenario tests (all languages)
  docs/            # Cross-language documentation
  justfile         # Task runner recipes

1.2 Prerequisites

Recommended: Install just command runner

The just task runner provides unified commands across all SDKs.

Prerequisites per Language
flowchart LR
  subgraph TS["Node.js / TypeScript"]
    TS1["Node.js v18+"]
    TS2["cd nodejs && npm ci"]
  end
  subgraph PY["Python"]
    PY1["Python 3.8+ and uv"]
    PY2["cd python && uv pip install -e .[dev]"]
  end
  subgraph GO["Go"]
    GO1["Go 1.24+ and golangci-lint"]
    GO2["cd go && go mod download"]
  end
  subgraph CS["C# / .NET"]
    CS1[".NET 8.0+ and Node.js v18+"]
    CS2["cd dotnet && dotnet restore"]
  end
          

1.3 One-Command Setup

just install

This installs all dependencies for all four SDKs plus the test harness.

1.4 Dev Container

A devcontainer at .devcontainer/devcontainer.json provides a fully-configured environment with Copilot CLI, GitHub CLI, Go, Node.js, uv, just, and .NET.

1.5 VS Code Settings

The repo provides VS Code settings with Prettier (default formatter), Ruff (Python), Go extension, format on save, trailing whitespace trimming, and 4-space tabs.

1.6 Code Ownership

The entire repository is owned by @github/copilot-sdk (.github/CODEOWNERS:1).

2. Development Workflow

2.1 The just Task Runner

CommandDescription
just testRun tests for all 4 languages
just lintLint all 4 languages
just formatFormat all 4 languages
just installInstall all dependencies
just playgroundStart the SDK playground
just validate-docsValidate documentation code examples
just scenario-buildBuild all E2E scenario samples
just scenario-verifyRun full E2E scenario verification

2.2 Per-Language Commands

# Format
cd nodejs && npm run format
# Lint
cd nodejs && npm run format:check && npm run lint && npm run typecheck
# Test (Vitest)
cd nodejs && npm test
# Format
cd python && uv run ruff format .
# Lint
cd python && uv run ruff check . && uv run ty check copilot
# Test
cd python && uv run pytest
# Format (excludes generated code)
cd go && find . -name "*.go" -not -path "*/generated/*" -exec gofmt -w {} +
# Lint
cd go && golangci-lint run ./...
# Test
cd go && go test ./...
# Format
cd dotnet && dotnet format src/GitHub.Copilot.SDK.csproj
# Lint
cd dotnet && dotnet format src/GitHub.Copilot.SDK.csproj --verify-no-changes
# Test
cd dotnet && dotnet test test/GitHub.Copilot.SDK.Test.csproj

2.3 Submitting a Pull Request

  1. Fork and clone the repository
  2. Install dependencies for the SDK(s) you are modifying
  3. Make sure tests pass on your machine
  4. Make sure linting passes
  5. Create a new branch: git checkout -b my-branch-name
  6. Make your change, add tests, verify tests/linter pass
  7. Push to your fork and submit a pull request
  8. Wait for review

2.4 What Contributions Are Welcome

  • Fixing bugs in the existing feature set
  • Making the SDKs more idiomatic for each language
  • Improving documentation
  • New features (post an issue or start a discussion first)
Not Currently Accepted

New language SDKs are not accepted in the repo — maintainers encourage external community projects instead.

3. Code Generation Workflow

3.1 Overview

Type generation is centralized in scripts/codegen/. The system reads JSON schemas from the @github/copilot package and generates typed code for all four languages.

3.2 Code Generation Scripts

ScriptOutputCommand
typescript.tsnodejs/src/generated/npm run generate:ts
python.tspython generated typesnpm run generate:python
go.tsgo generated typesnpm run generate:go
csharp.tsdotnet generated typesnpm run generate:csharp
cd scripts/codegen && npm run generate

3.3 Generation Pipeline

Code Generation Pipeline
flowchart TD
    A["JSON-RPC Schema\n(rpc-schema.json)"] --> B["Code Generator\n(just codegen)"]
    B --> C["TypeScript\ngenerated types"]
    B --> D["Python\ngenerated types"]
    B --> E["Go\ngenerated types"]
    B --> F["C#\ngenerated types"]
          

3.5 Generated File Locations

LanguageSession EventsRPC Types
TSnodejs/src/generated/session-events.tsnodejs/src/generated/rpc.ts
Pythonpython/copilot/generated/python/copilot/generated/
Gogo/generated_session_events.gogo/rpc/
C#dotnet/src/ (generated)dotnet/src/ (generated)
Never Hand-Edit Generated Files

Generated files include AUTO-GENERATED FILE - DO NOT EDIT banners.

4. Adding a New RPC Method

Adding a New RPC Method
flowchart TD
    A["Update rpc-schema.json"] --> B["Run just codegen"]
    B --> C["Review generated diffs"]
    C --> D["Implement handler in each SDK"]
    D --> E["Add unit tests per SDK"]
    E --> F["Add E2E scenario test"]
    F --> G["Update CLI if needed"]
          

5. Adding a New Feature

5.1 SDK Source Code Locations

LanguageSourceUnit TestsE2E Tests
TSnodejs/src/nodejs/test/nodejs/test/e2e/
Pythonpython/copilot/python/test_*.pypython/e2e/
Gogo/go/*_test.gogo/ (inline)
C#dotnet/src/dotnet/test/dotnet/test/

5.3 Feature Implementation Checklist

  1. Design the API surface in each language idiomatically
  2. Implement in all 4 SDKs — changes must be synchronized
  3. Write unit tests for each SDK
  4. Write E2E scenario tests
  5. Add snapshot data if new CAPI exchanges are needed
  6. Write documentation in docs/features/
  7. Validate docs with just validate-docs

6. Writing Tests

6.1 Unit Tests

Unit Test Frameworks
flowchart LR
  subgraph TS["Node.js - Vitest"]
    TS1["nodejs/test/"]
    TS2["npm test"]
  end
  subgraph PY["Python - pytest"]
    PY1["python/test_*.py"]
    PY2["uv run pytest"]
  end
  subgraph GO["Go"]
    GO1["go/*_test.go"]
    GO2["go test ./..."]
  end
  subgraph CS[".NET"]
    CS1["dotnet/test/"]
    CS2["dotnet test"]
  end
          

6.2 E2E Tests and the Replay Proxy

E2E tests run against a replaying CAPI proxy located in test/harness/:

  • server.ts — The replay proxy server
  • replayingCapiProxy.ts — Replays recorded CAPI exchanges
  • capturingHttpProxy.ts — Captures live exchanges for recording
  • test-mcp-server.mjs — Mock MCP server for tests

Snapshot data in test/snapshots/: YAML files with recorded request/response exchanges, organized by feature area.

6.3 Scenario Tests

scenarios/
  auth/        # Authentication flows
  bundling/    # Deployment architectures
  callbacks/   # Lifecycle hooks, permissions
  modes/       # Preset modes (CLI, filesystem)
  prompts/     # Prompt configuration
  sessions/    # Session management
  tools/       # Tool capabilities
  transport/   # Wire protocols

6.4 Adding a New Test Scenario

  1. Create subdirectory under the appropriate category in test/scenarios/
  2. Add implementations in typescript/, python/, go/, csharp/
  3. Add a verify.sh script
  4. Update snapshot YAML files if needed
  5. Run just scenario-build to verify

7. Creating Custom Agents

Custom agents have their own system prompt, tool restrictions, and optional MCP servers. The runtime automatically delegates based on intent matching.

7.1 Defining Custom Agents

const session = await client.createSession({
    model: "gpt-4.1",
    customAgents: [
        {
            name: "researcher",
            displayName: "Research Agent",
            description: "Explores codebases using read-only tools",
            tools: ["grep", "glob", "view"],
            prompt: "You are a research assistant.",
        },
        {
            name: "editor",
            displayName: "Editor Agent",
            tools: ["view", "edit", "bash"],
            prompt: "You are a code editor.",
        },
    ],
    onPermissionRequest: async () => ({ kind: "approved" }),
});
session = await client.create_session({
    "model": "gpt-4.1",
    "custom_agents": [
        {
            "name": "researcher",
            "display_name": "Research Agent",
            "tools": ["grep", "glob", "view"],
            "prompt": "You are a research assistant.",
        },
    ],
    "on_permission_request": lambda req, inv: PermissionRequestResult(kind="approved"),
})
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
    Model: "gpt-4.1",
    CustomAgents: []copilot.CustomAgentConfig{
        {
            Name:        "researcher",
            DisplayName: "Research Agent",
            Tools:       []string{"grep", "glob", "view"},
            Prompt:      "You are a research assistant.",
        },
    },
})

7.2 Configuration Reference

PropertyTypeRequiredDescription
namestringYesUnique identifier
displayNamestringNoHuman-readable name
descriptionstringNoHelps runtime select the agent
toolsstring[] | nullNoTool names. null = all
promptstringYesSystem prompt
mcpServersobjectNoPer-agent MCP configs
inferbooleanNoAuto-select (default: true)

7.4 Sub-Agent Events

EventEmitted When
subagent.selectedRuntime selects an agent
subagent.startedSub-agent begins execution
subagent.completedSub-agent finishes
subagent.failedSub-agent encounters an error
subagent.deselectedRuntime switches away

8. Extending with Skills

Skills are reusable prompt modules loaded from directories containing SKILL.md files.

8.2 SKILL.md Format

---
name: code-review
description: Specialized code review capabilities
---

# Code Review Guidelines
When reviewing code, always check for:
1. Security vulnerabilities
2. Performance issues
3. Code style
4. Test coverage

8.3 Loading Skills

const session = await client.createSession({
    skillDirectories: ["./skills/code-review", "./skills/documentation"],
});
session = await client.create_session({
    "skill_directories": ["./skills/code-review", "./skills/documentation"],
})

8.5 Configuration Fields Per Language

LanguageLoad FieldDisable Field
TSskillDirectoriesdisabledSkills
Pythonskill_directoriesdisabled_skills
GoSkillDirectoriesDisabledSkills
C#SkillDirectoriesDisabledSkills

9. Extending with MCP Servers

MCP (Model Context Protocol) servers provide pre-built tools that Copilot can invoke during conversations.

9.1 Server Types

TypeDescriptionUse Case
Local/StdioSubprocess via stdin/stdoutLocal tools, file access
HTTP/SSERemote server via HTTPShared services, cloud tools

9.2 Configuration

const session = await client.createSession({
    mcpServers: {
        "my-local-server": {
            type: "local",
            command: "node",
            args: ["./mcp-server.js"],
            env: { DEBUG: "true" },
            tools: ["*"],
            timeout: 30000,
        },
    },
});
session = await client.create_session({
    "mcp_servers": {
        "my-local-server": {
            "type": "local",
            "command": "python",
            "args": ["./mcp_server.py"],
            "tools": ["*"],
        },
    },
})
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
    MCPServers: map[string]copilot.MCPServerConfig{
        "my-local-server": {
            "type":    "local",
            "command": "node",
            "args":    []string{"./mcp-server.js"},
            "tools":   []string{"*"},
        },
    },
})

9.3 Local Server Options

PropertyTypeRequiredDescription
type"local"NoServer type
commandstringYesCommand to execute
argsstring[]YesCommand arguments
envobjectNoEnvironment variables
toolsstring[]NoTools to enable
timeoutnumberNoTimeout in ms

10. Extending with Hooks

Hooks let you plug custom logic into every stage of a Copilot session.

10.1 Available Hooks

HookWhen It FiresWhat You Can Do
onSessionStartSession beginsInject context, load preferences
onUserPromptSubmittedUser sends messageRewrite prompts, add context
onPreToolUseBefore tool executesAllow / deny / modify the call
onPostToolUseAfter tool returnsTransform results, redact secrets
onSessionEndSession endsClean up, record metrics
onErrorOccurredError is raisedCustom logging, retry logic

10.3 Hook Input/Output Reference

  • onUserPromptSubmitted: Input: { prompt, timestamp, cwd }. Output: { modifiedPrompt?, additionalContext? }
  • onPreToolUse: Input: { toolName, toolArgs }. Output: { permissionDecision?, modifiedArgs? }
  • onPostToolUse: Input: { toolName, toolResult }. Output: { modifiedResult? }
  • onSessionStart: Input: { source, initialPrompt? }. Output: { additionalContext? }
  • onSessionEnd: Input: { reason, finalMessage? }. Output: { sessionSummary? }

10.5 Best Practices

Hook Best Practices
  1. Keep hooks fast — they run inline and delay the conversation
  2. Return null when unchanged — tells SDK to use defaults
  3. Be explicit with permission decisions
  4. Don't swallow critical errors — always log or alert
  5. Use additionalContext over modifiedPrompt — preserves user intent
  6. Scope state by session ID

11. Fork Guide

  1. Fork at github.com/github/copilot-sdk/fork
  2. Clone: git clone https://github.com/YOUR-USERNAME/copilot-sdk.git
  3. Install: just install
  4. Verify: just test

11.3 Keeping Your Fork Updated

git remote add upstream https://github.com/github/copilot-sdk.git
git fetch upstream
git merge upstream/main
cd scripts/codegen && npm run generate  # if schema changed
just test

11.4 Building Extensions (Without Forking)

For most customization, you do not need to fork. The SDK provides extension points:

  • Custom Tools: defineTool() / @define_tool / DefineTool() / AIFunctionFactory.Create()
  • Custom Agents: customAgents in session config
  • Skills: skillDirectories for prompt modules
  • MCP Servers: mcpServers for external tools
  • Hooks: hooks for lifecycle interception
  • CLI Extensions: .github/extensions/ for .mjs extensions

12. AI Agent Guide

How an AI coding agent should approach modifying this codebase.

12.1 Files to Read First

  1. README.md — Architecture and quick start
  2. Language entry points: nodejs/src/client.ts, python/README.md, go/README.md
  3. Test harness: test/harness/*
  4. Schemas and codegen: scripts/codegen/
  5. Session snapshots: test/snapshots/

12.2 Where to Add New Code

WhatLocation
SDK sourcenodejs/src, python/copilot, go, dotnet/src
Unit testsnodejs/test, python/*, go/*, dotnet/test
E2E tests*/e2e/ folders using shared replay proxy
Generated typesRun cd scripts/codegen && npm run generate

12.5 Cross-Language Change Checklist

AI Agent Change Checklist
  1. Check if change affects all four SDKs
  2. Run code generation if schema changed
  3. Add tests in all affected languages
  4. Update snapshot data if new CAPI exchanges
  5. Run just lint before committing
  6. Run just test to verify
  7. Validate docs: just validate-docs
  8. Never edit files with AUTO-GENERATED banners

12.8 Extension Authoring

  1. Scaffold: extensions_manage({ operation: "scaffold", name: "my-extension" })
  2. Edit: Modify extension.mjs (must be ES module, must call joinSession())
  3. Reload: extensions_reload({})
  4. Verify: extensions_manage({ operation: "list" })
Extension Gotchas
  • stdout is reserved for JSON-RPC — use session.log() instead
  • Tool name collisions across extensions are fatal
  • Do not call session.send() from onUserPromptSubmitted
  • Extensions reload on /clear — in-memory state is lost
  • Only .mjs is supported (not TypeScript)

Appendix: Quick Reference Card

Quick Reference Card
flowchart LR
  A["<b>just install</b><br/>Install Everything"] ~~~ B["<b>npm run generate</b><br/>Regenerate All Types"] ~~~ C["<b>just format && lint && test</b><br/>Full CI Check"]
          

Key File Locations

PurposePath
Root READMEREADME.md
Contributing guideCONTRIBUTING.md
Task runnerjustfile
AI agent instructions.github/copilot-instructions.md
Node.js SDK sourcenodejs/src/
Python SDK sourcepython/copilot/
Go SDK sourcego/
.NET SDK sourcedotnet/src/
Code generatorsscripts/codegen/
Test harnesstest/harness/
E2E snapshotstest/snapshots/
E2E scenariostest/scenarios/
Feature docsdocs/features/
Protocol versionsdk-protocol-version.json