How to leverage tools in prompt orchestrations
How to leverage tools in prompt orchestrations
Tools are the hands and eyes of your AI agents. Without them, an agent can only think—with them, it can search code, edit files, run commands, ask you questions, and delegate to other agents. This article is the definitive reference for understanding, selecting, and using tools effectively.
Up to this point in the series, you’ve learned how to create the building blocks of GitHub Copilot customization: prompt files, agent files, instruction files, skills, MCP servers, and hooks. Each of those articles mentioned tools—restricting them in agent YAML, referencing them in prompts, building them as MCP servers. But none explained what tools actually are at runtime, how they work under the hood, or how to choose the right tool for each situation.
This article fills that gap. It covers the complete tool architecture—from the YAML declarations you write to the runtime function calls the model makes—and gives you the knowledge to select, combine, and optimize tools for any prompt orchestration.
Table of contents
- 🎯 The two-level tool architecture
- 📋 Level 1: YAML capability declarations
- ⚙️ Level 2: Runtime tool calls
- 🔍 Search tools: finding the right information
- ✏️ Edit tools: modifying the workspace
- 🖥️ Execution tools: running commands
- 💬 Interaction tools: communicating with users
- 🤝 Delegation tools: orchestrating agents
- 🔌 Tool sources: built-in vs. MCP vs. extensions
- 📊 Token cost analysis
- 🎯 Tool selection strategy
- 🛡️ Tool restrictions and safety
- 🔧 Practical patterns
- ⚠️ Common pitfalls
- 🎯 Conclusion
- 📚 References
🎯 The two-level tool architecture
GitHub Copilot’s tool system operates at two distinct levels. Understanding this separation is essential for effective prompt engineering—confusing Level 1 and Level 2 is the most common source of tool-related mistakes.
Two names, two layers
| Level | What you write | What runs | Example |
|---|---|---|---|
| Level 1 — YAML declarations | tools: ['codebase', 'editor'] in frontmatter |
Nothing directly—these are permissions | codebase, editor, filesystem, fetch |
| Level 2 — Runtime tool calls | The model invokes functions during execution | Actual operations on your workspace | read_file, grep_search, create_file, run_in_terminal |
The relationship is hierarchical: Level 1 declarations enable groups of Level 2 runtime tools. When you write tools: ['editor'] in YAML, you’re granting the agent permission to use runtime tools like create_file, replace_string_in_file, and multi_replace_string_in_file. When you write tools: ['codebase'], you’re enabling semantic_search, grep_search, file_search, and read_file.
YAML Declaration (Level 1) Runtime Tools Enabled (Level 2)
─────────────────────────────────────────────────────────────────
tools: ['codebase'] ───► semantic_search, grep_search,
file_search, read_file
tools: ['editor'] ───► create_file, replace_string_in_file,
multi_replace_string_in_file, read_file
tools: ['filesystem'] ───► list_dir, file_search, read_file
tools: ['fetch'] ───► fetch_webpage
tools: ['web_search'] ───► (web search integration)
tools: ['search'] ───► grep_search, file_search
tools: ['usages'] ───► list_code_usages
tools: ['problems'] ───► get_errors
tools: ['changes'] ───► get_changed_files
What “always available” means
Some runtime tools are always available regardless of your YAML tools field. These can’t be restricted:
| Always-Available Tool | Purpose |
|---|---|
manage_todo_list |
Track task progress in structured todo list |
ask_questions |
Present structured questions to the user |
runSubagent |
Delegate work to subagents |
tool_search_tool_regex |
Discover deferred tools from MCP/extensions |
This means even an agent with tools: ['codebase'] (read-only) can still track progress, ask the user for clarification, delegate to subagents, and discover MCP tools. You can’t create a truly “passive” agent—every agent has at least these four capabilities.
📋 Level 1: YAML capability declarations
Level 1 tools are what you write in the tools field of prompt and agent YAML frontmatter. They control what categories of operations an agent can perform.
Core built-in capabilities
| Capability | Description | Read-Only | Network Access |
|---|---|---|---|
codebase |
Semantic search across workspace for code patterns, symbols, implementations | Yes | No |
editor |
File read/write operations—create, modify, delete files | No | No |
filesystem |
Directory navigation, file queries, metadata access | Yes | No |
fetch |
Retrieve content from web URLs and REST APIs | Yes | Yes |
web_search |
Search the internet for current information | Yes | Yes |
search |
Workspace text search (exact string and regex) | Yes | No |
usages |
Find code usages, references, and call hierarchies | Yes | No |
problems |
Get compile errors, lint warnings from VS Code | Yes | No |
changes |
View git changes and diffs | Yes | No |
Tool sets (predefined groups)
VS Code provides predefined groups of related capabilities for convenience:
| Tool Set | Included Capabilities | Use Case |
|---|---|---|
#edit |
editor, filesystem |
Code modification workflows |
#search |
codebase, search, usages |
Code discovery and analysis |
#reader |
codebase, problems, changes, usages |
Context gathering without modification |
# These are equivalent:
tools: ['codebase', 'search', 'usages']
tools: ['#search']MCP server tools
Tools from Model Context Protocol servers use the @server-name prefix:
tools: ['codebase', '@github/*'] # All tools from GitHub MCP server
tools: ['editor', '@azure/deploy'] # Specific Azure tool only
tools: ['#search', '@company-wiki/*'] # Search + custom MCPBuilt-in MCP servers:
| Server | Purpose | Enable Setting |
|---|---|---|
@github |
GitHub API—issues, PRs, repos, commits | github.copilot.chat.githubMcpServer.enabled |
@azure |
Azure resources, queries, documentation | Azure extension required |
For building your own MCP servers, see How to Create MCP Servers for Copilot.
Tool priority order
When multiple sources define tools, this priority order applies:
- Prompt file
toolsfield (highest priority) - Referenced agent’s
toolsfield - Default tools for current agent mode
# agent defines default tools
# security-reviewer.agent.md
---
tools: ['codebase', 'search', 'fetch']
---
# prompt overrides/extends them
# api-security-audit.prompt.md
---
agent: security-reviewer
tools: ['codebase', 'search', 'fetch', '@github/*'] # adds GitHub MCP
---Common capability combinations
| Profile | Capabilities | Use Case |
|---|---|---|
| Read-only | ['codebase', 'filesystem', 'search'] |
Planning, research, review |
| Local edit | ['codebase', 'editor', 'filesystem'] |
Refactoring, code generation |
| Research | ['codebase', 'fetch', 'web_search'] |
Documentation, best practices lookup |
| Full local | ['codebase', 'editor', 'filesystem', 'search', 'usages', 'problems'] |
Implementation agents |
| Unrestricted | [] or omit field |
All available capabilities enabled |
⚙️ Level 2: Runtime tool calls
Level 2 is where tools actually do things. When an agent runs, the model decides which runtime tools to call based on the task, context, and available capabilities. Each tool is a JSON-schema function with specific parameters, behaviors, and token costs.
Here’s the complete catalog of runtime tools, organized by function.
🔍 Search tools: finding the right information
Search is the most common tool category in prompt orchestrations. Choosing the right search tool is critical—using read_file on a 500-line file when grep_search would find the relevant line wastes thousands of tokens.
semantic_search
Purpose: Natural language search across the workspace. Uses embeddings to find code or documentation that’s conceptually related to your query, even if it doesn’t share exact keywords.
When to use:
- You don’t know the exact function name, variable, or keyword
- You’re looking for conceptual matches (“authentication flow”, “error handling patterns”)
- You want to discover related code across multiple files
When NOT to use:
- You know the exact string to find (use
grep_search) - You know the filename (use
file_search) - You need to read a specific file (use
read_file)
Key parameters:
query(required): Natural language describing what you’re looking for
Token cost: ~2,000 tokens for 10 results (variable based on snippet sizes)
Example prompt guidance:
Use semantic_search to find code related to "user authentication middleware."
Don't use it for exact string matches—use grep_search instead.⚠️ Important:
semantic_searchcannot be called in parallel with othersemantic_searchcalls. When you need multiple semantic searches, run them sequentially.
grep_search
Purpose: Fast text search using exact strings or regex patterns across the workspace. Returns matching lines with file paths and line numbers.
When to use:
- You know the exact text or pattern to find
- You’re searching for function names, variable names, error messages
- You want to see an overview of a specific file by searching within it
- You need regex pattern matching
Key parameters:
query(required): Text or regex pattern to search forisRegexp(required): Whether the pattern is a regexincludePattern(optional): Glob pattern to filter files (e.g.,src/**/*.ts)maxResults(optional): Limit number of results
Token cost: ~1,500 tokens for 20 matches (compact format)
Pro tip: Use grep_search with includePattern targeting a single file to get a structural overview instead of reading the entire file with read_file.
file_search
Purpose: Find files in the workspace by glob pattern. Returns file paths only—not content.
When to use:
- You know the filename or naming pattern
- You want to discover what files exist (e.g., “all test files”, “all config files”)
- You need to locate a specific file before reading it
Key parameters:
query(required): Glob pattern (e.g.,**/*.test.ts,src/**/config.*)maxResults(optional): Limit number of results
Token cost: ~100-500 tokens (paths only, very lightweight)
read_file
Purpose: Read the contents of a specific file, with line range control.
When to use:
- You need to read specific lines from a known file
- You’ve found a file via
file_searchorgrep_searchand need its content - You need to understand code before editing it
Key parameters:
filePath(required): Absolute path to the filestartLine(required): 1-based start lineendLine(required): 1-based end line (inclusive)
Token cost: ~10 tokens per line read. A 50-line read costs ~500 tokens; a 500-line read costs ~5,000 tokens.
Best practices:
- Always specify line ranges—don’t read entire large files
- Read larger ranges in fewer calls rather than many small reads
- Use
grep_searchorfile_searchfirst to identify what to read
list_dir
Purpose: List the contents of a directory. Returns names of files and subdirectories.
When to use:
- You need to understand project structure
- You’re looking for files in a specific directory
- You want to discover what’s available before searching
Key parameters:
path(required): Absolute path to the directory
Token cost: ~100-300 tokens (directory listings are compact)
Search tool decision tree
What are you looking for?
│
├── A concept, pattern, or idea ──────────► semantic_search
│
├── An exact string or regex ─────────────► grep_search
│ └── Within a specific file? ──────────► grep_search + includePattern
│
├── A file by name/pattern ───────────────► file_search
│
├── Content of a known file ──────────────► read_file (specify line range!)
│
└── Directory structure ──────────────────► list_dir
✏️ Edit tools: modifying the workspace
Edit tools change files on disk. They require the editor capability in YAML.
create_file
Purpose: Create a new file with specified content. Creates parent directories if they don’t exist.
When to use:
- Creating new files (code, documentation, config)
- Scaffolding project structures
- Generating output artifacts
When NOT to use:
- Editing an existing file (use
replace_string_in_file) - Renaming or moving files (use
run_in_terminalwithmv)
Key parameters:
filePath(required): Absolute path for the new filecontent(required): Complete file contents
Token cost: Primarily output tokens for the content generated. A 100-line file costs ~1,000 output tokens.
replace_string_in_file
Purpose: Replace exactly one occurrence of a literal string in an existing file. The key constraint: your oldString must match exactly one location in the file.
When to use:
- Modifying existing code or content
- Targeted edits where you know the exact context
- Any edit where you need to preserve surrounding code
Key parameters:
filePath(required): Absolute path to the fileoldString(required): The exact literal text to replace (include 3+ lines of context before and after)newString(required): The replacement text
Token cost: ~200-500 tokens per operation (context lines + replacement)
Best practices:
- Include at least three lines of unchanged context before and after the target text
- If the same string appears multiple times, add more context lines to disambiguate
- For multiple edits to the same file, try
multi_replace_string_in_fileinstead
multi_replace_string_in_file
Purpose: Apply multiple replacements in a single call—across the same file or different files. More efficient than sequential replace_string_in_file calls.
When to use:
- Making several related edits across files
- Batch-editing a single file with multiple changes
- Refactoring operations that touch multiple locations
Key parameters:
explanation(required): Description of the batch operationreplacements(required): Array of{filePath, oldString, newString}objects
Token cost: Similar per-replacement cost as replace_string_in_file, but avoids the overhead of multiple tool round-trips.
Edit tool decision tree
What do you need to do?
│
├── Create a new file ────────────────────► create_file
│
├── Edit one location in a file ──────────► replace_string_in_file
│
├── Edit multiple locations ──────────────► multi_replace_string_in_file
│ (same file or different files)
│
└── Rename or move a file ───────────────► run_in_terminal (mv command)
🖥️ Execution tools: running commands
Execution tools run shell commands and inspect build/lint results.
run_in_terminal
Purpose: Execute PowerShell/shell commands in a persistent terminal session. The terminal preserves environment variables, working directory, and context between calls.
When to use:
- Running build commands (
dotnet build,npm install) - Running tests (
pytest,npm test) - Git operations (
git status,git diff) - Any operation that requires shell access
Key parameters:
command(required): The command to executeexplanation(required): One-sentence description shown to the usergoal(required): Short description of purposeisBackground(required):truefor long-running processes (servers),falsefor blockingtimeout(optional): Milliseconds before stopping output tracking
Token cost: Variable—depends on command output. Build outputs can be 2,000-10,000+ tokens. Use filtering (e.g., Select-Object -Last 20) to limit output.
Best practices:
- Never run multiple
run_in_terminalcalls in parallel—they share a session - For long-running processes (servers, watch mode), use
isBackground: true - Filter verbose output to reduce token consumption
- Define explicit command boundaries in agent instructions (what’s ALLOWED vs. NEVER)
get_terminal_output
Purpose: Check the output of a previously started background terminal command.
When to use:
- Checking on a background server you started
- Getting build results from a background process
Key parameters:
id(required): Terminal ID returned byrun_in_terminal
Token cost: Depends on accumulated output.
get_errors
Purpose: Get compile errors, lint warnings, and diagnostics from VS Code’s problems panel.
When to use:
- After editing code to verify changes don’t introduce errors
- Checking overall project health
- Finding type errors, lint violations, import issues
Key parameters:
filePaths(optional): Specific files/folders to check. Omit for all errors.
Token cost: ~50-200 tokens per error. A clean project returns minimal tokens.
💬 Interaction tools: communicating with users
These tools let the agent interact with the user during execution. They’re always available—you can’t restrict them.
ask_questions
Purpose: Present structured questions to the user with predefined options. The only way to get explicit user input during prompt execution without interrupting the flow.
When to use:
- Clarifying ambiguous requirements before proceeding
- Getting user preferences on implementation choices
- Confirming decisions that meaningfully affect the outcome
When NOT to use:
- The answer is determinable from code or context
- Asking permission to continue or abort (just proceed)
- Reporting a problem (attempt to resolve it instead)
- Quizzes or polls (recommended options are pre-selected and visible—they’d reveal answers)
Key parameters:
questions(required): Array of 1-4 questions, each with:header(required): Short label (max 12 chars)question(required): Full question textoptions(optional): 0-6 options withlabel,description,recommendedmultiSelect(optional): Allow multiple selectionsallowFreeformInput(optional): Allow custom text alongside options
Token cost: ~300-500 tokens per question set (the question definition + user response).
Example prompt guidance:
If the user's intent is ambiguous, use ask_questions to clarify.
Batch related questions into a single call (max 4 questions).
Provide a sensible default as the recommended option so users can confirm quickly.manage_todo_list
Purpose: Maintain a structured todo list visible in the chat UI. Tracks task progress with explicit state transitions.
When to use:
- Complex multi-step work requiring planning and tracking
- When the user provides multiple tasks or requests
- Before starting work on any major step (mark as in-progress)
- Immediately after completing each step (mark as completed)
When NOT to use:
- Single, trivial tasks completable in one step
- Purely conversational or informational requests
- Simple file reads or searches
Key parameters:
todoList(required): Complete array of all todo items, each with:id(required): Sequential number starting from 1title(required): Concise 3-7 word action labelstatus(required):not-started,in-progress, orcompleted
Token cost: ~500 tokens overhead for a 5-step workflow (the full list is sent with every update).
How it works:
manage_todo_list serves two purposes:
- User visibility: Shows a live progress tracker in the chat UI, so the user can see what the agent is doing, what’s next, and what’s done
- Agent reliability: Acts as explicit state checkpointing—the agent commits to a task before starting it and records completion, reducing the chance of skipped steps or going in circles
The tradeoff is token cost vs. reliability. For a five-step workflow, the overhead is ~500 tokens across all updates. That’s negligible compared to the 50,000+ tokens a five-step orchestration typically consumes.
Best practices:
## Task Management
- Create a todo list at the start of multi-step work
- Mark ONE todo as in-progress at a time
- Mark each todo completed IMMEDIATELY after finishing it
- Don't batch completions—update status individuallyWhen to use
- [ ]checkboxes instead: Markdown checkboxes (- [ ] Step 1,- [x] Step 2) are a lighter-weight alternative that some models recognize as task tracking signals. They don’t render in the UI or provide the same reliability guarantees asmanage_todo_list, but they cost zero additional tokens since they’re just part of the prompt content.
🤝 Delegation tools: orchestrating agents
Delegation tools let agents spawn other agents to handle subtasks. They’re always available.
runSubagent
Purpose: Launch a new agent to handle a complex, multi-step task autonomously. The subagent gets its own clean context window and returns a single summary.
When to use:
- Complex tasks that benefit from isolated, focused context
- Research or analysis that would bloat the main agent’s context
- Tasks requiring different tools or model than the main agent
When NOT to use:
- Simple tasks completable in one or two tool calls
- Tasks that need the main conversation’s full context
Key parameters:
prompt(required): Detailed task description for the subagentdescription(required): Short 3-5 word summaryagentName(optional): Name of a specific custom agent to invoke
Token cost: The subagent consumes its own token budget. Only the final summary (typically 500-2,000 tokens) returns to the main context.
For complete subagent orchestration patterns, see How to Design Subagent Orchestrations.
tool_search_tool_regex
Purpose: Discover and load deferred tools from MCP servers and VS Code extensions. This is the meta-tool—it makes other tools available.
When to use:
- Before calling any MCP tool (e.g.,
mcp_github_*,mcp_azure_*) - Before calling any deferred extension tool
- When you need to discover what tools are available from a specific server
When NOT to use:
- For tools already loaded by a previous search
- For built-in tools (they’re always available)
Key parameters:
pattern(required): Python regex pattern matched case-insensitively against tool names, descriptions, and parameters
Token cost: ~200-500 tokens per search (returns matching tool schemas).
How deferred loading works:
Not all tools are loaded at startup. MCP server tools and VS Code extension tools are deferred—they don’t exist until you search for them. The workflow:
1. Agent needs to call mcp_github_create_issue
2. Agent first calls: tool_search_tool_regex("mcp_github_create")
3. Tool schema is returned and loaded into the session
4. Now agent can call mcp_github_create_issue normally
This lazy-loading mechanism keeps startup fast and avoids overwhelming the context with hundreds of tool schemas. But it means you must search before you call—calling a deferred tool directly will fail silently.
🔌 Tool sources: built-in vs. MCP vs. extensions
Tools come from three distinct sources, each with different loading mechanisms, availability, and characteristics.
Architecture overview
┌─────────────────────────────────────────────────────────────┐
│ VS CODE (Host) │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ AI Agent Context │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ │
│ │ │ Built-in │ │ MCP Server │ │ Extension │ │ │
│ │ │ Tools │ │ Tools │ │ Tools │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ read_file │ │ @github/* │ │ configure_ │ │ │
│ │ │ grep_search │ │ @azure/* │ │ notebook │ │ │
│ │ │ create_file │ │ @custom/* │ │ run_task │ │ │
│ │ │ ask_questions│ │ │ │ get_vscode │ │ │
│ │ │ manage_todo │ │ │ │ _api │ │ │
│ │ │ runSubagent │ │ │ │ │ │ │
│ │ │ ... │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ EAGER LOAD │ │ DEFERRED │ │ DEFERRED │ │ │
│ │ │ Always ready │ │ Need search │ │ Need search │ │ │
│ │ └──────────────┘ └──────┬───────┘ └──────┬──────┘ │ │
│ │ │ │ │ │
│ │ tool_search_tool_regex │ │ │
│ │ (discovers and loads)────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ MCP Server │ │ MCP Server │ │
│ │ Process (stdio) │ │ Process (SSE) │ │
│ │ e.g., IQPilot │ │ e.g., GitHub │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Source comparison
| Aspect | Built-in Tools | MCP Server Tools | Extension Tools |
|---|---|---|---|
| Loading | Eager—always available | Deferred—need tool_search_tool_regex |
Deferred—need tool_search_tool_regex |
| Provider | VS Code core | External processes (stdio/SSE) | VS Code extensions |
| Examples | read_file, create_file, manage_todo_list |
mcp_github_*, mcp_azure_*, custom servers |
run_task, configure_notebook, get_vscode_api |
| Configuration | None needed | .vscode/settings.json or .vscode/mcp.json |
Extension installation |
| Availability | VS Code + Visual Studio | VS Code only (currently) | VS Code only |
| Customization | None—fixed by VS Code | Full control—you can build your own | Extension developer controls |
📊 Token cost analysis
Every tool call consumes tokens—both for the request (function name + arguments) and the response (tool output). Understanding these costs helps you write token-efficient prompts.
Per-tool cost estimates
| Tool | Request Cost | Typical Response Cost | Total (Typical) | Notes |
|---|---|---|---|---|
read_file (50 lines) |
~50 tokens | ~500 tokens | ~550 | Scales linearly with line count |
read_file (500 lines) |
~50 tokens | ~5,000 tokens | ~5,050 | Expensive for large reads |
grep_search (20 matches) |
~80 tokens | ~1,500 tokens | ~1,580 | Compact per-match format |
semantic_search (10 results) |
~50 tokens | ~2,000 tokens | ~2,050 | Variable snippet sizes |
file_search |
~50 tokens | ~100-500 tokens | ~300 | Cheapest—paths only |
list_dir |
~30 tokens | ~100-300 tokens | ~200 | Very cheap |
create_file (100 lines) |
~1,000 tokens | ~50 tokens | ~1,050 | Cost is in output (content) |
replace_string_in_file |
~200-500 tokens | ~50 tokens | ~400 | Context lines drive cost |
multi_replace_string_in_file |
~500-2,000 tokens | ~100 tokens | ~1,200 | Amortizes round-trip overhead |
run_in_terminal |
~100 tokens | ~200-10,000 tokens | ~2,000 | Use output filtering |
get_errors |
~30 tokens | ~50-1,000 tokens | ~300 | Clean project = cheap |
ask_questions |
~300-500 tokens | ~100 tokens | ~500 | Per question set |
manage_todo_list |
~200-500 tokens | ~50 tokens | ~400 | Full list sent each update |
runSubagent |
~200 tokens | ~500-2,000 tokens | ~1,500 | Only summary returns |
tool_search_tool_regex |
~50 tokens | ~200-500 tokens | ~350 | Loads tool schemas |
Expensive patterns to avoid
| Pattern | Cost | Better Alternative |
|---|---|---|
| Reading entire large files | ~5,000+ tokens | grep_search to find relevant lines, then read_file on specific range |
Sequential replace_string_in_file × 5 |
~2,500 tokens | Single multi_replace_string_in_file = ~1,200 tokens |
Multiple semantic_search calls |
~2,000 each | One grep_search with regex alternation (e.g., word1\|word2\|word3) |
Unfiltered run_in_terminal output |
~10,000+ tokens | Pipe through Select-Object -Last 20 or filter with Where-Object |
read_file to scan for a string |
~5,000 tokens | grep_search with includePattern on the file |
Cheap patterns to prefer
| Pattern | Why It’s Cheap |
|---|---|
file_search before read_file |
Paths cost ~100 tokens; avoids reading wrong files |
grep_search with includePattern |
Scoped search is fast and focused |
list_dir for project discovery |
Directory names cost ~200 tokens |
Parallel independent read_file calls |
Same total tokens, but faster wall-clock time |
get_errors after edits |
Cheap validation; catches issues before expensive retry loops |
For comprehensive token optimization across multi-agent workflows, see How to Optimize Token Consumption.
🎯 Tool selection strategy
By prompt type
| Prompt Type | Recommended YAML Capabilities | Key Runtime Tools |
|---|---|---|
| Planning / Research | ['codebase', 'filesystem', 'search'] |
semantic_search, grep_search, file_search, read_file, list_dir |
| Code Review | ['codebase', 'search', 'problems', 'changes'] |
grep_search, read_file, get_errors, get_changed_files |
| Implementation | ['codebase', 'editor', 'filesystem', 'search'] |
read_file, create_file, replace_string_in_file, multi_replace_string_in_file, grep_search |
| Documentation | ['codebase', 'editor', 'filesystem', 'web_search'] |
semantic_search, read_file, create_file, replace_string_in_file |
| Testing | ['codebase', 'editor', 'search', 'problems'] |
grep_search, read_file, create_file, run_in_terminal, get_errors |
| Orchestrator | ['codebase', 'search'] + agent |
semantic_search, read_file, runSubagent, manage_todo_list |
| Security Audit | ['codebase', 'search', 'web_search'] |
grep_search, read_file, semantic_search |
By agent role
In multi-agent architectures, restrict tools to match each agent’s responsibilities:
# Researcher — read-only, can't modify anything
---
name: Researcher
tools: ['codebase', 'search', 'filesystem', 'fetch']
---
# Builder — can edit, can't browse the web
---
name: Builder
tools: ['codebase', 'editor', 'filesystem', 'search']
---
# Reviewer — read-only with error checking
---
name: Reviewer
tools: ['codebase', 'search', 'problems', 'changes']
---
# Orchestrator — delegates, doesn't implement
---
name: Orchestrator
tools: ['codebase', 'search', 'agent']
---🛡️ Tool restrictions and safety
Why restrict tools?
- Security: Limit external network access (
fetch,web_search) for internal agents - Safety: Prevent unintended file modifications by read-only agents
- Focus: Remove irrelevant capabilities to reduce tool-calling noise
- Token efficiency: Fewer available tools means less schema in context
Restricting tools in agent YAML
# Security reviewer — must NOT edit files
tools: ['codebase', 'search', 'fetch'] # Excludes 'editor'
# Planner — must NOT execute commands
tools: ['codebase', 'search', 'usages'] # Excludes 'editor' and terminal
# Auditor — no external access
tools: ['codebase', 'editor', 'filesystem'] # Excludes 'fetch', 'web_search'Terminal command boundaries
When an agent has terminal access, define explicit boundaries in its instructions:
## Terminal usage boundaries
**ALLOWED commands:**
- `npm install`, `npm run build`, `npm test`
- `dotnet build`, `dotnet test`
- `git status`, `git diff`
**NEVER execute:**
- `rm -rf`, `del /s`, or any recursive deletion
- Commands that modify system configuration
- Commands that access credentials or secretsWhat you CAN’T restrict
Remember: manage_todo_list, ask_questions, runSubagent, and tool_search_tool_regex are always available regardless of the tools field. To prevent subagent usage, use the agents field instead:
# Can use runSubagent but restricted to specific agents
agents: ['researcher', 'reviewer'] # Only these subagents allowed
agents: [] # No subagent invocationTool availability by platform
| Capability | VS Code 1.109+ | Visual Studio 17.14+ | GitHub CLI | Copilot SDK |
|---|---|---|---|---|
codebase |
Full | Full | Partial | Full |
editor |
Full | Full | Full | Full |
filesystem |
Full | Full | Full | Full |
fetch |
Full | Limited | Full | Full |
web_search |
Full | Limited | Limited | Depends on model |
| MCP tools | Full | Not supported | Not supported | Full |
runSubagent |
Full | Not supported | Not supported | Full |
🔧 Practical patterns
Pattern 1: Guiding tool behavior from prompt instructions
You can influence which runtime tools the model uses and how it uses them by including guidance in your prompt’s markdown body:
## Context gathering strategy
1. Start with `file_search` for `**/*.config.*` to discover configuration files
2. Use `grep_search` to find references to the target feature flag
3. Only use `read_file` on files you've confirmed are relevant (specify line ranges)
4. DON'T use `semantic_search` for this task—we need exact matches
## Editing strategy
When making changes, use `multi_replace_string_in_file` to batch all edits
rather than making sequential single-file edits.
Always run `get_errors` after editing to verify your changes compile.This pattern bridges Level 1 (what’s available) and Level 2 (what gets used). Even if all tools are available, your prompt instructions can steer the agent toward the most efficient ones.
Pattern 2: Context-efficient file analysis
Instead of reading entire files, use a two-phase approach:
## Analysis approach
**Phase 1: Discovery** (cheap)
1. Use `list_dir` to understand project structure
2. Use `file_search` to find relevant files by name pattern
3. Use `grep_search` to find specific patterns within those files
**Phase 2: Deep read** (targeted)
4. Use `read_file` ONLY on the specific line ranges identified in Phase 1
5. Read 30-50 lines of context around each match, not the entire filePattern 3: Error-driven editing loop
## Implementation workflow
1. Read the target file with `read_file` to understand existing structure
2. Make changes with `replace_string_in_file` or `create_file`
3. Run `get_errors` on the modified file
4. If errors exist, fix them and re-run `get_errors`
5. Repeat until clean (max 3 iterations, then ask the user for help)Pattern 4: Tool fallback chains
## Search strategy with fallbacks
1. Try `grep_search` with the exact function name
2. If no results, broaden with `grep_search` using regex: `functionName|related_name`
3. If still no results, use `semantic_search` with a natural language description
4. If still no results, use `list_dir` to explore the project structure manuallyPattern 5: Parallel tool execution
Independent read operations can run in parallel for faster results:
## Parallel context gathering
Read these files simultaneously:
- `read_file` on src/config.ts (lines 1-50)
- `read_file` on src/types.ts (lines 1-30)
- `grep_search` for "export interface" in src/**
Then analyze the results together before proceeding.⚠️ Parallelism rules: Multiple
read_file,grep_search,file_search, andlist_dircalls can run in parallel.semantic_searchcannot run in parallel with othersemantic_searchcalls.run_in_terminalcalls should never run in parallel (they share a shell session).
⚠️ Common pitfalls
Pitfall 1: Confusing Level 1 and Level 2
# ❌ Wrong: these are runtime tool names, not YAML capabilities
tools: ['read_file', 'grep_search', 'create_file']
# ✅ Correct: use capability categories
tools: ['codebase', 'editor', 'filesystem']The tools YAML field takes capability categories (Level 1), not individual function names (Level 2).
Pitfall 2: Reading entire files
# ❌ Expensive: reads entire 500-line file (~5,000 tokens)
Read src/service.ts to find the authenticate function.
# ✅ Efficient: grep finds the line, then read the context (~600 tokens)
Use grep_search for "authenticate" in src/service.ts,
then read_file on the matching line range.Pitfall 3: Sequential single-file edits
# ❌ Slow and expensive: 5 separate tool calls
Replace "oldName" with "newName" in each of these 5 files.
# ✅ Efficient: single batched call
Use multi_replace_string_in_file to rename across all 5 files at once.Pitfall 4: Missing deferred tool search
# ❌ Will fail: MCP tools aren't loaded by default
Call mcp_github_create_issue to open a bug report.
# ✅ Correct: search first, then call
Search for GitHub MCP tools with tool_search_tool_regex,
then call mcp_github_create_issue.Pitfall 5: Unrestricted terminal access
# ❌ Dangerous: agent can run anything
---
name: Code Helper
tools: ['editor', 'codebase'] # terminal implicitly available
---
# ✅ Safer: explicit tool list, terminal boundaries in instructions
---
name: Code Helper
tools: ['editor', 'codebase', 'search']
---
Only use run_in_terminal for: npm install, npm test, dotnet build.
NEVER run destructive commands or access credentials.Pitfall 6: Not validating after edits
# ❌ Risky: doesn't check for errors
Edit the file and move on.
# ✅ Robust: validates every change
After each edit, use get_errors to verify.
If errors exist, fix them before proceeding.🎯 Conclusion
Tools are the foundation of everything your prompts and agents can do. Understanding the two-level architecture—YAML capability declarations that gate runtime function calls—gives you precise control over agent behavior.
Key takeaways:
- Level 1 (YAML) controls permissions; Level 2 (runtime) controls actions. Don’t confuse them.
- Search tools are your most-used category. Use the decision tree: concepts →
semantic_search, exact strings →grep_search, file names →file_search, known content →read_filewith line ranges. - Edit tools should be batched with
multi_replace_string_in_filewhen possible, and always validated withget_errorsafterward. - Interaction tools (
ask_questions,manage_todo_list) are always available and cost-effective for improving reliability. - Delegation tools (
runSubagent) are key for orchestration—covered in depth in the subagent orchestrations article. - Token costs matter. Prefer
grep_search+ targetedread_fileover reading entire files. Batch edits. Filter terminal output. - Restrict tools by agent role to improve safety, focus, and token efficiency.
With this foundation, you’re ready to build orchestrations where each agent has exactly the tools it needs—no more, no less. The next article, How to Design Orchestrator Prompts, uses these tools as building blocks for multi-agent coordination.
Series navigation:
📚 References
📘 GitHub Copilot Chat Tools — VS Code Documentation 📘 [Official]
Primary reference for tool configuration in VS Code. Covers the tools YAML field, tool sets, MCP tool references, and platform support. Essential reading for Level 1 configuration.
📘 VS Code Chat Subagents Documentation 📘 [Official]
Official documentation for the runSubagent tool and custom agents as subagents. Covers user-invokable, disable-model-invocation, and the agents array.
📘 Model Context Protocol Specification 📘 [Official]
The formal specification for MCP tools, resources, and transport protocols. Required reading for building custom MCP servers.
📘 VS Code MCP Server Configuration 📘 [Official]
How to configure MCP servers in VS Code settings and workspace files. Covers stdio and SSE transports, authentication, and server management.
📗 GitHub Copilot Customization — Custom Instructions 📗 [Verified Community]
GitHub’s documentation for custom instructions, prompts, and agent files. Cross-reference for how tool configuration fits into the broader customization system.
📙 How to Create MCP Servers for Copilot [Internal]
Series article covering MCP server development—the supply side of tools. Read this to understand how custom tools are built and registered.
📙 How to Structure Content for Agent Files [Internal]
Series article covering agent YAML configuration, including the tools field, agents field for subagent restrictions, and tool priority ordering.
📙 How to Design Subagent Orchestrations [Internal]
Series article with deep coverage of the runSubagent tool, coordinator/worker patterns, and context isolation mechanics.