@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
|
@@ -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';
|
|
@@ -1,82 +1,82 @@
|
|
|
1
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
-
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
-
import { ToolRegistry } from './tool-registry.js';
|
|
4
|
-
import { getConfig } from './config-manager.js';
|
|
5
|
-
import { createApiClient } from '../utils/api-client.js';
|
|
6
|
-
import { getSharedTools } from '../tools/shared-tools.js';
|
|
7
|
-
import { getChatGptTools } from '../tools/chatgpt-tools.js';
|
|
8
|
-
import { getDevDocTools } from '../tools/dev-doc-tools.js';
|
|
9
|
-
import { getOnboardingTools } from '../tools/onboarding-tools.js';
|
|
10
|
-
import { toErrorContent } from '../tools/internal/formatters.js';
|
|
11
|
-
|
|
12
|
-
function buildServerInfo(profile) {
|
|
13
|
-
return {
|
|
14
|
-
name: `ainote-mcp-${profile}`,
|
|
15
|
-
version: '1.1.0'
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function registerTools(registry, { includeChatGpt }) {
|
|
20
|
-
// Onboarding tools are always registered (work without API key)
|
|
21
|
-
registry.registerMany(getOnboardingTools());
|
|
22
|
-
registry.registerMany(getSharedTools());
|
|
23
|
-
registry.registerMany(getDevDocTools());
|
|
24
|
-
|
|
25
|
-
if (includeChatGpt) {
|
|
26
|
-
registry.registerMany(getChatGptTools());
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function createServer({ profile, defaultAuthContext } = {}) {
|
|
31
|
-
const config = getConfig();
|
|
32
|
-
const includeChatGpt = profile === 'chatgpt' && config.features.chatgptSupport;
|
|
33
|
-
const registry = new ToolRegistry();
|
|
34
|
-
|
|
35
|
-
registerTools(registry, { includeChatGpt });
|
|
36
|
-
|
|
37
|
-
const server = new Server(buildServerInfo(profile), {
|
|
38
|
-
capabilities: {
|
|
39
|
-
tools: {}
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
44
|
-
tools: registry.listDefinitions()
|
|
45
|
-
}));
|
|
46
|
-
|
|
47
|
-
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
48
|
-
try {
|
|
49
|
-
const { name, arguments: args } = request.params;
|
|
50
|
-
console.error(`[MCP] Calling tool: ${name}`, JSON.stringify(args));
|
|
51
|
-
|
|
52
|
-
const authContext = extra?.authInfo ?? defaultAuthContext;
|
|
53
|
-
const apiClient = createApiClient(authContext);
|
|
54
|
-
|
|
55
|
-
const result = await registry.execute(name, args, {
|
|
56
|
-
apiClient,
|
|
57
|
-
request,
|
|
58
|
-
extra
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
console.error(`[MCP] Tool ${name} completed successfully`);
|
|
62
|
-
return result;
|
|
63
|
-
} catch (error) {
|
|
64
|
-
console.error(`[MCP] Tool error:`, error);
|
|
65
|
-
return toErrorContent(error);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
return { server, registry };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function createClaudeServer(options = {}) {
|
|
73
|
-
return createServer({ profile: 'claude', ...options });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function createChatGptServer(options = {}) {
|
|
77
|
-
return createServer({ profile: 'chatgpt', ...options });
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function createServerForProfile(profile, options = {}) {
|
|
81
|
-
return createServer({ profile, ...options });
|
|
82
|
-
}
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import { ToolRegistry } from './tool-registry.js';
|
|
4
|
+
import { getConfig } from './config-manager.js';
|
|
5
|
+
import { createApiClient } from '../utils/api-client.js';
|
|
6
|
+
import { getSharedTools } from '../tools/shared-tools.js';
|
|
7
|
+
import { getChatGptTools } from '../tools/chatgpt-tools.js';
|
|
8
|
+
import { getDevDocTools } from '../tools/dev-doc-tools.js';
|
|
9
|
+
import { getOnboardingTools } from '../tools/onboarding-tools.js';
|
|
10
|
+
import { toErrorContent } from '../tools/internal/formatters.js';
|
|
11
|
+
|
|
12
|
+
function buildServerInfo(profile) {
|
|
13
|
+
return {
|
|
14
|
+
name: `ainote-mcp-${profile}`,
|
|
15
|
+
version: '1.1.0'
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function registerTools(registry, { includeChatGpt }) {
|
|
20
|
+
// Onboarding tools are always registered (work without API key)
|
|
21
|
+
registry.registerMany(getOnboardingTools());
|
|
22
|
+
registry.registerMany(getSharedTools());
|
|
23
|
+
registry.registerMany(getDevDocTools());
|
|
24
|
+
|
|
25
|
+
if (includeChatGpt) {
|
|
26
|
+
registry.registerMany(getChatGptTools());
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function createServer({ profile, defaultAuthContext } = {}) {
|
|
31
|
+
const config = getConfig();
|
|
32
|
+
const includeChatGpt = profile === 'chatgpt' && config.features.chatgptSupport;
|
|
33
|
+
const registry = new ToolRegistry();
|
|
34
|
+
|
|
35
|
+
registerTools(registry, { includeChatGpt });
|
|
36
|
+
|
|
37
|
+
const server = new Server(buildServerInfo(profile), {
|
|
38
|
+
capabilities: {
|
|
39
|
+
tools: {}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
44
|
+
tools: registry.listDefinitions()
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
48
|
+
try {
|
|
49
|
+
const { name, arguments: args } = request.params;
|
|
50
|
+
console.error(`[MCP] Calling tool: ${name}`, JSON.stringify(args));
|
|
51
|
+
|
|
52
|
+
const authContext = extra?.authInfo ?? defaultAuthContext;
|
|
53
|
+
const apiClient = createApiClient(authContext);
|
|
54
|
+
|
|
55
|
+
const result = await registry.execute(name, args, {
|
|
56
|
+
apiClient,
|
|
57
|
+
request,
|
|
58
|
+
extra
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
console.error(`[MCP] Tool ${name} completed successfully`);
|
|
62
|
+
return result;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(`[MCP] Tool error:`, error);
|
|
65
|
+
return toErrorContent(error);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return { server, registry };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function createClaudeServer(options = {}) {
|
|
73
|
+
return createServer({ profile: 'claude', ...options });
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function createChatGptServer(options = {}) {
|
|
77
|
+
return createServer({ profile: 'chatgpt', ...options });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function createServerForProfile(profile, options = {}) {
|
|
81
|
+
return createServer({ profile, ...options });
|
|
82
|
+
}
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
export class ToolRegistry {
|
|
2
|
-
constructor() {
|
|
3
|
-
this.tools = new Map();
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
register(tool) {
|
|
7
|
-
if (!tool?.definition?.name) {
|
|
8
|
-
throw new Error('Invalid tool registration: missing tool name');
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
this.tools.set(tool.definition.name, tool);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
registerMany(tools = []) {
|
|
15
|
-
tools.forEach((tool) => this.register(tool));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
listDefinitions() {
|
|
19
|
-
return Array.from(this.tools.values()).map((tool) => tool.definition);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async execute(name, args, context) {
|
|
23
|
-
const tool = this.tools.get(name);
|
|
24
|
-
|
|
25
|
-
if (!tool) {
|
|
26
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return tool.handler(args ?? {}, context ?? {});
|
|
30
|
-
}
|
|
31
|
-
}
|
|
1
|
+
export class ToolRegistry {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.tools = new Map();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
register(tool) {
|
|
7
|
+
if (!tool?.definition?.name) {
|
|
8
|
+
throw new Error('Invalid tool registration: missing tool name');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.tools.set(tool.definition.name, tool);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
registerMany(tools = []) {
|
|
15
|
+
tools.forEach((tool) => this.register(tool));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
listDefinitions() {
|
|
19
|
+
return Array.from(this.tools.values()).map((tool) => tool.definition);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async execute(name, args, context) {
|
|
23
|
+
const tool = this.tools.get(name);
|
|
24
|
+
|
|
25
|
+
if (!tool) {
|
|
26
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return tool.handler(args ?? {}, context ?? {});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
import { toSuccessContent } from './internal/formatters.js';
|
|
2
|
-
|
|
3
|
-
const MAX_RESULTS = 20;
|
|
4
|
-
|
|
5
|
-
function searchDefinition() {
|
|
6
|
-
return {
|
|
7
|
-
name: 'search',
|
|
8
|
-
description: 'Search tasks and categories in AI Note',
|
|
9
|
-
inputSchema: {
|
|
10
|
-
type: 'object',
|
|
11
|
-
properties: {
|
|
12
|
-
query: {
|
|
13
|
-
type: 'string',
|
|
14
|
-
description: 'Search query for tasks content, categories, or due dates'
|
|
15
|
-
},
|
|
16
|
-
limit: {
|
|
17
|
-
type: 'number',
|
|
18
|
-
description: 'Maximum number of search results (default: 20)'
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
required: ['query']
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function fetchDefinition() {
|
|
27
|
-
return {
|
|
28
|
-
name: 'fetch',
|
|
29
|
-
description: 'Fetch detailed information about a specific task',
|
|
30
|
-
inputSchema: {
|
|
31
|
-
type: 'object',
|
|
32
|
-
properties: {
|
|
33
|
-
id: {
|
|
34
|
-
type: 'string',
|
|
35
|
-
description: 'Task ID to fetch detailed information'
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
required: ['id']
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function getChatGptTools() {
|
|
44
|
-
return [
|
|
45
|
-
{
|
|
46
|
-
definition: searchDefinition(),
|
|
47
|
-
handler: async (args, { apiClient }) => {
|
|
48
|
-
const limit = typeof args.limit === 'number' ? Math.min(args.limit, MAX_RESULTS) : MAX_RESULTS;
|
|
49
|
-
const response = await apiClient.get('/api/mcp/tasks', {
|
|
50
|
-
params: {
|
|
51
|
-
search: args.query,
|
|
52
|
-
limit
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const results = Array.isArray(response.data?.tasks)
|
|
57
|
-
? response.data.tasks.map((task) => ({
|
|
58
|
-
id: task.id,
|
|
59
|
-
title: task.content,
|
|
60
|
-
due_date: task.due_date,
|
|
61
|
-
is_important: task.is_important,
|
|
62
|
-
url: task.url ?? `https://app.ainote.dev/tasks/${task.id}`,
|
|
63
|
-
category: task.category
|
|
64
|
-
}))
|
|
65
|
-
: response.data;
|
|
66
|
-
|
|
67
|
-
return toSuccessContent({
|
|
68
|
-
results,
|
|
69
|
-
meta: {
|
|
70
|
-
source: 'ai-note',
|
|
71
|
-
query: args.query,
|
|
72
|
-
limit
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
definition: fetchDefinition(),
|
|
79
|
-
handler: async (args, { apiClient }) => {
|
|
80
|
-
const response = await apiClient.get(`/api/mcp/tasks/${args.id}`);
|
|
81
|
-
const task = response.data?.task ?? response.data;
|
|
82
|
-
|
|
83
|
-
return toSuccessContent({
|
|
84
|
-
task,
|
|
85
|
-
meta: {
|
|
86
|
-
source: 'ai-note',
|
|
87
|
-
fetched_at: new Date().toISOString()
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
];
|
|
93
|
-
}
|
|
1
|
+
import { toSuccessContent } from './internal/formatters.js';
|
|
2
|
+
|
|
3
|
+
const MAX_RESULTS = 20;
|
|
4
|
+
|
|
5
|
+
function searchDefinition() {
|
|
6
|
+
return {
|
|
7
|
+
name: 'search',
|
|
8
|
+
description: 'Search tasks and categories in AI Note',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
query: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
description: 'Search query for tasks content, categories, or due dates'
|
|
15
|
+
},
|
|
16
|
+
limit: {
|
|
17
|
+
type: 'number',
|
|
18
|
+
description: 'Maximum number of search results (default: 20)'
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
required: ['query']
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function fetchDefinition() {
|
|
27
|
+
return {
|
|
28
|
+
name: 'fetch',
|
|
29
|
+
description: 'Fetch detailed information about a specific task',
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {
|
|
33
|
+
id: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
description: 'Task ID to fetch detailed information'
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
required: ['id']
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getChatGptTools() {
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
definition: searchDefinition(),
|
|
47
|
+
handler: async (args, { apiClient }) => {
|
|
48
|
+
const limit = typeof args.limit === 'number' ? Math.min(args.limit, MAX_RESULTS) : MAX_RESULTS;
|
|
49
|
+
const response = await apiClient.get('/api/mcp/tasks', {
|
|
50
|
+
params: {
|
|
51
|
+
search: args.query,
|
|
52
|
+
limit
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const results = Array.isArray(response.data?.tasks)
|
|
57
|
+
? response.data.tasks.map((task) => ({
|
|
58
|
+
id: task.id,
|
|
59
|
+
title: task.content,
|
|
60
|
+
due_date: task.due_date,
|
|
61
|
+
is_important: task.is_important,
|
|
62
|
+
url: task.url ?? `https://app.ainote.dev/tasks/${task.id}`,
|
|
63
|
+
category: task.category
|
|
64
|
+
}))
|
|
65
|
+
: response.data;
|
|
66
|
+
|
|
67
|
+
return toSuccessContent({
|
|
68
|
+
results,
|
|
69
|
+
meta: {
|
|
70
|
+
source: 'ai-note',
|
|
71
|
+
query: args.query,
|
|
72
|
+
limit
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
definition: fetchDefinition(),
|
|
79
|
+
handler: async (args, { apiClient }) => {
|
|
80
|
+
const response = await apiClient.get(`/api/mcp/tasks/${args.id}`);
|
|
81
|
+
const task = response.data?.task ?? response.data;
|
|
82
|
+
|
|
83
|
+
return toSuccessContent({
|
|
84
|
+
task,
|
|
85
|
+
meta: {
|
|
86
|
+
source: 'ai-note',
|
|
87
|
+
fetched_at: new Date().toISOString()
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
];
|
|
93
|
+
}
|