How to Add and Integrate Custom Tools with the Copilot SDK

The Copilot SDK allows you to extend Copilot's capabilities by registering JSON-serializable tool schemas and handler functions that the runtime invokes during chat sessions, returning structured results back to the LLM.

The GitHub Copilot SDK enables developers to enhance AI-assisted coding workflows by adding custom tools that Copilot can invoke during conversations. When you add and integrate custom tools with the Copilot SDK, you create a bridge between the LLM and your external APIs or business logic. This article walks through the complete implementation process using the actual source code from the github/copilot-sdk repository.

Define the Tool Schema

Start by creating a Tool object that describes the function signature using JSON Schema. In nodejs/src/toolSet.ts, the Tool type defines the structure including name, description, args, and optional flags.

The schema must specify:

  • name: Unique identifier for the tool
  • description: Natural language explanation for the LLM to understand when to invoke the tool
  • args: JSON Schema object defining required and optional parameters

Skip Permission Prompts

Set skipPermission: true in the tool definition to bypass the user confirmation UI for trusted internal tools. This flag is evaluated during the permission check phase in the session lifecycle.

Register Custom Tools

Before starting a session, call registerTools() (or registerTool() for single registrations) on the SDK client instance. According to nodejs/src/index.ts, this method merges your custom definitions with the SDK's built-in tool set.

The registration requires two arguments:

  1. An array of Tool schema objects
  2. A handler function that receives ToolInvocation and returns Promise<ToolResult> or ToolResult

Implement the Handler Function

The handler receives a ToolInvocation object containing the parsed arguments and must return a ToolResult or ToolBinaryResult for binary data. As defined in nodejs/src/toolSet.ts, these types enforce the contract between your implementation and the LLM.

Handlers can be synchronous or asynchronous. The SDK automatically wraps the return value into a ToolExecutionComplete event and streams it back to the LLM as a structured response.

Session Execution Flow

During active sessions managed in nodejs/src/session.ts, the SDK listens for toolRequest events from Copilot. When the LLM decides to invoke a tool, the SDK:

  • Creates a ToolInvocation object with validated arguments
  • Executes your registered handler
  • Emits ToolExecutionProgressData events for streaming partial results (optional)
  • Returns the final ToolResult to the LLM via the ToolExecutionComplete event

Optional Hooks and Permissions

The SDK provides lifecycle hooks for governance and observability. As documented in docs/hooks/pre-tool-use.md and implemented in nodejs/src/session.ts:

  • onPreToolUse: Validate arguments or enforce policies before the tool executes
  • onPostToolUse: Process results for logging, telemetry, or side effects

Complete Working Example

Below is a minimal Node.js example that adds a weather-lookup tool and integrates it into a Copilot chat session.

import { Copilot } from '@github/copilot-sdk/nodejs';
import { Tool, ToolInvocation, ToolResult } from '@github/copilot-sdk/nodejs';

// 1️⃣ Define the custom tool
const weatherTool: Tool = {
  name: 'weather',
  description: 'Look up the current weather for a city',
  args: {
    type: 'object',
    properties: {
      city: { type: 'string', description: 'Name of the city' },
      units: { type: 'string', enum: ['metric', 'imperial'], default: 'metric' },
    },
    required: ['city'],
  },
  // Trusted internal tool – skip the permission prompt
  skipPermission: true,
};

// 2️⃣ Provide the implementation
async function runWeatherTool(invocation: ToolInvocation): Promise<ToolResult> {
  const { city, units } = invocation.args as { city: string; units?: string };
  // (In a real app you would call a weather API here)
  const fakeTemp = 22; // placeholder
  const response = `The temperature in ${city} is ${fakeTemp}°${units === 'imperial' ? 'F' : 'C'}.`;
  return { result: response };
}

// 3️⃣ Register the tool with the SDK client
const client = new Copilot();
client.registerTools([weatherTool], runWeatherTool);

// 4️⃣ Start a chat session
async function chat() {
  const session = client.createSession({ streaming: true });
  const response = await session.prompt('What’s the weather like in Paris?');
  console.log(response); // → “The temperature in Paris is …”
}
chat();

Summary

  • Define tools using the Tool type in nodejs/src/toolSet.ts with JSON Schema arguments
  • Register handlers via registerTools() on the client before creating sessions
  • Return ToolResult objects from handlers to communicate results to the LLM
  • Use skipPermission: true for trusted tools to bypass confirmation dialogs
  • Leverage hooks in session.ts for pre-flight validation and post-execution logging

Frequently Asked Questions

What is the difference between registerTools() and registerTool()?

registerTools() accepts an array of tool definitions and registers them in a single call, while registerTool() registers one tool at a time. Both methods are available on the Copilot client instance exposed in nodejs/src/index.ts and merge your definitions with the SDK's internal tool set.

Can custom tools return binary data?

Yes. When your tool produces binary output (such as images or files), return a ToolBinaryResult instead of a ToolResult. The SDK handles binary serialization differently from text results, ensuring proper encoding when streaming back to the LLM via the ToolExecutionComplete event.

How do I handle errors in custom tool handlers?

Throwing an error inside your handler function will cause the SDK to emit an error event back to the LLM. Alternatively, you can return a ToolResult with an error message in the result field. Use the onPreToolUse hook in nodejs/src/session.ts to catch validation errors before the handler executes.

Where can I find complete runnable examples?

The nodejs/examples/basic-example.ts file in the repository contains a fully functional demonstration of a chat session with custom tool integration. For step-by-step guidance, see docs/getting-started.md Section "Step 4: add a custom tool".

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 →