@ainote/mcp 1.3.2 → 1.3.3
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/LICENSE +20 -20
- package/README-ko.md +374 -374
- package/README.md +800 -800
- package/index.js +61 -61
- package/lib/auth/device-flow.js +285 -274
- package/lib/auth/mcp-key.js +40 -40
- package/lib/auth/oauth.js +18 -18
- package/lib/auth/refresh.js +78 -78
- package/lib/auth/strategy-factory.js +31 -31
- package/lib/auth/token-store.js +302 -286
- package/lib/cli/call.js +211 -211
- package/lib/cli/login.js +238 -238
- package/lib/cli/logout.js +39 -39
- package/lib/cli/signup.js +118 -118
- package/lib/cli/whoami.js +77 -77
- package/lib/core/api-client.js +1 -1
- package/lib/core/config-manager.js +106 -106
- package/lib/core/config.js +1 -1
- package/lib/core/mcp-server.js +2 -2
- package/lib/core/server-factory.js +82 -82
- package/lib/core/tool-registry.js +31 -31
- package/lib/tools/chatgpt-tools.js +93 -93
- package/lib/tools/dev-doc-tools.js +643 -643
- package/lib/tools/index.js +5 -5
- package/lib/tools/internal/claude-scanner.js +150 -150
- package/lib/tools/internal/encryption.js +88 -88
- package/lib/tools/internal/formatters.js +80 -80
- package/lib/tools/internal/keychain.js +98 -98
- package/lib/tools/internal/path-mapper.js +221 -221
- package/lib/tools/onboarding-tools.js +124 -124
- package/lib/tools/shared-tools.js +280 -280
- package/lib/utils/api-client.js +152 -152
- package/package.json +101 -101
- package/server.js +99 -99
package/lib/cli/signup.js
CHANGED
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
import * as readline from 'node:readline/promises';
|
|
2
|
-
import { stdin as input, stdout as output } from 'node:process';
|
|
3
|
-
import axios from 'axios';
|
|
4
|
-
import { getConfig } from '../core/config-manager.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Interactive CLI signup for AI Note MCP.
|
|
8
|
-
* Usage: ainote-mcp signup
|
|
9
|
-
*/
|
|
10
|
-
export async function runSignup() {
|
|
11
|
-
const config = getConfig();
|
|
12
|
-
const rl = readline.createInterface({ input, output });
|
|
13
|
-
|
|
14
|
-
console.log('\n AI Note MCP — Sign Up\n');
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
const email = await rl.question(' Email: ');
|
|
18
|
-
const password = await rl.question(' Password (min 6 chars): ');
|
|
19
|
-
const name = await rl.question(' Name (optional, press Enter to skip): ');
|
|
20
|
-
|
|
21
|
-
if (!email.trim() || !password.trim()) {
|
|
22
|
-
console.error('\n Error: Email and password are required.\n');
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (password.trim().length < 6) {
|
|
27
|
-
console.error('\n Error: Password must be at least 6 characters.\n');
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
console.log('\n Creating account...');
|
|
32
|
-
|
|
33
|
-
const rpcRequest = {
|
|
34
|
-
jsonrpc: '2.0',
|
|
35
|
-
id: 1,
|
|
36
|
-
method: 'tools/call',
|
|
37
|
-
params: {
|
|
38
|
-
name: 'signup_and_get_key',
|
|
39
|
-
arguments: {
|
|
40
|
-
email: email.trim(),
|
|
41
|
-
password: password.trim(),
|
|
42
|
-
...(name.trim() ? { name: name.trim() } : {})
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const response = await axios.post(`${config.api.url}/api/mcp`, rpcRequest, {
|
|
48
|
-
headers: { 'Content-Type': 'application/json' },
|
|
49
|
-
timeout: 30000
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const data = response.data?.data;
|
|
53
|
-
|
|
54
|
-
// Check for JSON-RPC error
|
|
55
|
-
if (data?.error) {
|
|
56
|
-
console.error(`\n Error: ${data.error.message}`);
|
|
57
|
-
if (data.error.data?.suggestion) {
|
|
58
|
-
console.log(` Hint: ${data.error.data.suggestion}`);
|
|
59
|
-
}
|
|
60
|
-
console.log();
|
|
61
|
-
process.exit(1);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Extract MCP key from structured resource data
|
|
65
|
-
const content = data?.result?.content;
|
|
66
|
-
let mcpKey = null;
|
|
67
|
-
|
|
68
|
-
if (Array.isArray(content)) {
|
|
69
|
-
for (const item of content) {
|
|
70
|
-
if (item.type === 'resource' && item.resource?.text) {
|
|
71
|
-
try {
|
|
72
|
-
const resourceData = JSON.parse(item.resource.text);
|
|
73
|
-
mcpKey = resourceData.mcp_key;
|
|
74
|
-
} catch { /* ignore */ }
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (mcpKey) {
|
|
80
|
-
console.log('\n Account created successfully!\n');
|
|
81
|
-
console.log(` Your MCP API Key:\n`);
|
|
82
|
-
console.log(` ${mcpKey}\n`);
|
|
83
|
-
console.log(' Add this to your MCP config:\n');
|
|
84
|
-
console.log(' {');
|
|
85
|
-
console.log(' "mcpServers": {');
|
|
86
|
-
console.log(' "ainote": {');
|
|
87
|
-
console.log(' "command": "npx",');
|
|
88
|
-
console.log(' "args": ["-y", "@ainote/mcp"],');
|
|
89
|
-
console.log(' "env": {');
|
|
90
|
-
console.log(` "AINOTE_API_KEY": "${mcpKey}"`);
|
|
91
|
-
console.log(' }');
|
|
92
|
-
console.log(' }');
|
|
93
|
-
console.log(' }');
|
|
94
|
-
console.log(' }\n');
|
|
95
|
-
console.log(' Then restart Claude Desktop.\n');
|
|
96
|
-
} else {
|
|
97
|
-
// Fallback: print text content
|
|
98
|
-
if (Array.isArray(content)) {
|
|
99
|
-
for (const item of content) {
|
|
100
|
-
if (item.type === 'text') {
|
|
101
|
-
console.log(`\n${item.text}\n`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
console.log('\n Account created! Check the response for your API key.\n');
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
} catch (error) {
|
|
109
|
-
if (error.response?.data) {
|
|
110
|
-
console.error(`\n Error: ${JSON.stringify(error.response.data)}\n`);
|
|
111
|
-
} else {
|
|
112
|
-
console.error(`\n Error: ${error.message}\n`);
|
|
113
|
-
}
|
|
114
|
-
process.exit(1);
|
|
115
|
-
} finally {
|
|
116
|
-
rl.close();
|
|
117
|
-
}
|
|
118
|
-
}
|
|
1
|
+
import * as readline from 'node:readline/promises';
|
|
2
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import { getConfig } from '../core/config-manager.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Interactive CLI signup for AI Note MCP.
|
|
8
|
+
* Usage: ainote-mcp signup
|
|
9
|
+
*/
|
|
10
|
+
export async function runSignup() {
|
|
11
|
+
const config = getConfig();
|
|
12
|
+
const rl = readline.createInterface({ input, output });
|
|
13
|
+
|
|
14
|
+
console.log('\n AI Note MCP — Sign Up\n');
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const email = await rl.question(' Email: ');
|
|
18
|
+
const password = await rl.question(' Password (min 6 chars): ');
|
|
19
|
+
const name = await rl.question(' Name (optional, press Enter to skip): ');
|
|
20
|
+
|
|
21
|
+
if (!email.trim() || !password.trim()) {
|
|
22
|
+
console.error('\n Error: Email and password are required.\n');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (password.trim().length < 6) {
|
|
27
|
+
console.error('\n Error: Password must be at least 6 characters.\n');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.log('\n Creating account...');
|
|
32
|
+
|
|
33
|
+
const rpcRequest = {
|
|
34
|
+
jsonrpc: '2.0',
|
|
35
|
+
id: 1,
|
|
36
|
+
method: 'tools/call',
|
|
37
|
+
params: {
|
|
38
|
+
name: 'signup_and_get_key',
|
|
39
|
+
arguments: {
|
|
40
|
+
email: email.trim(),
|
|
41
|
+
password: password.trim(),
|
|
42
|
+
...(name.trim() ? { name: name.trim() } : {})
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const response = await axios.post(`${config.api.url}/api/mcp`, rpcRequest, {
|
|
48
|
+
headers: { 'Content-Type': 'application/json' },
|
|
49
|
+
timeout: 30000
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const data = response.data?.data;
|
|
53
|
+
|
|
54
|
+
// Check for JSON-RPC error
|
|
55
|
+
if (data?.error) {
|
|
56
|
+
console.error(`\n Error: ${data.error.message}`);
|
|
57
|
+
if (data.error.data?.suggestion) {
|
|
58
|
+
console.log(` Hint: ${data.error.data.suggestion}`);
|
|
59
|
+
}
|
|
60
|
+
console.log();
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Extract MCP key from structured resource data
|
|
65
|
+
const content = data?.result?.content;
|
|
66
|
+
let mcpKey = null;
|
|
67
|
+
|
|
68
|
+
if (Array.isArray(content)) {
|
|
69
|
+
for (const item of content) {
|
|
70
|
+
if (item.type === 'resource' && item.resource?.text) {
|
|
71
|
+
try {
|
|
72
|
+
const resourceData = JSON.parse(item.resource.text);
|
|
73
|
+
mcpKey = resourceData.mcp_key;
|
|
74
|
+
} catch { /* ignore */ }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (mcpKey) {
|
|
80
|
+
console.log('\n Account created successfully!\n');
|
|
81
|
+
console.log(` Your MCP API Key:\n`);
|
|
82
|
+
console.log(` ${mcpKey}\n`);
|
|
83
|
+
console.log(' Add this to your MCP config:\n');
|
|
84
|
+
console.log(' {');
|
|
85
|
+
console.log(' "mcpServers": {');
|
|
86
|
+
console.log(' "ainote": {');
|
|
87
|
+
console.log(' "command": "npx",');
|
|
88
|
+
console.log(' "args": ["-y", "@ainote/mcp"],');
|
|
89
|
+
console.log(' "env": {');
|
|
90
|
+
console.log(` "AINOTE_API_KEY": "${mcpKey}"`);
|
|
91
|
+
console.log(' }');
|
|
92
|
+
console.log(' }');
|
|
93
|
+
console.log(' }');
|
|
94
|
+
console.log(' }\n');
|
|
95
|
+
console.log(' Then restart Claude Desktop.\n');
|
|
96
|
+
} else {
|
|
97
|
+
// Fallback: print text content
|
|
98
|
+
if (Array.isArray(content)) {
|
|
99
|
+
for (const item of content) {
|
|
100
|
+
if (item.type === 'text') {
|
|
101
|
+
console.log(`\n${item.text}\n`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
console.log('\n Account created! Check the response for your API key.\n');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (error.response?.data) {
|
|
110
|
+
console.error(`\n Error: ${JSON.stringify(error.response.data)}\n`);
|
|
111
|
+
} else {
|
|
112
|
+
console.error(`\n Error: ${error.message}\n`);
|
|
113
|
+
}
|
|
114
|
+
process.exit(1);
|
|
115
|
+
} finally {
|
|
116
|
+
rl.close();
|
|
117
|
+
}
|
|
118
|
+
}
|
package/lib/cli/whoami.js
CHANGED
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `ainote-mcp whoami` — show the currently authenticated user.
|
|
3
|
-
*
|
|
4
|
-
* Strategy:
|
|
5
|
-
* 1. Read credentials from the token store.
|
|
6
|
-
* 2. If we have an access_token, hit GET /api/users/me with Bearer auth and
|
|
7
|
-
* print the response.
|
|
8
|
-
* 3. If the API call fails (404, 401, network) but we have a stored mcp_key,
|
|
9
|
-
* print a minimal local-only summary so the user at least sees that
|
|
10
|
-
* credentials exist for this host.
|
|
11
|
-
*/
|
|
12
|
-
import process from 'node:process';
|
|
13
|
-
import axios from 'axios';
|
|
14
|
-
import { getCredentials } from '../auth/token-store.js';
|
|
15
|
-
|
|
16
|
-
const DEFAULT_API_URL = 'https://ainote-5muq.onrender.com';
|
|
17
|
-
|
|
18
|
-
function summarizeUser(user) {
|
|
19
|
-
if (!user || typeof user !== 'object') return null;
|
|
20
|
-
const parts = [];
|
|
21
|
-
if (user.email) parts.push(user.email);
|
|
22
|
-
else if (user.name) parts.push(user.name);
|
|
23
|
-
else if (user.id) parts.push(`user #${user.id}`);
|
|
24
|
-
return parts.join(' ');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function run() {
|
|
28
|
-
const apiUrl = (process.env.AINOTE_API_URL || DEFAULT_API_URL).replace(/\/+$/, '');
|
|
29
|
-
const creds = await getCredentials(apiUrl);
|
|
30
|
-
|
|
31
|
-
if (!creds) {
|
|
32
|
-
console.error(' Not logged in. Run `ainote-mcp login`.');
|
|
33
|
-
process.exit(1);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let identity = null;
|
|
38
|
-
|
|
39
|
-
if (creds.accessToken) {
|
|
40
|
-
try {
|
|
41
|
-
const response = await axios.get(`${apiUrl}/api/users/me`, {
|
|
42
|
-
headers: {
|
|
43
|
-
Authorization: `Bearer ${creds.accessToken}`,
|
|
44
|
-
Accept: 'application/json'
|
|
45
|
-
},
|
|
46
|
-
timeout: 15000,
|
|
47
|
-
validateStatus: () => true
|
|
48
|
-
});
|
|
49
|
-
if (response.status >= 200 && response.status < 300) {
|
|
50
|
-
const body = response.data || {};
|
|
51
|
-
identity = summarizeUser(body.user || body.data || body) || summarizeUser(body);
|
|
52
|
-
}
|
|
53
|
-
} catch {
|
|
54
|
-
// fall through to local-only fallback below
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (identity) {
|
|
59
|
-
console.log(` Logged in as ${identity}`);
|
|
60
|
-
} else {
|
|
61
|
-
// Local-only fallback: report what we have.
|
|
62
|
-
const host = (() => {
|
|
63
|
-
try { return new URL(apiUrl).host; } catch { return apiUrl; }
|
|
64
|
-
})();
|
|
65
|
-
console.log(` Credentials present for ${host}`);
|
|
66
|
-
if (creds.mcpKey) console.log(` MCP key: ${creds.mcpKey.slice(0, 8)}…`);
|
|
67
|
-
if (creds.expiresAt) {
|
|
68
|
-
const remaining = Math.floor((creds.expiresAt - Date.now()) / 1000);
|
|
69
|
-
if (remaining > 0) console.log(` Access token expires in ~${remaining}s`);
|
|
70
|
-
else console.log(` Access token expired (refresh on next call).`);
|
|
71
|
-
}
|
|
72
|
-
if (!creds.accessToken && !creds.mcpKey) {
|
|
73
|
-
console.error(' Could not verify identity. Try `ainote-mcp login` again.');
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* `ainote-mcp whoami` — show the currently authenticated user.
|
|
3
|
+
*
|
|
4
|
+
* Strategy:
|
|
5
|
+
* 1. Read credentials from the token store.
|
|
6
|
+
* 2. If we have an access_token, hit GET /api/users/me with Bearer auth and
|
|
7
|
+
* print the response.
|
|
8
|
+
* 3. If the API call fails (404, 401, network) but we have a stored mcp_key,
|
|
9
|
+
* print a minimal local-only summary so the user at least sees that
|
|
10
|
+
* credentials exist for this host.
|
|
11
|
+
*/
|
|
12
|
+
import process from 'node:process';
|
|
13
|
+
import axios from 'axios';
|
|
14
|
+
import { getCredentials } from '../auth/token-store.js';
|
|
15
|
+
|
|
16
|
+
const DEFAULT_API_URL = 'https://ainote-5muq.onrender.com';
|
|
17
|
+
|
|
18
|
+
function summarizeUser(user) {
|
|
19
|
+
if (!user || typeof user !== 'object') return null;
|
|
20
|
+
const parts = [];
|
|
21
|
+
if (user.email) parts.push(user.email);
|
|
22
|
+
else if (user.name) parts.push(user.name);
|
|
23
|
+
else if (user.id) parts.push(`user #${user.id}`);
|
|
24
|
+
return parts.join(' ');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function run() {
|
|
28
|
+
const apiUrl = (process.env.AINOTE_API_URL || DEFAULT_API_URL).replace(/\/+$/, '');
|
|
29
|
+
const creds = await getCredentials(apiUrl);
|
|
30
|
+
|
|
31
|
+
if (!creds) {
|
|
32
|
+
console.error(' Not logged in. Run `ainote-mcp login`.');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let identity = null;
|
|
38
|
+
|
|
39
|
+
if (creds.accessToken) {
|
|
40
|
+
try {
|
|
41
|
+
const response = await axios.get(`${apiUrl}/api/users/me`, {
|
|
42
|
+
headers: {
|
|
43
|
+
Authorization: `Bearer ${creds.accessToken}`,
|
|
44
|
+
Accept: 'application/json'
|
|
45
|
+
},
|
|
46
|
+
timeout: 15000,
|
|
47
|
+
validateStatus: () => true
|
|
48
|
+
});
|
|
49
|
+
if (response.status >= 200 && response.status < 300) {
|
|
50
|
+
const body = response.data || {};
|
|
51
|
+
identity = summarizeUser(body.user || body.data || body) || summarizeUser(body);
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// fall through to local-only fallback below
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (identity) {
|
|
59
|
+
console.log(` Logged in as ${identity}`);
|
|
60
|
+
} else {
|
|
61
|
+
// Local-only fallback: report what we have.
|
|
62
|
+
const host = (() => {
|
|
63
|
+
try { return new URL(apiUrl).host; } catch { return apiUrl; }
|
|
64
|
+
})();
|
|
65
|
+
console.log(` Credentials present for ${host}`);
|
|
66
|
+
if (creds.mcpKey) console.log(` MCP key: ${creds.mcpKey.slice(0, 8)}…`);
|
|
67
|
+
if (creds.expiresAt) {
|
|
68
|
+
const remaining = Math.floor((creds.expiresAt - Date.now()) / 1000);
|
|
69
|
+
if (remaining > 0) console.log(` Access token expires in ~${remaining}s`);
|
|
70
|
+
else console.log(` Access token expired (refresh on next call).`);
|
|
71
|
+
}
|
|
72
|
+
if (!creds.accessToken && !creds.mcpKey) {
|
|
73
|
+
console.error(' Could not verify identity. Try `ainote-mcp login` again.');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
package/lib/core/api-client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from '../utils/api-client.js';
|
|
1
|
+
export * from '../utils/api-client.js';
|
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
const DEFAULT_API_URL = 'https://ainote-5muq.onrender.com';
|
|
2
|
-
const DEFAULT_HTTP_PORT = 3030;
|
|
3
|
-
|
|
4
|
-
const parseBoolean = (value, fallback) => {
|
|
5
|
-
if (value === undefined) {
|
|
6
|
-
return fallback;
|
|
7
|
-
}
|
|
8
|
-
if (typeof value === 'boolean') {
|
|
9
|
-
return value;
|
|
10
|
-
}
|
|
11
|
-
const normalized = `${value}`.trim().toLowerCase();
|
|
12
|
-
if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) {
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
return fallback;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const parseNumber = (value, fallback) => {
|
|
22
|
-
if (value === undefined) {
|
|
23
|
-
return fallback;
|
|
24
|
-
}
|
|
25
|
-
const parsed = Number(value);
|
|
26
|
-
return Number.isFinite(parsed) ? parsed : fallback;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const parseList = (value) => {
|
|
30
|
-
if (!value) {
|
|
31
|
-
return [];
|
|
32
|
-
}
|
|
33
|
-
if (Array.isArray(value)) {
|
|
34
|
-
return value;
|
|
35
|
-
}
|
|
36
|
-
return value
|
|
37
|
-
.split(',')
|
|
38
|
-
.map((item) => item.trim())
|
|
39
|
-
.filter((item) => item.length > 0);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Synchronous config snapshot. Reads env vars only — used by the JSON-RPC
|
|
44
|
-
* stdio entry path that cannot await.
|
|
45
|
-
*
|
|
46
|
-
* Note: stored CLI credentials (mcp_key from `ainote-mcp login`) are loaded
|
|
47
|
-
* asynchronously by `getConfigAsync()` below. Callers that can await should
|
|
48
|
-
* prefer the async form.
|
|
49
|
-
*/
|
|
50
|
-
export function getConfig() {
|
|
51
|
-
const features = {
|
|
52
|
-
chatgptSupport: parseBoolean(process.env.AINOTE_ENABLE_CHATGPT_SUPPORT, true),
|
|
53
|
-
oauthAuth: parseBoolean(process.env.AINOTE_ENABLE_OAUTH_AUTH, false),
|
|
54
|
-
sseTransport: parseBoolean(process.env.AINOTE_ENABLE_SSE_TRANSPORT, true)
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
api: {
|
|
59
|
-
key: process.env.AINOTE_API_KEY,
|
|
60
|
-
url: process.env.AINOTE_API_URL || DEFAULT_API_URL
|
|
61
|
-
},
|
|
62
|
-
http: {
|
|
63
|
-
port: parseNumber(process.env.AINOTE_MCP_HTTP_PORT, DEFAULT_HTTP_PORT),
|
|
64
|
-
endpoint: process.env.AINOTE_MCP_HTTP_ENDPOINT || '/sse',
|
|
65
|
-
messageEndpoint: process.env.AINOTE_MCP_HTTP_MESSAGES_ENDPOINT || '/messages',
|
|
66
|
-
allowedOrigins: parseList(process.env.AINOTE_MCP_ALLOWED_ORIGINS),
|
|
67
|
-
allowedHosts: parseList(process.env.AINOTE_MCP_ALLOWED_HOSTS)
|
|
68
|
-
},
|
|
69
|
-
features,
|
|
70
|
-
requiresLogin: !process.env.AINOTE_API_KEY
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Async config that consults the local token store for an mcp_key when the
|
|
76
|
-
* environment variable is unset. Used by the stdio entry path that runs
|
|
77
|
-
* `await getConfigAsync()` at startup.
|
|
78
|
-
*/
|
|
79
|
-
export async function getConfigAsync() {
|
|
80
|
-
const base = getConfig();
|
|
81
|
-
|
|
82
|
-
if (base.api.key) {
|
|
83
|
-
return { ...base, requiresLogin: false };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
const { getCredentials } = await import('../auth/token-store.js');
|
|
88
|
-
const creds = await getCredentials(base.api.url);
|
|
89
|
-
if (creds && creds.mcpKey) {
|
|
90
|
-
return {
|
|
91
|
-
...base,
|
|
92
|
-
api: { ...base.api, key: creds.mcpKey },
|
|
93
|
-
requiresLogin: false,
|
|
94
|
-
api_key_source: 'token-store'
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
} catch (err) {
|
|
98
|
-
process.stderr.write(`[ainote-mcp] token store unavailable: ${err.message}\n`);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return { ...base, requiresLogin: true };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function getFeatureFlags() {
|
|
105
|
-
return getConfig().features;
|
|
106
|
-
}
|
|
1
|
+
const DEFAULT_API_URL = 'https://ainote-5muq.onrender.com';
|
|
2
|
+
const DEFAULT_HTTP_PORT = 3030;
|
|
3
|
+
|
|
4
|
+
const parseBoolean = (value, fallback) => {
|
|
5
|
+
if (value === undefined) {
|
|
6
|
+
return fallback;
|
|
7
|
+
}
|
|
8
|
+
if (typeof value === 'boolean') {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
const normalized = `${value}`.trim().toLowerCase();
|
|
12
|
+
if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return fallback;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const parseNumber = (value, fallback) => {
|
|
22
|
+
if (value === undefined) {
|
|
23
|
+
return fallback;
|
|
24
|
+
}
|
|
25
|
+
const parsed = Number(value);
|
|
26
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const parseList = (value) => {
|
|
30
|
+
if (!value) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
return value
|
|
37
|
+
.split(',')
|
|
38
|
+
.map((item) => item.trim())
|
|
39
|
+
.filter((item) => item.length > 0);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Synchronous config snapshot. Reads env vars only — used by the JSON-RPC
|
|
44
|
+
* stdio entry path that cannot await.
|
|
45
|
+
*
|
|
46
|
+
* Note: stored CLI credentials (mcp_key from `ainote-mcp login`) are loaded
|
|
47
|
+
* asynchronously by `getConfigAsync()` below. Callers that can await should
|
|
48
|
+
* prefer the async form.
|
|
49
|
+
*/
|
|
50
|
+
export function getConfig() {
|
|
51
|
+
const features = {
|
|
52
|
+
chatgptSupport: parseBoolean(process.env.AINOTE_ENABLE_CHATGPT_SUPPORT, true),
|
|
53
|
+
oauthAuth: parseBoolean(process.env.AINOTE_ENABLE_OAUTH_AUTH, false),
|
|
54
|
+
sseTransport: parseBoolean(process.env.AINOTE_ENABLE_SSE_TRANSPORT, true)
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
api: {
|
|
59
|
+
key: process.env.AINOTE_API_KEY,
|
|
60
|
+
url: process.env.AINOTE_API_URL || DEFAULT_API_URL
|
|
61
|
+
},
|
|
62
|
+
http: {
|
|
63
|
+
port: parseNumber(process.env.AINOTE_MCP_HTTP_PORT, DEFAULT_HTTP_PORT),
|
|
64
|
+
endpoint: process.env.AINOTE_MCP_HTTP_ENDPOINT || '/sse',
|
|
65
|
+
messageEndpoint: process.env.AINOTE_MCP_HTTP_MESSAGES_ENDPOINT || '/messages',
|
|
66
|
+
allowedOrigins: parseList(process.env.AINOTE_MCP_ALLOWED_ORIGINS),
|
|
67
|
+
allowedHosts: parseList(process.env.AINOTE_MCP_ALLOWED_HOSTS)
|
|
68
|
+
},
|
|
69
|
+
features,
|
|
70
|
+
requiresLogin: !process.env.AINOTE_API_KEY
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Async config that consults the local token store for an mcp_key when the
|
|
76
|
+
* environment variable is unset. Used by the stdio entry path that runs
|
|
77
|
+
* `await getConfigAsync()` at startup.
|
|
78
|
+
*/
|
|
79
|
+
export async function getConfigAsync() {
|
|
80
|
+
const base = getConfig();
|
|
81
|
+
|
|
82
|
+
if (base.api.key) {
|
|
83
|
+
return { ...base, requiresLogin: false };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const { getCredentials } = await import('../auth/token-store.js');
|
|
88
|
+
const creds = await getCredentials(base.api.url);
|
|
89
|
+
if (creds && creds.mcpKey) {
|
|
90
|
+
return {
|
|
91
|
+
...base,
|
|
92
|
+
api: { ...base.api, key: creds.mcpKey },
|
|
93
|
+
requiresLogin: false,
|
|
94
|
+
api_key_source: 'token-store'
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
process.stderr.write(`[ainote-mcp] token store unavailable: ${err.message}\n`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return { ...base, requiresLogin: true };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function getFeatureFlags() {
|
|
105
|
+
return getConfig().features;
|
|
106
|
+
}
|
package/lib/core/config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './config-manager.js';
|
|
1
|
+
export * from './config-manager.js';
|
package/lib/core/mcp-server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
// Deprecated: use server-factory instead.
|
|
2
|
-
export { createClaudeServer, createChatGptServer, createServerForProfile } from './server-factory.js';
|
|
1
|
+
// Deprecated: use server-factory instead.
|
|
2
|
+
export { createClaudeServer, createChatGptServer, createServerForProfile } from './server-factory.js';
|