@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 +33 -0
- package/dist/nado-mcp-cli.mjs +138 -7
- package/package.json +1 -1
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
|
package/dist/nado-mcp-cli.mjs
CHANGED
|
@@ -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
|
-
|
|
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' : '
|
|
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
|
|
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
|
|
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
|
}
|