@sanity/cli 6.0.0 → 6.1.0
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 +78 -111
- package/dist/actions/mcp/detectAvailableEditors.js +19 -10
- package/dist/actions/mcp/detectAvailableEditors.js.map +1 -1
- package/dist/actions/mcp/editorConfigs.js +222 -158
- package/dist/actions/mcp/editorConfigs.js.map +1 -1
- package/dist/actions/mcp/promptForMCPSetup.js +16 -7
- package/dist/actions/mcp/promptForMCPSetup.js.map +1 -1
- package/dist/actions/mcp/setupMCP.js +62 -23
- package/dist/actions/mcp/setupMCP.js.map +1 -1
- package/dist/actions/mcp/types.js.map +1 -1
- package/dist/actions/mcp/validateEditorTokens.js +56 -0
- package/dist/actions/mcp/validateEditorTokens.js.map +1 -0
- package/dist/actions/telemetry/telemetryDebug.js +2 -2
- package/dist/actions/telemetry/telemetryDebug.js.map +1 -1
- package/dist/commands/init.js +9 -4
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp/configure.js +3 -1
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/commands/preview.js +3 -4
- package/dist/commands/preview.js.map +1 -1
- package/dist/hooks/prerun/setupTelemetry.js +7 -7
- package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
- package/dist/services/mcp.js +55 -1
- package/dist/services/mcp.js.map +1 -1
- package/dist/util/getLocalPackageVersion.js +4 -2
- package/dist/util/getLocalPackageVersion.js.map +1 -1
- package/dist/util/getProjectDefaults.js +22 -28
- package/dist/util/getProjectDefaults.js.map +1 -1
- package/dist/util/gitConfig.js +45 -0
- package/dist/util/gitConfig.js.map +1 -0
- package/dist/util/telemetry/telemetryStoreDebug.js +2 -2
- package/dist/util/telemetry/telemetryStoreDebug.js.map +1 -1
- package/oclif.manifest.json +454 -454
- package/package.json +3 -5
|
@@ -37,9 +37,10 @@ const debug = subdebug('mcp:detectAvailableEditors');
|
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* Check if an editor's config is usable and whether Sanity MCP is already configured.
|
|
40
|
+
* If configured, extracts the existing auth token.
|
|
40
41
|
* Returns null only if config exists but can't be parsed (to avoid data loss).
|
|
41
42
|
*/ async function checkEditorConfig(name, configPath) {
|
|
42
|
-
const { configKey, format } = EDITOR_CONFIGS[name];
|
|
43
|
+
const { configKey, format, readToken } = EDITOR_CONFIGS[name];
|
|
43
44
|
// Config file doesn't exist - can create it
|
|
44
45
|
if (!existsSync(configPath)) {
|
|
45
46
|
return {
|
|
@@ -58,10 +59,17 @@ const debug = subdebug('mcp:detectAvailableEditors');
|
|
|
58
59
|
;
|
|
59
60
|
}
|
|
60
61
|
// Check if Sanity MCP is already configured
|
|
61
|
-
const
|
|
62
|
+
const sanityConfig = config[configKey]?.Sanity;
|
|
63
|
+
const configured = Boolean(sanityConfig);
|
|
64
|
+
// Extract existing token if configured
|
|
65
|
+
let existingToken;
|
|
66
|
+
if (configured && typeof sanityConfig === 'object' && sanityConfig !== null) {
|
|
67
|
+
existingToken = readToken(sanityConfig);
|
|
68
|
+
}
|
|
62
69
|
return {
|
|
63
70
|
configPath,
|
|
64
71
|
configured,
|
|
72
|
+
existingToken,
|
|
65
73
|
name
|
|
66
74
|
};
|
|
67
75
|
} catch (err) {
|
|
@@ -73,15 +81,16 @@ const debug = subdebug('mcp:detectAvailableEditors');
|
|
|
73
81
|
* Detect which editors are installed and have parseable configs.
|
|
74
82
|
* Editors with unparseable configs are skipped to avoid data loss.
|
|
75
83
|
*/ export async function detectAvailableEditors() {
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
// Detect all editors in parallel to avoid stacking timeouts —
|
|
85
|
+
// CLI-based editors (Claude Code, Codex CLI, OpenCode) each have a
|
|
86
|
+
// 5s execa timeout, so sequential detection can add ~15s on machines
|
|
87
|
+
// where none are installed.
|
|
88
|
+
const results = await Promise.all(Object.entries(EDITOR_CONFIGS).map(async ([name, config])=>{
|
|
78
89
|
const configPath = await config.detect();
|
|
79
|
-
if (configPath)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
return editors;
|
|
90
|
+
if (!configPath) return null;
|
|
91
|
+
return checkEditorConfig(name, configPath);
|
|
92
|
+
}));
|
|
93
|
+
return results.filter((editor)=>editor !== null);
|
|
85
94
|
}
|
|
86
95
|
|
|
87
96
|
//# sourceMappingURL=detectAvailableEditors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/mcp/detectAvailableEditors.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport fs from 'node:fs/promises'\n\nimport {subdebug} from '@sanity/cli-core'\nimport {type ParseError, parse as parseJsonc} from 'jsonc-parser'\nimport {parse as parseToml} from 'smol-toml'\n\nimport {EDITOR_CONFIGS, type EditorName} from './editorConfigs.js'\n\nconst debug = subdebug('mcp:detectAvailableEditors')\n\ninterface
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/mcp/detectAvailableEditors.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport fs from 'node:fs/promises'\n\nimport {subdebug} from '@sanity/cli-core'\nimport {type ParseError, parse as parseJsonc} from 'jsonc-parser'\nimport {parse as parseToml} from 'smol-toml'\n\nimport {EDITOR_CONFIGS, type EditorName} from './editorConfigs.js'\nimport {type Editor} from './types.js'\n\nconst debug = subdebug('mcp:detectAvailableEditors')\n\ninterface MCPConfig {\n [key: string]: Record<string, unknown> | undefined\n}\n\n/**\n * Safely parse config file content\n * Returns parsed config or null if unparseable\n */\nfunction parseConfig(content: string, format: 'jsonc' | 'toml'): MCPConfig | null {\n const trimmed = content.trim()\n if (trimmed === '') {\n return {} // Empty file - safe to write, treat as empty config\n }\n\n if (format === 'toml') {\n try {\n const parsed = parseToml(content)\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return null\n }\n\n return parsed as MCPConfig\n } catch {\n return null\n }\n }\n\n const errors: ParseError[] = []\n const parsed = parseJsonc(content, errors, {allowTrailingComma: true})\n\n if (errors.length > 0 || typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return null // Parse failed\n }\n\n return parsed as MCPConfig\n}\n\n/**\n * Check if an editor's config is usable and whether Sanity MCP is already configured.\n * If configured, extracts the existing auth token.\n * Returns null only if config exists but can't be parsed (to avoid data loss).\n */\nasync function checkEditorConfig(name: EditorName, configPath: string): Promise<Editor | null> {\n const {configKey, format, readToken} = EDITOR_CONFIGS[name]\n\n // Config file doesn't exist - can create it\n if (!existsSync(configPath)) {\n return {configPath, configured: false, name}\n }\n\n // Config exists - try to parse it\n try {\n const content = await fs.readFile(configPath, 'utf8')\n const config = parseConfig(content, format)\n\n if (config === null) {\n debug('Skipping %s: could not parse %s', name, configPath)\n return null // Can't parse - skip this editor\n }\n\n // Check if Sanity MCP is already configured\n const sanityConfig = config[configKey]?.Sanity\n const configured = Boolean(sanityConfig)\n\n // Extract existing token if configured\n let existingToken: string | undefined\n if (configured && typeof sanityConfig === 'object' && sanityConfig !== null) {\n existingToken = readToken(sanityConfig as Record<string, unknown>)\n }\n\n return {configPath, configured, existingToken, name}\n } catch (err) {\n debug('Skipping %s: could not read %s: %s', name, configPath, err)\n return null\n }\n}\n\n/**\n * Detect which editors are installed and have parseable configs.\n * Editors with unparseable configs are skipped to avoid data loss.\n */\nexport async function detectAvailableEditors(): Promise<Editor[]> {\n // Detect all editors in parallel to avoid stacking timeouts —\n // CLI-based editors (Claude Code, Codex CLI, OpenCode) each have a\n // 5s execa timeout, so sequential detection can add ~15s on machines\n // where none are installed.\n const results = await Promise.all(\n Object.entries(EDITOR_CONFIGS).map(async ([name, config]) => {\n const configPath = await config.detect()\n if (!configPath) return null\n return checkEditorConfig(name as EditorName, configPath)\n }),\n )\n\n return results.filter((editor): editor is Editor => editor !== null)\n}\n"],"names":["existsSync","fs","subdebug","parse","parseJsonc","parseToml","EDITOR_CONFIGS","debug","parseConfig","content","format","trimmed","trim","parsed","Array","isArray","errors","allowTrailingComma","length","checkEditorConfig","name","configPath","configKey","readToken","configured","readFile","config","sanityConfig","Sanity","Boolean","existingToken","err","detectAvailableEditors","results","Promise","all","Object","entries","map","detect","filter","editor"],"mappings":"AAAA,SAAQA,UAAU,QAAO,UAAS;AAClC,OAAOC,QAAQ,mBAAkB;AAEjC,SAAQC,QAAQ,QAAO,mBAAkB;AACzC,SAAyBC,SAASC,UAAU,QAAO,eAAc;AACjE,SAAQD,SAASE,SAAS,QAAO,YAAW;AAE5C,SAAQC,cAAc,QAAwB,qBAAoB;AAGlE,MAAMC,QAAQL,SAAS;AAMvB;;;CAGC,GACD,SAASM,YAAYC,OAAe,EAAEC,MAAwB;IAC5D,MAAMC,UAAUF,QAAQG,IAAI;IAC5B,IAAID,YAAY,IAAI;QAClB,OAAO,CAAC,EAAE,oDAAoD;;IAChE;IAEA,IAAID,WAAW,QAAQ;QACrB,IAAI;YACF,MAAMG,SAASR,UAAUI;YACzB,IAAI,OAAOI,WAAW,YAAYA,WAAW,QAAQC,MAAMC,OAAO,CAACF,SAAS;gBAC1E,OAAO;YACT;YAEA,OAAOA;QACT,EAAE,OAAM;YACN,OAAO;QACT;IACF;IAEA,MAAMG,SAAuB,EAAE;IAC/B,MAAMH,SAAST,WAAWK,SAASO,QAAQ;QAACC,oBAAoB;IAAI;IAEpE,IAAID,OAAOE,MAAM,GAAG,KAAK,OAAOL,WAAW,YAAYA,WAAW,QAAQC,MAAMC,OAAO,CAACF,SAAS;QAC/F,OAAO,KAAK,eAAe;;IAC7B;IAEA,OAAOA;AACT;AAEA;;;;CAIC,GACD,eAAeM,kBAAkBC,IAAgB,EAAEC,UAAkB;IACnE,MAAM,EAACC,SAAS,EAAEZ,MAAM,EAAEa,SAAS,EAAC,GAAGjB,cAAc,CAACc,KAAK;IAE3D,4CAA4C;IAC5C,IAAI,CAACpB,WAAWqB,aAAa;QAC3B,OAAO;YAACA;YAAYG,YAAY;YAAOJ;QAAI;IAC7C;IAEA,kCAAkC;IAClC,IAAI;QACF,MAAMX,UAAU,MAAMR,GAAGwB,QAAQ,CAACJ,YAAY;QAC9C,MAAMK,SAASlB,YAAYC,SAASC;QAEpC,IAAIgB,WAAW,MAAM;YACnBnB,MAAM,mCAAmCa,MAAMC;YAC/C,OAAO,KAAK,iCAAiC;;QAC/C;QAEA,4CAA4C;QAC5C,MAAMM,eAAeD,MAAM,CAACJ,UAAU,EAAEM;QACxC,MAAMJ,aAAaK,QAAQF;QAE3B,uCAAuC;QACvC,IAAIG;QACJ,IAAIN,cAAc,OAAOG,iBAAiB,YAAYA,iBAAiB,MAAM;YAC3EG,gBAAgBP,UAAUI;QAC5B;QAEA,OAAO;YAACN;YAAYG;YAAYM;YAAeV;QAAI;IACrD,EAAE,OAAOW,KAAK;QACZxB,MAAM,sCAAsCa,MAAMC,YAAYU;QAC9D,OAAO;IACT;AACF;AAEA;;;CAGC,GACD,OAAO,eAAeC;IACpB,8DAA8D;IAC9D,mEAAmE;IACnE,qEAAqE;IACrE,4BAA4B;IAC5B,MAAMC,UAAU,MAAMC,QAAQC,GAAG,CAC/BC,OAAOC,OAAO,CAAC/B,gBAAgBgC,GAAG,CAAC,OAAO,CAAClB,MAAMM,OAAO;QACtD,MAAML,aAAa,MAAMK,OAAOa,MAAM;QACtC,IAAI,CAAClB,YAAY,OAAO;QACxB,OAAOF,kBAAkBC,MAAoBC;IAC/C;IAGF,OAAOY,QAAQO,MAAM,CAAC,CAACC,SAA6BA,WAAW;AACjE"}
|
|
@@ -11,194 +11,258 @@ const defaultHttpConfig = (token)=>({
|
|
|
11
11
|
url: MCP_SERVER_URL
|
|
12
12
|
});
|
|
13
13
|
const homeDir = os.homedir();
|
|
14
|
+
// -- Detect functions --
|
|
15
|
+
async function detectClaudeCode() {
|
|
16
|
+
try {
|
|
17
|
+
await execa('claude', [
|
|
18
|
+
'--version'
|
|
19
|
+
], {
|
|
20
|
+
stdio: 'pipe',
|
|
21
|
+
timeout: 5000
|
|
22
|
+
});
|
|
23
|
+
return path.join(homeDir, '.claude.json');
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function detectCodexCli() {
|
|
29
|
+
try {
|
|
30
|
+
await execa('codex', [
|
|
31
|
+
'--version'
|
|
32
|
+
], {
|
|
33
|
+
stdio: 'pipe',
|
|
34
|
+
timeout: 5000
|
|
35
|
+
});
|
|
36
|
+
const codexHome = process.env.CODEX_HOME || path.join(homeDir, '.codex');
|
|
37
|
+
return path.join(codexHome, 'config.toml');
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function detectCursor() {
|
|
43
|
+
const cursorDir = path.join(homeDir, '.cursor');
|
|
44
|
+
return existsSync(cursorDir) ? path.join(cursorDir, 'mcp.json') : null;
|
|
45
|
+
}
|
|
46
|
+
async function detectGeminiCli() {
|
|
47
|
+
const geminiDir = path.join(homeDir, '.gemini');
|
|
48
|
+
return existsSync(geminiDir) ? path.join(geminiDir, 'settings.json') : null;
|
|
49
|
+
}
|
|
50
|
+
async function detectGitHubCopilotCli() {
|
|
51
|
+
const copilotDir = process.platform === 'linux' && process.env.XDG_CONFIG_HOME ? path.join(process.env.XDG_CONFIG_HOME, 'copilot') : path.join(homeDir, '.copilot');
|
|
52
|
+
return existsSync(copilotDir) ? path.join(copilotDir, 'mcp-config.json') : null;
|
|
53
|
+
}
|
|
54
|
+
async function detectOpenCode() {
|
|
55
|
+
try {
|
|
56
|
+
await execa('opencode', [
|
|
57
|
+
'--version'
|
|
58
|
+
], {
|
|
59
|
+
stdio: 'pipe',
|
|
60
|
+
timeout: 5000
|
|
61
|
+
});
|
|
62
|
+
return path.join(homeDir, '.config/opencode/opencode.json');
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function detectVSCode() {
|
|
68
|
+
let configDir = null;
|
|
69
|
+
switch(process.platform){
|
|
70
|
+
case 'darwin':
|
|
71
|
+
{
|
|
72
|
+
configDir = path.join(homeDir, 'Library/Application Support/Code/User');
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case 'win32':
|
|
76
|
+
{
|
|
77
|
+
if (process.env.APPDATA) {
|
|
78
|
+
configDir = path.join(process.env.APPDATA, 'Code/User');
|
|
79
|
+
}
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
default:
|
|
83
|
+
{
|
|
84
|
+
configDir = path.join(homeDir, '.config/Code/User');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return configDir && existsSync(configDir) ? path.join(configDir, 'mcp.json') : null;
|
|
88
|
+
}
|
|
89
|
+
async function detectVSCodeInsiders() {
|
|
90
|
+
let configDir = null;
|
|
91
|
+
switch(process.platform){
|
|
92
|
+
case 'darwin':
|
|
93
|
+
{
|
|
94
|
+
configDir = path.join(homeDir, 'Library/Application Support/Code - Insiders/User');
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
case 'win32':
|
|
98
|
+
{
|
|
99
|
+
if (process.env.APPDATA) {
|
|
100
|
+
configDir = path.join(process.env.APPDATA, 'Code - Insiders/User');
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
default:
|
|
105
|
+
{
|
|
106
|
+
configDir = path.join(homeDir, '.config/Code - Insiders/User');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return configDir && existsSync(configDir) ? path.join(configDir, 'mcp.json') : null;
|
|
110
|
+
}
|
|
111
|
+
async function detectZed() {
|
|
112
|
+
let configDir = null;
|
|
113
|
+
switch(process.platform){
|
|
114
|
+
case 'win32':
|
|
115
|
+
{
|
|
116
|
+
if (process.env.APPDATA) {
|
|
117
|
+
configDir = path.join(process.env.APPDATA, 'Zed');
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
default:
|
|
122
|
+
{
|
|
123
|
+
configDir = path.join(homeDir, '.config/zed');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return configDir && existsSync(configDir) ? path.join(configDir, 'settings.json') : null;
|
|
127
|
+
}
|
|
128
|
+
// -- Read token helpers --
|
|
129
|
+
/**
|
|
130
|
+
* Extract a Bearer token from a headers-like object.
|
|
131
|
+
* Looks for `Authorization: "Bearer <token>"` and returns the token portion.
|
|
132
|
+
*/ function extractBearerToken(headers) {
|
|
133
|
+
if (typeof headers !== 'object' || headers === null) return undefined;
|
|
134
|
+
const auth = headers.Authorization;
|
|
135
|
+
if (typeof auth !== 'string') return undefined;
|
|
136
|
+
const match = auth.match(/^Bearer\s+(.+)$/);
|
|
137
|
+
return match?.[1];
|
|
138
|
+
}
|
|
139
|
+
function readTokenFromHeaders(serverConfig) {
|
|
140
|
+
return extractBearerToken(serverConfig.headers);
|
|
141
|
+
}
|
|
142
|
+
function readTokenFromHttpHeaders(serverConfig) {
|
|
143
|
+
return extractBearerToken(serverConfig.http_headers);
|
|
144
|
+
}
|
|
145
|
+
// -- Build server config functions --
|
|
146
|
+
function buildClaudeCodeServerConfig(token) {
|
|
147
|
+
return defaultHttpConfig(token);
|
|
148
|
+
}
|
|
149
|
+
function buildCodexCliServerConfig(token) {
|
|
150
|
+
return {
|
|
151
|
+
http_headers: {
|
|
152
|
+
Authorization: `Bearer ${token}`
|
|
153
|
+
},
|
|
154
|
+
type: 'http',
|
|
155
|
+
url: MCP_SERVER_URL
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function buildCursorServerConfig(token) {
|
|
159
|
+
return defaultHttpConfig(token);
|
|
160
|
+
}
|
|
161
|
+
function buildGeminiCliServerConfig(token) {
|
|
162
|
+
return defaultHttpConfig(token);
|
|
163
|
+
}
|
|
164
|
+
function buildGitHubCopilotCliServerConfig(token) {
|
|
165
|
+
return {
|
|
166
|
+
headers: {
|
|
167
|
+
Authorization: `Bearer ${token}`
|
|
168
|
+
},
|
|
169
|
+
tools: [
|
|
170
|
+
'*'
|
|
171
|
+
],
|
|
172
|
+
type: 'http',
|
|
173
|
+
url: MCP_SERVER_URL
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function buildOpenCodeServerConfig(token) {
|
|
177
|
+
return {
|
|
178
|
+
headers: {
|
|
179
|
+
Authorization: `Bearer ${token}`
|
|
180
|
+
},
|
|
181
|
+
type: 'remote',
|
|
182
|
+
url: MCP_SERVER_URL
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function buildVSCodeServerConfig(token) {
|
|
186
|
+
return defaultHttpConfig(token);
|
|
187
|
+
}
|
|
188
|
+
function buildVSCodeInsidersServerConfig(token) {
|
|
189
|
+
return defaultHttpConfig(token);
|
|
190
|
+
}
|
|
191
|
+
function buildZedServerConfig(token) {
|
|
192
|
+
return {
|
|
193
|
+
headers: {
|
|
194
|
+
Authorization: `Bearer ${token}`
|
|
195
|
+
},
|
|
196
|
+
settings: {},
|
|
197
|
+
url: MCP_SERVER_URL
|
|
198
|
+
};
|
|
199
|
+
}
|
|
14
200
|
/**
|
|
15
201
|
* Centralized editor configuration including detection logic.
|
|
16
202
|
* To add a new editor: add an entry here - EditorName type is derived automatically.
|
|
17
203
|
*/ export const EDITOR_CONFIGS = {
|
|
18
204
|
'Claude Code': {
|
|
19
|
-
buildServerConfig:
|
|
205
|
+
buildServerConfig: buildClaudeCodeServerConfig,
|
|
20
206
|
configKey: 'mcpServers',
|
|
21
|
-
detect:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
'--version'
|
|
25
|
-
], {
|
|
26
|
-
stdio: 'pipe',
|
|
27
|
-
timeout: 5000
|
|
28
|
-
});
|
|
29
|
-
return path.join(homeDir, '.claude.json');
|
|
30
|
-
} catch {
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
format: 'jsonc'
|
|
207
|
+
detect: detectClaudeCode,
|
|
208
|
+
format: 'jsonc',
|
|
209
|
+
readToken: readTokenFromHeaders
|
|
35
210
|
},
|
|
36
211
|
'Codex CLI': {
|
|
37
|
-
buildServerConfig:
|
|
38
|
-
http_headers: {
|
|
39
|
-
Authorization: `Bearer ${token}`
|
|
40
|
-
},
|
|
41
|
-
type: 'http',
|
|
42
|
-
url: MCP_SERVER_URL
|
|
43
|
-
}),
|
|
212
|
+
buildServerConfig: buildCodexCliServerConfig,
|
|
44
213
|
configKey: 'mcp_servers',
|
|
45
|
-
detect:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
'--version'
|
|
49
|
-
], {
|
|
50
|
-
stdio: 'pipe',
|
|
51
|
-
timeout: 5000
|
|
52
|
-
});
|
|
53
|
-
const codexHome = process.env.CODEX_HOME || path.join(homeDir, '.codex');
|
|
54
|
-
return path.join(codexHome, 'config.toml');
|
|
55
|
-
} catch {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
format: 'toml'
|
|
214
|
+
detect: detectCodexCli,
|
|
215
|
+
format: 'toml',
|
|
216
|
+
readToken: readTokenFromHttpHeaders
|
|
60
217
|
},
|
|
61
218
|
Cursor: {
|
|
62
|
-
buildServerConfig:
|
|
219
|
+
buildServerConfig: buildCursorServerConfig,
|
|
63
220
|
configKey: 'mcpServers',
|
|
64
|
-
detect:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
},
|
|
68
|
-
format: 'jsonc'
|
|
221
|
+
detect: detectCursor,
|
|
222
|
+
format: 'jsonc',
|
|
223
|
+
readToken: readTokenFromHeaders
|
|
69
224
|
},
|
|
70
225
|
'Gemini CLI': {
|
|
71
|
-
buildServerConfig:
|
|
226
|
+
buildServerConfig: buildGeminiCliServerConfig,
|
|
72
227
|
configKey: 'mcpServers',
|
|
73
|
-
detect:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
},
|
|
77
|
-
format: 'jsonc'
|
|
228
|
+
detect: detectGeminiCli,
|
|
229
|
+
format: 'jsonc',
|
|
230
|
+
readToken: readTokenFromHeaders
|
|
78
231
|
},
|
|
79
232
|
'GitHub Copilot CLI': {
|
|
80
|
-
buildServerConfig:
|
|
81
|
-
headers: {
|
|
82
|
-
Authorization: `Bearer ${token}`
|
|
83
|
-
},
|
|
84
|
-
tools: [
|
|
85
|
-
'*'
|
|
86
|
-
],
|
|
87
|
-
type: 'http',
|
|
88
|
-
url: MCP_SERVER_URL
|
|
89
|
-
}),
|
|
233
|
+
buildServerConfig: buildGitHubCopilotCliServerConfig,
|
|
90
234
|
configKey: 'mcpServers',
|
|
91
|
-
detect:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
},
|
|
95
|
-
format: 'jsonc'
|
|
235
|
+
detect: detectGitHubCopilotCli,
|
|
236
|
+
format: 'jsonc',
|
|
237
|
+
readToken: readTokenFromHeaders
|
|
96
238
|
},
|
|
97
239
|
OpenCode: {
|
|
98
|
-
buildServerConfig:
|
|
99
|
-
headers: {
|
|
100
|
-
Authorization: `Bearer ${token}`
|
|
101
|
-
},
|
|
102
|
-
type: 'remote',
|
|
103
|
-
url: MCP_SERVER_URL
|
|
104
|
-
}),
|
|
240
|
+
buildServerConfig: buildOpenCodeServerConfig,
|
|
105
241
|
configKey: 'mcp',
|
|
106
|
-
detect:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
'--version'
|
|
110
|
-
], {
|
|
111
|
-
stdio: 'pipe',
|
|
112
|
-
timeout: 5000
|
|
113
|
-
});
|
|
114
|
-
return path.join(homeDir, '.config/opencode/opencode.json');
|
|
115
|
-
} catch {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
format: 'jsonc'
|
|
242
|
+
detect: detectOpenCode,
|
|
243
|
+
format: 'jsonc',
|
|
244
|
+
readToken: readTokenFromHeaders
|
|
120
245
|
},
|
|
121
246
|
'VS Code': {
|
|
122
|
-
buildServerConfig:
|
|
247
|
+
buildServerConfig: buildVSCodeServerConfig,
|
|
123
248
|
configKey: 'servers',
|
|
124
|
-
detect:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
case 'darwin':
|
|
128
|
-
{
|
|
129
|
-
configDir = path.join(homeDir, 'Library/Application Support/Code/User');
|
|
130
|
-
break;
|
|
131
|
-
}
|
|
132
|
-
case 'win32':
|
|
133
|
-
{
|
|
134
|
-
if (process.env.APPDATA) {
|
|
135
|
-
configDir = path.join(process.env.APPDATA, 'Code/User');
|
|
136
|
-
}
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
default:
|
|
140
|
-
{
|
|
141
|
-
configDir = path.join(homeDir, '.config/Code/User');
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return configDir && existsSync(configDir) ? path.join(configDir, 'mcp.json') : null;
|
|
145
|
-
},
|
|
146
|
-
format: 'jsonc'
|
|
249
|
+
detect: detectVSCode,
|
|
250
|
+
format: 'jsonc',
|
|
251
|
+
readToken: readTokenFromHeaders
|
|
147
252
|
},
|
|
148
253
|
'VS Code Insiders': {
|
|
149
|
-
buildServerConfig:
|
|
254
|
+
buildServerConfig: buildVSCodeInsidersServerConfig,
|
|
150
255
|
configKey: 'servers',
|
|
151
|
-
detect:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
case 'darwin':
|
|
155
|
-
{
|
|
156
|
-
configDir = path.join(homeDir, 'Library/Application Support/Code - Insiders/User');
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
case 'win32':
|
|
160
|
-
{
|
|
161
|
-
if (process.env.APPDATA) {
|
|
162
|
-
configDir = path.join(process.env.APPDATA, 'Code - Insiders/User');
|
|
163
|
-
}
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
default:
|
|
167
|
-
{
|
|
168
|
-
configDir = path.join(homeDir, '.config/Code - Insiders/User');
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return configDir && existsSync(configDir) ? path.join(configDir, 'mcp.json') : null;
|
|
172
|
-
},
|
|
173
|
-
format: 'jsonc'
|
|
256
|
+
detect: detectVSCodeInsiders,
|
|
257
|
+
format: 'jsonc',
|
|
258
|
+
readToken: readTokenFromHeaders
|
|
174
259
|
},
|
|
175
260
|
Zed: {
|
|
176
|
-
buildServerConfig:
|
|
177
|
-
headers: {
|
|
178
|
-
Authorization: `Bearer ${token}`
|
|
179
|
-
},
|
|
180
|
-
settings: {},
|
|
181
|
-
url: MCP_SERVER_URL
|
|
182
|
-
}),
|
|
261
|
+
buildServerConfig: buildZedServerConfig,
|
|
183
262
|
configKey: 'context_servers',
|
|
184
|
-
detect:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
case 'win32':
|
|
188
|
-
{
|
|
189
|
-
if (process.env.APPDATA) {
|
|
190
|
-
configDir = path.join(process.env.APPDATA, 'Zed');
|
|
191
|
-
}
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
default:
|
|
195
|
-
{
|
|
196
|
-
configDir = path.join(homeDir, '.config/zed');
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
return configDir && existsSync(configDir) ? path.join(configDir, 'settings.json') : null;
|
|
200
|
-
},
|
|
201
|
-
format: 'jsonc'
|
|
263
|
+
detect: detectZed,
|
|
264
|
+
format: 'jsonc',
|
|
265
|
+
readToken: readTokenFromHeaders
|
|
202
266
|
}
|
|
203
267
|
};
|
|
204
268
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/mcp/editorConfigs.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\nimport {execa} from 'execa'\n\nimport {MCP_SERVER_URL} from '../../services/mcp.js'\n\ninterface EditorConfig {\n buildServerConfig: (token: string) => Record<string, unknown>\n configKey: string\n /** Returns the config file path if editor is detected, null otherwise */\n detect: () => Promise<string | null>\n format: 'jsonc' | 'toml'\n}\n\nconst defaultHttpConfig = (token: string) => ({\n headers: {Authorization: `Bearer ${token}`},\n type: 'http',\n url: MCP_SERVER_URL,\n})\n\nconst homeDir = os.homedir()\n\n/**\n * Centralized editor configuration including detection logic.\n * To add a new editor: add an entry here - EditorName type is derived automatically.\n */\nexport const EDITOR_CONFIGS = {\n 'Claude Code': {\n buildServerConfig: defaultHttpConfig,\n configKey: 'mcpServers',\n detect: async () => {\n try {\n await execa('claude', ['--version'], {stdio: 'pipe', timeout: 5000})\n return path.join(homeDir, '.claude.json')\n } catch {\n return null\n }\n },\n format: 'jsonc',\n },\n 'Codex CLI': {\n buildServerConfig: (token) => ({\n http_headers: {Authorization: `Bearer ${token}`},\n type: 'http',\n url: MCP_SERVER_URL,\n }),\n configKey: 'mcp_servers',\n detect: async () => {\n try {\n await execa('codex', ['--version'], {stdio: 'pipe', timeout: 5000})\n const codexHome = process.env.CODEX_HOME || path.join(homeDir, '.codex')\n return path.join(codexHome, 'config.toml')\n } catch {\n return null\n }\n },\n format: 'toml',\n },\n Cursor: {\n buildServerConfig: defaultHttpConfig,\n configKey: 'mcpServers',\n detect: async () => {\n const cursorDir = path.join(homeDir, '.cursor')\n return existsSync(cursorDir) ? path.join(cursorDir, 'mcp.json') : null\n },\n format: 'jsonc',\n },\n 'Gemini CLI': {\n buildServerConfig: defaultHttpConfig,\n configKey: 'mcpServers',\n detect: async () => {\n const geminiDir = path.join(homeDir, '.gemini')\n return existsSync(geminiDir) ? path.join(geminiDir, 'settings.json') : null\n },\n format: 'jsonc',\n },\n 'GitHub Copilot CLI': {\n buildServerConfig: (token: string) => ({\n headers: {Authorization: `Bearer ${token}`},\n tools: ['*'],\n type: 'http',\n url: MCP_SERVER_URL,\n }),\n configKey: 'mcpServers',\n detect: async () => {\n const copilotDir =\n process.platform === 'linux' && process.env.XDG_CONFIG_HOME\n ? path.join(process.env.XDG_CONFIG_HOME, 'copilot')\n : path.join(homeDir, '.copilot')\n return existsSync(copilotDir) ? path.join(copilotDir, 'mcp-config.json') : null\n },\n format: 'jsonc',\n },\n OpenCode: {\n buildServerConfig: (token) => ({\n headers: {Authorization: `Bearer ${token}`},\n type: 'remote',\n url: MCP_SERVER_URL,\n }),\n configKey: 'mcp',\n detect: async () => {\n try {\n await execa('opencode', ['--version'], {stdio: 'pipe', timeout: 5000})\n return path.join(homeDir, '.config/opencode/opencode.json')\n } catch {\n return null\n }\n },\n format: 'jsonc',\n },\n 'VS Code': {\n buildServerConfig: defaultHttpConfig,\n configKey: 'servers',\n detect: async () => {\n let configDir: string | null = null\n switch (process.platform) {\n case 'darwin': {\n configDir = path.join(homeDir, 'Library/Application Support/Code/User')\n break\n }\n case 'win32': {\n if (process.env.APPDATA) {\n configDir = path.join(process.env.APPDATA, 'Code/User')\n }\n break\n }\n default: {\n configDir = path.join(homeDir, '.config/Code/User')\n }\n }\n return configDir && existsSync(configDir) ? path.join(configDir, 'mcp.json') : null\n },\n format: 'jsonc',\n },\n 'VS Code Insiders': {\n buildServerConfig: defaultHttpConfig,\n configKey: 'servers',\n detect: async () => {\n let configDir: string | null = null\n switch (process.platform) {\n case 'darwin': {\n configDir = path.join(homeDir, 'Library/Application Support/Code - Insiders/User')\n break\n }\n case 'win32': {\n if (process.env.APPDATA) {\n configDir = path.join(process.env.APPDATA, 'Code - Insiders/User')\n }\n break\n }\n default: {\n configDir = path.join(homeDir, '.config/Code - Insiders/User')\n }\n }\n return configDir && existsSync(configDir) ? path.join(configDir, 'mcp.json') : null\n },\n format: 'jsonc',\n },\n Zed: {\n buildServerConfig: (token) => ({\n headers: {Authorization: `Bearer ${token}`},\n settings: {},\n url: MCP_SERVER_URL,\n }),\n configKey: 'context_servers',\n detect: async () => {\n let configDir: string | null = null\n switch (process.platform) {\n case 'win32': {\n if (process.env.APPDATA) {\n configDir = path.join(process.env.APPDATA, 'Zed')\n }\n break\n }\n default: {\n configDir = path.join(homeDir, '.config/zed')\n }\n }\n return configDir && existsSync(configDir) ? path.join(configDir, 'settings.json') : null\n },\n format: 'jsonc',\n },\n} satisfies Record<string, EditorConfig>\n\n/** Derived from EDITOR_CONFIGS keys - add a new editor there and this updates automatically */\nexport type EditorName = keyof typeof EDITOR_CONFIGS\n"],"names":["existsSync","os","path","execa","MCP_SERVER_URL","defaultHttpConfig","token","headers","Authorization","type","url","homeDir","homedir","EDITOR_CONFIGS","buildServerConfig","configKey","detect","stdio","timeout","join","format","http_headers","codexHome","process","env","CODEX_HOME","Cursor","cursorDir","geminiDir","tools","copilotDir","platform","XDG_CONFIG_HOME","OpenCode","configDir","APPDATA","Zed","settings"],"mappings":"AAAA,SAAQA,UAAU,QAAO,UAAS;AAClC,OAAOC,QAAQ,UAAS;AACxB,OAAOC,UAAU,YAAW;AAE5B,SAAQC,KAAK,QAAO,QAAO;AAE3B,SAAQC,cAAc,QAAO,wBAAuB;AAUpD,MAAMC,oBAAoB,CAACC,QAAmB,CAAA;QAC5CC,SAAS;YAACC,eAAe,CAAC,OAAO,EAAEF,OAAO;QAAA;QAC1CG,MAAM;QACNC,KAAKN;IACP,CAAA;AAEA,MAAMO,UAAUV,GAAGW,OAAO;AAE1B;;;CAGC,GACD,OAAO,MAAMC,iBAAiB;IAC5B,eAAe;QACbC,mBAAmBT;QACnBU,WAAW;QACXC,QAAQ;YACN,IAAI;gBACF,MAAMb,MAAM,UAAU;oBAAC;iBAAY,EAAE;oBAACc,OAAO;oBAAQC,SAAS;gBAAI;gBAClE,OAAOhB,KAAKiB,IAAI,CAACR,SAAS;YAC5B,EAAE,OAAM;gBACN,OAAO;YACT;QACF;QACAS,QAAQ;IACV;IACA,aAAa;QACXN,mBAAmB,CAACR,QAAW,CAAA;gBAC7Be,cAAc;oBAACb,eAAe,CAAC,OAAO,EAAEF,OAAO;gBAAA;gBAC/CG,MAAM;gBACNC,KAAKN;YACP,CAAA;QACAW,WAAW;QACXC,QAAQ;YACN,IAAI;gBACF,MAAMb,MAAM,SAAS;oBAAC;iBAAY,EAAE;oBAACc,OAAO;oBAAQC,SAAS;gBAAI;gBACjE,MAAMI,YAAYC,QAAQC,GAAG,CAACC,UAAU,IAAIvB,KAAKiB,IAAI,CAACR,SAAS;gBAC/D,OAAOT,KAAKiB,IAAI,CAACG,WAAW;YAC9B,EAAE,OAAM;gBACN,OAAO;YACT;QACF;QACAF,QAAQ;IACV;IACAM,QAAQ;QACNZ,mBAAmBT;QACnBU,WAAW;QACXC,QAAQ;YACN,MAAMW,YAAYzB,KAAKiB,IAAI,CAACR,SAAS;YACrC,OAAOX,WAAW2B,aAAazB,KAAKiB,IAAI,CAACQ,WAAW,cAAc;QACpE;QACAP,QAAQ;IACV;IACA,cAAc;QACZN,mBAAmBT;QACnBU,WAAW;QACXC,QAAQ;YACN,MAAMY,YAAY1B,KAAKiB,IAAI,CAACR,SAAS;YACrC,OAAOX,WAAW4B,aAAa1B,KAAKiB,IAAI,CAACS,WAAW,mBAAmB;QACzE;QACAR,QAAQ;IACV;IACA,sBAAsB;QACpBN,mBAAmB,CAACR,QAAmB,CAAA;gBACrCC,SAAS;oBAACC,eAAe,CAAC,OAAO,EAAEF,OAAO;gBAAA;gBAC1CuB,OAAO;oBAAC;iBAAI;gBACZpB,MAAM;gBACNC,KAAKN;YACP,CAAA;QACAW,WAAW;QACXC,QAAQ;YACN,MAAMc,aACJP,QAAQQ,QAAQ,KAAK,WAAWR,QAAQC,GAAG,CAACQ,eAAe,GACvD9B,KAAKiB,IAAI,CAACI,QAAQC,GAAG,CAACQ,eAAe,EAAE,aACvC9B,KAAKiB,IAAI,CAACR,SAAS;YACzB,OAAOX,WAAW8B,cAAc5B,KAAKiB,IAAI,CAACW,YAAY,qBAAqB;QAC7E;QACAV,QAAQ;IACV;IACAa,UAAU;QACRnB,mBAAmB,CAACR,QAAW,CAAA;gBAC7BC,SAAS;oBAACC,eAAe,CAAC,OAAO,EAAEF,OAAO;gBAAA;gBAC1CG,MAAM;gBACNC,KAAKN;YACP,CAAA;QACAW,WAAW;QACXC,QAAQ;YACN,IAAI;gBACF,MAAMb,MAAM,YAAY;oBAAC;iBAAY,EAAE;oBAACc,OAAO;oBAAQC,SAAS;gBAAI;gBACpE,OAAOhB,KAAKiB,IAAI,CAACR,SAAS;YAC5B,EAAE,OAAM;gBACN,OAAO;YACT;QACF;QACAS,QAAQ;IACV;IACA,WAAW;QACTN,mBAAmBT;QACnBU,WAAW;QACXC,QAAQ;YACN,IAAIkB,YAA2B;YAC/B,OAAQX,QAAQQ,QAAQ;gBACtB,KAAK;oBAAU;wBACbG,YAAYhC,KAAKiB,IAAI,CAACR,SAAS;wBAC/B;oBACF;gBACA,KAAK;oBAAS;wBACZ,IAAIY,QAAQC,GAAG,CAACW,OAAO,EAAE;4BACvBD,YAAYhC,KAAKiB,IAAI,CAACI,QAAQC,GAAG,CAACW,OAAO,EAAE;wBAC7C;wBACA;oBACF;gBACA;oBAAS;wBACPD,YAAYhC,KAAKiB,IAAI,CAACR,SAAS;oBACjC;YACF;YACA,OAAOuB,aAAalC,WAAWkC,aAAahC,KAAKiB,IAAI,CAACe,WAAW,cAAc;QACjF;QACAd,QAAQ;IACV;IACA,oBAAoB;QAClBN,mBAAmBT;QACnBU,WAAW;QACXC,QAAQ;YACN,IAAIkB,YAA2B;YAC/B,OAAQX,QAAQQ,QAAQ;gBACtB,KAAK;oBAAU;wBACbG,YAAYhC,KAAKiB,IAAI,CAACR,SAAS;wBAC/B;oBACF;gBACA,KAAK;oBAAS;wBACZ,IAAIY,QAAQC,GAAG,CAACW,OAAO,EAAE;4BACvBD,YAAYhC,KAAKiB,IAAI,CAACI,QAAQC,GAAG,CAACW,OAAO,EAAE;wBAC7C;wBACA;oBACF;gBACA;oBAAS;wBACPD,YAAYhC,KAAKiB,IAAI,CAACR,SAAS;oBACjC;YACF;YACA,OAAOuB,aAAalC,WAAWkC,aAAahC,KAAKiB,IAAI,CAACe,WAAW,cAAc;QACjF;QACAd,QAAQ;IACV;IACAgB,KAAK;QACHtB,mBAAmB,CAACR,QAAW,CAAA;gBAC7BC,SAAS;oBAACC,eAAe,CAAC,OAAO,EAAEF,OAAO;gBAAA;gBAC1C+B,UAAU,CAAC;gBACX3B,KAAKN;YACP,CAAA;QACAW,WAAW;QACXC,QAAQ;YACN,IAAIkB,YAA2B;YAC/B,OAAQX,QAAQQ,QAAQ;gBACtB,KAAK;oBAAS;wBACZ,IAAIR,QAAQC,GAAG,CAACW,OAAO,EAAE;4BACvBD,YAAYhC,KAAKiB,IAAI,CAACI,QAAQC,GAAG,CAACW,OAAO,EAAE;wBAC7C;wBACA;oBACF;gBACA;oBAAS;wBACPD,YAAYhC,KAAKiB,IAAI,CAACR,SAAS;oBACjC;YACF;YACA,OAAOuB,aAAalC,WAAWkC,aAAahC,KAAKiB,IAAI,CAACe,WAAW,mBAAmB;QACtF;QACAd,QAAQ;IACV;AACF,EAAwC"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/mcp/editorConfigs.ts"],"sourcesContent":["import {existsSync} from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\nimport {execa} from 'execa'\n\nimport {MCP_SERVER_URL} from '../../services/mcp.js'\n\ninterface EditorConfig {\n buildServerConfig: (token: string) => Record<string, unknown>\n configKey: string\n /** Returns the config file path if editor is detected, null otherwise */\n detect: () => Promise<string | null>\n format: 'jsonc' | 'toml'\n /** Extracts the auth token from a parsed Sanity server config block */\n readToken: (serverConfig: Record<string, unknown>) => string | undefined\n}\n\nconst defaultHttpConfig = (token: string) => ({\n headers: {Authorization: `Bearer ${token}`},\n type: 'http',\n url: MCP_SERVER_URL,\n})\n\nconst homeDir = os.homedir()\n\n// -- Detect functions --\n\nasync function detectClaudeCode(): Promise<string | null> {\n try {\n await execa('claude', ['--version'], {stdio: 'pipe', timeout: 5000})\n return path.join(homeDir, '.claude.json')\n } catch {\n return null\n }\n}\n\nasync function detectCodexCli(): Promise<string | null> {\n try {\n await execa('codex', ['--version'], {stdio: 'pipe', timeout: 5000})\n const codexHome = process.env.CODEX_HOME || path.join(homeDir, '.codex')\n return path.join(codexHome, 'config.toml')\n } catch {\n return null\n }\n}\n\nasync function detectCursor(): Promise<string | null> {\n const cursorDir = path.join(homeDir, '.cursor')\n return existsSync(cursorDir) ? path.join(cursorDir, 'mcp.json') : null\n}\n\nasync function detectGeminiCli(): Promise<string | null> {\n const geminiDir = path.join(homeDir, '.gemini')\n return existsSync(geminiDir) ? path.join(geminiDir, 'settings.json') : null\n}\n\nasync function detectGitHubCopilotCli(): Promise<string | null> {\n const copilotDir =\n process.platform === 'linux' && process.env.XDG_CONFIG_HOME\n ? path.join(process.env.XDG_CONFIG_HOME, 'copilot')\n : path.join(homeDir, '.copilot')\n return existsSync(copilotDir) ? path.join(copilotDir, 'mcp-config.json') : null\n}\n\nasync function detectOpenCode(): Promise<string | null> {\n try {\n await execa('opencode', ['--version'], {stdio: 'pipe', timeout: 5000})\n return path.join(homeDir, '.config/opencode/opencode.json')\n } catch {\n return null\n }\n}\n\nasync function detectVSCode(): Promise<string | null> {\n let configDir: string | null = null\n switch (process.platform) {\n case 'darwin': {\n configDir = path.join(homeDir, 'Library/Application Support/Code/User')\n break\n }\n case 'win32': {\n if (process.env.APPDATA) {\n configDir = path.join(process.env.APPDATA, 'Code/User')\n }\n break\n }\n default: {\n configDir = path.join(homeDir, '.config/Code/User')\n }\n }\n return configDir && existsSync(configDir) ? path.join(configDir, 'mcp.json') : null\n}\n\nasync function detectVSCodeInsiders(): Promise<string | null> {\n let configDir: string | null = null\n switch (process.platform) {\n case 'darwin': {\n configDir = path.join(homeDir, 'Library/Application Support/Code - Insiders/User')\n break\n }\n case 'win32': {\n if (process.env.APPDATA) {\n configDir = path.join(process.env.APPDATA, 'Code - Insiders/User')\n }\n break\n }\n default: {\n configDir = path.join(homeDir, '.config/Code - Insiders/User')\n }\n }\n return configDir && existsSync(configDir) ? path.join(configDir, 'mcp.json') : null\n}\n\nasync function detectZed(): Promise<string | null> {\n let configDir: string | null = null\n switch (process.platform) {\n case 'win32': {\n if (process.env.APPDATA) {\n configDir = path.join(process.env.APPDATA, 'Zed')\n }\n break\n }\n default: {\n configDir = path.join(homeDir, '.config/zed')\n }\n }\n return configDir && existsSync(configDir) ? path.join(configDir, 'settings.json') : null\n}\n\n// -- Read token helpers --\n\n/**\n * Extract a Bearer token from a headers-like object.\n * Looks for `Authorization: \"Bearer <token>\"` and returns the token portion.\n */\nfunction extractBearerToken(headers: unknown): string | undefined {\n if (typeof headers !== 'object' || headers === null) return undefined\n const auth = (headers as Record<string, unknown>).Authorization\n if (typeof auth !== 'string') return undefined\n const match = auth.match(/^Bearer\\s+(.+)$/)\n return match?.[1]\n}\n\nfunction readTokenFromHeaders(serverConfig: Record<string, unknown>): string | undefined {\n return extractBearerToken(serverConfig.headers)\n}\n\nfunction readTokenFromHttpHeaders(serverConfig: Record<string, unknown>): string | undefined {\n return extractBearerToken(serverConfig.http_headers)\n}\n\n// -- Build server config functions --\n\nfunction buildClaudeCodeServerConfig(token: string): Record<string, unknown> {\n return defaultHttpConfig(token)\n}\n\nfunction buildCodexCliServerConfig(token: string): Record<string, unknown> {\n return {\n http_headers: {Authorization: `Bearer ${token}`},\n type: 'http',\n url: MCP_SERVER_URL,\n }\n}\n\nfunction buildCursorServerConfig(token: string): Record<string, unknown> {\n return defaultHttpConfig(token)\n}\n\nfunction buildGeminiCliServerConfig(token: string): Record<string, unknown> {\n return defaultHttpConfig(token)\n}\n\nfunction buildGitHubCopilotCliServerConfig(token: string): Record<string, unknown> {\n return {\n headers: {Authorization: `Bearer ${token}`},\n tools: ['*'],\n type: 'http',\n url: MCP_SERVER_URL,\n }\n}\n\nfunction buildOpenCodeServerConfig(token: string): Record<string, unknown> {\n return {\n headers: {Authorization: `Bearer ${token}`},\n type: 'remote',\n url: MCP_SERVER_URL,\n }\n}\n\nfunction buildVSCodeServerConfig(token: string): Record<string, unknown> {\n return defaultHttpConfig(token)\n}\n\nfunction buildVSCodeInsidersServerConfig(token: string): Record<string, unknown> {\n return defaultHttpConfig(token)\n}\n\nfunction buildZedServerConfig(token: string): Record<string, unknown> {\n return {\n headers: {Authorization: `Bearer ${token}`},\n settings: {},\n url: MCP_SERVER_URL,\n }\n}\n\n/**\n * Centralized editor configuration including detection logic.\n * To add a new editor: add an entry here - EditorName type is derived automatically.\n */\nexport const EDITOR_CONFIGS = {\n 'Claude Code': {\n buildServerConfig: buildClaudeCodeServerConfig,\n configKey: 'mcpServers',\n detect: detectClaudeCode,\n format: 'jsonc',\n readToken: readTokenFromHeaders,\n },\n 'Codex CLI': {\n buildServerConfig: buildCodexCliServerConfig,\n configKey: 'mcp_servers',\n detect: detectCodexCli,\n format: 'toml',\n readToken: readTokenFromHttpHeaders,\n },\n Cursor: {\n buildServerConfig: buildCursorServerConfig,\n configKey: 'mcpServers',\n detect: detectCursor,\n format: 'jsonc',\n readToken: readTokenFromHeaders,\n },\n 'Gemini CLI': {\n buildServerConfig: buildGeminiCliServerConfig,\n configKey: 'mcpServers',\n detect: detectGeminiCli,\n format: 'jsonc',\n readToken: readTokenFromHeaders,\n },\n 'GitHub Copilot CLI': {\n buildServerConfig: buildGitHubCopilotCliServerConfig,\n configKey: 'mcpServers',\n detect: detectGitHubCopilotCli,\n format: 'jsonc',\n readToken: readTokenFromHeaders,\n },\n OpenCode: {\n buildServerConfig: buildOpenCodeServerConfig,\n configKey: 'mcp',\n detect: detectOpenCode,\n format: 'jsonc',\n readToken: readTokenFromHeaders,\n },\n 'VS Code': {\n buildServerConfig: buildVSCodeServerConfig,\n configKey: 'servers',\n detect: detectVSCode,\n format: 'jsonc',\n readToken: readTokenFromHeaders,\n },\n 'VS Code Insiders': {\n buildServerConfig: buildVSCodeInsidersServerConfig,\n configKey: 'servers',\n detect: detectVSCodeInsiders,\n format: 'jsonc',\n readToken: readTokenFromHeaders,\n },\n Zed: {\n buildServerConfig: buildZedServerConfig,\n configKey: 'context_servers',\n detect: detectZed,\n format: 'jsonc',\n readToken: readTokenFromHeaders,\n },\n} satisfies Record<string, EditorConfig>\n\n/** Derived from EDITOR_CONFIGS keys - add a new editor there and this updates automatically */\nexport type EditorName = keyof typeof EDITOR_CONFIGS\n"],"names":["existsSync","os","path","execa","MCP_SERVER_URL","defaultHttpConfig","token","headers","Authorization","type","url","homeDir","homedir","detectClaudeCode","stdio","timeout","join","detectCodexCli","codexHome","process","env","CODEX_HOME","detectCursor","cursorDir","detectGeminiCli","geminiDir","detectGitHubCopilotCli","copilotDir","platform","XDG_CONFIG_HOME","detectOpenCode","detectVSCode","configDir","APPDATA","detectVSCodeInsiders","detectZed","extractBearerToken","undefined","auth","match","readTokenFromHeaders","serverConfig","readTokenFromHttpHeaders","http_headers","buildClaudeCodeServerConfig","buildCodexCliServerConfig","buildCursorServerConfig","buildGeminiCliServerConfig","buildGitHubCopilotCliServerConfig","tools","buildOpenCodeServerConfig","buildVSCodeServerConfig","buildVSCodeInsidersServerConfig","buildZedServerConfig","settings","EDITOR_CONFIGS","buildServerConfig","configKey","detect","format","readToken","Cursor","OpenCode","Zed"],"mappings":"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"}
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import { checkbox } from '@sanity/cli-core/ux';
|
|
2
|
+
function getEditorLabel(editor) {
|
|
3
|
+
if (editor.configured && editor.authStatus === 'unauthorized') {
|
|
4
|
+
return `${editor.name} (auth expired)`;
|
|
5
|
+
}
|
|
6
|
+
if (editor.configured && !editor.existingToken) {
|
|
7
|
+
return `${editor.name} (missing credentials)`;
|
|
8
|
+
}
|
|
9
|
+
return editor.name;
|
|
10
|
+
}
|
|
2
11
|
/**
|
|
3
|
-
* Prompt user to select which editors to configure
|
|
4
|
-
*
|
|
5
|
-
*
|
|
12
|
+
* Prompt user to select which editors to configure.
|
|
13
|
+
*
|
|
14
|
+
* Expects only actionable editors (unconfigured, or configured with
|
|
15
|
+
* invalid/missing credentials). Annotates entries with auth status.
|
|
6
16
|
*/ export async function promptForMCPSetup(editors) {
|
|
7
17
|
const editorChoices = editors.map((e)=>({
|
|
8
|
-
checked:
|
|
9
|
-
name: e
|
|
18
|
+
checked: true,
|
|
19
|
+
name: getEditorLabel(e),
|
|
10
20
|
value: e.name
|
|
11
21
|
}));
|
|
12
|
-
const
|
|
22
|
+
const selectedNames = await checkbox({
|
|
13
23
|
choices: editorChoices,
|
|
14
24
|
message: 'Configure Sanity MCP server?'
|
|
15
25
|
});
|
|
16
|
-
const selectedNames = result;
|
|
17
26
|
// User can deselect all to skip
|
|
18
27
|
if (!selectedNames || selectedNames.length === 0) {
|
|
19
28
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/mcp/promptForMCPSetup.ts"],"sourcesContent":["import {checkbox} from '@sanity/cli-core/ux'\n\nimport {type Editor} from './types.js'\n\n/**\n * Prompt user to select which editors to configure
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/mcp/promptForMCPSetup.ts"],"sourcesContent":["import {checkbox} from '@sanity/cli-core/ux'\n\nimport {type Editor} from './types.js'\n\nfunction getEditorLabel(editor: Editor): string {\n if (editor.configured && editor.authStatus === 'unauthorized') {\n return `${editor.name} (auth expired)`\n }\n if (editor.configured && !editor.existingToken) {\n return `${editor.name} (missing credentials)`\n }\n return editor.name\n}\n\n/**\n * Prompt user to select which editors to configure.\n *\n * Expects only actionable editors (unconfigured, or configured with\n * invalid/missing credentials). Annotates entries with auth status.\n */\nexport async function promptForMCPSetup(editors: Editor[]): Promise<Editor[] | null> {\n const editorChoices = editors.map((e) => ({\n checked: true, // Pre-select all actionable editors\n name: getEditorLabel(e),\n value: e.name,\n }))\n\n const selectedNames = await checkbox({\n choices: editorChoices,\n message: 'Configure Sanity MCP server?',\n })\n\n // User can deselect all to skip\n if (!selectedNames || selectedNames.length === 0) {\n return null\n }\n\n return editors.filter((e) => selectedNames.includes(e.name))\n}\n"],"names":["checkbox","getEditorLabel","editor","configured","authStatus","name","existingToken","promptForMCPSetup","editors","editorChoices","map","e","checked","value","selectedNames","choices","message","length","filter","includes"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,sBAAqB;AAI5C,SAASC,eAAeC,MAAc;IACpC,IAAIA,OAAOC,UAAU,IAAID,OAAOE,UAAU,KAAK,gBAAgB;QAC7D,OAAO,GAAGF,OAAOG,IAAI,CAAC,eAAe,CAAC;IACxC;IACA,IAAIH,OAAOC,UAAU,IAAI,CAACD,OAAOI,aAAa,EAAE;QAC9C,OAAO,GAAGJ,OAAOG,IAAI,CAAC,sBAAsB,CAAC;IAC/C;IACA,OAAOH,OAAOG,IAAI;AACpB;AAEA;;;;;CAKC,GACD,OAAO,eAAeE,kBAAkBC,OAAiB;IACvD,MAAMC,gBAAgBD,QAAQE,GAAG,CAAC,CAACC,IAAO,CAAA;YACxCC,SAAS;YACTP,MAAMJ,eAAeU;YACrBE,OAAOF,EAAEN,IAAI;QACf,CAAA;IAEA,MAAMS,gBAAgB,MAAMd,SAAS;QACnCe,SAASN;QACTO,SAAS;IACX;IAEA,gCAAgC;IAChC,IAAI,CAACF,iBAAiBA,cAAcG,MAAM,KAAK,GAAG;QAChD,OAAO;IACT;IAEA,OAAOT,QAAQU,MAAM,CAAC,CAACP,IAAMG,cAAcK,QAAQ,CAACR,EAAEN,IAAI;AAC5D"}
|