`${PLUGIN_ROOT}` / `${PLUGIN_DATA}` never expanded for Agent Plugins 1.0 (canonical schema) plugins — pluginRootTokens empty for format 3
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
Type: Bug
For a plugin registered via `chat.pluginLocations` whose `plugin.json` declares the canonical Agent Plugins 1.0 schema (`"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json"`), `${PLUGIN_ROOT}` and `${PLUGIN_DATA}` are never expanded in the plugin's `mcp.json` `args`, `env`, or `cwd` fields — contrary to spec [§9.2](https://agent-plugins.org/specification#9-environment-variables-and-placeholder-expansion) and the [VS Code docs](https://code.visualstudio.com/docs/agent-customization/agent-plugins#_plugin-environment-variables).
#### Steps to reproduce
1. Create a local plugin directory:
```
my-plugin/
plugin.json
mcp.json
```
2. `plugin.json`:
```json
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "my-plugin"
}
```
3. `mcp.json`:
```json
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
"mcpServers": {
"test": {
"type": "stdio",
"command": "/bin/echo",
"args": ["ROOT=${PLUGIN_ROOT}", "DATA=${PLUGIN_DATA}"]
}
}
}
```
4. Register it in `settings.json`:
```json
"chat.plugins.enabled": true,
"chat.pluginLocations": {
"~/path/to/my-plugin": true
}
```
5. Reload VS Code, run "MCP: List Servers" → `test` → start it, check the server output.
#### Expected
Output shows `ROOT=/absolute/path/to/my-plugin DATA=`.
#### Actual
Output shows the literal, unexpanded string: `ROOT=${PLUGIN_ROOT} DATA=${PLUGIN_DATA}`.
#### No workaround found
Removing the `agent-plugins.org` `$schema` from `plugin.json` changes discovery semantics and does not make this `mcp.json`-based package work under the legacy format. It is therefore not a usable workaround.
#### Root cause (found by inspecting `workbench.desktop.main.js`)
VS Code maintains a per-plugin-format table with `pluginRootTokens`/`pluginRootEnvVars` used by the MCP config expansion function. For legacy formats (Copilot, Claude, legacy OpenPlugin), this is populated:
```js
pluginRootTokens: ["${PLUGIN_ROOT}", "${CLAUDE_PLUGIN_ROOT}"],
pluginRootEnvVars: ["PLUGIN_ROOT", "CLAUDE_PLUGIN_ROOT"]
```
But for the canonical Agent Plugins 1.0 format (detected via the `agent-plugins.org/.../plugin.schema.json` `$schema` match), the table entry has:
```js
pluginRootTokens: [],
pluginRootEnvVars: [],
```
The expansion function iterates this array to do the token substitution, so an empty array means no substitution ever happens, and `PLUGIN_ROOT`/`PLUGIN_DATA` are also never injected into the subprocess environment for this format.
The fix needs to be format-aware: simply adding canonical tokens to the existing generic arrays may also expand fields that Agent Plugins 1.0 does not permit to use placeholders, such as `command` (and possibly HTTP `url` or `headers`). For the canonical format, expansion must be limited to stdio `args`, `env` values, and `cwd`, while `PLUGIN_ROOT` and `PLUGIN_DATA` are injected into the subprocess environment.
The VS Code documentation currently says that Agent Plugins 1.0 placeholders are preserved for the plugin runtime to expand, whereas the Agent Plugins specification requires the client to perform expansion. The missing subprocess environment variables show that VS Code does not provide a usable runtime-expansion alternative.
#### Does this reproduce with `--disable-extensions`?
Yes. Ran `code --disable-extensions --new-window` against a minimal repro folder containing only the `plugin.json`/`mcp.json` above, registered via `chat.pluginLocations`. Server output was identical:
```
2026-09-08 08:33:12.066 [info] Starting server test
2026-09-08 08:33:12.066 [info] Connection state: Starting
2026-09-08 08:33:12.066 [info] Starting server from LocalProcess extension host
2026-09-08 08:33:12.068 [info] Connection state: Starting
2026-09-08 08:33:12.068 [info] Connection state: Running
2026-09-08 08:33:12.142 [warning] Failed to parse message: "ROOT=${PLUGIN_ROOT} DATA=${PLUGIN_DATA}\n"
2026-09-08 08:33:12.142 [info] Connection state: Stopped
```
(The "Failed to parse message" warning is expected/harmless — `/bin/echo`'s plain-text output isn't valid MCP JSON-RPC, it's only used here to observe the literal `args` values.)
#### Environment
- VS Code: 1.136.1 (arm64, macOS)
- `chat.plugins.enabled`: true
VS Code version: Code 1.136.1 (Universal) (a44adf7f53e00964ab890f9f8758a334f1fc15bc, 2026-09-03T05:06:41Z)
OS version: Darwin arm64 25.6.0
Modes:
System Info
|Item|Value|
|---|---|
|CPUs|Apple M1 Pro (8 x 2400)|
|GPU Status|2d_canvas: enabled
GPU0: VENDOR= 0x106b [Google Inc. (Apple)], DEVICE=0x0000 [ANGLE (Apple, ANGLE Metal Renderer: Apple M1 Pro, Version 26.6.2 (Build 25G83))], DRIVER_VENDOR=Apple, DRIVER_VERSION=26.6.2 *ACTIVE*
Machine model name: MacBookPro
Machine model version: 18.3
direct_rendering_display_compositor: disabled_off_ok
gpu_compositing: enabled
multiple_raster_threads: enabled_on
opengl: enabled_on
rasterization: enabled
raw_draw: disabled_off_ok
skia_graphite: enabled_on
trees_in_viz: disabled_off
video_decode: enabled
video_encode: enabled
webgl: enabled
webgpu: enabled
webnn: disabled_off|
|Load (avg)|10, 11, 9|
|Memory (System)|16.00GB (0.11GB free)|
|Process Argv|--crash-reporter-id 7ac02f85-68ed-428c-9d8a-35f6e2d25298|
|Screen Reader|no|
|VM|0%|
Extensions disabled
A/B Experiments
```
vsliv368:30146709
pythonvspyt551:31249599
binariesv615:30325510
nativeloc1:31344060
dwcopilot:31170013
dwoutputs:31242946
copilot_t_ci:31333650
e5gg6876:31282496
pythonrdcb7:31342333
6518g693:31463988
aj953862:31281341
envsactivate1:31551504
478i5457:31544283
cloudbuttont:31379625
3efgi100_wstrepl:31403338
ddidtcf:31399634
ec5jj548:31422691
diffpatch-lysithea-24-production:31579162
cp_cls_t_966_ss:31526232
4je02754:31572277
8hhj4413:31478653
ge8j1254_inline_auto_hint_haiku:31490510
cp_cls_t_1081:31454832
conptydll_true:31498968
e9c30283:31461165
46204921:31447328
ei9d7968:31496641
chat:31457767
8hig5102:31480529
89g7j272:31518289
i2gc6536:31499202
52612955:31516516
ddid_t:31478206
hmra_i5g22:31518061
7df3h592:31512476
cp_cls_t_1082:31535311
logging_enabled_new:31498466
j0d79568:31499440
jb_cp_cls_t_632:31543129
56dj4588:31512888
32d76977:31512328
ha629193:31508444
jh5f2457_c:31540921
jbcp_cls_pctr_t:31531130
cp_intellij_t_nes:31548657
jf4hg949:31526829
ahp-both-windows:31556933
ihg5j128:31534457
7g2b5551:31542111
sandbox-ui-on-2:31543161
enable_editor_pane_layout:31569726
allow-none:31555437
ac562147:31564510
c7c27ce7:31554789
1h923230:31564177
1532g621_copy:31554320
treatment-23-1:31555779
unuse_dynamic_mcp:31555281
0d8dfbc3:31570291
switchtoauto:31559321
0c1h4866:31566224
vrbsty_tr:31561058
session-mark-done:31558131
5b8j3302:31564601
mangle-name-treatment:31572634
signed-out-control:31574474
intellij_nes_preview_t:31580086
39a5d156:31574986
2bfb8b39:31576696
permission_prompt_control:1332567
ccr_pr_nudge_control:1319470
vsc_wsm_t:1340632
jidc7660:1370295
auto_mode_treatment:1367907
h5e14506:1348996
cfee5631:1379228
```
Contributor guide
Assessment
This issue has not been assessed yet.