@ainote/mcp 1.3.1 → 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 -777
- package/index.js +61 -56
- 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 -0
- 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/utils/api-client.js
CHANGED
|
@@ -1,152 +1,152 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
|
-
import { getConfig } from '../core/config-manager.js';
|
|
3
|
-
|
|
4
|
-
const LOGIN_HINT = 'Please run `ainote-mcp login` to re-authenticate.';
|
|
5
|
-
|
|
6
|
-
function buildHeaders(authContext, config) {
|
|
7
|
-
const headers = {
|
|
8
|
-
'Content-Type': 'application/json'
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const contextType = authContext?.type ?? 'mcpKey';
|
|
12
|
-
|
|
13
|
-
if (contextType === 'anonymous') {
|
|
14
|
-
// No auth headers for anonymous mode (onboarding tools)
|
|
15
|
-
return headers;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (contextType === 'oauth') {
|
|
19
|
-
const bearerToken = authContext?.token;
|
|
20
|
-
if (!bearerToken) {
|
|
21
|
-
throw new Error('OAuth authentication requires a bearer token');
|
|
22
|
-
}
|
|
23
|
-
headers.Authorization = `Bearer ${bearerToken}`;
|
|
24
|
-
} else {
|
|
25
|
-
const apiKey = authContext?.apiKey ?? config.api.key;
|
|
26
|
-
if (!apiKey) {
|
|
27
|
-
throw new Error(`AINOTE_API_KEY environment variable is required for MCP authentication. ${LOGIN_HINT}`);
|
|
28
|
-
}
|
|
29
|
-
headers.Authorization = `McpKey ${apiKey}`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return headers;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
let requestIdCounter = 1;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* JSON-RPC 2.0 요청 생성 헬퍼
|
|
39
|
-
*/
|
|
40
|
-
function createJsonRpcRequest(method, params = {}) {
|
|
41
|
-
return {
|
|
42
|
-
jsonrpc: '2.0',
|
|
43
|
-
id: requestIdCounter++,
|
|
44
|
-
method,
|
|
45
|
-
params
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* JSON-RPC 2.0 tools/call 요청 생성
|
|
51
|
-
*/
|
|
52
|
-
function createToolCallRequest(toolName, arguments_) {
|
|
53
|
-
return createJsonRpcRequest('tools/call', {
|
|
54
|
-
name: toolName,
|
|
55
|
-
arguments: arguments_
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function isBearerAuth(authContext) {
|
|
60
|
-
return authContext?.type === 'oauth';
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function tryRefreshAndUpdateHeaders(axiosInstance, config) {
|
|
64
|
-
// Only Bearer (access_token) flows can refresh. mcp_key flows cannot.
|
|
65
|
-
try {
|
|
66
|
-
const { refreshAccessToken } = await import('../auth/refresh.js');
|
|
67
|
-
const { getCredentials, setCredentials, clearCredentials } = await import('../auth/token-store.js');
|
|
68
|
-
const creds = await getCredentials(config.api.url);
|
|
69
|
-
if (!creds || !creds.refreshToken) {
|
|
70
|
-
return { refreshed: false, reason: 'no_refresh_token' };
|
|
71
|
-
}
|
|
72
|
-
const fresh = await refreshAccessToken({ apiUrl: config.api.url, refreshToken: creds.refreshToken });
|
|
73
|
-
const expiresAt = fresh.expires_in ? Date.now() + fresh.expires_in * 1000 : undefined;
|
|
74
|
-
await setCredentials(config.api.url, {
|
|
75
|
-
accessToken: fresh.access_token,
|
|
76
|
-
refreshToken: fresh.refresh_token,
|
|
77
|
-
expiresAt
|
|
78
|
-
});
|
|
79
|
-
axiosInstance.defaults.headers.common.Authorization = `Bearer ${fresh.access_token}`;
|
|
80
|
-
return { refreshed: true };
|
|
81
|
-
} catch (err) {
|
|
82
|
-
try {
|
|
83
|
-
const { clearCredentials } = await import('../auth/token-store.js');
|
|
84
|
-
await clearCredentials(config.api.url);
|
|
85
|
-
} catch { /* ignore */ }
|
|
86
|
-
return { refreshed: false, reason: err.message };
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function createApiClient(authContext) {
|
|
91
|
-
const config = getConfig();
|
|
92
|
-
const headers = buildHeaders(authContext, config);
|
|
93
|
-
|
|
94
|
-
const axiosInstance = axios.create({
|
|
95
|
-
baseURL: config.api.url,
|
|
96
|
-
headers
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Intercept 401s on bearer-authed requests and try one refresh + retry.
|
|
100
|
-
axiosInstance.interceptors.response.use(
|
|
101
|
-
(response) => response,
|
|
102
|
-
async (error) => {
|
|
103
|
-
const status = error.response?.status;
|
|
104
|
-
const originalRequest = error.config || {};
|
|
105
|
-
if (status !== 401 || originalRequest._retriedRefresh) {
|
|
106
|
-
return Promise.reject(error);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Determine which auth scheme was in play for the original request.
|
|
110
|
-
const sentAuth = originalRequest.headers?.Authorization || originalRequest.headers?.authorization || '';
|
|
111
|
-
const isBearer = isBearerAuth(authContext) || /^Bearer\s/i.test(sentAuth);
|
|
112
|
-
const isMcpKey = /^McpKey\s/i.test(sentAuth);
|
|
113
|
-
|
|
114
|
-
if (isBearer) {
|
|
115
|
-
const result = await tryRefreshAndUpdateHeaders(axiosInstance, config);
|
|
116
|
-
if (result.refreshed) {
|
|
117
|
-
originalRequest._retriedRefresh = true;
|
|
118
|
-
originalRequest.headers = {
|
|
119
|
-
...(originalRequest.headers || {}),
|
|
120
|
-
Authorization: axiosInstance.defaults.headers.common.Authorization
|
|
121
|
-
};
|
|
122
|
-
return axiosInstance.request(originalRequest);
|
|
123
|
-
}
|
|
124
|
-
const wrapped = new Error(`Authentication failed and refresh was not possible. ${LOGIN_HINT}`);
|
|
125
|
-
wrapped.cause = error;
|
|
126
|
-
return Promise.reject(wrapped);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (isMcpKey) {
|
|
130
|
-
const wrapped = new Error(`MCP key was rejected by the server. ${LOGIN_HINT}`);
|
|
131
|
-
wrapped.cause = error;
|
|
132
|
-
return Promise.reject(wrapped);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return Promise.reject(error);
|
|
136
|
-
}
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
// JSON-RPC helper 메서드 추가
|
|
140
|
-
axiosInstance.callTool = async (toolName, arguments_) => {
|
|
141
|
-
const rpcRequest = createToolCallRequest(toolName, arguments_);
|
|
142
|
-
const response = await axiosInstance.post('/api/mcp', rpcRequest);
|
|
143
|
-
|
|
144
|
-
if (response.data.error) {
|
|
145
|
-
throw new Error(`JSON-RPC Error [${response.data.error.code}]: ${response.data.error.message}`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return response.data.result;
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
return axiosInstance;
|
|
152
|
-
}
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getConfig } from '../core/config-manager.js';
|
|
3
|
+
|
|
4
|
+
const LOGIN_HINT = 'Please run `ainote-mcp login` to re-authenticate.';
|
|
5
|
+
|
|
6
|
+
function buildHeaders(authContext, config) {
|
|
7
|
+
const headers = {
|
|
8
|
+
'Content-Type': 'application/json'
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const contextType = authContext?.type ?? 'mcpKey';
|
|
12
|
+
|
|
13
|
+
if (contextType === 'anonymous') {
|
|
14
|
+
// No auth headers for anonymous mode (onboarding tools)
|
|
15
|
+
return headers;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (contextType === 'oauth') {
|
|
19
|
+
const bearerToken = authContext?.token;
|
|
20
|
+
if (!bearerToken) {
|
|
21
|
+
throw new Error('OAuth authentication requires a bearer token');
|
|
22
|
+
}
|
|
23
|
+
headers.Authorization = `Bearer ${bearerToken}`;
|
|
24
|
+
} else {
|
|
25
|
+
const apiKey = authContext?.apiKey ?? config.api.key;
|
|
26
|
+
if (!apiKey) {
|
|
27
|
+
throw new Error(`AINOTE_API_KEY environment variable is required for MCP authentication. ${LOGIN_HINT}`);
|
|
28
|
+
}
|
|
29
|
+
headers.Authorization = `McpKey ${apiKey}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return headers;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let requestIdCounter = 1;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* JSON-RPC 2.0 요청 생성 헬퍼
|
|
39
|
+
*/
|
|
40
|
+
function createJsonRpcRequest(method, params = {}) {
|
|
41
|
+
return {
|
|
42
|
+
jsonrpc: '2.0',
|
|
43
|
+
id: requestIdCounter++,
|
|
44
|
+
method,
|
|
45
|
+
params
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* JSON-RPC 2.0 tools/call 요청 생성
|
|
51
|
+
*/
|
|
52
|
+
function createToolCallRequest(toolName, arguments_) {
|
|
53
|
+
return createJsonRpcRequest('tools/call', {
|
|
54
|
+
name: toolName,
|
|
55
|
+
arguments: arguments_
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isBearerAuth(authContext) {
|
|
60
|
+
return authContext?.type === 'oauth';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function tryRefreshAndUpdateHeaders(axiosInstance, config) {
|
|
64
|
+
// Only Bearer (access_token) flows can refresh. mcp_key flows cannot.
|
|
65
|
+
try {
|
|
66
|
+
const { refreshAccessToken } = await import('../auth/refresh.js');
|
|
67
|
+
const { getCredentials, setCredentials, clearCredentials } = await import('../auth/token-store.js');
|
|
68
|
+
const creds = await getCredentials(config.api.url);
|
|
69
|
+
if (!creds || !creds.refreshToken) {
|
|
70
|
+
return { refreshed: false, reason: 'no_refresh_token' };
|
|
71
|
+
}
|
|
72
|
+
const fresh = await refreshAccessToken({ apiUrl: config.api.url, refreshToken: creds.refreshToken });
|
|
73
|
+
const expiresAt = fresh.expires_in ? Date.now() + fresh.expires_in * 1000 : undefined;
|
|
74
|
+
await setCredentials(config.api.url, {
|
|
75
|
+
accessToken: fresh.access_token,
|
|
76
|
+
refreshToken: fresh.refresh_token,
|
|
77
|
+
expiresAt
|
|
78
|
+
});
|
|
79
|
+
axiosInstance.defaults.headers.common.Authorization = `Bearer ${fresh.access_token}`;
|
|
80
|
+
return { refreshed: true };
|
|
81
|
+
} catch (err) {
|
|
82
|
+
try {
|
|
83
|
+
const { clearCredentials } = await import('../auth/token-store.js');
|
|
84
|
+
await clearCredentials(config.api.url);
|
|
85
|
+
} catch { /* ignore */ }
|
|
86
|
+
return { refreshed: false, reason: err.message };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function createApiClient(authContext) {
|
|
91
|
+
const config = getConfig();
|
|
92
|
+
const headers = buildHeaders(authContext, config);
|
|
93
|
+
|
|
94
|
+
const axiosInstance = axios.create({
|
|
95
|
+
baseURL: config.api.url,
|
|
96
|
+
headers
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Intercept 401s on bearer-authed requests and try one refresh + retry.
|
|
100
|
+
axiosInstance.interceptors.response.use(
|
|
101
|
+
(response) => response,
|
|
102
|
+
async (error) => {
|
|
103
|
+
const status = error.response?.status;
|
|
104
|
+
const originalRequest = error.config || {};
|
|
105
|
+
if (status !== 401 || originalRequest._retriedRefresh) {
|
|
106
|
+
return Promise.reject(error);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Determine which auth scheme was in play for the original request.
|
|
110
|
+
const sentAuth = originalRequest.headers?.Authorization || originalRequest.headers?.authorization || '';
|
|
111
|
+
const isBearer = isBearerAuth(authContext) || /^Bearer\s/i.test(sentAuth);
|
|
112
|
+
const isMcpKey = /^McpKey\s/i.test(sentAuth);
|
|
113
|
+
|
|
114
|
+
if (isBearer) {
|
|
115
|
+
const result = await tryRefreshAndUpdateHeaders(axiosInstance, config);
|
|
116
|
+
if (result.refreshed) {
|
|
117
|
+
originalRequest._retriedRefresh = true;
|
|
118
|
+
originalRequest.headers = {
|
|
119
|
+
...(originalRequest.headers || {}),
|
|
120
|
+
Authorization: axiosInstance.defaults.headers.common.Authorization
|
|
121
|
+
};
|
|
122
|
+
return axiosInstance.request(originalRequest);
|
|
123
|
+
}
|
|
124
|
+
const wrapped = new Error(`Authentication failed and refresh was not possible. ${LOGIN_HINT}`);
|
|
125
|
+
wrapped.cause = error;
|
|
126
|
+
return Promise.reject(wrapped);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (isMcpKey) {
|
|
130
|
+
const wrapped = new Error(`MCP key was rejected by the server. ${LOGIN_HINT}`);
|
|
131
|
+
wrapped.cause = error;
|
|
132
|
+
return Promise.reject(wrapped);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return Promise.reject(error);
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// JSON-RPC helper 메서드 추가
|
|
140
|
+
axiosInstance.callTool = async (toolName, arguments_) => {
|
|
141
|
+
const rpcRequest = createToolCallRequest(toolName, arguments_);
|
|
142
|
+
const response = await axiosInstance.post('/api/mcp', rpcRequest);
|
|
143
|
+
|
|
144
|
+
if (response.data.error) {
|
|
145
|
+
throw new Error(`JSON-RPC Error [${response.data.error.code}]: ${response.data.error.message}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return response.data.result;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
return axiosInstance;
|
|
152
|
+
}
|
package/package.json
CHANGED
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@ainote/mcp",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"mcpName": "io.github.seunghan91/ainote",
|
|
5
|
-
"description": "MCP server for AI Note - Tasks, Dev Docs, and multi-device file sync (CLAUDE.md, memory files, Cursor rules, Windsurf rules)",
|
|
6
|
-
"main": "index.js",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"bin": {
|
|
9
|
-
"ainote-mcp": "./index.js",
|
|
10
|
-
"ainote-mcp-http": "./server.js"
|
|
11
|
-
},
|
|
12
|
-
"scripts": {
|
|
13
|
-
"start": "node index.js",
|
|
14
|
-
"start:http": "node server.js",
|
|
15
|
-
"test": "NODE_ENV=test NODE_OPTIONS=--experimental-vm-modules jest",
|
|
16
|
-
"test:watch": "NODE_ENV=test NODE_OPTIONS=--experimental-vm-modules jest --watch",
|
|
17
|
-
"test:coverage": "NODE_ENV=test NODE_OPTIONS=--experimental-vm-modules jest --coverage",
|
|
18
|
-
"test:verbose": "NODE_ENV=test NODE_OPTIONS=--experimental-vm-modules jest --verbose"
|
|
19
|
-
},
|
|
20
|
-
"keywords": [
|
|
21
|
-
"mcp",
|
|
22
|
-
"modelcontextprotocol",
|
|
23
|
-
"model-context-protocol",
|
|
24
|
-
"claude",
|
|
25
|
-
"claude-code",
|
|
26
|
-
"claude-desktop",
|
|
27
|
-
"cursor",
|
|
28
|
-
"windsurf",
|
|
29
|
-
"copilot",
|
|
30
|
-
"ainote",
|
|
31
|
-
"ai-note",
|
|
32
|
-
"dev-docs",
|
|
33
|
-
"claude-md",
|
|
34
|
-
"memory",
|
|
35
|
-
"tasks",
|
|
36
|
-
"task-management",
|
|
37
|
-
"todo",
|
|
38
|
-
"productivity",
|
|
39
|
-
"api",
|
|
40
|
-
"integration"
|
|
41
|
-
],
|
|
42
|
-
"author": "AI Note Team <support@ainote.app>",
|
|
43
|
-
"license": "MIT",
|
|
44
|
-
"repository": {
|
|
45
|
-
"type": "git",
|
|
46
|
-
"url": "git+https://github.com/ainote-dev/ainote-mcp.git"
|
|
47
|
-
},
|
|
48
|
-
"bugs": {
|
|
49
|
-
"url": "https://github.com/ainote-dev/ainote-mcp/issues"
|
|
50
|
-
},
|
|
51
|
-
"homepage": "https://github.com/ainote-dev/ainote-mcp#readme",
|
|
52
|
-
"engines": {
|
|
53
|
-
"node": ">=18.0.0"
|
|
54
|
-
},
|
|
55
|
-
"dependencies": {
|
|
56
|
-
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
57
|
-
"axios": "^1.6.0",
|
|
58
|
-
"cors": "^2.8.5",
|
|
59
|
-
"cross-keychain": "^1.1.0",
|
|
60
|
-
"express": "^5.1.0",
|
|
61
|
-
"morgan": "^1.10.0",
|
|
62
|
-
"open": "^10.2.0"
|
|
63
|
-
},
|
|
64
|
-
"devDependencies": {
|
|
65
|
-
"@jest/globals": "^29.7.0",
|
|
66
|
-
"axios-mock-adapter": "^1.22.0",
|
|
67
|
-
"jest": "^29.7.0"
|
|
68
|
-
},
|
|
69
|
-
"jest": {
|
|
70
|
-
"transform": {},
|
|
71
|
-
"testEnvironment": "node",
|
|
72
|
-
"collectCoverageFrom": [
|
|
73
|
-
"index.js",
|
|
74
|
-
"server.js",
|
|
75
|
-
"lib/**/*.js",
|
|
76
|
-
"!node_modules/**"
|
|
77
|
-
],
|
|
78
|
-
"coverageDirectory": "coverage",
|
|
79
|
-
"coverageReporters": [
|
|
80
|
-
"text",
|
|
81
|
-
"lcov",
|
|
82
|
-
"html"
|
|
83
|
-
],
|
|
84
|
-
"testMatch": [
|
|
85
|
-
"**/__tests__/**/*.test.js",
|
|
86
|
-
"**/?(*.)+(spec|test).js"
|
|
87
|
-
]
|
|
88
|
-
},
|
|
89
|
-
"files": [
|
|
90
|
-
"index.js",
|
|
91
|
-
"server.js",
|
|
92
|
-
"lib",
|
|
93
|
-
"README.md",
|
|
94
|
-
"README-ko.md",
|
|
95
|
-
"LICENSE"
|
|
96
|
-
],
|
|
97
|
-
"publishConfig": {
|
|
98
|
-
"access": "public",
|
|
99
|
-
"registry": "https://registry.npmjs.org/"
|
|
100
|
-
}
|
|
101
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@ainote/mcp",
|
|
3
|
+
"version": "1.3.3",
|
|
4
|
+
"mcpName": "io.github.seunghan91/ainote",
|
|
5
|
+
"description": "MCP server for AI Note - Tasks, Dev Docs, and multi-device file sync (CLAUDE.md, memory files, Cursor rules, Windsurf rules)",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"ainote-mcp": "./index.js",
|
|
10
|
+
"ainote-mcp-http": "./server.js"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"start": "node index.js",
|
|
14
|
+
"start:http": "node server.js",
|
|
15
|
+
"test": "NODE_ENV=test NODE_OPTIONS=--experimental-vm-modules jest",
|
|
16
|
+
"test:watch": "NODE_ENV=test NODE_OPTIONS=--experimental-vm-modules jest --watch",
|
|
17
|
+
"test:coverage": "NODE_ENV=test NODE_OPTIONS=--experimental-vm-modules jest --coverage",
|
|
18
|
+
"test:verbose": "NODE_ENV=test NODE_OPTIONS=--experimental-vm-modules jest --verbose"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"mcp",
|
|
22
|
+
"modelcontextprotocol",
|
|
23
|
+
"model-context-protocol",
|
|
24
|
+
"claude",
|
|
25
|
+
"claude-code",
|
|
26
|
+
"claude-desktop",
|
|
27
|
+
"cursor",
|
|
28
|
+
"windsurf",
|
|
29
|
+
"copilot",
|
|
30
|
+
"ainote",
|
|
31
|
+
"ai-note",
|
|
32
|
+
"dev-docs",
|
|
33
|
+
"claude-md",
|
|
34
|
+
"memory",
|
|
35
|
+
"tasks",
|
|
36
|
+
"task-management",
|
|
37
|
+
"todo",
|
|
38
|
+
"productivity",
|
|
39
|
+
"api",
|
|
40
|
+
"integration"
|
|
41
|
+
],
|
|
42
|
+
"author": "AI Note Team <support@ainote.app>",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/ainote-dev/ainote-mcp.git"
|
|
47
|
+
},
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/ainote-dev/ainote-mcp/issues"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/ainote-dev/ainote-mcp#readme",
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18.0.0"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
57
|
+
"axios": "^1.6.0",
|
|
58
|
+
"cors": "^2.8.5",
|
|
59
|
+
"cross-keychain": "^1.1.0",
|
|
60
|
+
"express": "^5.1.0",
|
|
61
|
+
"morgan": "^1.10.0",
|
|
62
|
+
"open": "^10.2.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@jest/globals": "^29.7.0",
|
|
66
|
+
"axios-mock-adapter": "^1.22.0",
|
|
67
|
+
"jest": "^29.7.0"
|
|
68
|
+
},
|
|
69
|
+
"jest": {
|
|
70
|
+
"transform": {},
|
|
71
|
+
"testEnvironment": "node",
|
|
72
|
+
"collectCoverageFrom": [
|
|
73
|
+
"index.js",
|
|
74
|
+
"server.js",
|
|
75
|
+
"lib/**/*.js",
|
|
76
|
+
"!node_modules/**"
|
|
77
|
+
],
|
|
78
|
+
"coverageDirectory": "coverage",
|
|
79
|
+
"coverageReporters": [
|
|
80
|
+
"text",
|
|
81
|
+
"lcov",
|
|
82
|
+
"html"
|
|
83
|
+
],
|
|
84
|
+
"testMatch": [
|
|
85
|
+
"**/__tests__/**/*.test.js",
|
|
86
|
+
"**/?(*.)+(spec|test).js"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"files": [
|
|
90
|
+
"index.js",
|
|
91
|
+
"server.js",
|
|
92
|
+
"lib",
|
|
93
|
+
"README.md",
|
|
94
|
+
"README-ko.md",
|
|
95
|
+
"LICENSE"
|
|
96
|
+
],
|
|
97
|
+
"publishConfig": {
|
|
98
|
+
"access": "public",
|
|
99
|
+
"registry": "https://registry.npmjs.org/"
|
|
100
|
+
}
|
|
101
|
+
}
|