@nado-language/mcp 0.1.7 → 0.1.9

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
@@ -163,6 +163,8 @@ For Codex, the command uses the Codex CLI when it is on `PATH`. If the user only
163
163
 
164
164
  Restart Codex Desktop after login completes.
165
165
  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.
167
+ 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.
166
168
 
167
169
  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.
168
170
 
@@ -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'],
@@ -30,7 +30,7 @@ const command = process.argv[2] || 'help';
30
30
  const args = process.argv.slice(3);
31
31
 
32
32
  try {
33
- if (command === 'help' || command === '--help' || command === '-h') {
33
+ if (shouldPrintHelp(command, args)) {
34
34
  printHelp();
35
35
  } else if (command === 'version' || command === '--version' || command === '-v') {
36
36
  console.log(packageVersion);
@@ -253,6 +253,10 @@ async function doctor() {
253
253
  console.log(`Auth refresh token: ${auth.refreshToken ? 'present' : 'missing'}`);
254
254
  console.log(`Auth email/password fallback: ${auth.email ? `configured for ${auth.email}` : 'missing'}`);
255
255
  console.log(`Codex Desktop config: ${registrationText(codex)}`);
256
+ if (codex.issues?.length) {
257
+ console.log('Codex Desktop config issues:');
258
+ for (const issue of codex.issues) console.log(` - ${issue}`);
259
+ }
256
260
  console.log(`Claude Desktop config: ${registrationText(claude)}`);
257
261
  console.log(`OpenCode config: ${registrationText(opencode)}`);
258
262
  console.log('');
@@ -260,10 +264,12 @@ async function doctor() {
260
264
  console.log('A green local server check only proves the stdio server can list tools; it does not prove an already-open chat loaded them.');
261
265
  console.log('');
262
266
  console.log('If the server check is ok but Nado tools are not visible in the AI app:');
263
- console.log(' 1. Fully quit and restart the desktop app after `nado-mcp connect`.');
264
- console.log(' 2. Start a new chat/session; existing sessions may not reload newly added MCP tools.');
265
- console.log(' 3. Run `nado-mcp status` for auth and `nado-mcp probe list` for local server tools.');
266
- console.log(' 4. If a chat tries local files or Google Drive for "나두/Nado 암기장", that chat did not load the Nado MCP tools.');
267
+ console.log(' 1. In Codex, run `/mcp` in the chat/TUI and confirm nado-language is active, not only listed in Settings.');
268
+ console.log(' 2. Fully quit and restart the desktop app after `nado-mcp connect`.');
269
+ console.log(' 3. Start a new chat/session; existing sessions may not reload newly added MCP tools.');
270
+ console.log(' 4. On Windows, remove Working directory/cwd unless it is an existing absolute path. The Nado server does not need cwd.');
271
+ console.log(' 5. Run `nado-mcp status` for auth and `nado-mcp probe list` for local server tools.');
272
+ console.log(' 6. If a chat tries local files or Google Drive for "나두/Nado 암기장", that chat did not load the Nado MCP tools.');
267
273
  }
268
274
 
269
275
  function printToolProbeSummary() {
@@ -327,13 +333,93 @@ function codexRegistrationStatus() {
327
333
 
328
334
  try {
329
335
  const text = readFileSync(configPath, 'utf8');
330
- const sectionPattern = new RegExp(`^\\s*\\[mcp_servers\\.${escapeRegExp(serverName)}]\\s*$`, 'm');
331
- return { path: configPath, fileExists: true, registered: sectionPattern.test(text) };
336
+ const sectionText = readTomlSection(text, `mcp_servers.${serverName}`);
337
+ const registered = Boolean(sectionText);
338
+ const issues = registered ? codexMcpSectionIssues(sectionText) : [];
339
+ return { path: configPath, fileExists: true, registered, issues };
332
340
  } catch (error) {
333
341
  return { path: configPath, fileExists: true, registered: false, error: error instanceof Error ? error.message : String(error) };
334
342
  }
335
343
  }
336
344
 
345
+ function readTomlSection(text, sectionName) {
346
+ const target = String(sectionName || '').trim();
347
+ const lines = String(text || '').split(/\r?\n/);
348
+ const output = [];
349
+ let inSection = false;
350
+
351
+ for (const line of lines) {
352
+ const header = line.match(/^\s*\[([^\]]+)]\s*$/);
353
+ if (header) {
354
+ if (inSection) break;
355
+ inSection = header[1] === target;
356
+ continue;
357
+ }
358
+ if (inSection) output.push(line);
359
+ }
360
+
361
+ return inSection ? output.join('\n') : '';
362
+ }
363
+
364
+ function codexMcpSectionIssues(sectionText) {
365
+ const issues = [];
366
+ const command = tomlStringValue(sectionText, 'command');
367
+ const cwd = tomlStringValue(sectionText, 'cwd');
368
+ const args = tomlArrayValue(sectionText, 'args');
369
+
370
+ if (!command) {
371
+ issues.push('Missing command. Codex cannot start a stdio MCP server without command.');
372
+ } else if (path.isAbsolute(command) && !existsSync(command)) {
373
+ issues.push(`Command path does not exist: ${command}`);
374
+ }
375
+
376
+ if (!Array.isArray(args) || args.length === 0) {
377
+ issues.push('Missing args. Expected the Nado server script path in args.');
378
+ } else {
379
+ const serverArg = args[0];
380
+ if (typeof serverArg === 'string' && path.isAbsolute(serverArg) && !existsSync(serverArg)) {
381
+ issues.push(`Server script path does not exist: ${serverArg}`);
382
+ }
383
+ }
384
+
385
+ if (cwd) {
386
+ const expandedCwd = expandHome(cwd);
387
+ const exists = existsSync(expandedCwd);
388
+ const detail = exists ? `configured as ${cwd}` : `configured as ${cwd}, which does not exist on this machine`;
389
+ issues.push(`Remove Working directory/cwd unless required; Nado MCP does not need it (${detail}).`);
390
+ }
391
+
392
+ return issues;
393
+ }
394
+
395
+ function tomlRawValue(sectionText, key) {
396
+ const pattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*(.+?)\\s*$`, 'm');
397
+ return sectionText.match(pattern)?.[1]?.trim() || '';
398
+ }
399
+
400
+ function tomlStringValue(sectionText, key) {
401
+ const raw = tomlRawValue(sectionText, key);
402
+ if (!raw) return '';
403
+ try {
404
+ const parsed = JSON.parse(raw);
405
+ return typeof parsed === 'string' ? parsed : '';
406
+ } catch {
407
+ const singleQuoted = raw.match(/^'([^']*)'$/);
408
+ return singleQuoted ? singleQuoted[1] : '';
409
+ }
410
+ }
411
+
412
+ function tomlArrayValue(sectionText, key) {
413
+ const raw = tomlRawValue(sectionText, key);
414
+ if (!raw) return null;
415
+ try {
416
+ const parsed = JSON.parse(raw);
417
+ return Array.isArray(parsed) ? parsed : null;
418
+ } catch {
419
+ return null;
420
+ }
421
+ }
422
+
337
423
  function jsonRegistrationStatus(configPath, keyPath) {
338
424
  if (!existsSync(configPath)) return { path: configPath, fileExists: false, registered: false };
339
425
 
@@ -536,6 +622,11 @@ function commandExists(commandName) {
536
622
  return !result.error && result.status === 0;
537
623
  }
538
624
 
625
+ function shouldPrintHelp(currentCommand, currentArgs) {
626
+ if (currentCommand === 'help' || currentCommand === '--help' || currentCommand === '-h') return true;
627
+ return currentArgs.includes('--help') || currentArgs.includes('-h');
628
+ }
629
+
539
630
  function codexDesktopTomlSection() {
540
631
  const spec = stdioServerSpec();
541
632
  return [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nado-language/mcp",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
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,