@ainote/mcp 1.0.3 â 1.1.1
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 +206 -6
- package/index.js +21 -15
- package/lib/auth/strategy-factory.js +17 -4
- package/lib/cli/signup.js +118 -0
- package/lib/core/server-factory.js +5 -0
- package/lib/tools/dev-doc-tools.js +255 -0
- package/lib/tools/index.js +2 -0
- package/lib/tools/internal/formatters.js +43 -0
- package/lib/tools/onboarding-tools.js +124 -0
- package/lib/tools/shared-tools.js +98 -4
- package/lib/utils/api-client.js +5 -0
- package/package.json +9 -2
- package/server.js +2 -6
package/README.md
CHANGED
|
@@ -52,17 +52,86 @@ All transports expose the same tool catalog. Choose the option that matches your
|
|
|
52
52
|
## Features
|
|
53
53
|
|
|
54
54
|
- ð **Task Management**: Create, update, delete, and list tasks directly from Claude Desktop
|
|
55
|
+
- ð **Dev Docs**: Manage CLAUDE.md, Cursor rules, Windsurf rules, and dev documents centrally via MCP
|
|
56
|
+
- ðïļ **Dev Category Hierarchy**: Auto-organized under `dev/` with subcategories (claude, cursor, windsurf, copilot, docs)
|
|
55
57
|
- ð·ïļ **Category Support**: Organize tasks with categories
|
|
56
|
-
- ð **Advanced
|
|
58
|
+
- ð **Advanced Filtering**: 18+ filter options including date ranges, location, overdue tasks
|
|
57
59
|
- â **Priority Management**: Mark tasks as important
|
|
58
|
-
- ð
**Due
|
|
59
|
-
- ð **
|
|
60
|
+
- ð
**Smart Due Dates**: Human-readable format with relative time (e.g., `'26.1.25 14:30 - ėĪë`)
|
|
61
|
+
- ð **Dual Authentication**: Support for both User API Key (24-char) and MCP Key (64-char)
|
|
62
|
+
- ð **Usage Tracking**: Automatic API usage statistics per MCP key
|
|
63
|
+
- ð **Streaming API**: Real-time streaming for bulk operations and large datasets
|
|
64
|
+
- ðĪ **Bot Integration**: Telegram bot support via Clawdbot + mcporter
|
|
65
|
+
|
|
66
|
+
## Quick Start (No Account Needed)
|
|
67
|
+
|
|
68
|
+
You can start using AI Note MCP **without an existing account**. The MCP server includes onboarding tools that let you sign up and get an API key directly from Claude:
|
|
69
|
+
|
|
70
|
+
### 1. Add to your MCP config (no API key yet)
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"ainote": {
|
|
76
|
+
"command": "npx",
|
|
77
|
+
"args": ["-y", "@ainote/mcp"]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2. Restart Claude Desktop
|
|
84
|
+
|
|
85
|
+
### 3. Ask Claude to sign you up
|
|
86
|
+
|
|
87
|
+
> "Sign me up for AI Note with email user@example.com and password mypassword123"
|
|
88
|
+
|
|
89
|
+
Claude will call the `signup_and_get_key` tool and return your MCP API key.
|
|
90
|
+
|
|
91
|
+
### 4. Add the API key to your config
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"mcpServers": {
|
|
96
|
+
"ainote": {
|
|
97
|
+
"command": "npx",
|
|
98
|
+
"args": ["-y", "@ainote/mcp"],
|
|
99
|
+
"env": {
|
|
100
|
+
"AINOTE_API_KEY": "<your-key-from-step-3>"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 5. Restart Claude Desktop again
|
|
108
|
+
|
|
109
|
+
Now all tools (tasks, dev docs, etc.) are available.
|
|
110
|
+
|
|
111
|
+
### CLI Signup
|
|
112
|
+
|
|
113
|
+
You can also sign up directly from the terminal:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npx @ainote/mcp signup
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This will interactively ask for email, password, and name, then return your MCP API key.
|
|
120
|
+
|
|
121
|
+
### Onboarding MCP Tools
|
|
122
|
+
|
|
123
|
+
These tools also work inside Claude (no API key needed):
|
|
124
|
+
|
|
125
|
+
| Tool | Description |
|
|
126
|
+
|------|-------------|
|
|
127
|
+
| `signup_and_get_key` | Create account + get MCP key |
|
|
128
|
+
| `login_and_get_key` | Login + get MCP key (existing account) |
|
|
129
|
+
| `get_setup_guide` | Setup instructions |
|
|
60
130
|
|
|
61
131
|
## Prerequisites
|
|
62
132
|
|
|
63
133
|
- Node.js >= 18.0.0
|
|
64
134
|
- npm or yarn
|
|
65
|
-
- AI Note API access (API key required)
|
|
66
135
|
- Claude Desktop with MCP support enabled
|
|
67
136
|
|
|
68
137
|
## Installation
|
|
@@ -217,6 +286,44 @@ Authorization: Bearer YOUR_API_KEY
|
|
|
217
286
|
|
|
218
287
|
This HTTP endpoint provides identical functionality to the stdio version, making AI Note MCP accessible to a broader range of platforms and integration scenarios.
|
|
219
288
|
|
|
289
|
+
### 4. **Streaming API** - For Large Data Operations
|
|
290
|
+
- **URL**: `POST https://api.ainote.dev/mcp/stream`
|
|
291
|
+
- **Protocol**: Server-Sent Events (SSE)
|
|
292
|
+
- **Usage**: Bulk operations, large task lists, real-time progress
|
|
293
|
+
- **Best for**: Applications that need to handle large datasets or show progress indicators
|
|
294
|
+
|
|
295
|
+
#### Streaming Methods
|
|
296
|
+
| Method | Description |
|
|
297
|
+
|--------|-------------|
|
|
298
|
+
| `tasks/list` | Stream task list with filters |
|
|
299
|
+
| `tasks/create` | Create single task with progress |
|
|
300
|
+
| `tasks/analyze` | Get task statistics and insights |
|
|
301
|
+
| `tasks/bulk_update` | Update multiple tasks with progress |
|
|
302
|
+
| `tasks/bulk_create` | Create up to 1000 tasks at once |
|
|
303
|
+
|
|
304
|
+
#### Streaming Events
|
|
305
|
+
```
|
|
306
|
+
event: progress
|
|
307
|
+
data: {"status": "starting", "total": 100}
|
|
308
|
+
|
|
309
|
+
event: data
|
|
310
|
+
data: {"type": "task", "data": {...}}
|
|
311
|
+
|
|
312
|
+
event: result
|
|
313
|
+
data: {"status": "success", "count": 100}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### 5. **Telegram Bot Integration** - Via Clawdbot
|
|
317
|
+
- **Setup Guide**: [CLAWDBOT_SETUP.md](./CLAWDBOT_SETUP.md)
|
|
318
|
+
- **Tool**: mcporter for MCP server bridging
|
|
319
|
+
- **Usage**: Natural language task management in Telegram
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
# Quick setup
|
|
323
|
+
npm install -g mcporter
|
|
324
|
+
mcporter call ainote.list_tasks
|
|
325
|
+
```
|
|
326
|
+
|
|
220
327
|
### Local Development Bridge (`ainote-mcp-http`)
|
|
221
328
|
|
|
222
329
|
Run the bundled HTTP/SSE bridge when you need a local endpoint for ChatGPT or other MCP clients that speak SSE:
|
|
@@ -248,12 +355,31 @@ When OAuth is enabled the server expects `Authorization: Bearer ...` headers fro
|
|
|
248
355
|
|
|
249
356
|
### list_tasks
|
|
250
357
|
|
|
251
|
-
List tasks from AI Note with filtering options.
|
|
358
|
+
List tasks from AI Note with advanced filtering options.
|
|
252
359
|
|
|
253
|
-
**Parameters:**
|
|
360
|
+
**Basic Parameters:**
|
|
254
361
|
- `status` (optional): Filter by status - "pending" or "completed"
|
|
255
362
|
- `limit` (optional): Maximum number of tasks (default: 25, max: 500)
|
|
256
363
|
- `search` (optional): Search keyword in task content
|
|
364
|
+
- `is_important` (optional): Filter important tasks only
|
|
365
|
+
|
|
366
|
+
**Location & Category:**
|
|
367
|
+
- `location` (optional): Filter by location (partial match, e.g., "ėŽėë")
|
|
368
|
+
- `category_id` (optional): Filter by category UUID
|
|
369
|
+
|
|
370
|
+
**Date Range Filters:**
|
|
371
|
+
- `due_date_start` / `due_date_end`: Filter by due date range
|
|
372
|
+
- `completed_date_start` / `completed_date_end`: Filter by completion date
|
|
373
|
+
- `created_date_start` / `created_date_end`: Filter by creation date
|
|
374
|
+
|
|
375
|
+
**Special Filters:**
|
|
376
|
+
- `overdue` (optional): Filter overdue incomplete tasks
|
|
377
|
+
- `due_today` (optional): Filter tasks due today
|
|
378
|
+
- `has_notification` (optional): Filter by notification status
|
|
379
|
+
|
|
380
|
+
**Sorting:**
|
|
381
|
+
- `sort_by` (optional): Sort field - "due_date", "created_at", "completed_at", "updated_at", "is_important"
|
|
382
|
+
- `sort_order` (optional): Sort direction - "asc" or "desc"
|
|
257
383
|
|
|
258
384
|
### create_task
|
|
259
385
|
|
|
@@ -288,6 +414,80 @@ List all available categories.
|
|
|
288
414
|
|
|
289
415
|
**Parameters:** None
|
|
290
416
|
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
### Dev Doc Tools (v1.1.0+)
|
|
420
|
+
|
|
421
|
+
Manage AI coding tool configuration files and dev documents centrally. All docs are organized under the `dev/` category hierarchy.
|
|
422
|
+
|
|
423
|
+
#### list_dev_docs
|
|
424
|
+
|
|
425
|
+
List dev documents with optional filtering.
|
|
426
|
+
|
|
427
|
+
**Parameters:**
|
|
428
|
+
- `category` (optional): Subcategory filter (claude, cursor, windsurf, copilot, docs)
|
|
429
|
+
- `search` (optional): Search keyword in document title
|
|
430
|
+
- `content_type` (optional): Filter by type - "markdown", "json", "yaml", "text"
|
|
431
|
+
|
|
432
|
+
#### get_dev_doc
|
|
433
|
+
|
|
434
|
+
Get a single dev document by title or id.
|
|
435
|
+
|
|
436
|
+
**Parameters:**
|
|
437
|
+
- `title` or `id` (one required): Document identifier
|
|
438
|
+
- `category` (optional): Subcategory to disambiguate title
|
|
439
|
+
- `include_versions` (optional): Include version history
|
|
440
|
+
|
|
441
|
+
#### create_dev_doc
|
|
442
|
+
|
|
443
|
+
Create a new dev document under `dev/` category.
|
|
444
|
+
|
|
445
|
+
**Parameters:**
|
|
446
|
+
- `title` (required): Document title (e.g., "project-a-claude.md")
|
|
447
|
+
- `content` (required): Document content
|
|
448
|
+
- `category` (optional): Subcategory (default: "docs")
|
|
449
|
+
- `content_type` (optional): Auto-detected from title extension
|
|
450
|
+
|
|
451
|
+
#### update_dev_doc
|
|
452
|
+
|
|
453
|
+
Update a dev document with replace, append, or prepend modes.
|
|
454
|
+
|
|
455
|
+
**Parameters:**
|
|
456
|
+
- `title` or `id` (one required): Document identifier
|
|
457
|
+
- `content` (required): New content
|
|
458
|
+
- `mode` (optional): "replace" (default), "append", or "prepend"
|
|
459
|
+
|
|
460
|
+
#### delete_dev_doc
|
|
461
|
+
|
|
462
|
+
Soft delete a dev document.
|
|
463
|
+
|
|
464
|
+
**Parameters:**
|
|
465
|
+
- `title` or `id` (one required): Document identifier
|
|
466
|
+
|
|
467
|
+
#### list_dev_categories
|
|
468
|
+
|
|
469
|
+
List all subcategories under `dev/` with document counts.
|
|
470
|
+
|
|
471
|
+
**Parameters:** None
|
|
472
|
+
|
|
473
|
+
#### Example: Store CLAUDE.md in ainote
|
|
474
|
+
|
|
475
|
+
```
|
|
476
|
+
You: "Save this project's CLAUDE.md content to ainote"
|
|
477
|
+
Claude: I'll create a dev doc in the claude category...
|
|
478
|
+
â create_dev_doc(title: "myproject-claude.md", content: "...", category: "claude")
|
|
479
|
+
|
|
480
|
+
You: "Update my cursor rules in ainote"
|
|
481
|
+
Claude: I'll update the cursor rules doc...
|
|
482
|
+
â update_dev_doc(title: "api-rules.mdc", category: "cursor", content: "...")
|
|
483
|
+
|
|
484
|
+
You: "Show me all my dev docs"
|
|
485
|
+
Claude: I'll list all documents under dev/...
|
|
486
|
+
â list_dev_docs()
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
291
491
|
## API Reference
|
|
292
492
|
|
|
293
493
|
The MCP server communicates with the AI Note API using the following endpoints:
|
package/index.js
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* AI Note MCP Server - Claude Desktop Integration (stdio transport)
|
|
4
|
+
* Also supports CLI subcommands: signup
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
import { resolveAuthContext } from './lib/auth/strategy-factory.js';
|
|
8
|
-
import { createClaudeServer } from './lib/core/server-factory.js';
|
|
7
|
+
const subcommand = process.argv[2];
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
if (subcommand === 'signup') {
|
|
10
|
+
const { runSignup } = await import('./lib/cli/signup.js');
|
|
11
|
+
await runSignup();
|
|
12
|
+
} else {
|
|
13
|
+
// Default: start MCP stdio server
|
|
14
|
+
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
15
|
+
const { resolveAuthContext } = await import('./lib/auth/strategy-factory.js');
|
|
16
|
+
const { createClaudeServer } = await import('./lib/core/server-factory.js');
|
|
11
17
|
|
|
12
|
-
|
|
18
|
+
const transport = new StdioServerTransport();
|
|
19
|
+
const defaultAuthContext = resolveAuthContext('stdio');
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
throw error;
|
|
20
|
-
}
|
|
21
|
+
if (defaultAuthContext.type === 'anonymous') {
|
|
22
|
+
console.error('[ainote-mcp] No AINOTE_API_KEY found. Starting in onboarding mode.');
|
|
23
|
+
console.error('[ainote-mcp] Use signup_and_get_key or login_and_get_key to get started.');
|
|
24
|
+
console.error('[ainote-mcp] Or run: npx @ainote/mcp signup');
|
|
25
|
+
}
|
|
21
26
|
|
|
22
|
-
const { server } = createClaudeServer({ defaultAuthContext });
|
|
27
|
+
const { server } = createClaudeServer({ defaultAuthContext });
|
|
23
28
|
|
|
24
|
-
await server.connect(transport);
|
|
25
|
-
console.error('AI Note MCP stdio server started (Claude Desktop compatible)');
|
|
29
|
+
await server.connect(transport);
|
|
30
|
+
console.error('AI Note MCP stdio server started (Claude Desktop compatible)');
|
|
31
|
+
}
|
|
@@ -4,15 +4,28 @@ import { getFeatureFlags } from '../core/config.js';
|
|
|
4
4
|
|
|
5
5
|
export function resolveAuthContext(transportType, headers = {}) {
|
|
6
6
|
if (transportType === 'stdio') {
|
|
7
|
-
|
|
7
|
+
try {
|
|
8
|
+
return authenticateWithMcpKey();
|
|
9
|
+
} catch {
|
|
10
|
+
// No API key configured â allow anonymous mode for onboarding tools
|
|
11
|
+
return { type: 'anonymous' };
|
|
12
|
+
}
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
const features = getFeatureFlags();
|
|
11
16
|
|
|
12
17
|
if (transportType === 'sse' && features.oauthAuth) {
|
|
13
|
-
|
|
18
|
+
try {
|
|
19
|
+
return authenticateWithOAuth(headers);
|
|
20
|
+
} catch {
|
|
21
|
+
// Fall through to MCP key or anonymous
|
|
22
|
+
}
|
|
14
23
|
}
|
|
15
24
|
|
|
16
|
-
// Default fallback to MCP key
|
|
17
|
-
|
|
25
|
+
// Default fallback to MCP key, then anonymous for onboarding
|
|
26
|
+
try {
|
|
27
|
+
return authenticateWithMcpKey();
|
|
28
|
+
} catch {
|
|
29
|
+
return { type: 'anonymous' };
|
|
30
|
+
}
|
|
18
31
|
}
|
|
@@ -0,0 +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
|
+
}
|
|
@@ -5,6 +5,8 @@ import { getConfig } from './config-manager.js';
|
|
|
5
5
|
import { createApiClient } from '../utils/api-client.js';
|
|
6
6
|
import { getSharedTools } from '../tools/shared-tools.js';
|
|
7
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';
|
|
8
10
|
import { toErrorContent } from '../tools/internal/formatters.js';
|
|
9
11
|
|
|
10
12
|
function buildServerInfo(profile) {
|
|
@@ -15,7 +17,10 @@ function buildServerInfo(profile) {
|
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
function registerTools(registry, { includeChatGpt }) {
|
|
20
|
+
// Onboarding tools are always registered (work without API key)
|
|
21
|
+
registry.registerMany(getOnboardingTools());
|
|
18
22
|
registry.registerMany(getSharedTools());
|
|
23
|
+
registry.registerMany(getDevDocTools());
|
|
19
24
|
|
|
20
25
|
if (includeChatGpt) {
|
|
21
26
|
registry.registerMany(getChatGptTools());
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { toSuccessContent } from './internal/formatters.js';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
function listDevDocsDefinition() {
|
|
6
|
+
return {
|
|
7
|
+
name: 'list_dev_docs',
|
|
8
|
+
description: `List your synced dev documents stored in AI Note cloud.
|
|
9
|
+
|
|
10
|
+
Shows all documents under dev/ category including memory files, AI configs, and project docs.
|
|
11
|
+
Use this to see what's synced and which files have local_path set for multi-device sync.
|
|
12
|
+
|
|
13
|
+
Categories: memory, claude, cursor, windsurf, env, docs, or custom subcategories.`,
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
category: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'Subcategory filter (claude, cursor, windsurf, copilot, docs, etc.)'
|
|
20
|
+
},
|
|
21
|
+
search: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'Search keyword in document title'
|
|
24
|
+
},
|
|
25
|
+
content_type: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
enum: ['markdown', 'json', 'yaml', 'text'],
|
|
28
|
+
description: 'Filter by content type'
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getDevDocDefinition() {
|
|
36
|
+
return {
|
|
37
|
+
name: 'get_dev_doc',
|
|
38
|
+
description: 'Get a single dev document by title or id. Returns full content.',
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
title: { type: 'string', description: 'Document title (e.g., project-a-memory.md)' },
|
|
43
|
+
id: { type: 'string', description: 'Document UUID' },
|
|
44
|
+
category: { type: 'string', description: 'Subcategory (claude, cursor, etc.) to disambiguate' },
|
|
45
|
+
include_versions: { type: 'boolean', description: 'Include version history (default: false)' }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function createDevDocDefinition() {
|
|
52
|
+
return {
|
|
53
|
+
name: 'create_dev_doc',
|
|
54
|
+
description: `Save a file to AI Note cloud for multi-device sync.
|
|
55
|
+
|
|
56
|
+
PRIMARY USE CASES:
|
|
57
|
+
- Memory files: ~/.claude/projects/.../memory/MEMORY.md
|
|
58
|
+
- AI configs: CLAUDE.md, .cursorrules, .windsurfrules (not in git)
|
|
59
|
+
- Project docs: architecture notes, planning docs
|
|
60
|
+
|
|
61
|
+
Set local_path to enable pull_dev_docs auto-sync on other devices.
|
|
62
|
+
Categories: memory | claude | cursor | env | docs`,
|
|
63
|
+
inputSchema: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
title: { type: 'string', description: 'Document title (e.g., project-a-claude.md)' },
|
|
67
|
+
content: { type: 'string', description: 'Document content (markdown, json, yaml)' },
|
|
68
|
+
category: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
description: 'Subcategory (claude, cursor, windsurf, copilot, docs). Default: docs'
|
|
71
|
+
},
|
|
72
|
+
content_type: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
enum: ['markdown', 'json', 'yaml', 'text'],
|
|
75
|
+
description: 'Content type. Auto-detected from title extension if omitted.'
|
|
76
|
+
},
|
|
77
|
+
local_path: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
description: 'Absolute local file path for multi-device sync (e.g., ~/.claude/projects/.../MEMORY.md)'
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
required: ['title', 'content']
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function updateDevDocDefinition() {
|
|
88
|
+
return {
|
|
89
|
+
name: 'update_dev_doc',
|
|
90
|
+
description: 'Update a dev document. Supports replace, append, or prepend modes.',
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
title: { type: 'string', description: 'Document title' },
|
|
95
|
+
id: { type: 'string', description: 'Document UUID' },
|
|
96
|
+
category: { type: 'string', description: 'Subcategory to disambiguate' },
|
|
97
|
+
content: { type: 'string', description: 'New content' },
|
|
98
|
+
mode: {
|
|
99
|
+
type: 'string',
|
|
100
|
+
enum: ['replace', 'append', 'prepend'],
|
|
101
|
+
description: 'Update mode (default: replace)'
|
|
102
|
+
},
|
|
103
|
+
local_path: {
|
|
104
|
+
type: 'string',
|
|
105
|
+
description: 'Update the local file path for multi-device sync'
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
required: ['content']
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function deleteDevDocDefinition() {
|
|
114
|
+
return {
|
|
115
|
+
name: 'delete_dev_doc',
|
|
116
|
+
description: 'Delete a dev document (soft delete).',
|
|
117
|
+
inputSchema: {
|
|
118
|
+
type: 'object',
|
|
119
|
+
properties: {
|
|
120
|
+
title: { type: 'string', description: 'Document title' },
|
|
121
|
+
id: { type: 'string', description: 'Document UUID' },
|
|
122
|
+
category: { type: 'string', description: 'Subcategory to disambiguate' }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function pullDevDocsDefinition() {
|
|
129
|
+
return {
|
|
130
|
+
name: 'pull_dev_docs',
|
|
131
|
+
description: `Restore all synced files to THIS device. Writes files to their local paths on disk.
|
|
132
|
+
|
|
133
|
+
USE THIS WHEN:
|
|
134
|
+
- Setting up a new machine (desktop, laptop)
|
|
135
|
+
- Reinstalled macOS / fresh environment
|
|
136
|
+
- Want to sync latest versions of memory/config files from cloud
|
|
137
|
+
|
|
138
|
+
WHAT HAPPENS:
|
|
139
|
+
1. Fetches all dev docs with local_path set
|
|
140
|
+
2. Creates missing parent directories
|
|
141
|
+
3. Writes content to each local_path on this machine
|
|
142
|
+
|
|
143
|
+
Run once after installing ainote MCP on a new device to restore everything.`,
|
|
144
|
+
inputSchema: {
|
|
145
|
+
type: 'object',
|
|
146
|
+
properties: {
|
|
147
|
+
category: {
|
|
148
|
+
type: 'string',
|
|
149
|
+
description: 'Subcategory filter. Omit to pull all docs with local_path.'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function listDevCategoriesDefinition() {
|
|
157
|
+
return {
|
|
158
|
+
name: 'list_dev_categories',
|
|
159
|
+
description: 'List all subcategories under dev/. Shows document count per category.',
|
|
160
|
+
inputSchema: {
|
|
161
|
+
type: 'object',
|
|
162
|
+
properties: {}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function getDevDocTools() {
|
|
168
|
+
return [
|
|
169
|
+
{
|
|
170
|
+
definition: listDevDocsDefinition(),
|
|
171
|
+
handler: async (args, { apiClient }) => {
|
|
172
|
+
const result = await apiClient.callTool('list_dev_docs', args);
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
definition: getDevDocDefinition(),
|
|
178
|
+
handler: async (args, { apiClient }) => {
|
|
179
|
+
const result = await apiClient.callTool('get_dev_doc', args);
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
definition: createDevDocDefinition(),
|
|
185
|
+
handler: async (args, { apiClient }) => {
|
|
186
|
+
const result = await apiClient.callTool('create_dev_doc', args);
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
definition: updateDevDocDefinition(),
|
|
192
|
+
handler: async (args, { apiClient }) => {
|
|
193
|
+
const result = await apiClient.callTool('update_dev_doc', args);
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
definition: deleteDevDocDefinition(),
|
|
199
|
+
handler: async (args, { apiClient }) => {
|
|
200
|
+
const result = await apiClient.callTool('delete_dev_doc', args);
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
definition: pullDevDocsDefinition(),
|
|
206
|
+
handler: async (args, { apiClient }) => {
|
|
207
|
+
const result = await apiClient.callTool('pull_dev_docs', args);
|
|
208
|
+
|
|
209
|
+
// Extract docs from resource data and write to local paths
|
|
210
|
+
let written = [];
|
|
211
|
+
let skipped = [];
|
|
212
|
+
let errors = [];
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const resourceText = result?.content?.find(c => c.type === 'resource')?.resource?.text;
|
|
216
|
+
if (resourceText) {
|
|
217
|
+
const data = JSON.parse(resourceText);
|
|
218
|
+
for (const doc of data.docs || []) {
|
|
219
|
+
if (!doc.local_path || !doc.content) {
|
|
220
|
+
skipped.push(doc.title);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
const expandedPath = doc.local_path.replace(/^~/, process.env.HOME || '');
|
|
224
|
+
try {
|
|
225
|
+
const dir = path.dirname(expandedPath);
|
|
226
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
227
|
+
fs.writeFileSync(expandedPath, doc.content, 'utf8');
|
|
228
|
+
written.push(`â
${doc.title} â ${expandedPath}`);
|
|
229
|
+
} catch (e) {
|
|
230
|
+
errors.push(`â ${doc.title}: ${e.message}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
} catch (e) {
|
|
235
|
+
errors.push(`Parse error: ${e.message}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const summary = [
|
|
239
|
+
written.length ? `Written (${written.length}):\n${written.join('\n')}` : null,
|
|
240
|
+
skipped.length ? `Skipped (no local_path): ${skipped.join(', ')}` : null,
|
|
241
|
+
errors.length ? `Errors:\n${errors.join('\n')}` : null,
|
|
242
|
+
].filter(Boolean).join('\n\n');
|
|
243
|
+
|
|
244
|
+
return [{ type: 'text', text: summary || 'No docs with local_path found.' }];
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
definition: listDevCategoriesDefinition(),
|
|
249
|
+
handler: async (_args, { apiClient }) => {
|
|
250
|
+
const result = await apiClient.callTool('list_dev_categories', {});
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
];
|
|
255
|
+
}
|
package/lib/tools/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { getSharedTools } from './shared-tools.js';
|
|
2
2
|
export { getChatGptTools } from './chatgpt-tools.js';
|
|
3
|
+
export { getDevDocTools } from './dev-doc-tools.js';
|
|
4
|
+
export { getOnboardingTools } from './onboarding-tools.js';
|
|
3
5
|
export { toSuccessContent, toErrorContent } from './internal/formatters.js';
|
|
@@ -35,3 +35,46 @@ export function toErrorContent(error) {
|
|
|
35
35
|
isError: true
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Parse structured data from MCP response
|
|
41
|
+
* Looks for JSON resource in content array and extracts structured data
|
|
42
|
+
*/
|
|
43
|
+
export function parseStructuredData(result) {
|
|
44
|
+
if (!result?.content || !Array.isArray(result.content)) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Look for resource content with JSON data
|
|
49
|
+
for (const item of result.content) {
|
|
50
|
+
if (item.type === 'resource' && item.resource?.mimeType === 'application/json') {
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(item.resource.text);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error('Failed to parse structured data:', error);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Enhance result with structured data annotation
|
|
65
|
+
* Adds structured data to the response while preserving original text
|
|
66
|
+
*/
|
|
67
|
+
export function enhanceWithStructuredData(result) {
|
|
68
|
+
const structuredData = parseStructuredData(result);
|
|
69
|
+
|
|
70
|
+
if (structuredData) {
|
|
71
|
+
// Add metadata about structured data availability
|
|
72
|
+
return {
|
|
73
|
+
...result,
|
|
74
|
+
_structuredData: structuredData,
|
|
75
|
+
_hasStructuredData: true
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getConfig } from '../core/config-manager.js';
|
|
3
|
+
import { toErrorContent } from './internal/formatters.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Onboarding tools for signup/login without API key.
|
|
7
|
+
* These tools call the Rails API directly (no auth headers).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
function signupDefinition() {
|
|
11
|
+
return {
|
|
12
|
+
name: 'signup_and_get_key',
|
|
13
|
+
description: `Create a new AI Note account and get an MCP API key. No authentication required.
|
|
14
|
+
Use this if you don't have an account yet. After getting the key, add it to your MCP config and restart.`,
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
email: { type: 'string', description: 'Email address for the new account' },
|
|
19
|
+
password: { type: 'string', description: 'Password (minimum 6 characters)' },
|
|
20
|
+
name: { type: 'string', description: 'Display name (optional)' }
|
|
21
|
+
},
|
|
22
|
+
required: ['email', 'password']
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loginDefinition() {
|
|
28
|
+
return {
|
|
29
|
+
name: 'login_and_get_key',
|
|
30
|
+
description: `Log in to an existing AI Note account and get an MCP API key. No authentication required.
|
|
31
|
+
Use this if you already have an account but need your MCP key.`,
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
email: { type: 'string', description: 'Your account email address' },
|
|
36
|
+
password: { type: 'string', description: 'Your account password' }
|
|
37
|
+
},
|
|
38
|
+
required: ['email', 'password']
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function setupGuideDefinition() {
|
|
44
|
+
return {
|
|
45
|
+
name: 'get_setup_guide',
|
|
46
|
+
description: 'Get instructions for setting up AI Note MCP in Claude Desktop, Cursor, or other MCP clients. Includes CLI signup method (npx @ainote/mcp signup). No authentication required.',
|
|
47
|
+
inputSchema: {
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Call Rails MCP API directly without auth headers (for onboarding tools).
|
|
56
|
+
*/
|
|
57
|
+
async function callOnboardingTool(toolName, args = {}) {
|
|
58
|
+
const config = getConfig();
|
|
59
|
+
const rpcRequest = {
|
|
60
|
+
jsonrpc: '2.0',
|
|
61
|
+
id: Date.now(),
|
|
62
|
+
method: 'tools/call',
|
|
63
|
+
params: { name: toolName, arguments: args }
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const response = await axios.post(`${config.api.url}/api/mcp`, rpcRequest, {
|
|
67
|
+
headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
timeout: 30000
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (response.data?.data?.error) {
|
|
72
|
+
const err = response.data.data.error;
|
|
73
|
+
throw new Error(err.message || 'Onboarding tool call failed');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Rails wraps in { success: true, data: { jsonrpc, id, result } }
|
|
77
|
+
const rpcResult = response.data?.data?.result;
|
|
78
|
+
if (rpcResult) {
|
|
79
|
+
return rpcResult;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Fallback: return the raw response content
|
|
83
|
+
return {
|
|
84
|
+
content: [{
|
|
85
|
+
type: 'text',
|
|
86
|
+
text: JSON.stringify(response.data, null, 2)
|
|
87
|
+
}]
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function getOnboardingTools() {
|
|
92
|
+
return [
|
|
93
|
+
{
|
|
94
|
+
definition: signupDefinition(),
|
|
95
|
+
handler: async (args) => {
|
|
96
|
+
try {
|
|
97
|
+
return await callOnboardingTool('signup_and_get_key', args);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return toErrorContent(error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
definition: loginDefinition(),
|
|
105
|
+
handler: async (args) => {
|
|
106
|
+
try {
|
|
107
|
+
return await callOnboardingTool('login_and_get_key', args);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
return toErrorContent(error);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
definition: setupGuideDefinition(),
|
|
115
|
+
handler: async () => {
|
|
116
|
+
try {
|
|
117
|
+
return await callOnboardingTool('get_setup_guide');
|
|
118
|
+
} catch (error) {
|
|
119
|
+
return toErrorContent(error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
];
|
|
124
|
+
}
|
|
@@ -1,9 +1,40 @@
|
|
|
1
|
-
import { toSuccessContent } from './internal/formatters.js';
|
|
1
|
+
import { toSuccessContent, enhanceWithStructuredData } from './internal/formatters.js';
|
|
2
2
|
|
|
3
3
|
function listTasksDefinition() {
|
|
4
4
|
return {
|
|
5
5
|
name: 'list_tasks',
|
|
6
|
-
description:
|
|
6
|
+
description: `List tasks from AI Note with natural language support and advanced filtering.
|
|
7
|
+
|
|
8
|
+
NATURAL LANGUAGE EXAMPLES:
|
|
9
|
+
âĒ Location: "ėŽėëėė", "ėėļė ėë", "ę°ëĻ ęīë Ļ"
|
|
10
|
+
âĒ Time: "ėĪë", "ėīëē ėĢž", "ëĪė ėĢž", "ė§ëëŽ", "1ėė"
|
|
11
|
+
âĒ Importance: "ėĪėí", "ė°ė ėė ëė", "ęļí"
|
|
12
|
+
âĒ Status: "ėëĢí", "ëŊļėëĢ", "ė ëë"
|
|
13
|
+
âĒ Special: "ë§ę°ėž ė§ë", "ëĶėīė§", "ėĪë ë§ę°", "ėëĶž ėĪė ë"
|
|
14
|
+
âĒ Sort: "ë§ę°ėž ėėžëĄ", "ėĩė ėėžëĄ", "ėĪëë ėėžëĄ"
|
|
15
|
+
|
|
16
|
+
QUERY EXAMPLES:
|
|
17
|
+
1. "ėŽėëėė ėīëē ėĢž ë§ę°ėļ ėĪėí ëŊļėëĢ í ėž"
|
|
18
|
+
â {location: "ėŽėë", due_date_start: "2025-01-27", due_date_end: "2025-02-02",
|
|
19
|
+
is_important: true, status: "pending"}
|
|
20
|
+
|
|
21
|
+
2. "ė§ëëŽ ėëĢí ė
ëŽī ėđīí
ęģ ëĶŽ í ėžëĪė ėëĢėž ėėžëĄ"
|
|
22
|
+
â {category_id: "...", status: "completed",
|
|
23
|
+
completed_date_start: "2024-12-01", completed_date_end: "2024-12-31",
|
|
24
|
+
sort_by: "completed_at", sort_order: "asc"}
|
|
25
|
+
|
|
26
|
+
3. "ë§ę°ėž ė§ëŽëë° ėė§ ė ëë í ėžëĪ ë§ę°ėž ëđ ëĨļ ėėžëĄ"
|
|
27
|
+
â {overdue: true, sort_by: "due_date", sort_order: "asc"}
|
|
28
|
+
|
|
29
|
+
TIME CALCULATIONS (today = 2025-01-27):
|
|
30
|
+
âĒ "ėĪë" â due_today: true
|
|
31
|
+
âĒ "ėīëē ėĢž" â due_date_start: "2025-01-27", due_date_end: "2025-02-02"
|
|
32
|
+
âĒ "ëĪė ėĢž" â due_date_start: "2025-02-03", due_date_end: "2025-02-09"
|
|
33
|
+
âĒ "ėīëē ëŽ" â due_date_start: "2025-01-01", due_date_end: "2025-01-31"
|
|
34
|
+
âĒ "ė§ë ėĢž" â completed_date_start: "2025-01-20", completed_date_end: "2025-01-26"
|
|
35
|
+
âĒ "ė§ë ëŽ" â completed_date_start: "2024-12-01", completed_date_end: "2024-12-31"
|
|
36
|
+
|
|
37
|
+
Returns structured data with all task fields including location, dates, and categories.`,
|
|
7
38
|
inputSchema: {
|
|
8
39
|
type: 'object',
|
|
9
40
|
properties: {
|
|
@@ -12,6 +43,10 @@ function listTasksDefinition() {
|
|
|
12
43
|
enum: ['pending', 'completed'],
|
|
13
44
|
description: 'Filter by task status'
|
|
14
45
|
},
|
|
46
|
+
is_important: {
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
description: 'Filter by important tasks only'
|
|
49
|
+
},
|
|
15
50
|
limit: {
|
|
16
51
|
type: 'number',
|
|
17
52
|
description: 'Maximum number of tasks to return (default: 25, max: 500)'
|
|
@@ -19,6 +54,60 @@ function listTasksDefinition() {
|
|
|
19
54
|
search: {
|
|
20
55
|
type: 'string',
|
|
21
56
|
description: 'Search keyword in task content'
|
|
57
|
+
},
|
|
58
|
+
location: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: 'Filter by location (partial match, e.g., "ėŽėë", "ėėļ")'
|
|
61
|
+
},
|
|
62
|
+
category_id: {
|
|
63
|
+
type: 'string',
|
|
64
|
+
description: 'Filter by category UUID'
|
|
65
|
+
},
|
|
66
|
+
due_date_start: {
|
|
67
|
+
type: 'string',
|
|
68
|
+
description: 'Filter tasks with due_date >= this date (ISO 8601 format)'
|
|
69
|
+
},
|
|
70
|
+
due_date_end: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'Filter tasks with due_date <= this date (ISO 8601 format)'
|
|
73
|
+
},
|
|
74
|
+
completed_date_start: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
description: 'Filter tasks completed >= this date (ISO 8601 format)'
|
|
77
|
+
},
|
|
78
|
+
completed_date_end: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description: 'Filter tasks completed <= this date (ISO 8601 format)'
|
|
81
|
+
},
|
|
82
|
+
created_date_start: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
description: 'Filter tasks created >= this date (ISO 8601 format)'
|
|
85
|
+
},
|
|
86
|
+
created_date_end: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
description: 'Filter tasks created <= this date (ISO 8601 format)'
|
|
89
|
+
},
|
|
90
|
+
overdue: {
|
|
91
|
+
type: 'boolean',
|
|
92
|
+
description: 'Filter overdue incomplete tasks (due_date < today)'
|
|
93
|
+
},
|
|
94
|
+
due_today: {
|
|
95
|
+
type: 'boolean',
|
|
96
|
+
description: 'Filter tasks due today'
|
|
97
|
+
},
|
|
98
|
+
has_notification: {
|
|
99
|
+
type: 'boolean',
|
|
100
|
+
description: 'Filter by notification enabled status'
|
|
101
|
+
},
|
|
102
|
+
sort_by: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
enum: ['due_date', 'created_at', 'completed_at', 'updated_at', 'is_important'],
|
|
105
|
+
description: 'Sort field (default: created_at)'
|
|
106
|
+
},
|
|
107
|
+
sort_order: {
|
|
108
|
+
type: 'string',
|
|
109
|
+
enum: ['asc', 'desc'],
|
|
110
|
+
description: 'Sort order (default: desc)'
|
|
22
111
|
}
|
|
23
112
|
}
|
|
24
113
|
}
|
|
@@ -28,7 +117,7 @@ function listTasksDefinition() {
|
|
|
28
117
|
function createTaskDefinition() {
|
|
29
118
|
return {
|
|
30
119
|
name: 'create_task',
|
|
31
|
-
description: 'Create a new task in AI Note',
|
|
120
|
+
description: 'Create a new task in AI Note with optional notification',
|
|
32
121
|
inputSchema: {
|
|
33
122
|
type: 'object',
|
|
34
123
|
properties: {
|
|
@@ -47,6 +136,10 @@ function createTaskDefinition() {
|
|
|
47
136
|
category_id: {
|
|
48
137
|
type: 'string',
|
|
49
138
|
description: 'Category ID'
|
|
139
|
+
},
|
|
140
|
+
notification_minutes_before: {
|
|
141
|
+
type: 'number',
|
|
142
|
+
description: 'Set notification X minutes before due_date (e.g., 5 for 5 minutes before, 60 for 1 hour before). Requires due_date to be set.'
|
|
50
143
|
}
|
|
51
144
|
},
|
|
52
145
|
required: ['content']
|
|
@@ -117,7 +210,8 @@ export function getSharedTools() {
|
|
|
117
210
|
definition: listTasksDefinition(),
|
|
118
211
|
handler: async (args, { apiClient }) => {
|
|
119
212
|
const result = await apiClient.callTool('list_tasks', args);
|
|
120
|
-
|
|
213
|
+
// Enhance result with parsed structured data
|
|
214
|
+
return enhanceWithStructuredData(result);
|
|
121
215
|
}
|
|
122
216
|
},
|
|
123
217
|
{
|
package/lib/utils/api-client.js
CHANGED
|
@@ -8,6 +8,11 @@ function buildHeaders(authContext, config) {
|
|
|
8
8
|
|
|
9
9
|
const contextType = authContext?.type ?? 'mcpKey';
|
|
10
10
|
|
|
11
|
+
if (contextType === 'anonymous') {
|
|
12
|
+
// No auth headers for anonymous mode (onboarding tools)
|
|
13
|
+
return headers;
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
if (contextType === 'oauth') {
|
|
12
17
|
const bearerToken = authContext?.token;
|
|
13
18
|
if (!bearerToken) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainote/mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "MCP server for AI Note -
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "MCP server for AI Note - Tasks, Dev Docs, and multi-device file sync (CLAUDE.md, memory files, Cursor rules, Windsurf rules)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -21,9 +21,16 @@
|
|
|
21
21
|
"modelcontextprotocol",
|
|
22
22
|
"model-context-protocol",
|
|
23
23
|
"claude",
|
|
24
|
+
"claude-code",
|
|
24
25
|
"claude-desktop",
|
|
26
|
+
"cursor",
|
|
27
|
+
"windsurf",
|
|
28
|
+
"copilot",
|
|
25
29
|
"ainote",
|
|
26
30
|
"ai-note",
|
|
31
|
+
"dev-docs",
|
|
32
|
+
"claude-md",
|
|
33
|
+
"memory",
|
|
27
34
|
"tasks",
|
|
28
35
|
"task-management",
|
|
29
36
|
"todo",
|
package/server.js
CHANGED
|
@@ -80,12 +80,8 @@ app.post(config.http.messageEndpoint, async (req, res) => {
|
|
|
80
80
|
return res.status(404).json({ error: `No transport found for sessionId ${sessionId}` });
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
req.auth = authContext;
|
|
86
|
-
} catch (error) {
|
|
87
|
-
return res.status(401).json({ error: error.message });
|
|
88
|
-
}
|
|
83
|
+
const authContext = resolveAuthContext('sse', req.headers);
|
|
84
|
+
req.auth = authContext;
|
|
89
85
|
|
|
90
86
|
try {
|
|
91
87
|
await session.transport.handlePostMessage(req, res, req.body);
|