Tools
Extending Copilot with custom capabilities.
What Are Tools?
Tools are functions that Copilot can call during a conversation. When Copilot needs information it doesn’t have (like current weather, database records, or external APIs), it calls your tools.
User: "What's the weather in Tokyo?"
Copilot thinks: "I need current weather data. Let me call the weather tool."
↓
Your tool executes: getWeather("Tokyo")
↓
Tool returns: { temp: 22, condition: "sunny" }
↓
Copilot: "It's currently 22°C and sunny in Tokyo."
Tool Anatomy
A tool has three parts:
1. Definition (what Copilot sees)
{
name: "get_weather",
description: "Get current weather for a city",
parameters: {
type: "object",
properties: {
city: {
type: "string",
description: "City name"
}
},
required: ["city"]
}
}
2. Implementation (what runs)
async function getWeather(params: { city: string }) {
const response = await fetch(`https://api.weather.com/${params.city}`);
return response.json();
}
3. Handler (connecting them)
function handleToolCall(name: string, params: unknown) {
if (name === "get_weather") {
return getWeather(params as { city: string });
}
}
Defining Tools (Node.js)
Manual definition
const weatherTool = {
name: "get_weather",
description: "Get current weather for a location",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "City name" },
units: {
type: "string",
enum: ["celsius", "fahrenheit"],
description: "Temperature units"
}
},
required: ["city"]
}
};
Using Zod (recommended)
The SDK can generate schemas from Zod types:
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
const WeatherParams = z.object({
city: z.string().describe("City name"),
units: z.enum(["celsius", "fahrenheit"]).optional().describe("Temperature units")
});
const weatherTool = {
name: "get_weather",
description: "Get current weather for a location",
parameters: zodToJsonSchema(WeatherParams)
};
Using Tools in Sessions
const session = await client.createSession({
tools: [weatherTool]
});
// Handle tool calls
for await (const event of session.send("What's the weather in Tokyo?")) {
if (event.type === "toolCall") {
const { name, arguments: args } = event;
// Execute tool
let result;
if (name === "get_weather") {
result = await getWeather(JSON.parse(args));
}
// Return result to Copilot
await session.submitToolResult(event.callId, JSON.stringify(result));
}
}
Tool Call Flow
┌─────────────────────────────────────────────────────────────────────────┐
│ TOOL CALL SEQUENCE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Copilot decides to call tool │
│ ↓ │
│ 2. toolCall event received by your app │
│ { type: "toolCall", callId: "abc", name: "get_weather", args: ... }│
│ ↓ │
│ 3. Your handler executes the tool │
│ const result = await getWeather(args); │
│ ↓ │
│ 4. Submit result back │
│ await session.submitToolResult(callId, result); │
│ ↓ │
│ 5. Copilot continues (may call more tools or respond) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Writing Good Tool Descriptions
Copilot uses descriptions to decide when and how to use tools. Good descriptions are critical.
Bad descriptions
// Too vague
description: "Gets stuff"
// Too technical
description: "Executes HTTP GET request to /api/v2/weather endpoint"
// Missing context
description: "Returns temperature"
Good descriptions
// Clear purpose
description: "Get the current weather conditions for a city"
// With constraints
description: "Search for products by name. Returns up to 10 results. Use for finding specific items."
// With guidance
description: "Execute a SQL query against the database. Only use for read operations. Prefer specific queries over SELECT *."
Parameter Descriptions
Each parameter should explain:
- What it’s for
- What format/values are valid
- Any constraints
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "SQL SELECT query. Must not modify data."
},
limit: {
type: "number",
description: "Maximum rows to return. Default 100, max 1000."
},
format: {
type: "string",
enum: ["json", "csv"],
description: "Output format. Use 'csv' for large datasets."
}
}
}
Tool Results
Results should be structured for Copilot to understand:
// Good: Structured, includes context
return {
city: "Tokyo",
temperature: 22,
units: "celsius",
condition: "sunny",
lastUpdated: "2024-01-15T10:30:00Z"
};
// Bad: Unstructured string
return "22 degrees sunny";
// For errors: Include error info
return {
error: true,
message: "City not found",
suggestion: "Did you mean 'Kyoto'?"
};
Error Handling in Tools
async function handleToolCall(name: string, args: string) {
try {
const params = JSON.parse(args);
if (name === "get_weather") {
return await getWeather(params);
}
return { error: true, message: `Unknown tool: ${name}` };
} catch (error) {
// Return error to Copilot so it can recover
return {
error: true,
message: error.message,
details: "The tool encountered an error. Please try again or use different parameters."
};
}
}
Tool Patterns
Confirmation tools
For destructive actions, require confirmation:
const deleteFileTool = {
name: "delete_file",
description: "Delete a file. Requires user confirmation first.",
parameters: {
properties: {
path: { type: "string" },
confirmed: {
type: "boolean",
description: "Must be true. Ask user for confirmation before setting."
}
},
required: ["path", "confirmed"]
}
};
function deleteFile({ path, confirmed }) {
if (!confirmed) {
return { error: true, message: "Please confirm deletion with the user first" };
}
// Actually delete
}
Multi-step tools
Tools can guide Copilot through workflows:
// Step 1: Search
const searchTool = {
name: "search_docs",
description: "Search documentation. Use results to find relevant doc_id, then use get_doc to retrieve full content."
};
// Step 2: Retrieve
const getDocTool = {
name: "get_doc",
description: "Get full document content by doc_id (from search_docs results)"
};
Tools with side effects
Be explicit about what tools do:
const sendEmailTool = {
name: "send_email",
description: "Send an email. THIS ACTUALLY SENDS - verify recipient and content with user before calling."
};
MCP (Model Context Protocol) Tools
The SDK supports MCP servers, which provide pre-built tool sets:
const session = await client.createSession({
mcpServers: [{
name: "github",
command: "npx",
args: ["@github/mcp-server"]
}]
});
MCP servers provide tools like:
- GitHub operations (issues, PRs, repos)
- File system access
- Database connections
- External API integrations
See MCP documentation for available servers.