@nado-language/mcp 0.1.3 → 0.1.5

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
@@ -54,6 +54,8 @@ NADO_MCP_REFRESH_TOKEN='supabase-user-refresh-token'
54
54
 
55
55
  By default this uses the existing Azure Static Web Apps production site as a static OAuth relay. It does not require a new Azure Function, App Service, database, or paid runtime. The local helper still receives the final callback on `127.0.0.1`; Azure only serves the static relay page.
56
56
 
57
+ The installed CLI opens the Nado relay page first. The relay stores the local callback in browser session storage, then sends Supabase a fixed redirect URL. This avoids Supabase rejecting a dynamic `redirect_to` URL with `local_callback` query parameters and falling back to the normal Nado web site.
58
+
57
59
  The MCP server refreshes expired access tokens with `NADO_MCP_REFRESH_TOKEN` and updates the auth file when Supabase rotates the refresh token.
58
60
 
59
61
  Supported local browser providers are `google`, `kakao`, and `apple`. Naver login is not available in the local MCP flow yet because the current Naver Edge Function uses fixed web/native redirect URLs.
@@ -64,6 +66,8 @@ Supabase Auth must allow the Azure relay redirect URL:
64
66
  https://language.nado.ai.kr/auth/mcp-callback
65
67
  ```
66
68
 
69
+ Register the exact URL above. Do not include `local_callback`, `provider`, or other query parameters in the Supabase allow list.
70
+
67
71
  For direct local callback mode, run with `--redirect-mode local` and allow:
68
72
 
69
73
  ```text
@@ -73,6 +77,7 @@ http://127.0.0.1:*/callback
73
77
  You can inspect or clear local MCP auth with:
74
78
 
75
79
  ```bash
80
+ nado-mcp --version
76
81
  nado-mcp status
77
82
  nado-mcp logout
78
83
  ```
@@ -84,6 +89,14 @@ npm run mcp:nado:auth -- status
84
89
  npm run mcp:nado:auth -- logout
85
90
  ```
86
91
 
92
+ If the browser shows an error about an old, incomplete, truncated, or invalid PKCE login URL, close every old Nado MCP login tab, upgrade the package, and rerun login. Printed login URLs are single-run URLs and should not be reused:
93
+
94
+ ```bash
95
+ npm install --global @nado-language/mcp@latest
96
+ nado-mcp --version
97
+ nado-mcp login --provider google
98
+ ```
99
+
87
100
  Manual access-token option:
88
101
 
89
102
  ```bash
@@ -16,12 +16,15 @@ const SUPPORTED_REDIRECT_MODES = new Set(['azure', 'local']);
16
16
 
17
17
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
18
18
  const repoRoot = path.resolve(scriptDir, '..');
19
+ const packageVersion = readPackageVersion();
19
20
 
20
21
  const { command, options } = parseCli(process.argv.slice(2));
21
22
 
22
23
  try {
23
24
  if (options.help || command === 'help') {
24
25
  printHelp();
26
+ } else if (command === 'version') {
27
+ console.log(packageVersion);
25
28
  } else if (command === 'login') {
26
29
  await login(options);
27
30
  } else if (command === 'status') {
@@ -39,7 +42,10 @@ try {
39
42
  function parseCli(argv) {
40
43
  let command = 'login';
41
44
  let args = argv;
42
- if (argv[0] && !argv[0].startsWith('-')) {
45
+ if (argv[0] === '--version' || argv[0] === '-v') {
46
+ command = 'version';
47
+ args = argv.slice(1);
48
+ } else if (argv[0] && !argv[0].startsWith('-')) {
43
49
  command = argv[0];
44
50
  args = argv.slice(1);
45
51
  }
@@ -77,6 +83,7 @@ function parseCli(argv) {
77
83
  else if (flag === '--timeout-ms') options.timeoutMs = Number(readValue());
78
84
  else if (flag === '--no-open') options.noOpen = true;
79
85
  else if (flag === '--help' || flag === '-h') options.help = true;
86
+ else if (flag === '--version' || flag === '-v') command = 'version';
80
87
  else throw new Error(`Unknown option: ${arg}`);
81
88
  }
82
89
 
@@ -183,19 +190,18 @@ async function login(options) {
183
190
 
184
191
  const localCallbackUrl = new URL(`http://127.0.0.1:${address.port}/callback`);
185
192
  localCallbackUrl.searchParams.set('state', state);
186
- const redirectTo = buildRedirectTo(options, localCallbackUrl.toString());
187
- const authUrl = buildAuthorizeUrl({
188
- supabaseUrl: options.supabaseUrl,
193
+ const browserUrl = buildBrowserLoginUrl({
194
+ options,
189
195
  provider,
190
- redirectTo,
196
+ localCallbackUrl: localCallbackUrl.toString(),
191
197
  codeChallenge,
192
198
  });
193
199
 
194
200
  console.log(`Opening browser for Nado MCP login (${provider}).`);
195
201
  console.log(`Local callback: ${localCallbackUrl.toString()}`);
196
202
  if (options.redirectMode === 'azure') console.log(`Azure relay: ${options.relayUrl}`);
197
- if (!options.noOpen) openBrowser(authUrl);
198
- console.log(`If the browser did not open, visit:\n${authUrl}`);
203
+ if (!options.noOpen) openBrowser(browserUrl);
204
+ console.log(`If the browser did not open, visit:\n${browserUrl}`);
199
205
 
200
206
  let timeoutId;
201
207
  const timeout = new Promise((_, reject) => {
@@ -217,18 +223,40 @@ function loginTimeoutError(options) {
217
223
  'Timed out waiting for browser login.',
218
224
  `Rerun \`nado-mcp login --provider ${options.provider} --timeout-ms 900000\` and keep the terminal open until the browser says login completed.`,
219
225
  'If the browser did not open, copy the printed URL into the same desktop browser where you can sign in.',
220
- 'If you already signed in but it still timed out, check that the browser can reach the printed 127.0.0.1 local callback URL and that Supabase Auth allows the relay URL.',
226
+ 'If Google login succeeds but the browser lands on the normal Nado site, upgrade @nado-language/mcp and confirm Supabase Auth allows the exact relay URL without query parameters.',
227
+ 'If it still times out after the relay page says it is returning to the local helper, check that the browser can reach the printed 127.0.0.1 local callback URL.',
221
228
  ].join(' '));
222
229
  }
223
230
 
224
- function buildRedirectTo(options, localCallbackUrl) {
225
- if (options.redirectMode === 'local') return localCallbackUrl;
231
+ function buildBrowserLoginUrl({ options, provider, localCallbackUrl, codeChallenge }) {
232
+ if (options.redirectMode === 'local') {
233
+ return buildAuthorizeUrl({
234
+ supabaseUrl: options.supabaseUrl,
235
+ provider,
236
+ redirectTo: localCallbackUrl,
237
+ codeChallenge,
238
+ });
239
+ }
240
+
241
+ return buildRelayStartUrl({
242
+ relayUrl: options.relayUrl,
243
+ localCallbackUrl,
244
+ provider,
245
+ supabaseUrl: options.supabaseUrl,
246
+ codeChallenge,
247
+ });
248
+ }
226
249
 
227
- const relayUrl = new URL(options.relayUrl);
250
+ function buildRelayStartUrl({ relayUrl: value, localCallbackUrl, provider, supabaseUrl, codeChallenge }) {
251
+ const relayUrl = new URL(value);
228
252
  if (relayUrl.protocol !== 'https:') {
229
253
  throw new Error('--relay-url must be an HTTPS URL when --redirect-mode azure is used.');
230
254
  }
231
255
  relayUrl.searchParams.set('local_callback', localCallbackUrl);
256
+ relayUrl.searchParams.set('provider', provider);
257
+ relayUrl.searchParams.set('supabase_url', supabaseUrl);
258
+ relayUrl.searchParams.set('code_challenge', codeChallenge);
259
+ relayUrl.searchParams.set('client_version', packageVersion);
232
260
  return relayUrl.toString();
233
261
  }
234
262
 
@@ -436,6 +464,24 @@ function resolvePath(value) {
436
464
  return path.isAbsolute(value) ? value : path.resolve(process.cwd(), value);
437
465
  }
438
466
 
467
+ function readPackageVersion() {
468
+ const candidates = [
469
+ path.join(repoRoot, 'packages', 'nado-mcp', 'package.json'),
470
+ path.join(scriptDir, '..', 'package.json'),
471
+ ];
472
+ for (const filePath of candidates) {
473
+ try {
474
+ const pkg = JSON.parse(readFileSync(filePath, 'utf8'));
475
+ if (pkg?.name === '@nado-language/mcp' && typeof pkg.version === 'string') {
476
+ return pkg.version;
477
+ }
478
+ } catch {
479
+ // Continue to the next candidate.
480
+ }
481
+ }
482
+ return '0.0.0-dev';
483
+ }
484
+
439
485
  function listen(server, port) {
440
486
  return new Promise((resolve, reject) => {
441
487
  server.once('error', reject);
@@ -477,6 +523,7 @@ Usage:
477
523
  nado-mcp login [--provider google|kakao|apple]
478
524
  nado-mcp status
479
525
  nado-mcp logout
526
+ nado-mcp-auth --version
480
527
 
481
528
  Repo checkout aliases:
482
529
  npm run mcp:nado:auth -- [login] [--provider google|kakao|apple]
@@ -493,11 +540,16 @@ Options:
493
540
  --timeout-ms <number> Login wait timeout. Default: 300000
494
541
  --supabase-url <url> Supabase project URL
495
542
  --anon-key <key> Supabase anon key
543
+ --version Print installed Nado MCP version
496
544
 
497
545
  Default mode uses the existing Azure Static Web Apps site as a zero-new-resource
498
546
  OAuth relay. Supabase Auth must allow this redirect URL:
499
547
  ${DEFAULT_RELAY_URL}
500
548
 
549
+ The relay starts login with local state in browser sessionStorage, then sends
550
+ Supabase the fixed redirect URL above. Do not add local_callback query strings
551
+ to the Supabase allow list.
552
+
501
553
  The optional local mode requires Supabase Auth to allow:
502
554
  http://127.0.0.1:*/callback
503
555
  `);
@@ -11,6 +11,7 @@ const sourceRoot = path.resolve(scriptDir, '..');
11
11
  const packageDistDir = scriptDir;
12
12
  const serverName = 'nado-language';
13
13
  const opencodeSchema = 'https://opencode.ai/config.json';
14
+ const packageVersion = readPackageVersion();
14
15
 
15
16
  const serverPath = firstExisting([
16
17
  path.join(packageDistDir, 'nado-language-server.mjs'),
@@ -31,6 +32,8 @@ const args = process.argv.slice(3);
31
32
  try {
32
33
  if (command === 'help' || command === '--help' || command === '-h') {
33
34
  printHelp();
35
+ } else if (command === 'version' || command === '--version' || command === '-v') {
36
+ console.log(packageVersion);
34
37
  } else if (command === 'server') {
35
38
  await runNode(serverPath, args, { stdio: 'inherit' });
36
39
  } else if (command === 'login') {
@@ -240,6 +243,7 @@ async function doctor() {
240
243
  const probe = probeTools();
241
244
 
242
245
  console.log('Nado MCP doctor');
246
+ console.log(`Version: ${packageVersion}`);
243
247
  console.log(`Node: ${process.version}`);
244
248
  console.log(`Server: ${serverPath}${existsSync(serverPath) ? '' : ' (missing)'}`);
245
249
  console.log(`Auth CLI: ${authPath}${existsSync(authPath) ? '' : ' (missing)'}`);
@@ -789,6 +793,24 @@ function expandHome(value) {
789
793
  return text;
790
794
  }
791
795
 
796
+ function readPackageVersion() {
797
+ const candidates = [
798
+ path.join(sourceRoot, 'packages', 'nado-mcp', 'package.json'),
799
+ path.join(packageDistDir, '..', 'package.json'),
800
+ ];
801
+ for (const filePath of candidates) {
802
+ try {
803
+ const pkg = JSON.parse(readFileSync(filePath, 'utf8'));
804
+ if (pkg?.name === '@nado-language/mcp' && typeof pkg.version === 'string') {
805
+ return pkg.version;
806
+ }
807
+ } catch {
808
+ // Continue to the next candidate.
809
+ }
810
+ }
811
+ return '0.0.0-dev';
812
+ }
813
+
792
814
  function printHelp() {
793
815
  console.log(`Nado Language MCP
794
816
 
@@ -807,6 +829,7 @@ Usage:
807
829
  nado-mcp server Start the stdio MCP server
808
830
  nado-mcp probe list List exposed MCP tools
809
831
  nado-mcp doctor Print local paths and auth status
832
+ nado-mcp --version Print installed Nado MCP version
810
833
 
811
834
  Supported automatic setup clients:
812
835
  codex, claude, opencode
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nado-language/mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
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,