@nado-language/mcp 0.1.4 → 0.1.6
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 +16 -6
- package/dist/nado-language-server.mjs +1 -1
- package/dist/nado-mcp-auth.mjs +137 -17
- package/dist/nado-mcp-cli.mjs +23 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,25 +42,25 @@ nado-mcp login
|
|
|
42
42
|
Source checkout alias:
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
npm run mcp:nado:auth
|
|
45
|
+
npm run mcp:nado:auth
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
This opens the
|
|
48
|
+
This opens the Nado web connect page. The user signs in there with any login method already supported by Nado, then the page sends the browser session to the local `127.0.0.1` helper and writes ignored local tokens to the user's OS config directory for package installs or `.env.mcp.local` in a repo checkout:
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
NADO_MCP_ACCESS_TOKEN='supabase-user-access-token'
|
|
52
52
|
NADO_MCP_REFRESH_TOKEN='supabase-user-refresh-token'
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
By default this uses the existing Azure Static Web Apps production site as a
|
|
55
|
+
By default this uses the existing Azure Static Web Apps production site as a provider-neutral connect page. It does not require a new Azure Function, App Service, database, or paid runtime. The browser posts the session directly to the local helper; tokens are not placed in the browser URL.
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
Legacy direct OAuth remains available with `nado-mcp login --provider google|kakao|apple`, but normal users should omit `--provider`.
|
|
58
58
|
|
|
59
59
|
The MCP server refreshes expired access tokens with `NADO_MCP_REFRESH_TOKEN` and updates the auth file when Supabase rotates the refresh token.
|
|
60
60
|
|
|
61
|
-
Supported
|
|
61
|
+
Supported web login methods are the same as Nado web login, including Google, Kakao, Naver, and Apple.
|
|
62
62
|
|
|
63
|
-
Supabase Auth
|
|
63
|
+
Legacy direct OAuth provider mode requires Supabase Auth to allow the Azure relay redirect URL:
|
|
64
64
|
|
|
65
65
|
```text
|
|
66
66
|
https://language.nado.ai.kr/auth/mcp-callback
|
|
@@ -77,6 +77,7 @@ http://127.0.0.1:*/callback
|
|
|
77
77
|
You can inspect or clear local MCP auth with:
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
|
+
nado-mcp --version
|
|
80
81
|
nado-mcp status
|
|
81
82
|
nado-mcp logout
|
|
82
83
|
```
|
|
@@ -88,6 +89,14 @@ npm run mcp:nado:auth -- status
|
|
|
88
89
|
npm run mcp:nado:auth -- logout
|
|
89
90
|
```
|
|
90
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
|
|
98
|
+
```
|
|
99
|
+
|
|
91
100
|
Manual access-token option:
|
|
92
101
|
|
|
93
102
|
```bash
|
|
@@ -119,6 +128,7 @@ Optional environment:
|
|
|
119
128
|
```bash
|
|
120
129
|
export NADO_MCP_SUPABASE_URL='https://ptbwzhxifxdnfmqsiugi.supabase.co'
|
|
121
130
|
export NADO_MCP_SUPABASE_ANON_KEY='...'
|
|
131
|
+
export NADO_MCP_CONNECT_URL='https://language.nado.ai.kr/mcp/connect'
|
|
122
132
|
export NADO_MCP_AUTH_RELAY_URL='https://language.nado.ai.kr/auth/mcp-callback'
|
|
123
133
|
```
|
|
124
134
|
|
|
@@ -257,7 +257,7 @@ async function getAccessToken() {
|
|
|
257
257
|
const email = clampText(env.NADO_MCP_EMAIL || '', 320);
|
|
258
258
|
const password = env.NADO_MCP_PASSWORD || '';
|
|
259
259
|
if (!email || !password) {
|
|
260
|
-
throw new Error('AUTH_NOT_CONFIGURED: run `nado-mcp login`, or in a repo checkout run `npm run mcp:nado:auth
|
|
260
|
+
throw new Error('AUTH_NOT_CONFIGURED: run `nado-mcp login`, or in a repo checkout run `npm run mcp:nado:auth`, before using Nado MCP tools.');
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
if (cachedPasswordGrant && cachedPasswordGrant.expiresAt > Date.now() + 60_000) {
|
package/dist/nado-mcp-auth.mjs
CHANGED
|
@@ -11,17 +11,21 @@ import { fileURLToPath } from 'node:url';
|
|
|
11
11
|
const DEFAULT_SUPABASE_URL = 'https://ptbwzhxifxdnfmqsiugi.supabase.co';
|
|
12
12
|
const DEFAULT_SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InB0Ynd6aHhpZnhkbmZtcXNpdWdpIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzU1MTU4MjEsImV4cCI6MjA5MTA5MTgyMX0.c0SU8lvIb8BbwhYyI529dn7tQUfwTl1cGqeahGKaD_g';
|
|
13
13
|
const DEFAULT_RELAY_URL = 'https://language.nado.ai.kr/auth/mcp-callback';
|
|
14
|
+
const DEFAULT_CONNECT_URL = 'https://language.nado.ai.kr/mcp/connect';
|
|
14
15
|
const SUPPORTED_PROVIDERS = new Set(['google', 'kakao', 'apple']);
|
|
15
16
|
const SUPPORTED_REDIRECT_MODES = new Set(['azure', 'local']);
|
|
16
17
|
|
|
17
18
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
18
19
|
const repoRoot = path.resolve(scriptDir, '..');
|
|
20
|
+
const packageVersion = readPackageVersion();
|
|
19
21
|
|
|
20
22
|
const { command, options } = parseCli(process.argv.slice(2));
|
|
21
23
|
|
|
22
24
|
try {
|
|
23
25
|
if (options.help || command === 'help') {
|
|
24
26
|
printHelp();
|
|
27
|
+
} else if (command === 'version') {
|
|
28
|
+
console.log(packageVersion);
|
|
25
29
|
} else if (command === 'login') {
|
|
26
30
|
await login(options);
|
|
27
31
|
} else if (command === 'status') {
|
|
@@ -39,19 +43,23 @@ try {
|
|
|
39
43
|
function parseCli(argv) {
|
|
40
44
|
let command = 'login';
|
|
41
45
|
let args = argv;
|
|
42
|
-
if (argv[0]
|
|
46
|
+
if (argv[0] === '--version' || argv[0] === '-v') {
|
|
47
|
+
command = 'version';
|
|
48
|
+
args = argv.slice(1);
|
|
49
|
+
} else if (argv[0] && !argv[0].startsWith('-')) {
|
|
43
50
|
command = argv[0];
|
|
44
51
|
args = argv.slice(1);
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
const options = {
|
|
48
|
-
provider: '
|
|
55
|
+
provider: '',
|
|
49
56
|
port: 0,
|
|
50
57
|
envFile: process.env.NADO_MCP_AUTH_ENV_FILE || defaultAuthEnvFile(),
|
|
51
58
|
supabaseUrl: process.env.NADO_MCP_SUPABASE_URL || process.env.EXPO_PUBLIC_SUPABASE_URL || DEFAULT_SUPABASE_URL,
|
|
52
59
|
anonKey: process.env.NADO_MCP_SUPABASE_ANON_KEY || process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY || DEFAULT_SUPABASE_ANON_KEY,
|
|
53
60
|
redirectMode: 'azure',
|
|
54
61
|
relayUrl: process.env.NADO_MCP_AUTH_RELAY_URL || DEFAULT_RELAY_URL,
|
|
62
|
+
connectUrl: process.env.NADO_MCP_CONNECT_URL || DEFAULT_CONNECT_URL,
|
|
55
63
|
noOpen: false,
|
|
56
64
|
timeoutMs: 300_000,
|
|
57
65
|
help: false,
|
|
@@ -74,9 +82,11 @@ function parseCli(argv) {
|
|
|
74
82
|
else if (flag === '--anon-key') options.anonKey = readValue();
|
|
75
83
|
else if (flag === '--redirect-mode') options.redirectMode = readValue();
|
|
76
84
|
else if (flag === '--relay-url') options.relayUrl = readValue();
|
|
85
|
+
else if (flag === '--connect-url') options.connectUrl = readValue();
|
|
77
86
|
else if (flag === '--timeout-ms') options.timeoutMs = Number(readValue());
|
|
78
87
|
else if (flag === '--no-open') options.noOpen = true;
|
|
79
88
|
else if (flag === '--help' || flag === '-h') options.help = true;
|
|
89
|
+
else if (flag === '--version' || flag === '-v') command = 'version';
|
|
80
90
|
else throw new Error(`Unknown option: ${arg}`);
|
|
81
91
|
}
|
|
82
92
|
|
|
@@ -116,10 +126,7 @@ function defaultUserAuthEnvFile() {
|
|
|
116
126
|
|
|
117
127
|
async function login(options) {
|
|
118
128
|
const provider = String(options.provider || '').toLowerCase();
|
|
119
|
-
if (provider
|
|
120
|
-
throw new Error('Naver login is not available for local MCP auth yet because the Naver Edge Function uses fixed redirect URLs. Use google, kakao, or apple.');
|
|
121
|
-
}
|
|
122
|
-
if (!SUPPORTED_PROVIDERS.has(provider)) {
|
|
129
|
+
if (provider && !SUPPORTED_PROVIDERS.has(provider)) {
|
|
123
130
|
throw new Error(`Unsupported provider: ${provider}. Use google, kakao, or apple.`);
|
|
124
131
|
}
|
|
125
132
|
|
|
@@ -138,6 +145,10 @@ async function login(options) {
|
|
|
138
145
|
sendHtml(response, 404, 'Nado MCP Auth', 'Unknown callback path.');
|
|
139
146
|
return;
|
|
140
147
|
}
|
|
148
|
+
if (request.method === 'OPTIONS') {
|
|
149
|
+
sendCors(response, 204);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
141
152
|
if (settled) {
|
|
142
153
|
sendHtml(response, 200, 'Nado MCP Auth', 'Login already completed. You can close this tab.');
|
|
143
154
|
return;
|
|
@@ -147,6 +158,23 @@ async function login(options) {
|
|
|
147
158
|
if (oauthError) throw new Error(oauthError);
|
|
148
159
|
if (url.searchParams.get('state') !== state) throw new Error('Invalid OAuth state.');
|
|
149
160
|
|
|
161
|
+
if (request.method === 'POST') {
|
|
162
|
+
const session = await readPostedSession(request);
|
|
163
|
+
const user = await fetchUser(options.supabaseUrl, options.anonKey, session.access_token);
|
|
164
|
+
|
|
165
|
+
writeAuthEnv(options.envFile, {
|
|
166
|
+
NADO_MCP_SUPABASE_URL: options.supabaseUrl,
|
|
167
|
+
NADO_MCP_SUPABASE_ANON_KEY: options.anonKey,
|
|
168
|
+
NADO_MCP_ACCESS_TOKEN: session.access_token,
|
|
169
|
+
NADO_MCP_REFRESH_TOKEN: session.refresh_token,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
settled = true;
|
|
173
|
+
sendJson(response, 200, { ok: true, email: user.email || null });
|
|
174
|
+
resolve({ session, user });
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
150
178
|
const code = url.searchParams.get('code');
|
|
151
179
|
if (!code) throw new Error('Missing OAuth code.');
|
|
152
180
|
|
|
@@ -190,9 +218,9 @@ async function login(options) {
|
|
|
190
218
|
codeChallenge,
|
|
191
219
|
});
|
|
192
220
|
|
|
193
|
-
console.log(
|
|
221
|
+
console.log('Opening browser for Nado MCP login.');
|
|
194
222
|
console.log(`Local callback: ${localCallbackUrl.toString()}`);
|
|
195
|
-
if (options.redirectMode === 'azure') console.log(`Azure relay: ${options.relayUrl}`);
|
|
223
|
+
if (provider && options.redirectMode === 'azure') console.log(`Azure relay: ${options.relayUrl}`);
|
|
196
224
|
if (!options.noOpen) openBrowser(browserUrl);
|
|
197
225
|
console.log(`If the browser did not open, visit:\n${browserUrl}`);
|
|
198
226
|
|
|
@@ -214,14 +242,21 @@ async function login(options) {
|
|
|
214
242
|
function loginTimeoutError(options) {
|
|
215
243
|
return new Error([
|
|
216
244
|
'Timed out waiting for browser login.',
|
|
217
|
-
|
|
245
|
+
'Rerun `nado-mcp login --timeout-ms 900000` and keep the terminal open until the browser says login completed.',
|
|
218
246
|
'If the browser did not open, copy the printed URL into the same desktop browser where you can sign in.',
|
|
219
|
-
'If
|
|
220
|
-
'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.',
|
|
247
|
+
'If login succeeds but this still times out, check that the browser can reach the printed 127.0.0.1 local callback URL.',
|
|
221
248
|
].join(' '));
|
|
222
249
|
}
|
|
223
250
|
|
|
224
251
|
function buildBrowserLoginUrl({ options, provider, localCallbackUrl, codeChallenge }) {
|
|
252
|
+
if (!provider) {
|
|
253
|
+
return buildConnectUrl({
|
|
254
|
+
connectUrl: options.connectUrl,
|
|
255
|
+
localCallbackUrl,
|
|
256
|
+
supabaseUrl: options.supabaseUrl,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
225
260
|
if (options.redirectMode === 'local') {
|
|
226
261
|
return buildAuthorizeUrl({
|
|
227
262
|
supabaseUrl: options.supabaseUrl,
|
|
@@ -249,9 +284,21 @@ function buildRelayStartUrl({ relayUrl: value, localCallbackUrl, provider, supab
|
|
|
249
284
|
relayUrl.searchParams.set('provider', provider);
|
|
250
285
|
relayUrl.searchParams.set('supabase_url', supabaseUrl);
|
|
251
286
|
relayUrl.searchParams.set('code_challenge', codeChallenge);
|
|
287
|
+
relayUrl.searchParams.set('client_version', packageVersion);
|
|
252
288
|
return relayUrl.toString();
|
|
253
289
|
}
|
|
254
290
|
|
|
291
|
+
function buildConnectUrl({ connectUrl: value, localCallbackUrl, supabaseUrl }) {
|
|
292
|
+
const connectUrl = new URL(value);
|
|
293
|
+
if (connectUrl.protocol !== 'https:' && connectUrl.hostname !== 'localhost' && connectUrl.hostname !== '127.0.0.1') {
|
|
294
|
+
throw new Error('--connect-url must be an HTTPS URL unless it points to localhost.');
|
|
295
|
+
}
|
|
296
|
+
connectUrl.searchParams.set('local_callback', localCallbackUrl);
|
|
297
|
+
connectUrl.searchParams.set('supabase_url', supabaseUrl);
|
|
298
|
+
connectUrl.searchParams.set('client_version', packageVersion);
|
|
299
|
+
return connectUrl.toString();
|
|
300
|
+
}
|
|
301
|
+
|
|
255
302
|
function printStatus(options) {
|
|
256
303
|
const values = readEnvFile(options.envFile);
|
|
257
304
|
const accessToken = process.env.NADO_MCP_ACCESS_TOKEN || values.NADO_MCP_ACCESS_TOKEN || process.env.NADO_ACCESS_TOKEN || '';
|
|
@@ -331,6 +378,34 @@ async function readJsonResponse(response) {
|
|
|
331
378
|
}
|
|
332
379
|
}
|
|
333
380
|
|
|
381
|
+
async function readPostedSession(request) {
|
|
382
|
+
const body = await readRequestJson(request);
|
|
383
|
+
const session = body?.session && typeof body.session === 'object' ? body.session : body;
|
|
384
|
+
const accessToken = typeof session?.access_token === 'string' ? session.access_token : '';
|
|
385
|
+
const refreshToken = typeof session?.refresh_token === 'string' ? session.refresh_token : '';
|
|
386
|
+
if (!accessToken || !refreshToken) {
|
|
387
|
+
throw new Error('Missing session tokens from Nado web login.');
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
access_token: accessToken,
|
|
391
|
+
refresh_token: refreshToken,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async function readRequestJson(request) {
|
|
396
|
+
const chunks = [];
|
|
397
|
+
for await (const chunk of request) {
|
|
398
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
399
|
+
}
|
|
400
|
+
const text = Buffer.concat(chunks).toString('utf8');
|
|
401
|
+
if (!text) return {};
|
|
402
|
+
try {
|
|
403
|
+
return JSON.parse(text);
|
|
404
|
+
} catch {
|
|
405
|
+
throw new Error('Invalid JSON callback payload.');
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
334
409
|
function openBrowser(url) {
|
|
335
410
|
const platform = os.platform();
|
|
336
411
|
const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'cmd' : 'xdg-open';
|
|
@@ -456,6 +531,24 @@ function resolvePath(value) {
|
|
|
456
531
|
return path.isAbsolute(value) ? value : path.resolve(process.cwd(), value);
|
|
457
532
|
}
|
|
458
533
|
|
|
534
|
+
function readPackageVersion() {
|
|
535
|
+
const candidates = [
|
|
536
|
+
path.join(repoRoot, 'packages', 'nado-mcp', 'package.json'),
|
|
537
|
+
path.join(scriptDir, '..', 'package.json'),
|
|
538
|
+
];
|
|
539
|
+
for (const filePath of candidates) {
|
|
540
|
+
try {
|
|
541
|
+
const pkg = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
542
|
+
if (pkg?.name === '@nado-language/mcp' && typeof pkg.version === 'string') {
|
|
543
|
+
return pkg.version;
|
|
544
|
+
}
|
|
545
|
+
} catch {
|
|
546
|
+
// Continue to the next candidate.
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return '0.0.0-dev';
|
|
550
|
+
}
|
|
551
|
+
|
|
459
552
|
function listen(server, port) {
|
|
460
553
|
return new Promise((resolve, reject) => {
|
|
461
554
|
server.once('error', reject);
|
|
@@ -477,10 +570,29 @@ function closeServer(server) {
|
|
|
477
570
|
}
|
|
478
571
|
|
|
479
572
|
function sendHtml(response, status, title, message) {
|
|
480
|
-
response.writeHead(status, { 'content-type': 'text/html; charset=utf-8' });
|
|
573
|
+
response.writeHead(status, { ...callbackCorsHeaders(), 'content-type': 'text/html; charset=utf-8' });
|
|
481
574
|
response.end(`<!doctype html><html><head><meta charset="utf-8"><title>${escapeHtml(title)}</title></head><body><h1>${escapeHtml(title)}</h1><p>${escapeHtml(message)}</p></body></html>`);
|
|
482
575
|
}
|
|
483
576
|
|
|
577
|
+
function sendJson(response, status, body) {
|
|
578
|
+
response.writeHead(status, { ...callbackCorsHeaders(), 'content-type': 'application/json; charset=utf-8' });
|
|
579
|
+
response.end(JSON.stringify(body));
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function sendCors(response, status) {
|
|
583
|
+
response.writeHead(status, callbackCorsHeaders());
|
|
584
|
+
response.end();
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function callbackCorsHeaders() {
|
|
588
|
+
return {
|
|
589
|
+
'access-control-allow-origin': '*',
|
|
590
|
+
'access-control-allow-methods': 'GET,POST,OPTIONS',
|
|
591
|
+
'access-control-allow-headers': 'content-type',
|
|
592
|
+
'access-control-allow-private-network': 'true',
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
|
|
484
596
|
function escapeHtml(value) {
|
|
485
597
|
return String(value)
|
|
486
598
|
.replace(/&/g, '&')
|
|
@@ -494,28 +606,36 @@ function printHelp() {
|
|
|
494
606
|
console.log(`Nado Language MCP browser auth
|
|
495
607
|
|
|
496
608
|
Usage:
|
|
497
|
-
nado-mcp login
|
|
609
|
+
nado-mcp login
|
|
610
|
+
nado-mcp login --provider google|kakao|apple
|
|
498
611
|
nado-mcp status
|
|
499
612
|
nado-mcp logout
|
|
613
|
+
nado-mcp-auth --version
|
|
500
614
|
|
|
501
615
|
Repo checkout aliases:
|
|
502
|
-
npm run mcp:nado:auth -- [login]
|
|
616
|
+
npm run mcp:nado:auth -- [login]
|
|
503
617
|
npm run mcp:nado:auth -- status
|
|
504
618
|
npm run mcp:nado:auth -- logout
|
|
505
619
|
|
|
506
620
|
Options:
|
|
507
|
-
--provider <name> OAuth provider
|
|
621
|
+
--provider <name> Legacy direct OAuth provider. Normally omit this and sign in on the Nado web page.
|
|
508
622
|
--auth-file <path> Auth env file to write. Default: OS user config in package installs, .env.mcp.local in repo checkouts
|
|
509
623
|
--port <number> Local callback port. Default: 0 (random)
|
|
510
624
|
--redirect-mode <mode> azure or local. Default: azure
|
|
511
625
|
--relay-url <url> Azure Static Web Apps relay URL. Default: ${DEFAULT_RELAY_URL}
|
|
626
|
+
--connect-url <url> Provider-neutral Nado web connect URL. Default: ${DEFAULT_CONNECT_URL}
|
|
512
627
|
--no-open Print the URL without opening a browser
|
|
513
628
|
--timeout-ms <number> Login wait timeout. Default: 300000
|
|
514
629
|
--supabase-url <url> Supabase project URL
|
|
515
630
|
--anon-key <key> Supabase anon key
|
|
631
|
+
--version Print installed Nado MCP version
|
|
632
|
+
|
|
633
|
+
Default mode opens the Nado web connect page. Sign in there with any provider
|
|
634
|
+
supported by the web app, then the page posts the browser session to the local
|
|
635
|
+
127.0.0.1 helper. Tokens are not placed in the browser URL.
|
|
516
636
|
|
|
517
|
-
|
|
518
|
-
OAuth relay. Supabase Auth must allow this redirect URL:
|
|
637
|
+
Legacy provider mode uses the existing Azure Static Web Apps site as a
|
|
638
|
+
zero-new-resource OAuth relay. Supabase Auth must allow this redirect URL:
|
|
519
639
|
${DEFAULT_RELAY_URL}
|
|
520
640
|
|
|
521
641
|
The relay starts login with local state in browser sessionStorage, then sends
|
package/dist/nado-mcp-cli.mjs
CHANGED
|
@@ -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
|