@nado-language/mcp 0.1.0 → 0.1.2

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 CHANGED
@@ -14,6 +14,18 @@ The default MCP path is designed to avoid double charging for AI. ChatGPT, Claud
14
14
 
15
15
  ## Authentication
16
16
 
17
+ Install from npm:
18
+
19
+ ```bash
20
+ npm install --global @nado-language/mcp
21
+ ```
22
+
23
+ Install on macOS with Homebrew:
24
+
25
+ ```bash
26
+ brew install jintonyc/tap/nado-mcp
27
+ ```
28
+
17
29
  Installed package browser login:
18
30
 
19
31
  ```bash
@@ -103,12 +115,33 @@ export NADO_MCP_AUTH_RELAY_URL='https://language.nado.ai.kr/auth/mcp-callback'
103
115
 
104
116
  Installed package, one-command setup plus browser login:
105
117
 
118
+ ```bash
119
+ nado-mcp connect
120
+ ```
121
+
122
+ To force every supported local config writer:
123
+
124
+ ```bash
125
+ nado-mcp connect all
126
+ ```
127
+
128
+ Client-specific setup remains available:
129
+
106
130
  ```bash
107
131
  nado-mcp connect codex
108
132
  nado-mcp connect claude
109
133
  nado-mcp connect opencode
110
134
  ```
111
135
 
136
+ For Codex, the command uses the Codex CLI when it is on `PATH`. If the user only has Codex Desktop, it writes Codex Desktop's TOML config directly:
137
+
138
+ - macOS/Linux: `~/.codex/config.toml`
139
+ - Windows: `%USERPROFILE%\.codex\config.toml`
140
+
141
+ Restart Codex Desktop after login completes.
142
+
143
+ ChatGPT is different: it uses hosted/remote MCP apps configured from ChatGPT Apps settings, not local stdio config files. The local package can prepare local clients such as Codex, Claude Desktop, OpenCode, and generic JSON-based MCP clients.
144
+
112
145
  Setup without login:
113
146
 
114
147
  ```bash
@@ -48,8 +48,8 @@ try {
48
48
  await setup(parsed.client, parsed.setupOptions);
49
49
  } else if (command === 'connect' || command === 'install') {
50
50
  const parsed = splitClientAndSetupOptions(args);
51
- await setup(parsed.client, parsed.setupOptions, { loginAfter: true });
52
- await runAuth(['login', ...parsed.rest]);
51
+ const didRegister = await setup(parsed.client, parsed.setupOptions, { loginAfter: true });
52
+ if (didRegister) await runAuth(['login', ...parsed.rest]);
53
53
  } else if (command === 'config') {
54
54
  printConfig(args[0] || 'all');
55
55
  } else if (command === 'doctor') {
@@ -72,6 +72,16 @@ async function runAuth(authArgs) {
72
72
 
73
73
  async function setup(client, options = {}, flow = {}) {
74
74
  const normalized = String(client || '').toLowerCase();
75
+ if (normalized === 'auto') {
76
+ return setupAutoLocalClients(options, flow);
77
+ }
78
+ if (normalized === 'all') {
79
+ return setupAllLocalClients(options, flow);
80
+ }
81
+ if (normalized === 'chatgpt' || normalized === 'openai' || normalized === 'chatgpt-app') {
82
+ printChatGptSetup();
83
+ return false;
84
+ }
75
85
  if (normalized === 'codex') {
76
86
  setupCodex(flow);
77
87
  return true;
@@ -111,10 +121,38 @@ async function setup(client, options = {}, flow = {}) {
111
121
  return false;
112
122
  }
113
123
 
124
+ function setupAutoLocalClients(options = {}, flow = {}) {
125
+ const targets = [];
126
+ if (isCodexAvailable()) targets.push(['codex', () => setupCodex(flow)]);
127
+ if (isClaudeDesktopAvailable(options)) targets.push(['claude', () => setupClaudeDesktop(options, flow)]);
128
+ if (isOpenCodeAvailable(options)) targets.push(['opencode', () => setupOpenCode(options, flow)]);
129
+
130
+ if (targets.length === 0) {
131
+ console.log('No supported local MCP client was detected.');
132
+ printGenericSetup();
133
+ return false;
134
+ }
135
+
136
+ console.log(`Detected local MCP client${targets.length === 1 ? '' : 's'}: ${targets.map(([name]) => name).join(', ')}`);
137
+ for (const [, register] of targets) register();
138
+ printChatGptLocalLimit();
139
+ return true;
140
+ }
141
+
142
+ function setupAllLocalClients(options = {}, flow = {}) {
143
+ console.log('Registering Nado Language MCP with every supported local config writer.');
144
+ setupCodexDesktopConfig(flow);
145
+ setupClaudeDesktop(options, flow);
146
+ setupOpenCode(options, flow);
147
+ printChatGptLocalLimit();
148
+ return true;
149
+ }
150
+
114
151
  function setupCodex(flow = {}) {
115
152
  const check = spawnSync('codex', ['--version'], { stdio: 'ignore' });
116
153
  if (check.error || check.status !== 0) {
117
- throw new Error('Codex CLI was not found on PATH. Install/open Codex first, then run `nado-mcp setup codex` again.');
154
+ setupCodexDesktopConfig(flow, 'Codex CLI was not found on PATH.');
155
+ return;
118
156
  }
119
157
 
120
158
  const spec = stdioServerSpec();
@@ -128,6 +166,20 @@ function setupCodex(flow = {}) {
128
166
  printLoginNext(flow);
129
167
  }
130
168
 
169
+ function setupCodexDesktopConfig(flow = {}, reason = '') {
170
+ const configPath = codexDesktopConfigPath();
171
+ mkdirSync(path.dirname(configPath), { recursive: true });
172
+
173
+ const currentConfig = existsSync(configPath) ? readFileSync(configPath, 'utf8') : '';
174
+ const nextConfig = upsertTomlSection(currentConfig, `mcp_servers.${serverName}`, codexDesktopTomlSection());
175
+ writeFileSync(configPath, nextConfig);
176
+
177
+ if (reason) console.log(`${reason} Falling back to Codex Desktop config.`);
178
+ console.log(`Registered Nado Language MCP with Codex Desktop: ${configPath}`);
179
+ if (flow.loginAfter) console.log('Browser login will open next. Restart Codex Desktop after login completes.');
180
+ else console.log('Restart Codex Desktop, then run `nado-mcp login`.');
181
+ }
182
+
131
183
  function setupClaudeDesktop(options = {}, flow = {}) {
132
184
  const configPath = options.configFile || claudeDesktopConfigPath();
133
185
  mkdirSync(path.dirname(configPath), { recursive: true });
@@ -182,6 +234,7 @@ function doctor() {
182
234
  console.log(`Server: ${serverPath}${existsSync(serverPath) ? '' : ' (missing)'}`);
183
235
  console.log(`Auth CLI: ${authPath}${existsSync(authPath) ? '' : ' (missing)'}`);
184
236
  console.log(`Auth file: ${defaultUserAuthEnvFile()}${existsSync(defaultUserAuthEnvFile()) ? ' (present)' : ' (missing)'}`);
237
+ console.log(`Codex Desktop config: ${codexDesktopConfigPath()}`);
185
238
  console.log(`Claude Desktop config: ${claudeDesktopConfigPath()}`);
186
239
  console.log(`OpenCode config: ${opencodeConfigPath()}`);
187
240
  }
@@ -195,7 +248,7 @@ function splitClientAndSetupOptions(values) {
195
248
  const rawClient = hasClient ? values[0] : '';
196
249
  const optionValues = hasClient ? values.slice(1) : values;
197
250
  const parsed = takeSetupOptions(optionValues);
198
- const client = rawClient || (parsed.setupOptions.configFile ? 'mcp-json' : 'generic');
251
+ const client = rawClient || (parsed.setupOptions.configFile ? 'mcp-json' : 'auto');
199
252
  return { client, setupOptions: parsed.setupOptions, rest: parsed.rest };
200
253
  }
201
254
 
@@ -274,6 +327,67 @@ function opencodeConfig() {
274
327
  };
275
328
  }
276
329
 
330
+ function isCodexAvailable() {
331
+ if (process.env.NADO_MCP_CODEX_CONFIG_FILE) return true;
332
+ if (commandExists('codex')) return true;
333
+ return existsSync(path.dirname(codexDesktopConfigPath()));
334
+ }
335
+
336
+ function isClaudeDesktopAvailable(options = {}) {
337
+ if (options.configFile) return true;
338
+ if (existsSync(claudeDesktopConfigPath())) return true;
339
+ if (process.platform === 'darwin') {
340
+ return existsSync('/Applications/Claude.app') || existsSync(path.join(os.homedir(), 'Applications', 'Claude.app'));
341
+ }
342
+ if (process.platform === 'win32') {
343
+ const localAppData = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');
344
+ return existsSync(path.join(localAppData, 'Programs', 'Claude', 'Claude.exe'));
345
+ }
346
+ return commandExists('claude');
347
+ }
348
+
349
+ function isOpenCodeAvailable(options = {}) {
350
+ if (options.configFile || process.env.OPENCODE_CONFIG) return true;
351
+ if (existsSync(opencodeConfigPath())) return true;
352
+ return commandExists('opencode');
353
+ }
354
+
355
+ function commandExists(commandName) {
356
+ const result = spawnSync(commandName, ['--version'], { stdio: 'ignore' });
357
+ return !result.error && result.status === 0;
358
+ }
359
+
360
+ function codexDesktopTomlSection() {
361
+ const spec = stdioServerSpec();
362
+ return [
363
+ `[mcp_servers.${serverName}]`,
364
+ `command = ${tomlString(spec.command)}`,
365
+ `args = ${tomlString(spec.args)}`,
366
+ ].join('\n');
367
+ }
368
+
369
+ function tomlString(value) {
370
+ return JSON.stringify(value);
371
+ }
372
+
373
+ function upsertTomlSection(text, sectionName, sectionText) {
374
+ const output = [];
375
+ let removing = false;
376
+
377
+ for (const line of String(text || '').split(/\r?\n/)) {
378
+ const header = line.match(/^\s*\[([^\]]+)]\s*$/);
379
+ if (header) {
380
+ const name = header[1];
381
+ removing = name === sectionName || name.startsWith(`${sectionName}.`);
382
+ }
383
+
384
+ if (!removing) output.push(line);
385
+ }
386
+
387
+ const base = output.join('\n').trimEnd();
388
+ return `${base}${base ? '\n\n' : ''}${sectionText.trimEnd()}\n`;
389
+ }
390
+
277
391
  function printConfig(format) {
278
392
  const normalized = String(format || 'all').toLowerCase();
279
393
  if (normalized === 'command' || normalized === 'stdio') {
@@ -316,6 +430,16 @@ function printGenericSetup() {
316
430
  console.log('Then run `nado-mcp login`.');
317
431
  }
318
432
 
433
+ function printChatGptSetup() {
434
+ console.log('ChatGPT does not use local stdio MCP config files.');
435
+ console.log('Use a hosted/remote MCP endpoint in ChatGPT Apps settings, then scan tools in ChatGPT.');
436
+ console.log('Nado local package setup can configure Codex, Claude Desktop, OpenCode, and generic local MCP JSON clients.');
437
+ }
438
+
439
+ function printChatGptLocalLimit() {
440
+ console.log('Note: ChatGPT requires a hosted/remote MCP app registration in ChatGPT settings; local package setup cannot install into ChatGPT directly.');
441
+ }
442
+
319
443
  function printMcpServersSetup(client) {
320
444
  console.log(`No config file path was provided for ${client}.`);
321
445
  console.log('Pass `--config-file /path/to/mcp.json`, or paste this into the client MCP config:');
@@ -467,6 +591,11 @@ function defaultUserAuthEnvFile() {
467
591
  return path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), 'nado', 'mcp', 'auth.env');
468
592
  }
469
593
 
594
+ function codexDesktopConfigPath() {
595
+ if (process.env.NADO_MCP_CODEX_CONFIG_FILE) return expandHome(process.env.NADO_MCP_CODEX_CONFIG_FILE);
596
+ return path.join(os.homedir(), '.codex', 'config.toml');
597
+ }
598
+
470
599
  function claudeDesktopConfigPath() {
471
600
  if (process.platform === 'win32') {
472
601
  return path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
@@ -493,7 +622,9 @@ function printHelp() {
493
622
  console.log(`Nado Language MCP
494
623
 
495
624
  Usage:
496
- nado-mcp connect codex Register in Codex, then log in
625
+ nado-mcp connect Auto-register detected local MCP clients, then log in
626
+ nado-mcp connect all Register all supported local config writers, then log in
627
+ nado-mcp connect codex Register in Codex CLI/Desktop, then log in
497
628
  nado-mcp connect claude Register in Claude Desktop, then log in
498
629
  nado-mcp connect opencode Register in OpenCode, then log in
499
630
  nado-mcp setup <client> Register without login
@@ -515,7 +646,7 @@ Universal fallback:
515
646
 
516
647
  AI-agent friendly flow:
517
648
  1. Install the package
518
- 2. Run: nado-mcp connect <client>
519
- 3. If the client is unknown, paste the JSON from nado-mcp config
649
+ 2. Run: nado-mcp connect
650
+ 3. If the client is unknown or remote-only, paste the JSON from nado-mcp config
520
651
  `);
521
652
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nado-language/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Nado Language MCP server for saving AI-generated English flashcards and practicing saved materials.",
5
5
  "type": "module",
6
6
  "private": false,