Skip to content

feat: allow user-defined toolset names in agent (.agent.md) tools: frontmatter #299642

@sealad886

Description

@sealad886

Summary

User-defined toolsets (defined in *.toolsets.jsonc files) cannot be referenced by name in .agent.md tools: frontmatter. Any attempt to use a user toolset name produces "Unknown tool 'toolset-name'." validation errors. This makes user toolsets far less useful than intended — the most natural use case is writing tools: ['my-docs', 'search'] in an agent file and having that expand to the toolset's tool list.

Environment

  • VS Code Version: 1.110.0
  • Copilot Chat Extension: github.copilot-chat-0.38.1
  • OS: macOS 15.x

Steps to Reproduce

  1. Create a my-tools.toolsets.jsonc in your profile's promptsHome directory:

    {
      "my-docs": {
        "tools": ["context7/resolve-library-id", "context7/query-docs"],
        "description": "Library documentation tools"
      }
    }
  2. Reference the toolset name in an .agent.md file:

    ---
    name: my-agent
    tools: ['edit', 'search', 'my-docs']
    ---
  3. Open the agent file in VS Code — the my-docs entry is immediately underlined with:

    Unknown tool 'my-docs'.

Expected Behavior

User-defined toolset names (e.g., my-docs) should be valid entries in .agent.md tools: frontmatter arrays, expanding at runtime to the tools they contain — exactly as MCP server toolset wildcards (context7/*) work today.

Actual Behavior

All user-defined toolset names produce "Unknown tool 'toolset-name'." validation errors, making user-defined toolsets exclusively a chat-panel UI feature.

Root Cause (from source inspection)

In workbench.desktop.main.js, toolsWithFullReferenceName is built by iterating all registered toolsets but explicitly skipping user-defined ones:

toolsWithFullReferenceName = xe(e => {
  for (let n of this.toolSets.read(e))
    if (n.source.type !== "user") {   // ← user toolsets excluded here
      t.push([n, Nfo(n)]);
      for (let r of n.getTools()) t.push([r, die(r, n)]);
    }
  // ...
})

validateVSCodeTools() validates agent tools: entries against getFullReferenceNames(), which yields only from toolsWithFullReferenceName. Because user toolsets are excluded from that set, their names always fail validation.

Key Inconsistency: Validator vs. Runtime

The runtime tool-resolution path (toToolAndToolSetEnablementMap) explicitly handles source.type === "user" toolsets — the tools would be correctly enabled at runtime:

// Runtime path — this DOES handle user toolsets:
for (let r of this._toolSets)
  if (r.source.type === "user") {
    let s = mt.every(r.getTools(), l => n.get(l) === true);
    n.set(r, s);
  }

The validator is stricter than the runtime, producing spurious errors for something the runtime would correctly handle.

Proposed Fix

Include source.type === "user" toolsets in toolsWithFullReferenceName (or add a separate pass in validateVSCodeTools for user toolset names), so that:

  1. getFullReferenceNames() returns user toolset names alongside MCP server wildcards.
  2. The validator accepts them without errors.
  3. Validator and runtime behavior are consistent.

Workaround

Users must use MCP server wildcards directly (e.g., context7/*) instead of toolset names, and must manually update every agent file when adding a new MCP server — exactly the problem toolsets are supposed to solve.

Why This Matters

The primary value proposition of user-defined toolsets is to define a capability group once and reference it in many agent files. The current exclusion limits them to the chat-panel tool-picker UI while silently failing in .agent.md frontmatter — the precise context they were designed for.

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions