lunaarc-mcp 1.1.0
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 +123 -0
- package/bin/lunaarc-mcp +3 -0
- package/dist/api/client.d.ts +176 -0
- package/dist/api/client.js +124 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +84 -0
- package/dist/tools/kanban.d.ts +154 -0
- package/dist/tools/kanban.js +488 -0
- package/dist/tools/todos.d.ts +98 -0
- package/dist/tools/todos.js +231 -0
- package/dist/tools/wiki.d.ts +118 -0
- package/dist/tools/wiki.js +252 -0
- package/package.json +44 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.todosTools = void 0;
|
|
4
|
+
exports.handleTodosTool = handleTodosTool;
|
|
5
|
+
// Tool definitions for todo operations
|
|
6
|
+
exports.todosTools = [
|
|
7
|
+
{
|
|
8
|
+
name: 'todos_lists',
|
|
9
|
+
description: 'Get all todo lists with their todos. Shows list names, descriptions, and all todos with their completion status.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {},
|
|
13
|
+
required: [],
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'todos_read',
|
|
18
|
+
description: 'Read detailed information about a specific todo. Returns title, description, completion status, and due date.',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
todo_id: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'The todo ID (UUID)',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
required: ['todo_id'],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'todos_create',
|
|
32
|
+
description: 'Create a new todo in a specific todo list. Requires the todo list ID and a title.',
|
|
33
|
+
inputSchema: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
todo_list_id: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'The ID of the todo list to add the todo to (UUID)',
|
|
39
|
+
},
|
|
40
|
+
title: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
description: 'The todo title (required)',
|
|
43
|
+
},
|
|
44
|
+
description: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'Detailed description of the todo (optional)',
|
|
47
|
+
},
|
|
48
|
+
due_date: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: 'Due date in ISO format (optional, e.g., 2025-01-15)',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
required: ['todo_list_id', 'title'],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'todos_update',
|
|
58
|
+
description: 'Update an existing todo. Can change title, description, completion status, or due date.',
|
|
59
|
+
inputSchema: {
|
|
60
|
+
type: 'object',
|
|
61
|
+
properties: {
|
|
62
|
+
todo_id: {
|
|
63
|
+
type: 'string',
|
|
64
|
+
description: 'The todo ID to update (UUID)',
|
|
65
|
+
},
|
|
66
|
+
title: {
|
|
67
|
+
type: 'string',
|
|
68
|
+
description: 'New title (optional)',
|
|
69
|
+
},
|
|
70
|
+
description: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'New description (optional)',
|
|
73
|
+
},
|
|
74
|
+
completed: {
|
|
75
|
+
type: 'boolean',
|
|
76
|
+
description: 'Mark as completed (true) or incomplete (false) (optional)',
|
|
77
|
+
},
|
|
78
|
+
due_date: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description: 'New due date in ISO format, or null to remove (optional)',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
required: ['todo_id'],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
// Tool handlers
|
|
88
|
+
async function handleTodosTool(client, toolName, args) {
|
|
89
|
+
switch (toolName) {
|
|
90
|
+
case 'todos_lists': {
|
|
91
|
+
const lists = await client.getTodoLists();
|
|
92
|
+
if (lists.length === 0) {
|
|
93
|
+
return {
|
|
94
|
+
content: [{ type: 'text', text: '_No todo lists found_' }],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
let output = '# Todo Lists\n\n';
|
|
98
|
+
for (const list of lists) {
|
|
99
|
+
const completedCount = list.completed_count;
|
|
100
|
+
const totalCount = list.todo_count;
|
|
101
|
+
const progressIndicator = totalCount > 0 ? ` (${completedCount}/${totalCount})` : '';
|
|
102
|
+
output += `## ${list.name}${progressIndicator}\n`;
|
|
103
|
+
output += `**ID:** \`${list.id}\`\n`;
|
|
104
|
+
if (list.description) {
|
|
105
|
+
output += `${list.description}\n`;
|
|
106
|
+
}
|
|
107
|
+
output += '\n';
|
|
108
|
+
if (list.todos.length === 0) {
|
|
109
|
+
output += '_No todos_\n\n';
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
for (const todo of list.todos) {
|
|
113
|
+
const checkbox = todo.completed ? '[x]' : '[ ]';
|
|
114
|
+
const dueInfo = todo.due_date
|
|
115
|
+
? ` (Due: ${new Date(todo.due_date).toLocaleDateString()})`
|
|
116
|
+
: '';
|
|
117
|
+
output += `- ${checkbox} **${todo.title}**${dueInfo}\n`;
|
|
118
|
+
output += ` ID: \`${todo.id}\`\n`;
|
|
119
|
+
}
|
|
120
|
+
output += '\n';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
content: [{ type: 'text', text: output }],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
case 'todos_read': {
|
|
128
|
+
const todoId = args.todo_id;
|
|
129
|
+
if (!todoId) {
|
|
130
|
+
throw new Error('todo_id parameter is required');
|
|
131
|
+
}
|
|
132
|
+
const todo = await client.getTodo(todoId);
|
|
133
|
+
const status = todo.completed ? 'Completed' : 'Pending';
|
|
134
|
+
const statusIcon = todo.completed ? '✅' : '⬜';
|
|
135
|
+
let output = `# ${statusIcon} ${todo.title}
|
|
136
|
+
|
|
137
|
+
**ID:** \`${todo.id}\`
|
|
138
|
+
**Status:** ${status}
|
|
139
|
+
**Created:** ${new Date(todo.created_at).toLocaleString()}
|
|
140
|
+
**Updated:** ${new Date(todo.updated_at).toLocaleString()}`;
|
|
141
|
+
if (todo.due_date) {
|
|
142
|
+
output += `\n**Due Date:** ${new Date(todo.due_date).toLocaleDateString()}`;
|
|
143
|
+
}
|
|
144
|
+
if (todo.completed_at) {
|
|
145
|
+
output += `\n**Completed:** ${new Date(todo.completed_at).toLocaleString()}`;
|
|
146
|
+
}
|
|
147
|
+
output += '\n\n---\n\n';
|
|
148
|
+
output += todo.description || '_No description_';
|
|
149
|
+
return {
|
|
150
|
+
content: [{ type: 'text', text: output }],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
case 'todos_create': {
|
|
154
|
+
const todoListId = args.todo_list_id;
|
|
155
|
+
const title = args.title;
|
|
156
|
+
const description = args.description;
|
|
157
|
+
const dueDate = args.due_date;
|
|
158
|
+
if (!todoListId) {
|
|
159
|
+
throw new Error('todo_list_id parameter is required');
|
|
160
|
+
}
|
|
161
|
+
if (!title) {
|
|
162
|
+
throw new Error('title parameter is required');
|
|
163
|
+
}
|
|
164
|
+
const result = await client.createTodo({
|
|
165
|
+
todo_list_id: todoListId,
|
|
166
|
+
title,
|
|
167
|
+
description,
|
|
168
|
+
due_date: dueDate,
|
|
169
|
+
});
|
|
170
|
+
return {
|
|
171
|
+
content: [
|
|
172
|
+
{
|
|
173
|
+
type: 'text',
|
|
174
|
+
text: `✅ Todo created successfully!
|
|
175
|
+
|
|
176
|
+
**Todo ID:** \`${result.id}\`
|
|
177
|
+
**Title:** ${title}
|
|
178
|
+
${dueDate ? `**Due Date:** ${new Date(dueDate).toLocaleDateString()}` : ''}`,
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
case 'todos_update': {
|
|
184
|
+
const todoId = args.todo_id;
|
|
185
|
+
const title = args.title;
|
|
186
|
+
const description = args.description;
|
|
187
|
+
const completed = args.completed;
|
|
188
|
+
const dueDate = args.due_date;
|
|
189
|
+
if (!todoId) {
|
|
190
|
+
throw new Error('todo_id parameter is required');
|
|
191
|
+
}
|
|
192
|
+
if (title === undefined &&
|
|
193
|
+
description === undefined &&
|
|
194
|
+
completed === undefined &&
|
|
195
|
+
dueDate === undefined) {
|
|
196
|
+
throw new Error('At least one field (title, description, completed, due_date) must be provided');
|
|
197
|
+
}
|
|
198
|
+
await client.updateTodo(todoId, {
|
|
199
|
+
title,
|
|
200
|
+
description,
|
|
201
|
+
completed,
|
|
202
|
+
due_date: dueDate,
|
|
203
|
+
});
|
|
204
|
+
const updates = [];
|
|
205
|
+
if (title)
|
|
206
|
+
updates.push(`Title → ${title}`);
|
|
207
|
+
if (description !== undefined)
|
|
208
|
+
updates.push('Description updated');
|
|
209
|
+
if (completed !== undefined)
|
|
210
|
+
updates.push(`Status → ${completed ? 'Completed' : 'Pending'}`);
|
|
211
|
+
if (dueDate !== undefined) {
|
|
212
|
+
updates.push(dueDate ? `Due Date → ${new Date(dueDate).toLocaleDateString()}` : 'Due Date removed');
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
content: [
|
|
216
|
+
{
|
|
217
|
+
type: 'text',
|
|
218
|
+
text: `✅ Todo updated successfully!
|
|
219
|
+
|
|
220
|
+
**Todo ID:** \`${todoId}\`
|
|
221
|
+
|
|
222
|
+
**Changes:**
|
|
223
|
+
${updates.map((u) => `- ${u}`).join('\n')}`,
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
default:
|
|
229
|
+
throw new Error(`Unknown todos tool: ${toolName}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { LunaArcApiClient } from '../api/client.js';
|
|
2
|
+
export declare const wikiTools: ({
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object";
|
|
7
|
+
properties: {
|
|
8
|
+
id_or_slug?: undefined;
|
|
9
|
+
query?: undefined;
|
|
10
|
+
title?: undefined;
|
|
11
|
+
content?: undefined;
|
|
12
|
+
parent_id?: undefined;
|
|
13
|
+
icon?: undefined;
|
|
14
|
+
page_id?: undefined;
|
|
15
|
+
};
|
|
16
|
+
required: never[];
|
|
17
|
+
};
|
|
18
|
+
} | {
|
|
19
|
+
name: string;
|
|
20
|
+
description: string;
|
|
21
|
+
inputSchema: {
|
|
22
|
+
type: "object";
|
|
23
|
+
properties: {
|
|
24
|
+
id_or_slug: {
|
|
25
|
+
type: string;
|
|
26
|
+
description: string;
|
|
27
|
+
};
|
|
28
|
+
query?: undefined;
|
|
29
|
+
title?: undefined;
|
|
30
|
+
content?: undefined;
|
|
31
|
+
parent_id?: undefined;
|
|
32
|
+
icon?: undefined;
|
|
33
|
+
page_id?: undefined;
|
|
34
|
+
};
|
|
35
|
+
required: string[];
|
|
36
|
+
};
|
|
37
|
+
} | {
|
|
38
|
+
name: string;
|
|
39
|
+
description: string;
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: "object";
|
|
42
|
+
properties: {
|
|
43
|
+
query: {
|
|
44
|
+
type: string;
|
|
45
|
+
description: string;
|
|
46
|
+
};
|
|
47
|
+
id_or_slug?: undefined;
|
|
48
|
+
title?: undefined;
|
|
49
|
+
content?: undefined;
|
|
50
|
+
parent_id?: undefined;
|
|
51
|
+
icon?: undefined;
|
|
52
|
+
page_id?: undefined;
|
|
53
|
+
};
|
|
54
|
+
required: string[];
|
|
55
|
+
};
|
|
56
|
+
} | {
|
|
57
|
+
name: string;
|
|
58
|
+
description: string;
|
|
59
|
+
inputSchema: {
|
|
60
|
+
type: "object";
|
|
61
|
+
properties: {
|
|
62
|
+
title: {
|
|
63
|
+
type: string;
|
|
64
|
+
description: string;
|
|
65
|
+
};
|
|
66
|
+
content: {
|
|
67
|
+
type: string;
|
|
68
|
+
description: string;
|
|
69
|
+
};
|
|
70
|
+
parent_id: {
|
|
71
|
+
type: string;
|
|
72
|
+
description: string;
|
|
73
|
+
};
|
|
74
|
+
icon: {
|
|
75
|
+
type: string;
|
|
76
|
+
description: string;
|
|
77
|
+
};
|
|
78
|
+
id_or_slug?: undefined;
|
|
79
|
+
query?: undefined;
|
|
80
|
+
page_id?: undefined;
|
|
81
|
+
};
|
|
82
|
+
required: string[];
|
|
83
|
+
};
|
|
84
|
+
} | {
|
|
85
|
+
name: string;
|
|
86
|
+
description: string;
|
|
87
|
+
inputSchema: {
|
|
88
|
+
type: "object";
|
|
89
|
+
properties: {
|
|
90
|
+
page_id: {
|
|
91
|
+
type: string;
|
|
92
|
+
description: string;
|
|
93
|
+
};
|
|
94
|
+
title: {
|
|
95
|
+
type: string;
|
|
96
|
+
description: string;
|
|
97
|
+
};
|
|
98
|
+
content: {
|
|
99
|
+
type: string;
|
|
100
|
+
description: string;
|
|
101
|
+
};
|
|
102
|
+
icon: {
|
|
103
|
+
type: string;
|
|
104
|
+
description: string;
|
|
105
|
+
};
|
|
106
|
+
id_or_slug?: undefined;
|
|
107
|
+
query?: undefined;
|
|
108
|
+
parent_id?: undefined;
|
|
109
|
+
};
|
|
110
|
+
required: string[];
|
|
111
|
+
};
|
|
112
|
+
})[];
|
|
113
|
+
export declare function handleWikiTool(client: LunaArcApiClient, toolName: string, args: Record<string, unknown>): Promise<{
|
|
114
|
+
content: {
|
|
115
|
+
type: 'text';
|
|
116
|
+
text: string;
|
|
117
|
+
}[];
|
|
118
|
+
}>;
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wikiTools = void 0;
|
|
4
|
+
exports.handleWikiTool = handleWikiTool;
|
|
5
|
+
// Tool definitions for wiki operations
|
|
6
|
+
exports.wikiTools = [
|
|
7
|
+
{
|
|
8
|
+
name: 'wiki_list',
|
|
9
|
+
description: 'List all wiki pages in the project. Returns page titles, slugs, and hierarchy (parent_id). Use this to get an overview of the wiki structure.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {},
|
|
13
|
+
required: [],
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'wiki_read',
|
|
18
|
+
description: 'Read a specific wiki page by its ID or slug. Returns the full page content including markdown formatting.',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
id_or_slug: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'The page ID (UUID) or slug (URL-friendly name)',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
required: ['id_or_slug'],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'wiki_search',
|
|
32
|
+
description: 'Search wiki pages by title and content. Returns matching pages with a content snippet. Use this to find relevant information in the wiki.',
|
|
33
|
+
inputSchema: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
query: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'The search query (minimum 2 characters)',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
required: ['query'],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'wiki_page_create',
|
|
46
|
+
description: 'Create a new wiki page. The page will be created with the given title and optional content. A URL-friendly slug will be automatically generated from the title.',
|
|
47
|
+
inputSchema: {
|
|
48
|
+
type: 'object',
|
|
49
|
+
properties: {
|
|
50
|
+
title: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
description: 'The page title (required)',
|
|
53
|
+
},
|
|
54
|
+
content: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'The page content in Markdown format (optional)',
|
|
57
|
+
},
|
|
58
|
+
parent_id: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: 'Parent page ID to create this page as a child (optional, UUID)',
|
|
61
|
+
},
|
|
62
|
+
icon: {
|
|
63
|
+
type: 'string',
|
|
64
|
+
description: 'An emoji icon for the page (optional, e.g., "📚")',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
required: ['title'],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'wiki_page_update',
|
|
72
|
+
description: 'Update an existing wiki page. You can update the title, content, or icon. At least one field must be provided.',
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
page_id: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
description: 'The page ID (UUID) to update',
|
|
79
|
+
},
|
|
80
|
+
title: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
description: 'New page title (optional)',
|
|
83
|
+
},
|
|
84
|
+
content: {
|
|
85
|
+
type: 'string',
|
|
86
|
+
description: 'New page content in Markdown format (optional)',
|
|
87
|
+
},
|
|
88
|
+
icon: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'New emoji icon for the page (optional)',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
required: ['page_id'],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
// Tool handlers
|
|
98
|
+
async function handleWikiTool(client, toolName, args) {
|
|
99
|
+
switch (toolName) {
|
|
100
|
+
case 'wiki_list': {
|
|
101
|
+
const pages = await client.listWikiPages();
|
|
102
|
+
if (pages.length === 0) {
|
|
103
|
+
return {
|
|
104
|
+
content: [
|
|
105
|
+
{
|
|
106
|
+
type: 'text',
|
|
107
|
+
text: 'No wiki pages found in this project.',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Build tree structure for better readability
|
|
113
|
+
const rootPages = pages.filter((p) => !p.parent_id);
|
|
114
|
+
const childPages = pages.filter((p) => p.parent_id);
|
|
115
|
+
let output = `Found ${pages.length} wiki page(s):\n\n`;
|
|
116
|
+
function formatPage(page, indent) {
|
|
117
|
+
const prefix = ' '.repeat(indent);
|
|
118
|
+
const icon = page.icon || '📄';
|
|
119
|
+
let line = `${prefix}${icon} ${page.title} (slug: ${page.slug})\n`;
|
|
120
|
+
// Find children
|
|
121
|
+
const children = childPages.filter((c) => c.parent_id === page.id);
|
|
122
|
+
for (const child of children) {
|
|
123
|
+
line += formatPage(child, indent + 1);
|
|
124
|
+
}
|
|
125
|
+
return line;
|
|
126
|
+
}
|
|
127
|
+
for (const page of rootPages) {
|
|
128
|
+
output += formatPage(page, 0);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
content: [{ type: 'text', text: output }],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
case 'wiki_read': {
|
|
135
|
+
const idOrSlug = args.id_or_slug;
|
|
136
|
+
if (!idOrSlug) {
|
|
137
|
+
throw new Error('id_or_slug parameter is required');
|
|
138
|
+
}
|
|
139
|
+
const page = await client.getWikiPage(idOrSlug);
|
|
140
|
+
const output = `# ${page.icon || '📄'} ${page.title}
|
|
141
|
+
|
|
142
|
+
**Slug:** ${page.slug}
|
|
143
|
+
**Last updated:** ${new Date(page.updated_at).toLocaleString()}
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
${page.content || '_This page has no content yet._'}`;
|
|
148
|
+
return {
|
|
149
|
+
content: [{ type: 'text', text: output }],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
case 'wiki_search': {
|
|
153
|
+
const query = args.query;
|
|
154
|
+
if (!query || query.length < 2) {
|
|
155
|
+
throw new Error('Search query must be at least 2 characters');
|
|
156
|
+
}
|
|
157
|
+
const results = await client.searchWiki(query);
|
|
158
|
+
if (results.length === 0) {
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: 'text',
|
|
163
|
+
text: `No wiki pages found matching "${query}".`,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
let output = `Found ${results.length} result(s) for "${query}":\n\n`;
|
|
169
|
+
for (const result of results) {
|
|
170
|
+
output += `## ${result.icon || '📄'} ${result.title}\n`;
|
|
171
|
+
output += `**Slug:** ${result.slug}\n`;
|
|
172
|
+
output += `**Updated:** ${new Date(result.updated_at).toLocaleString()}\n`;
|
|
173
|
+
if (result.snippet) {
|
|
174
|
+
output += `> ${result.snippet}\n`;
|
|
175
|
+
}
|
|
176
|
+
output += '\n';
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
content: [{ type: 'text', text: output }],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
case 'wiki_page_create': {
|
|
183
|
+
const title = args.title;
|
|
184
|
+
const content = args.content;
|
|
185
|
+
const parentId = args.parent_id;
|
|
186
|
+
const icon = args.icon;
|
|
187
|
+
if (!title) {
|
|
188
|
+
throw new Error('title parameter is required');
|
|
189
|
+
}
|
|
190
|
+
const result = await client.createWikiPage({
|
|
191
|
+
title,
|
|
192
|
+
content,
|
|
193
|
+
parent_id: parentId,
|
|
194
|
+
icon,
|
|
195
|
+
});
|
|
196
|
+
return {
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: 'text',
|
|
200
|
+
text: `✅ Wiki page created successfully!
|
|
201
|
+
|
|
202
|
+
**Page ID:** \`${result.id}\`
|
|
203
|
+
**Title:** ${icon || '📄'} ${title}
|
|
204
|
+
${parentId ? `**Parent:** ${parentId}` : ''}
|
|
205
|
+
${content ? `\n**Content preview:**\n> ${content.substring(0, 200)}${content.length > 200 ? '...' : ''}` : ''}
|
|
206
|
+
|
|
207
|
+
The page is now available in the wiki.`,
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
case 'wiki_page_update': {
|
|
213
|
+
const pageId = args.page_id;
|
|
214
|
+
const title = args.title;
|
|
215
|
+
const content = args.content;
|
|
216
|
+
const icon = args.icon;
|
|
217
|
+
if (!pageId) {
|
|
218
|
+
throw new Error('page_id parameter is required');
|
|
219
|
+
}
|
|
220
|
+
if (!title && content === undefined && !icon) {
|
|
221
|
+
throw new Error('At least one field (title, content, icon) must be provided');
|
|
222
|
+
}
|
|
223
|
+
await client.updateWikiPage(pageId, {
|
|
224
|
+
title,
|
|
225
|
+
content,
|
|
226
|
+
icon,
|
|
227
|
+
});
|
|
228
|
+
const updates = [];
|
|
229
|
+
if (title)
|
|
230
|
+
updates.push(`Title → ${title}`);
|
|
231
|
+
if (content !== undefined)
|
|
232
|
+
updates.push('Content updated');
|
|
233
|
+
if (icon)
|
|
234
|
+
updates.push(`Icon → ${icon}`);
|
|
235
|
+
return {
|
|
236
|
+
content: [
|
|
237
|
+
{
|
|
238
|
+
type: 'text',
|
|
239
|
+
text: `✅ Wiki page updated successfully!
|
|
240
|
+
|
|
241
|
+
**Page ID:** \`${pageId}\`
|
|
242
|
+
|
|
243
|
+
**Changes:**
|
|
244
|
+
${updates.map((u) => `- ${u}`).join('\n')}`,
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
default:
|
|
250
|
+
throw new Error(`Unknown wiki tool: ${toolName}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lunaarc-mcp",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "MCP Server for LunaArc - Access Wiki and Kanban from AI assistants",
|
|
5
|
+
"main": "dist/server.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"lunaarc-mcp": "./bin/lunaarc-mcp"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "tsx src/server.ts",
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"lunaarc",
|
|
16
|
+
"mcp",
|
|
17
|
+
"model-context-protocol",
|
|
18
|
+
"ai",
|
|
19
|
+
"claude",
|
|
20
|
+
"wiki",
|
|
21
|
+
"kanban"
|
|
22
|
+
],
|
|
23
|
+
"author": "LunaArc",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/lunaarc/lunaarc-mcp"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.0.4"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^22.10.5",
|
|
34
|
+
"tsx": "^4.19.2",
|
|
35
|
+
"typescript": "^5.7.3"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"bin"
|
|
43
|
+
]
|
|
44
|
+
}
|