figma-rust-mcp 3.2.11 → 3.2.15

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.
@@ -0,0 +1,90 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ import { applyEdits, modify, parse } from 'jsonc-parser';
5
+
6
+ const SERVER_NAME = 'figma-rust-mcp';
7
+ const HTTP_URL = 'http://127.0.0.1:38451/mcp';
8
+
9
+ function existsAny(paths) {
10
+ return paths.some(candidate => candidate && fs.existsSync(candidate));
11
+ }
12
+
13
+ function hasCommand(name, env = process.env, platform = process.platform) {
14
+ const extensions = platform === 'win32' ? (env.PATHEXT || '.EXE;.CMD;.BAT').split(';') : [''];
15
+ return (env.PATH || '').split(path.delimiter).some(dir =>
16
+ extensions.some(ext => fs.existsSync(path.join(dir, `${name}${ext}`))));
17
+ }
18
+
19
+ export function detectAgents({ home = os.homedir(), env = process.env, platform = process.platform } = {}) {
20
+ const roaming = env.APPDATA || path.join(home, 'AppData', 'Roaming');
21
+ const local = env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');
22
+ const support = path.join(home, 'Library', 'Application Support');
23
+ const xdg = env.XDG_CONFIG_HOME || path.join(home, '.config');
24
+ const macApp = name => platform === 'darwin' ? `/Applications/${name}.app` : null;
25
+ const agents = [
26
+ { id: 'codex', label: 'Codex', configPath: path.join(env.CODEX_HOME || path.join(home, '.codex'), 'config.toml'), installed: existsAny([env.CODEX_HOME, path.join(home, '.codex')]) || hasCommand('codex', env, platform) },
27
+ { id: 'claude', label: 'Claude Code', configPath: path.join(env.CLAUDE_CONFIG_DIR || home, '.claude.json'), installed: existsAny([path.join(home, '.claude'), path.join(home, '.claude.json')]) || hasCommand('claude', env, platform) },
28
+ { id: 'antigravity', label: 'Google Antigravity', configPath: path.join(home, '.gemini', 'config', 'mcp_config.json'), installed: existsAny([path.join(home, '.antigravity-ide'), path.join(home, '.gemini', 'config', 'mcp_config.json'), platform === 'darwin' ? path.join(support, 'Antigravity') : null, platform === 'win32' ? path.join(local, 'Programs', 'Antigravity') : null, macApp('Antigravity')]) || hasCommand('antigravity', env, platform) },
29
+ { id: 'cursor', label: 'Cursor', configPath: path.join(home, '.cursor', 'mcp.json'), installed: existsAny([path.join(home, '.cursor'), platform === 'darwin' ? path.join(support, 'Cursor') : null, platform === 'win32' ? path.join(local, 'Programs', 'Cursor') : null, macApp('Cursor')]) || hasCommand('cursor', env, platform) },
30
+ { id: 'windsurf', label: 'Windsurf', configPath: path.join(home, '.codeium', 'windsurf', 'mcp_config.json'), installed: existsAny([path.join(home, '.codeium', 'windsurf'), platform === 'darwin' ? path.join(support, 'Windsurf') : null, platform === 'win32' ? path.join(local, 'Programs', 'Windsurf') : null, macApp('Windsurf')]) || hasCommand('windsurf', env, platform) },
31
+ { id: 'vscode', label: 'VS Code / Copilot', configPath: path.join(home, '.copilot', 'mcp-config.json'), installed: existsAny([path.join(home, '.vscode'), path.join(home, '.copilot'), platform === 'darwin' ? path.join(support, 'Code') : null, platform === 'linux' ? path.join(xdg, 'Code') : null, platform === 'win32' ? path.join(local, 'Programs', 'Microsoft VS Code') : null, platform === 'win32' ? path.join(roaming, 'Code') : null, macApp('Visual Studio Code')]) || hasCommand('code', env, platform) },
32
+ { id: 'zed', label: 'Zed', configPath: platform === 'darwin' ? path.join(support, 'Zed', 'settings.json') : platform === 'win32' ? path.join(roaming, 'Zed', 'settings.json') : path.join(xdg, 'zed', 'settings.json'), installed: existsAny([path.join(xdg, 'zed'), platform === 'darwin' ? path.join(support, 'Zed') : null, platform === 'win32' ? path.join(roaming, 'Zed') : null, macApp('Zed')]) || hasCommand('zed', env, platform) },
33
+ ];
34
+ return agents.filter(agent => agent.installed).map(({ installed, ...agent }) => agent);
35
+ }
36
+
37
+ function upsertJsonc(filePath, rootKey, value) {
38
+ const source = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '{}\n';
39
+ const errors = [];
40
+ const config = parse(source, errors, { allowTrailingComma: true, disallowComments: false });
41
+ if (errors.length) throw new Error(`Invalid JSON/JSONC in ${filePath}; left it unchanged.`);
42
+ const current = config?.[rootKey]?.[SERVER_NAME];
43
+ if (current && Object.entries(value).every(([key, entryValue]) => current[key] === entryValue)) return false;
44
+ const merged = current && typeof current === 'object' && !Array.isArray(current) ? { ...current, ...value } : value;
45
+ for (const key of ['command', 'args', 'serverUrl', 'url', 'type']) {
46
+ if (!Object.hasOwn(value, key) && Object.hasOwn(merged, key)) delete merged[key];
47
+ }
48
+ const edits = modify(source, [rootKey, SERVER_NAME], merged, { formattingOptions: { insertSpaces: true, tabSize: 2, eol: '\n' } });
49
+ if (edits.length) {
50
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
51
+ fs.writeFileSync(filePath, applyEdits(source, edits));
52
+ }
53
+ return edits.length > 0;
54
+ }
55
+
56
+ function upsertCodex(filePath) {
57
+ const source = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';
58
+ const lines = source.match(/[^\n]*\n|[^\n]+$/g) || [];
59
+ const headers = ['[mcp_servers.figma-rust-mcp]', '[mcp_servers."figma-rust-mcp"]'];
60
+ const start = lines.findIndex(line => headers.includes(line.trim()));
61
+ const block = `[mcp_servers.figma-rust-mcp]\nurl = ${JSON.stringify(HTTP_URL)}\n`;
62
+ let output;
63
+ if (start < 0) {
64
+ output = `${source.trimEnd()}${source.trim() ? '\n\n' : ''}${block}`;
65
+ } else {
66
+ let end = start + 1;
67
+ while (end < lines.length && !/^\s*\[/.test(lines[end])) end++;
68
+ const oldBlock = lines.slice(start, end).join('');
69
+ const newBlock = `${block}\n`;
70
+ if (oldBlock === newBlock || oldBlock === block) return false;
71
+ lines.splice(start, end - start, newBlock);
72
+ output = lines.join('');
73
+ }
74
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
75
+ fs.writeFileSync(filePath, output);
76
+ return true;
77
+ }
78
+
79
+ export function configureAgent(agent) {
80
+ const entry = agent.id === 'antigravity'
81
+ ? { serverUrl: 'http://127.0.0.1:38451/sse' }
82
+ : agent.id === 'claude' || agent.id === 'vscode'
83
+ ? { type: 'http', url: HTTP_URL }
84
+ : { url: HTTP_URL };
85
+ if (agent.id === 'codex') return upsertCodex(agent.configPath);
86
+ const rootKey = agent.id === 'zed' ? 'context_servers' : 'mcpServers';
87
+ return upsertJsonc(agent.configPath, rootKey, entry);
88
+ }
89
+
90
+ export const MCP_SERVER_NAME = SERVER_NAME;