a11y-devkit-deploy 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -11
- package/config/{a11y.json → settings.json} +2 -2
- package/package.json +1 -1
- package/src/cli.js +2 -1
- package/src/installers/mcp.js +131 -4
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ All MCP servers are configured to run via `npx`, which means:
|
|
|
64
64
|
|
|
65
65
|
This CLI automates the setup of accessibility tooling by:
|
|
66
66
|
|
|
67
|
-
1. **Installing skills from npm** - Downloads and installs accessibility skill packages (configurable in `config/
|
|
67
|
+
1. **Installing skills from npm** - Downloads and installs accessibility skill packages (configurable in `config/settings.json`)
|
|
68
68
|
2. **Configuring MCP servers** - Updates each IDE's MCP config to enable accessibility-focused MCP servers (also configurable)
|
|
69
69
|
|
|
70
70
|
**Default configuration includes:**
|
|
@@ -80,7 +80,7 @@ This CLI automates the setup of accessibility tooling by:
|
|
|
80
80
|
|
|
81
81
|
## Why This Tool?
|
|
82
82
|
|
|
83
|
-
**Zero Hardcoded Values** - Every aspect of the tool is driven by `config/
|
|
83
|
+
**Zero Hardcoded Values** - Every aspect of the tool is driven by `config/settings.json`:
|
|
84
84
|
- IDE paths and configuration files
|
|
85
85
|
- Skills to install
|
|
86
86
|
- MCP servers to configure
|
|
@@ -104,7 +104,7 @@ This CLI automates the setup of accessibility tooling by:
|
|
|
104
104
|
|
|
105
105
|
### Skills Installed (Default)
|
|
106
106
|
|
|
107
|
-
The following skill packages are installed from npm by default. **Add your own by editing `config/
|
|
107
|
+
The following skill packages are installed from npm by default. **Add your own by editing `config/settings.json`**:
|
|
108
108
|
|
|
109
109
|
| Skill | Package | Description |
|
|
110
110
|
|-------|---------|-------------|
|
|
@@ -143,7 +143,7 @@ The generated MCP config looks like this:
|
|
|
143
143
|
|
|
144
144
|
## Configuration
|
|
145
145
|
|
|
146
|
-
The entire tool is **fully config-driven**. Edit `config/
|
|
146
|
+
The entire tool is **fully config-driven**. Edit `config/settings.json` to customize everything without touching code.
|
|
147
147
|
|
|
148
148
|
### Adding a New Skill
|
|
149
149
|
|
|
@@ -218,14 +218,21 @@ Add an object to the `hostApplications` array with the host application's config
|
|
|
218
218
|
**Host Application Configuration Properties:**
|
|
219
219
|
- `id` - Unique identifier for the host application
|
|
220
220
|
- `displayName` - Human-readable name shown in prompts
|
|
221
|
-
- `mcpServerKey` - MCP config key name (`"servers"` or `"
|
|
221
|
+
- `mcpServerKey` - MCP config key name (`"servers"`, `"mcpServers"`, or `"mcp_servers"` for TOML)
|
|
222
222
|
- `skillsFolder` - Path to skills directory (relative to home/project root)
|
|
223
|
-
- `mcpConfigFile` - Path to MCP config file (relative to home/project root)
|
|
223
|
+
- `mcpConfigFile` - Path to MCP config file (relative to home/project root). Supports both JSON (`.json`) and TOML (`.toml`) formats. TOML format is auto-detected by file extension (used by Codex).
|
|
224
224
|
- `globalMcpConfigFile` - (Optional) Path to global MCP config relative to AppData/Application Support instead of home directory. Used for hosts like VSCode that store configs in platform-specific app directories:
|
|
225
225
|
- Windows: `%APPDATA%` (e.g., `C:\Users\name\AppData\Roaming`)
|
|
226
226
|
- macOS: `~/Library/Application Support`
|
|
227
227
|
- Linux: `$XDG_CONFIG_HOME` or `~/.config`
|
|
228
228
|
|
|
229
|
+
**Note:** Codex uses TOML format for its MCP configuration (`~/.codex/config.toml`), which requires the `mcpServerKey` to be `"mcp_servers"` and generates config entries like:
|
|
230
|
+
```toml
|
|
231
|
+
[mcp_servers.magentaa11y]
|
|
232
|
+
command = "npx"
|
|
233
|
+
args = ["-y", "magentaa11y-mcp"]
|
|
234
|
+
```
|
|
235
|
+
|
|
229
236
|
### Config Structure
|
|
230
237
|
|
|
231
238
|
- `skillsFolder` - Subfolder name to bundle skills under (e.g., "a11y")
|
|
@@ -255,7 +262,7 @@ your-project/
|
|
|
255
262
|
│ ├── mcp.json # Cursor MCP config
|
|
256
263
|
│ └── skills/ # Cursor skills
|
|
257
264
|
├── .codex/
|
|
258
|
-
│ ├──
|
|
265
|
+
│ ├── config.toml # Codex MCP config
|
|
259
266
|
│ └── skills/ # Codex skills
|
|
260
267
|
├── .github/
|
|
261
268
|
│ ├── mcp.json # VSCode MCP config
|
|
@@ -277,7 +284,7 @@ your-project/
|
|
|
277
284
|
├── mcp.json # Cursor global MCP config
|
|
278
285
|
└── skills/ # Cursor global skills
|
|
279
286
|
~/.codex/
|
|
280
|
-
├──
|
|
287
|
+
├── config.toml # Codex global MCP config
|
|
281
288
|
└── skills/ # Codex global skills
|
|
282
289
|
~/.github/
|
|
283
290
|
└── skills/ # VSCode global skills
|
|
@@ -293,11 +300,11 @@ your-project/
|
|
|
293
300
|
# macOS: ~/Library/Application Support/Code/User/mcp.json
|
|
294
301
|
```
|
|
295
302
|
|
|
296
|
-
**Note:** Paths are fully customizable per IDE in `config/
|
|
303
|
+
**Note:** Paths are fully customizable per IDE in `config/settings.json`
|
|
297
304
|
|
|
298
305
|
## MCP Servers Included (Default)
|
|
299
306
|
|
|
300
|
-
**Add your own by editing `config/
|
|
307
|
+
**Add your own by editing `config/settings.json`**:
|
|
301
308
|
|
|
302
309
|
| Server | Package | Description |
|
|
303
310
|
|--------|---------|-------------|
|
|
@@ -309,7 +316,7 @@ your-project/
|
|
|
309
316
|
|
|
310
317
|
## Complete Config Example
|
|
311
318
|
|
|
312
|
-
Here's what a complete `config/
|
|
319
|
+
Here's what a complete `config/settings.json` looks like:
|
|
313
320
|
|
|
314
321
|
```json
|
|
315
322
|
{
|
|
@@ -366,3 +373,4 @@ Here's what a complete `config/a11y.json` looks like:
|
|
|
366
373
|
```
|
|
367
374
|
|
|
368
375
|
Everything is customizable - add, remove, or modify any section to match your needs.
|
|
376
|
+
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
{
|
|
64
64
|
"id": "codex",
|
|
65
65
|
"displayName": "Codex",
|
|
66
|
-
"mcpServerKey": "
|
|
66
|
+
"mcpServerKey": "mcp_servers",
|
|
67
67
|
"skillsFolder": ".codex/skills",
|
|
68
|
-
"mcpConfigFile": ".codex/
|
|
68
|
+
"mcpConfigFile": ".codex/config.toml"
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
71
|
"id": "vscode",
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -34,7 +34,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
34
34
|
const __dirname = path.dirname(__filename);
|
|
35
35
|
|
|
36
36
|
async function loadConfig() {
|
|
37
|
-
const configPath = path.join(__dirname, "..", "config", "
|
|
37
|
+
const configPath = path.join(__dirname, "..", "config", "settings.json");
|
|
38
38
|
const raw = await fs.readFile(configPath, "utf8");
|
|
39
39
|
return JSON.parse(raw);
|
|
40
40
|
}
|
|
@@ -733,3 +733,4 @@ async function runGitMcpInstallation(projectRoot, platformInfo, config, hostPath
|
|
|
733
733
|
}
|
|
734
734
|
|
|
735
735
|
export { run };
|
|
736
|
+
|
package/src/installers/mcp.js
CHANGED
|
@@ -10,6 +10,10 @@ async function pathExists(target) {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
function isTomlFile(filePath) {
|
|
14
|
+
return filePath.endsWith('.toml');
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
async function loadJson(filePath) {
|
|
14
18
|
if (!(await pathExists(filePath))) {
|
|
15
19
|
return {};
|
|
@@ -25,6 +29,120 @@ async function loadJson(filePath) {
|
|
|
25
29
|
}
|
|
26
30
|
}
|
|
27
31
|
|
|
32
|
+
// Simple TOML parser for MCP server configs
|
|
33
|
+
function parseSimpleToml(content) {
|
|
34
|
+
const result = {};
|
|
35
|
+
const lines = content.split('\n');
|
|
36
|
+
let currentSection = null;
|
|
37
|
+
let currentTable = null;
|
|
38
|
+
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
const trimmed = line.trim();
|
|
41
|
+
|
|
42
|
+
// Skip empty lines and comments
|
|
43
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
44
|
+
|
|
45
|
+
// Parse table headers like [mcp_servers.name]
|
|
46
|
+
const tableMatch = trimmed.match(/^\[([^\]]+)\]$/);
|
|
47
|
+
if (tableMatch) {
|
|
48
|
+
const parts = tableMatch[1].split('.');
|
|
49
|
+
if (parts.length === 2) {
|
|
50
|
+
const [section, table] = parts;
|
|
51
|
+
if (!result[section]) result[section] = {};
|
|
52
|
+
if (!result[section][table]) result[section][table] = {};
|
|
53
|
+
currentSection = section;
|
|
54
|
+
currentTable = table;
|
|
55
|
+
}
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Parse key-value pairs
|
|
60
|
+
const kvMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
|
|
61
|
+
if (kvMatch && currentSection && currentTable) {
|
|
62
|
+
const [, key, value] = kvMatch;
|
|
63
|
+
|
|
64
|
+
// Parse arrays like ["a", "b"]
|
|
65
|
+
if (value.startsWith('[')) {
|
|
66
|
+
const arrayMatch = value.match(/\[(.*)\]/);
|
|
67
|
+
if (arrayMatch) {
|
|
68
|
+
const items = arrayMatch[1].split(',').map(item => {
|
|
69
|
+
const trimmedItem = item.trim();
|
|
70
|
+
// Remove quotes
|
|
71
|
+
if (trimmedItem.startsWith('"') && trimmedItem.endsWith('"')) {
|
|
72
|
+
return trimmedItem.slice(1, -1);
|
|
73
|
+
}
|
|
74
|
+
return trimmedItem;
|
|
75
|
+
});
|
|
76
|
+
result[currentSection][currentTable][key] = items;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Parse strings
|
|
80
|
+
else if (value.startsWith('"') && value.endsWith('"')) {
|
|
81
|
+
result[currentSection][currentTable][key] = value.slice(1, -1);
|
|
82
|
+
}
|
|
83
|
+
// Parse other values
|
|
84
|
+
else {
|
|
85
|
+
result[currentSection][currentTable][key] = value;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Simple TOML stringifier for MCP server configs
|
|
94
|
+
function stringifySimpleToml(obj) {
|
|
95
|
+
const lines = [];
|
|
96
|
+
|
|
97
|
+
for (const [section, tables] of Object.entries(obj)) {
|
|
98
|
+
if (typeof tables !== 'object' || tables === null) continue;
|
|
99
|
+
|
|
100
|
+
for (const [tableName, config] of Object.entries(tables)) {
|
|
101
|
+
if (typeof config !== 'object' || config === null) continue;
|
|
102
|
+
|
|
103
|
+
// Write table header [section.tableName]
|
|
104
|
+
lines.push(`[${section}.${tableName}]`);
|
|
105
|
+
|
|
106
|
+
// Write key-value pairs
|
|
107
|
+
for (const [key, value] of Object.entries(config)) {
|
|
108
|
+
if (Array.isArray(value)) {
|
|
109
|
+
// Format arrays
|
|
110
|
+
const arrayStr = value.map(v => `"${v}"`).join(', ');
|
|
111
|
+
lines.push(`${key} = [${arrayStr}]`);
|
|
112
|
+
} else if (typeof value === 'string') {
|
|
113
|
+
lines.push(`${key} = "${value}"`);
|
|
114
|
+
} else {
|
|
115
|
+
lines.push(`${key} = ${value}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Add blank line between tables
|
|
120
|
+
lines.push('');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return lines.join('\n');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function loadToml(filePath) {
|
|
128
|
+
if (!(await pathExists(filePath))) {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
134
|
+
return raw.trim() ? parseSimpleToml(raw) : {};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
const backupPath = `${filePath}.bak`;
|
|
137
|
+
await fs.copyFile(filePath, backupPath);
|
|
138
|
+
return {};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function loadConfig(filePath) {
|
|
143
|
+
return isTomlFile(filePath) ? loadToml(filePath) : loadJson(filePath);
|
|
144
|
+
}
|
|
145
|
+
|
|
28
146
|
function mergeServers(existing, incoming, serverKey = "servers") {
|
|
29
147
|
const existingServers = existing[serverKey] && typeof existing[serverKey] === "object"
|
|
30
148
|
? existing[serverKey]
|
|
@@ -84,9 +202,14 @@ function removeServers(existing, removeNames, serverKey = "servers") {
|
|
|
84
202
|
|
|
85
203
|
async function installMcpConfig(configPath, servers, serverKey = "servers") {
|
|
86
204
|
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
87
|
-
const existing = await
|
|
205
|
+
const existing = await loadConfig(configPath);
|
|
88
206
|
const updated = mergeServers(existing, servers, serverKey);
|
|
89
|
-
|
|
207
|
+
|
|
208
|
+
if (isTomlFile(configPath)) {
|
|
209
|
+
await fs.writeFile(configPath, stringifySimpleToml(updated), "utf8");
|
|
210
|
+
} else {
|
|
211
|
+
await fs.writeFile(configPath, `${JSON.stringify(updated, null, 2)}\n`, "utf8");
|
|
212
|
+
}
|
|
90
213
|
}
|
|
91
214
|
|
|
92
215
|
async function removeMcpConfig(configPath, serverNames, serverKey = "servers") {
|
|
@@ -94,14 +217,18 @@ async function removeMcpConfig(configPath, serverNames, serverKey = "servers") {
|
|
|
94
217
|
return { removed: 0, changed: false };
|
|
95
218
|
}
|
|
96
219
|
|
|
97
|
-
const existing = await
|
|
220
|
+
const existing = await loadConfig(configPath);
|
|
98
221
|
const { updated, removed } = removeServers(existing, serverNames, serverKey);
|
|
99
222
|
|
|
100
223
|
if (removed === 0) {
|
|
101
224
|
return { removed: 0, changed: false };
|
|
102
225
|
}
|
|
103
226
|
|
|
104
|
-
|
|
227
|
+
if (isTomlFile(configPath)) {
|
|
228
|
+
await fs.writeFile(configPath, stringifySimpleToml(updated), "utf8");
|
|
229
|
+
} else {
|
|
230
|
+
await fs.writeFile(configPath, `${JSON.stringify(updated, null, 2)}\n`, "utf8");
|
|
231
|
+
}
|
|
105
232
|
return { removed, changed: true };
|
|
106
233
|
}
|
|
107
234
|
|