@nado-language/mcp 0.1.8 → 0.1.10

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
@@ -6,7 +6,7 @@ This stdio MCP server lets AI chat clients save and practice Nado Language study
6
6
 
7
7
  - `nado_whoami`: validates the configured Nado account.
8
8
  - `nado_save_flashcard`: saves a structured flashcard generated by the user's chat AI. Nado does not call AI for this path.
9
- - `nado_save_study_item`: alias of `nado_save_flashcard` for natural memorization requests such as "암기할래", "외울래", "암기장에 넣어줘", and "단어장에 추가".
9
+ - `nado_save_study_item`: alias of `nado_save_flashcard` for natural memorization requests such as "hi 를 단어장에 저장해줘", "암기할래", "외울래", "암기장에 넣어줘", and "단어장에 추가".
10
10
  - `nado_update_study_item`: updates an existing saved Nado card with a revised meaning, description/explanation, examples, variants, or context. Use it for requests like "hello 설명 추가", "description 추가", "뜻 수정", and "예문 추가".
11
11
  - `nado_analyze_and_save_flashcard`: Pro/Admin only. Nado AI generates the learner definition, usage note, examples, and variants, then saves the flashcard.
12
12
  - `nado_list_study_items`: loads saved flashcards for the authenticated user.
@@ -16,9 +16,9 @@ The default MCP path is designed to avoid double charging for AI. ChatGPT, Claud
16
16
 
17
17
  Intent routing:
18
18
 
19
- - If the user provides a new English item and says "나두 암기장", "nado 암기장", "암기할래", "외울래", "암기장에 추가", "단어장에 넣어줘", "remember this", or "add this to my flashcards", use `nado_save_flashcard` or `nado_save_study_item`. Do not create local Markdown files or Google Drive documents for these Nado requests.
19
+ - If the user provides a new English item and says "hi 를 단어장에 저장해줘", "나두 암기장", "nado 암기장", "암기할래", "외울래", "암기장에 추가", "단어장에 넣어줘", "remember this", or "add this to my flashcards", use `nado_save_flashcard` or `nado_save_study_item`. Do not create local Markdown files or Google Drive documents for these Nado requests.
20
20
  - If the user asks to add or change description/설명/뜻/예문 on an already saved card, use `nado_update_study_item`.
21
- - If the user asks to study already saved cards with phrases like "외울래", "암기 연습", "퀴즈 내줘", "쓰기연습", "영작하기", or "복습할래", use `nado_generate_practice`.
21
+ - If the user asks to study already saved cards with phrases like "암기장에 있는 단어로 작문연습하자", "저장된 단어로 작문연습", "외울래", "암기 연습", "퀴즈 내줘", "쓰기연습", "영작하기", or "복습할래", use `nado_generate_practice`. For 작문연습/쓰기연습/영작하기, set `mode` to `writing`.
22
22
  - If the user wants to inspect saved items first, use `nado_list_study_items`.
23
23
 
24
24
  ## Authentication
@@ -136,7 +136,17 @@ export NADO_MCP_AUTH_RELAY_URL='https://language.nado.ai.kr/auth/mcp-callback'
136
136
 
137
137
  ## Client Registration
138
138
 
139
- Installed package, one-command setup plus browser login:
139
+ Installed package auto-registers the supported local config writers during install/update. It writes Codex Desktop, Claude Desktop, and OpenCode config entries when possible. It does not open browser login during install, and it does not force an already-open AI chat to reload tools.
140
+
141
+ After install, run browser login once:
142
+
143
+ ```bash
144
+ nado-mcp login
145
+ ```
146
+
147
+ Then fully restart the AI desktop app and start a new chat/session.
148
+
149
+ Manual one-command setup plus browser login remains available:
140
150
 
141
151
  ```bash
142
152
  nado-mcp connect
@@ -148,6 +158,12 @@ To force every supported local config writer:
148
158
  nado-mcp connect all
149
159
  ```
150
160
 
161
+ To skip install-time auto-registration, install with:
162
+
163
+ ```bash
164
+ NADO_MCP_SKIP_POSTINSTALL=1 npm install --global @nado-language/mcp
165
+ ```
166
+
151
167
  Client-specific setup remains available:
152
168
 
153
169
  ```bash
@@ -161,9 +177,9 @@ For Codex, the command uses the Codex CLI when it is on `PATH`. If the user only
161
177
  - macOS/Linux: `~/.codex/config.toml`
162
178
  - Windows: `%USERPROFILE%\.codex\config.toml`
163
179
 
164
- Restart Codex Desktop after login completes.
180
+ Restart Codex Desktop after install/login completes.
165
181
  If a client was already open, start a new chat/session after restart. Most local MCP clients load tool definitions when the session starts, so a config that was just written may not appear inside an already-running conversation.
166
- For Codex, confirm the server is active from the chat/TUI with `/mcp`. A server can appear in Settings while still failing to start for the current session.
182
+ For Codex, `/mcp` is an optional diagnostic check to confirm the server is active in the current chat/TUI. Normal users should not need it once install-time registration, login, restart, and new chat have completed. A server can appear in Settings while still failing to start for the current session.
167
183
  On Windows, leave the Codex MCP working directory/cwd empty unless it is an existing absolute path. The Nado server does not need a working directory, and a value such as `~/code` can prevent the stdio process from starting if the client does not expand it.
168
184
 
169
185
  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.
@@ -269,7 +285,15 @@ npm run mcp:nado:probe -- save-nado-ai "serendipity"
269
285
 
270
286
  ## Package CLI
271
287
 
272
- Install/register/login flow:
288
+ Install/update auto-registers supported local config writers for Codex Desktop, Claude Desktop, and OpenCode. It does not open browser login during install.
289
+
290
+ Login flow:
291
+
292
+ ```bash
293
+ nado-mcp login
294
+ ```
295
+
296
+ Manual register/login flow:
273
297
 
274
298
  ```bash
275
299
  nado-mcp connect
@@ -109,12 +109,12 @@ const tools = [
109
109
  },
110
110
  {
111
111
  name: 'nado_save_flashcard',
112
- description: 'Save a structured flashcard to the user\'s Nado Language/Nado 암기장. Use this when the user gives a new English item and asks to memorize/save it, for example "나두 암기장에 추가해줘", "nado 암기장에 넣어줘", "암기장에 추가해줘", "암기할래", "외울래", "단어장에 넣어줘", "remember this", or "add this to my flashcards". Do not create local Markdown files or Google Drive documents for these Nado requests. This does not call Nado AI; the user/chat AI supplies the content quality.',
112
+ description: 'Save a structured flashcard to the user\'s Nado Language/Nado 암기장. Use this when the user gives a new English item and asks to memorize/save it, for example "hi 를 단어장에 저장해줘", "나두 암기장에 추가해줘", "nado 암기장에 넣어줘", "암기장에 추가해줘", "암기할래", "외울래", "단어장에 넣어줘", "remember this", or "add this to my flashcards". Do not create local Markdown files or Google Drive documents for these Nado requests. This does not call Nado AI; the user/chat AI supplies the content quality.',
113
113
  inputSchema: saveFlashcardInputSchema,
114
114
  },
115
115
  {
116
116
  name: 'nado_save_study_item',
117
- description: 'Alias of nado_save_flashcard for Nado Language study-list and memorization intents. Prefer this or nado_save_flashcard when the user asks to save a specific word/phrase/sentence to Nado for later memorization, including Korean requests like "나두 암기장", "nado language", "암기할래", "외울래", "암기장에 넣어줘", or "단어장에 추가". Do not answer by editing local files or Drive docs when the user names Nado/Nado MCP. This free path does not call Nado AI.',
117
+ description: 'Alias of nado_save_flashcard for Nado Language study-list and memorization intents. Prefer this or nado_save_flashcard when the user asks to save a specific word/phrase/sentence to Nado for later memorization, including Korean requests like "hi 를 단어장에 저장해줘", "나두 암기장", "nado language", "암기할래", "외울래", "암기장에 넣어줘", or "단어장에 추가". Do not answer by editing local files or Drive docs when the user names Nado/Nado MCP. This free path does not call Nado AI.',
118
118
  inputSchema: saveFlashcardInputSchema,
119
119
  },
120
120
  {
@@ -156,7 +156,7 @@ const tools = [
156
156
  },
157
157
  {
158
158
  name: 'nado_generate_practice',
159
- description: 'Generate English practice content using only the authenticated user saved Nado Language study cards. Use when the user wants to study/review already saved items: "외울래", "암기 연습", "퀴즈 내줘", "쓰기연습", "영작하기", "복습할래", "practice my saved words". If the user supplies a new English item to save, use nado_save_flashcard instead.',
159
+ description: 'Generate English practice content using only the authenticated user saved Nado Language study cards. Use when the user wants to study/review already saved items: "암기장에 있는 단어로 작문연습하자", "저장된 단어로 작문연습", "외울래", "암기 연습", "퀴즈 내줘", "쓰기연습", "영작하기", "복습할래", "practice my saved words". For 작문연습/쓰기연습/영작하기 requests, use mode "writing". If the user supplies a new English item to save, use nado_save_flashcard instead.',
160
160
  inputSchema: {
161
161
  type: 'object',
162
162
  required: ['mode'],
@@ -44,6 +44,8 @@ try {
44
44
  await runAuth(['logout', ...args]);
45
45
  } else if (command === 'auth') {
46
46
  await runAuth(args.length > 0 ? args : ['login']);
47
+ } else if (command === 'postinstall') {
48
+ runPostinstallAutoSetup();
47
49
  } else if (command === 'probe') {
48
50
  await runNode(probePath, args, { stdio: 'inherit' });
49
51
  } else if (command === 'setup') {
@@ -155,6 +157,40 @@ function setupAllLocalClients(options = {}, flow = {}) {
155
157
  return true;
156
158
  }
157
159
 
160
+ function runPostinstallAutoSetup() {
161
+ if (process.env.NADO_MCP_SKIP_POSTINSTALL === '1' || process.env.NADO_MCP_NO_AUTO_SETUP === '1') {
162
+ console.log('Nado MCP postinstall: skipped automatic client registration.');
163
+ return;
164
+ }
165
+
166
+ try {
167
+ console.log('Nado MCP postinstall: registering supported local MCP clients.');
168
+ const registrations = [
169
+ ['Codex Desktop', () => setupCodexDesktopConfig({ postinstall: true })],
170
+ ['Claude Desktop', () => setupClaudeDesktop({}, { postinstall: true })],
171
+ ['OpenCode', () => setupOpenCode({}, { postinstall: true })],
172
+ ];
173
+ let registeredCount = 0;
174
+ for (const [label, register] of registrations) {
175
+ try {
176
+ register();
177
+ registeredCount += 1;
178
+ } catch (error) {
179
+ console.warn(`Nado MCP postinstall warning: ${label} registration failed (${error instanceof Error ? error.message : String(error)}).`);
180
+ }
181
+ }
182
+ printToolProbeSummary();
183
+ if (registeredCount > 0) {
184
+ console.log('Nado MCP postinstall: registration complete. Run `nado-mcp login` once, then restart the AI app and start a new chat.');
185
+ } else {
186
+ console.warn('Nado MCP postinstall warning: no local client config was registered. Run `nado-mcp setup all` manually, or use `nado-mcp config` for a generic MCP client.');
187
+ }
188
+ } catch (error) {
189
+ console.warn(`Nado MCP postinstall warning: automatic registration failed (${error instanceof Error ? error.message : String(error)}).`);
190
+ console.warn('Run `nado-mcp setup all` manually, or use `nado-mcp config` for a generic MCP client.');
191
+ }
192
+ }
193
+
158
194
  function setupCodex(flow = {}) {
159
195
  const check = spawnSync('codex', ['--version'], { stdio: 'ignore' });
160
196
  if (check.error || check.status !== 0) {
@@ -183,7 +219,8 @@ function setupCodexDesktopConfig(flow = {}, reason = '') {
183
219
 
184
220
  if (reason) console.log(`${reason} Falling back to Codex Desktop config.`);
185
221
  console.log(`Registered Nado Language MCP with Codex Desktop: ${configPath}`);
186
- if (flow.loginAfter) console.log('Browser login will open next. Restart Codex Desktop after login completes.');
222
+ if (flow.postinstall) console.log('Restart Codex Desktop or start a new Codex session before expecting Nado tools to appear.');
223
+ else if (flow.loginAfter) console.log('Browser login will open next. Restart Codex Desktop after login completes.');
187
224
  else console.log('Restart Codex Desktop, then run `nado-mcp login`.');
188
225
  }
189
226
 
@@ -200,7 +237,8 @@ function setupClaudeDesktop(options = {}, flow = {}) {
200
237
 
201
238
  writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
202
239
  console.log(`Registered Nado Language MCP with Claude Desktop: ${configPath}`);
203
- if (flow.loginAfter) console.log('Browser login will open next. Restart Claude Desktop after login completes.');
240
+ if (flow.postinstall) console.log('Restart Claude Desktop before expecting Nado tools to appear.');
241
+ else if (flow.loginAfter) console.log('Browser login will open next. Restart Claude Desktop after login completes.');
204
242
  else console.log('Restart Claude Desktop, then run `nado-mcp login`.');
205
243
  }
206
244
 
@@ -253,6 +291,10 @@ async function doctor() {
253
291
  console.log(`Auth refresh token: ${auth.refreshToken ? 'present' : 'missing'}`);
254
292
  console.log(`Auth email/password fallback: ${auth.email ? `configured for ${auth.email}` : 'missing'}`);
255
293
  console.log(`Codex Desktop config: ${registrationText(codex)}`);
294
+ if (codex.issues?.length) {
295
+ console.log('Codex Desktop config issues:');
296
+ for (const issue of codex.issues) console.log(` - ${issue}`);
297
+ }
256
298
  console.log(`Claude Desktop config: ${registrationText(claude)}`);
257
299
  console.log(`OpenCode config: ${registrationText(opencode)}`);
258
300
  console.log('');
@@ -329,13 +371,93 @@ function codexRegistrationStatus() {
329
371
 
330
372
  try {
331
373
  const text = readFileSync(configPath, 'utf8');
332
- const sectionPattern = new RegExp(`^\\s*\\[mcp_servers\\.${escapeRegExp(serverName)}]\\s*$`, 'm');
333
- return { path: configPath, fileExists: true, registered: sectionPattern.test(text) };
374
+ const sectionText = readTomlSection(text, `mcp_servers.${serverName}`);
375
+ const registered = Boolean(sectionText);
376
+ const issues = registered ? codexMcpSectionIssues(sectionText) : [];
377
+ return { path: configPath, fileExists: true, registered, issues };
334
378
  } catch (error) {
335
379
  return { path: configPath, fileExists: true, registered: false, error: error instanceof Error ? error.message : String(error) };
336
380
  }
337
381
  }
338
382
 
383
+ function readTomlSection(text, sectionName) {
384
+ const target = String(sectionName || '').trim();
385
+ const lines = String(text || '').split(/\r?\n/);
386
+ const output = [];
387
+ let inSection = false;
388
+
389
+ for (const line of lines) {
390
+ const header = line.match(/^\s*\[([^\]]+)]\s*$/);
391
+ if (header) {
392
+ if (inSection) break;
393
+ inSection = header[1] === target;
394
+ continue;
395
+ }
396
+ if (inSection) output.push(line);
397
+ }
398
+
399
+ return inSection ? output.join('\n') : '';
400
+ }
401
+
402
+ function codexMcpSectionIssues(sectionText) {
403
+ const issues = [];
404
+ const command = tomlStringValue(sectionText, 'command');
405
+ const cwd = tomlStringValue(sectionText, 'cwd');
406
+ const args = tomlArrayValue(sectionText, 'args');
407
+
408
+ if (!command) {
409
+ issues.push('Missing command. Codex cannot start a stdio MCP server without command.');
410
+ } else if (path.isAbsolute(command) && !existsSync(command)) {
411
+ issues.push(`Command path does not exist: ${command}`);
412
+ }
413
+
414
+ if (!Array.isArray(args) || args.length === 0) {
415
+ issues.push('Missing args. Expected the Nado server script path in args.');
416
+ } else {
417
+ const serverArg = args[0];
418
+ if (typeof serverArg === 'string' && path.isAbsolute(serverArg) && !existsSync(serverArg)) {
419
+ issues.push(`Server script path does not exist: ${serverArg}`);
420
+ }
421
+ }
422
+
423
+ if (cwd) {
424
+ const expandedCwd = expandHome(cwd);
425
+ const exists = existsSync(expandedCwd);
426
+ const detail = exists ? `configured as ${cwd}` : `configured as ${cwd}, which does not exist on this machine`;
427
+ issues.push(`Remove Working directory/cwd unless required; Nado MCP does not need it (${detail}).`);
428
+ }
429
+
430
+ return issues;
431
+ }
432
+
433
+ function tomlRawValue(sectionText, key) {
434
+ const pattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*(.+?)\\s*$`, 'm');
435
+ return sectionText.match(pattern)?.[1]?.trim() || '';
436
+ }
437
+
438
+ function tomlStringValue(sectionText, key) {
439
+ const raw = tomlRawValue(sectionText, key);
440
+ if (!raw) return '';
441
+ try {
442
+ const parsed = JSON.parse(raw);
443
+ return typeof parsed === 'string' ? parsed : '';
444
+ } catch {
445
+ const singleQuoted = raw.match(/^'([^']*)'$/);
446
+ return singleQuoted ? singleQuoted[1] : '';
447
+ }
448
+ }
449
+
450
+ function tomlArrayValue(sectionText, key) {
451
+ const raw = tomlRawValue(sectionText, key);
452
+ if (!raw) return null;
453
+ try {
454
+ const parsed = JSON.parse(raw);
455
+ return Array.isArray(parsed) ? parsed : null;
456
+ } catch {
457
+ return null;
458
+ }
459
+ }
460
+
339
461
  function jsonRegistrationStatus(configPath, keyPath) {
340
462
  if (!existsSync(configPath)) return { path: configPath, fileExists: false, registered: false };
341
463
 
@@ -785,6 +907,7 @@ function codexDesktopConfigPath() {
785
907
  }
786
908
 
787
909
  function claudeDesktopConfigPath() {
910
+ if (process.env.NADO_MCP_CLAUDE_CONFIG_FILE) return expandHome(process.env.NADO_MCP_CLAUDE_CONFIG_FILE);
788
911
  if (process.platform === 'win32') {
789
912
  return path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
790
913
  }
@@ -828,6 +951,7 @@ function printHelp() {
828
951
  console.log(`Nado Language MCP
829
952
 
830
953
  Usage:
954
+ nado-mcp login Open browser login and save local auth
831
955
  nado-mcp connect Auto-register detected local MCP clients, then log in
832
956
  nado-mcp connect all Register all supported local config writers, then log in
833
957
  nado-mcp connect codex Register in Codex CLI/Desktop, then log in
@@ -836,7 +960,6 @@ Usage:
836
960
  nado-mcp setup <client> Register without login
837
961
  nado-mcp setup mcp-json --file PATH Merge into a generic mcpServers JSON file
838
962
  nado-mcp config Print generic MCP config snippets
839
- nado-mcp login Open browser login and save local auth
840
963
  nado-mcp status Show local auth status
841
964
  nado-mcp logout Remove local auth tokens
842
965
  nado-mcp server Start the stdio MCP server
@@ -852,8 +975,9 @@ Universal fallback:
852
975
  nado-mcp config
853
976
 
854
977
  AI-agent friendly flow:
855
- 1. Install the package
856
- 2. Run: nado-mcp connect
857
- 3. If the client is unknown or remote-only, paste the JSON from nado-mcp config
978
+ 1. Install the package; install/update auto-registers Codex Desktop, Claude Desktop, and OpenCode
979
+ 2. Run: nado-mcp login
980
+ 3. Fully restart the AI app and start a new chat/session
981
+ 4. If the client is unknown or remote-only, paste the JSON from nado-mcp config
858
982
  `);
859
983
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nado-language/mcp",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
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,
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "scripts": {
21
21
  "prepack": "node ../../scripts/build-nado-mcp-package.mjs",
22
+ "postinstall": "node dist/nado-mcp-cli.mjs postinstall",
22
23
  "test": "node dist/nado-mcp-cli.mjs doctor"
23
24
  },
24
25
  "keywords": [