Skip to content

Commit d810ef5

Browse files
authored
Merge pull request #88 from Automattic/vscode-mcp-provider
Add VS Code workspace MCP configuration
2 parents 963b588 + d7b9a73 commit d810ef5

7 files changed

Lines changed: 353 additions & 7 deletions

File tree

assets/vscode/icon.png

167 KB
Loading

plugins/vscode/README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
# WordPress Studio for VS Code
22

3-
This is the smallest useful VS Code extension scaffold generated by Build with WordPress. It does not publish anything to the Marketplace from this repository.
3+
This VS Code extension integrates WordPress Studio MCP with the current workspace by writing VS Code's supported `.vscode/mcp.json` configuration shape. It does not publish anything to the Marketplace from this repository.
44

55
## What it includes
66

77
- `package.json` with VS Code extension metadata and command contributions.
8-
- `extension.js` with thin commands to check whether `studio` is available and to show or copy the bundled MCP config.
8+
- `extension.js` with commands to validate Studio availability, show/copy the bundled MCP config, and merge the WordPress Studio MCP servers into the open workspace.
99
- `mcp.json` with `wordpress-studio` and `wordpress-telemetry` server entries.
1010
- `skills/` as reference Build with WordPress playbooks for editor users and future extension behavior.
1111

12+
## MCP integration
13+
14+
VS Code supports workspace MCP configuration through `.vscode/mcp.json` with a top-level `servers` object. The `WordPress Studio: Configure Workspace MCP` command creates or updates that file in the open workspace, preserves unrelated server entries, and prompts before replacing an existing managed WordPress server that differs from the bundled configuration.
15+
16+
The extension targets VS Code `^1.95.0`. The published `@types/vscode@1.95.0` API surface does not include `contributes.mcpServerDefinitionProviders` or `vscode.lm.registerMcpServerDefinitionProvider`, so this package does not register an extension-owned MCP provider yet. When this package raises its VS Code engine to a version with stable MCP provider APIs, the file-write command can be complemented with provider registration.
17+
1218
## Marketplace placeholder
1319

1420
The manifest uses the Visual Studio Marketplace publisher `automattic`. This repo intentionally does not include publish automation or Marketplace credentials.
1521

1622
## Commands
1723

1824
- `WordPress Studio: Check Studio CLI` runs `studio --version` and reports whether the CLI is on `PATH`.
25+
- `WordPress Studio: Configure Workspace MCP` merges the bundled `wordpress-studio` server, and `wordpress-telemetry` when bundled, into the open workspace's `.vscode/mcp.json`.
26+
- `WordPress Studio: Validate MCP Config` runs `studio --version` and verifies the bundled VS Code MCP config contains `servers.wordpress-studio`.
1927
- `WordPress Studio: Show MCP Config` opens the bundled `mcp.json` in an untitled JSON editor.
2028
- `WordPress Studio: Copy MCP Config` copies the bundled `mcp.json` to the clipboard.
2129

plugins/vscode/extension.js

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
const vscode = require("vscode");
22
const { execFile } = require("child_process");
3-
const { readFile } = require("fs/promises");
3+
const { mkdir, readFile, writeFile } = require("fs/promises");
44
const path = require("path");
55

6+
const managedServerNames = ["wordpress-studio", "wordpress-telemetry"];
7+
68
function runStudioVersion() {
79
return new Promise((resolve, reject) => {
810
execFile("studio", ["--version"], { timeout: 10000 }, (error, stdout, stderr) => {
@@ -19,6 +21,62 @@ async function readMcpConfig(context) {
1921
return readFile(path.join(context.extensionPath, "mcp.json"), "utf8");
2022
}
2123

24+
async function readBundledMcpConfig(context) {
25+
const raw = await readMcpConfig(context);
26+
const config = JSON.parse(raw);
27+
28+
if (!config.servers || typeof config.servers !== "object") {
29+
throw new Error("Bundled MCP config is missing the VS Code servers wrapper.");
30+
}
31+
32+
if (!config.servers["wordpress-studio"]) {
33+
throw new Error("Bundled MCP config is missing servers.wordpress-studio.");
34+
}
35+
36+
return config;
37+
}
38+
39+
function getWorkspaceFolder() {
40+
const folders = vscode.workspace.workspaceFolders ?? [];
41+
42+
if (folders.length === 0) {
43+
return null;
44+
}
45+
46+
return folders[0];
47+
}
48+
49+
function configsMatch(left, right) {
50+
return JSON.stringify(left) === JSON.stringify(right);
51+
}
52+
53+
async function readWorkspaceMcpConfig(mcpPath) {
54+
try {
55+
const raw = await readFile(mcpPath, "utf8");
56+
const config = JSON.parse(raw);
57+
58+
if (!config || typeof config !== "object" || Array.isArray(config)) {
59+
throw new Error("Workspace MCP config must be a JSON object.");
60+
}
61+
62+
if (config.servers === undefined) {
63+
config.servers = {};
64+
}
65+
66+
if (!config.servers || typeof config.servers !== "object" || Array.isArray(config.servers)) {
67+
throw new Error("Workspace MCP config servers field must be a JSON object.");
68+
}
69+
70+
return config;
71+
} catch (error) {
72+
if (error.code === "ENOENT") {
73+
return { servers: {} };
74+
}
75+
76+
throw error;
77+
}
78+
}
79+
2280
async function checkStudio() {
2381
try {
2482
const version = await runStudioVersion();
@@ -32,6 +90,90 @@ async function checkStudio() {
3290
}
3391
}
3492

93+
async function configureWorkspaceMcp(context) {
94+
const workspaceFolder = getWorkspaceFolder();
95+
96+
if (!workspaceFolder) {
97+
vscode.window.showErrorMessage("Open a workspace folder before configuring WordPress Studio MCP.");
98+
return;
99+
}
100+
101+
let bundledConfig;
102+
try {
103+
bundledConfig = await readBundledMcpConfig(context);
104+
} catch (error) {
105+
vscode.window.showErrorMessage("Cannot configure WordPress Studio MCP. " + error.message);
106+
return;
107+
}
108+
109+
const workspaceMcpDir = path.join(workspaceFolder.uri.fsPath, ".vscode");
110+
const workspaceMcpPath = path.join(workspaceMcpDir, "mcp.json");
111+
let workspaceConfig;
112+
113+
try {
114+
workspaceConfig = await readWorkspaceMcpConfig(workspaceMcpPath);
115+
} catch (error) {
116+
vscode.window.showErrorMessage("Cannot read workspace MCP config. " + error.message);
117+
return;
118+
}
119+
120+
for (const serverName of managedServerNames) {
121+
const bundledServer = bundledConfig.servers[serverName];
122+
123+
if (!bundledServer) {
124+
continue;
125+
}
126+
127+
const existingServer = workspaceConfig.servers[serverName];
128+
if (existingServer && !configsMatch(existingServer, bundledServer)) {
129+
const choice = await vscode.window.showWarningMessage(
130+
"Workspace MCP server " + serverName + " already exists and differs from the bundled WordPress Studio config.",
131+
{ modal: true },
132+
"Replace",
133+
"Cancel"
134+
);
135+
136+
if (choice !== "Replace") {
137+
vscode.window.showInformationMessage("WordPress Studio MCP config was not changed.");
138+
return;
139+
}
140+
}
141+
142+
workspaceConfig.servers[serverName] = bundledServer;
143+
}
144+
145+
try {
146+
await mkdir(workspaceMcpDir, { recursive: true });
147+
await writeFile(workspaceMcpPath, JSON.stringify(workspaceConfig, null, 2) + "\n", "utf8");
148+
vscode.window.showInformationMessage("Configured WordPress Studio MCP at " + workspaceMcpPath + ".");
149+
} catch (error) {
150+
vscode.window.showErrorMessage("Cannot write workspace MCP config. " + error.message);
151+
}
152+
}
153+
154+
async function validateMcpConfig(context) {
155+
let version;
156+
try {
157+
version = await runStudioVersion();
158+
} catch (error) {
159+
vscode.window.showErrorMessage(
160+
"Studio CLI was not found. Install WordPress Studio and ensure the studio command is on PATH. " + error.message
161+
);
162+
return;
163+
}
164+
165+
try {
166+
await readBundledMcpConfig(context);
167+
} catch (error) {
168+
vscode.window.showErrorMessage("Bundled WordPress Studio MCP config is invalid. " + error.message);
169+
return;
170+
}
171+
172+
vscode.window.showInformationMessage(
173+
"WordPress Studio MCP config is valid. Studio CLI: " + (version || "available") + "."
174+
);
175+
}
176+
35177
async function showMcpConfig(context) {
36178
const config = await readMcpConfig(context);
37179
const document = await vscode.workspace.openTextDocument({
@@ -50,6 +192,8 @@ async function copyMcpConfig(context) {
50192
function activate(context) {
51193
context.subscriptions.push(
52194
vscode.commands.registerCommand("wordpressStudio.checkStudio", checkStudio),
195+
vscode.commands.registerCommand("wordpressStudio.configureWorkspaceMcp", () => configureWorkspaceMcp(context)),
196+
vscode.commands.registerCommand("wordpressStudio.validateMcpConfig", () => validateMcpConfig(context)),
53197
vscode.commands.registerCommand("wordpressStudio.showMcpConfig", () => showMcpConfig(context)),
54198
vscode.commands.registerCommand("wordpressStudio.copyMcpConfig", () => copyMcpConfig(context))
55199
);

plugins/vscode/images/icon.png

167 KB
Loading

plugins/vscode/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
"url": "https://github.com/Automattic/build-with-wordpress.git"
1414
},
1515
"license": "GPL-2.0-or-later",
16+
"icon": "images/icon.png",
1617
"files": [
1718
"extension.js",
1819
"mcp.json",
1920
"README.md",
2021
"LICENSE",
22+
"images/icon.png",
2123
"skills/**"
2224
],
2325
"engines": {
@@ -34,6 +36,8 @@
3436
],
3537
"activationEvents": [
3638
"onCommand:wordpressStudio.checkStudio",
39+
"onCommand:wordpressStudio.configureWorkspaceMcp",
40+
"onCommand:wordpressStudio.validateMcpConfig",
3741
"onCommand:wordpressStudio.showMcpConfig",
3842
"onCommand:wordpressStudio.copyMcpConfig"
3943
],
@@ -44,6 +48,14 @@
4448
"command": "wordpressStudio.checkStudio",
4549
"title": "WordPress Studio: Check Studio CLI"
4650
},
51+
{
52+
"command": "wordpressStudio.configureWorkspaceMcp",
53+
"title": "WordPress Studio: Configure Workspace MCP"
54+
},
55+
{
56+
"command": "wordpressStudio.validateMcpConfig",
57+
"title": "WordPress Studio: Validate MCP Config"
58+
},
4759
{
4860
"command": "wordpressStudio.showMcpConfig",
4961
"title": "WordPress Studio: Show MCP Config"

0 commit comments

Comments
 (0)