@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 +5 -3
- package/dist/nado-language-server.mjs +3 -3
- package/dist/nado-mcp-cli.mjs +98 -7
- package/package.json +1 -1
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'],
|
package/dist/nado-mcp-cli.mjs
CHANGED
|
@@ -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
|
|
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.
|
|
264
|
-
console.log(' 2.
|
|
265
|
-
console.log(' 3.
|
|
266
|
-
console.log(' 4.
|
|
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
|
|
331
|
-
|
|
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 [
|