Ensure renamed built-in tool references preserve backward compatibility. Use when renaming a toolReferenceName, tool set referenceName, or any tool identifier. Run on ANY change to tool registration code. Covers legacyToolReferenceFullNames for tools and legacyFullNames for tool sets.
When a tool or tool set reference name is changed, the old name must always be added to the deprecated/legacy array so that existing prompt files, tool configurations, and saved references continue to resolve correctly.
Run this skill on any change to built-in tool or tool set registration code to catch regressions:
toolReferenceNamereferenceNametoolSet/toolName path becomes a legacy name)Determine whether you are renaming a tool or a tool set, and where it is registered:
| Entity | Registration | Name field to rename |
|---|
| Legacy array |
|---|
| Stable ID (NEVER change) |
|---|
Tool (IToolData) | TypeScript | toolReferenceName | legacyToolReferenceFullNames | id |
| Tool (extension) | package.json languageModelTools | toolReferenceName | legacyToolReferenceFullNames | name (becomes id) |
Tool set (IToolSet) | TypeScript | referenceName | legacyFullNames | id |
| Tool set (extension) | package.json languageModelToolSets | name or referenceName | legacyFullNames | — |
Critical: For extension-contributed tools, the name field in package.json is mapped to id on IToolData (see languageModelToolsContribution.ts line id: rawTool.name). It is also used for activation events (onLanguageModelTool:<name>). Never rename the name field — only rename toolReferenceName.
Verify the old toolReferenceName value appears in legacyToolReferenceFullNames. Don't assume it's already there — check the actual array contents. If the old name is already listed (e.g., from a previous rename), confirm it wasn't removed. If it's not there, add it.
For internal/built-in tools (TypeScript IToolData):
// Before rename
export const MyToolData: IToolData = {
id: 'myExtension.myTool',
toolReferenceName: 'oldName',
// ...
};
// After rename — old name preserved
export const MyToolData: IToolData = {
id: 'myExtension.myTool',
toolReferenceName: 'newName',
legacyToolReferenceFullNames: ['oldName'],
// ...
};
If the tool previously lived inside a tool set, use the full toolSet/toolName form:
legacyToolReferenceFullNames: ['oldToolSet/oldToolName'],
If renaming multiple times, accumulate all prior names — never remove existing entries:
legacyToolReferenceFullNames: ['firstOldName', 'secondOldName'],
For tool sets, add the old name to the legacyFullNames option when calling createToolSet:
toolsService.createToolSet(source, id, 'newSetName', {
legacyFullNames: ['oldSetName'],
});
For extension-contributed tools (package.json), rename only toolReferenceName and add the old value to legacyToolReferenceFullNames. Do NOT rename the name field:
// CORRECT — only toolReferenceName changes, name stays stable
{
"name": "copilot_myTool", // ← KEEP this unchanged
"toolReferenceName": "newName", // ← renamed
"legacyToolReferenceFullNames": [
"oldName" // ← old toolReferenceName preserved
]
}
Legacy names must be respected everywhere a tool is looked up by reference name, not just in prompt resolution. Key consumers:
getDeprecatedFullReferenceNames() maps old → current names for .prompt.md validation and code actionsgetToolAliases() / getToolSetAliases() yield legacy names so tool picker and enablement maps resolve themisToolEligibleForAutoApproval() checks legacyToolReferenceFullNames (including the segment after / for namespaced legacy names) against chat.tools.eligibleForAutoApproval settingsLEGACY_TOOL_REFERENCE_FULL_NAMESAfter renaming, confirm:
#oldName in a .prompt.md file still resolves (shows no validation error)"chat.tools.eligibleForAutoApproval": { "oldName": false } still has that restriction honoredWhile legacy names ensure backward compatibility, update first-party references to use the new name:
.prompt.md files| File | What it contains |
|---|---|
src/vs/workbench/contrib/chat/common/tools/languageModelToolsService.ts | IToolData and IToolSet interfaces with legacy name fields |
src/vs/workbench/contrib/chat/browser/tools/languageModelToolsService.ts | Resolution logic: getToolAliases, getToolSetAliases, getDeprecatedFullReferenceNames, isToolEligibleForAutoApproval |
src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts | Extension point schema, validation, and the critical id: rawTool.name mapping (line ~274) |
src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/tools/runInTerminalTool.ts | Example of a tool with its own local auto-approval check against legacy names |
runInTerminal tool: renamed from runCommands/runInTerminal → legacyToolReferenceFullNames: ['runCommands/runInTerminal']todo tool: renamed from todos → legacyToolReferenceFullNames: ['todos']getTaskOutput tool: renamed from runTasks/getTaskOutput → legacyToolReferenceFullNames: ['runTasks/getTaskOutput']legacyToolReferenceFullNames and legacyFullNames, built the resolution infrastructure, and performed the first batch of tool renames. Use as a template for how to properly rename with legacy names.eligibleForAutoApproval setting wasn't checking legacy names — users who had restricted the old name lost that restriction. Shows why all consumers of tool reference names must account for legacy names.openSimpleBrowser → openIntegratedBrowser but also changed the name field (stable id) from copilot_openSimpleBrowser → copilot_openIntegratedBrowser. The toolReferenceName backward compat only worked by coincidence (the old name happened to already be in the legacy array from a prior change — it was not intentionally added as part of this rename).Run this check on any PR that touches tool registration (TypeScript IToolData, createToolSet, or package.json languageModelTools/languageModelToolSets):
toolReferenceName or referenceName values. For each change, confirm the previous value now appears in legacyToolReferenceFullNames or legacyFullNames. Don't assume it was already there — read the actual array.name fields on extension-contributed tools. The name field is the tool's stable id — it must never change. If it changed, flag it as a bug. (This breaks activation events, tool invocations by id, and any code referencing the tool by its name.)toolSet/toolName full path is in the legacy array.tools array in languageModelToolSets contributions). If a tool's toolReferenceName changed, any tool set tools array referencing the old name should be updated — but the legacy resolution system handles this, so the old name still works.name field on extension-contributed tools — the name in package.json becomes the id on IToolData (via id: rawTool.name in languageModelToolsContribution.ts). Changing it breaks activation events (onLanguageModelTool:<name>), any code referencing the tool by id, and tool invocations. Only rename toolReferenceName, never name. (See vscode-copilot-chat#3810 where both name and toolReferenceName were changed.)id field on TypeScript-registered tools — same principle as above. The id is a stable internal identifier and must never change.legacyToolReferenceFullNames contents, not just checking that the field exists. A legacy array might list names from an even older rename but not the current one being changed.RunInTerminalTool) all need to respect legacy names (see #278506).