@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/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
+ }
@@ -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
+ }
@@ -1 +1 @@
1
- export * from './config-manager.js';
1
+ export * from './config-manager.js';
@@ -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';