aiquila-mcp 0.3.20 → 0.3.22
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/dist/tool-registry.js +2 -0
- package/dist/tools/apps/activity.js +127 -0
- package/dist/tools/system/files.js +123 -0
- package/package.json +2 -1
package/dist/tool-registry.js
CHANGED
|
@@ -33,6 +33,7 @@ import { talkTools } from './tools/apps/talk.js';
|
|
|
33
33
|
import { userStatusTools } from './tools/apps/user-status.js';
|
|
34
34
|
import { absenceTools } from './tools/apps/absence.js';
|
|
35
35
|
import { notificationsTools } from './tools/apps/notifications.js';
|
|
36
|
+
import { activityTools } from './tools/apps/activity.js';
|
|
36
37
|
import { trashTools } from './tools/apps/trash.js';
|
|
37
38
|
import { versionsTools } from './tools/apps/versions.js';
|
|
38
39
|
import { projectsTools } from './tools/apps/projects.js';
|
|
@@ -86,6 +87,7 @@ export const TOOL_REGISTRY = [
|
|
|
86
87
|
{ category: 'translate', appIds: ['text_translate', 'translate'], tools: translateTools },
|
|
87
88
|
{ category: 'user_status', appIds: ['user_status'], tools: userStatusTools },
|
|
88
89
|
{ category: 'notifications', appIds: ['notifications'], tools: notificationsTools },
|
|
90
|
+
{ category: 'activity', appIds: ['activity'], tools: activityTools },
|
|
89
91
|
];
|
|
90
92
|
const ALL_CATEGORIES = new Set(TOOL_REGISTRY.map((e) => e.category));
|
|
91
93
|
/**
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { fetchOCS } from '../../client/ocs.js';
|
|
4
|
+
function formatActivities(activities) {
|
|
5
|
+
return activities
|
|
6
|
+
.map((a) => {
|
|
7
|
+
const time = a.datetime ? ` (${a.datetime})` : '';
|
|
8
|
+
const msg = a.message ? `\n ${a.message}` : '';
|
|
9
|
+
return `- [${a.app}] ${a.subject}${time}${msg}`;
|
|
10
|
+
})
|
|
11
|
+
.join('\n');
|
|
12
|
+
}
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// list_activity
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
export const listActivityTool = {
|
|
17
|
+
name: 'list_activity',
|
|
18
|
+
description: 'List recent entries from the Nextcloud activity feed (file changes, shares, ' +
|
|
19
|
+
'comments, calendar/contact edits, etc.) for the current user.',
|
|
20
|
+
inputSchema: z.object({
|
|
21
|
+
filter: z
|
|
22
|
+
.enum(['all', 'self', 'by'])
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("Which feed to read: 'all' (default), 'self' (your own actions), or 'by' (others' actions)"),
|
|
25
|
+
limit: z.number().optional().describe('Maximum number of activities to return (default 50)'),
|
|
26
|
+
since: z
|
|
27
|
+
.number()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe('Return activities after this activity_id (for pagination)'),
|
|
30
|
+
sort: z
|
|
31
|
+
.enum(['asc', 'desc'])
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Sort order by time: 'desc' (newest first, default) or 'asc'"),
|
|
34
|
+
}),
|
|
35
|
+
handler: async (args) => {
|
|
36
|
+
try {
|
|
37
|
+
const filter = args.filter ?? 'all';
|
|
38
|
+
const queryParams = {
|
|
39
|
+
limit: String(args.limit ?? 50),
|
|
40
|
+
sort: args.sort ?? 'desc',
|
|
41
|
+
};
|
|
42
|
+
if (args.since !== undefined)
|
|
43
|
+
queryParams.since = String(args.since);
|
|
44
|
+
const result = await fetchOCS(`/ocs/v2.php/apps/activity/api/v2/activity/${filter}`, { queryParams });
|
|
45
|
+
const activities = result.ocs.data;
|
|
46
|
+
if (activities.length === 0) {
|
|
47
|
+
return {
|
|
48
|
+
content: [{ type: 'text', text: 'No activity.' }],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const lastId = activities[activities.length - 1].activity_id;
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: 'text',
|
|
56
|
+
text: `Activity (${activities.length}):\n${formatActivities(activities)}\n\n` +
|
|
57
|
+
`Last activity_id: ${lastId} (pass as 'since' to page further).`,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: `Error listing activity: ${error instanceof Error ? error.message : String(error)}`,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
isError: true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// get_object_activity
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
export const getObjectActivityTool = {
|
|
79
|
+
name: 'get_object_activity',
|
|
80
|
+
description: 'List the activity history for a single object, e.g. one file. Use object_type ' +
|
|
81
|
+
"'files' with a file ID to see what happened to that file.",
|
|
82
|
+
inputSchema: z.object({
|
|
83
|
+
object_type: z.string().optional().describe("The object type to filter by (default 'files')"),
|
|
84
|
+
object_id: z.string().describe('The object ID (e.g. the Nextcloud file ID)'),
|
|
85
|
+
limit: z.number().optional().describe('Maximum number of activities to return (default 50)'),
|
|
86
|
+
}),
|
|
87
|
+
handler: async (args) => {
|
|
88
|
+
try {
|
|
89
|
+
const result = await fetchOCS('/ocs/v2.php/apps/activity/api/v2/activity/filter', {
|
|
90
|
+
queryParams: {
|
|
91
|
+
object_type: args.object_type ?? 'files',
|
|
92
|
+
object_id: args.object_id,
|
|
93
|
+
limit: String(args.limit ?? 50),
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
const activities = result.ocs.data;
|
|
97
|
+
if (activities.length === 0) {
|
|
98
|
+
return {
|
|
99
|
+
content: [{ type: 'text', text: 'No activity for this object.' }],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
type: 'text',
|
|
106
|
+
text: `Activity (${activities.length}):\n${formatActivities(activities)}`,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
return {
|
|
113
|
+
content: [
|
|
114
|
+
{
|
|
115
|
+
type: 'text',
|
|
116
|
+
text: `Error getting object activity: ${error instanceof Error ? error.message : String(error)}`,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
isError: true,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Export
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
export const activityTools = [listActivityTool, getObjectActivityTool];
|
|
@@ -478,6 +478,126 @@ export const bulkFileOperationsTool = {
|
|
|
478
478
|
};
|
|
479
479
|
},
|
|
480
480
|
};
|
|
481
|
+
/**
|
|
482
|
+
* Create a zip archive from one or more files/folders
|
|
483
|
+
*/
|
|
484
|
+
export const createArchiveTool = {
|
|
485
|
+
name: 'create_archive',
|
|
486
|
+
description: 'Create a zip archive in Nextcloud from one or more files and/or folders. Runs server-side; not bound by MCP file-size limits.',
|
|
487
|
+
inputSchema: z.object({
|
|
488
|
+
sources: z
|
|
489
|
+
.array(z.string())
|
|
490
|
+
.min(1)
|
|
491
|
+
.describe("Paths of files/folders to include (e.g., ['/Documents/a.txt', '/Photos'])"),
|
|
492
|
+
destination: z
|
|
493
|
+
.string()
|
|
494
|
+
.describe("Path for the resulting .zip file (e.g., '/Documents/backup.zip')"),
|
|
495
|
+
overwrite: z
|
|
496
|
+
.boolean()
|
|
497
|
+
.optional()
|
|
498
|
+
.default(false)
|
|
499
|
+
.describe('Whether to overwrite the destination if it already exists'),
|
|
500
|
+
}),
|
|
501
|
+
handler: async (args) => {
|
|
502
|
+
try {
|
|
503
|
+
const result = await fetchAiquilaAPI('/files/zip', {
|
|
504
|
+
method: 'POST',
|
|
505
|
+
body: {
|
|
506
|
+
sources: args.sources,
|
|
507
|
+
destination: args.destination,
|
|
508
|
+
overwrite: args.overwrite,
|
|
509
|
+
},
|
|
510
|
+
});
|
|
511
|
+
return {
|
|
512
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
catch (error) {
|
|
516
|
+
return {
|
|
517
|
+
content: [
|
|
518
|
+
{
|
|
519
|
+
type: 'text',
|
|
520
|
+
text: `Error creating archive: ${error instanceof Error ? error.message : String(error)}`,
|
|
521
|
+
},
|
|
522
|
+
],
|
|
523
|
+
isError: true,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
};
|
|
528
|
+
/**
|
|
529
|
+
* Extract a zip archive into a destination folder
|
|
530
|
+
*/
|
|
531
|
+
export const extractArchiveTool = {
|
|
532
|
+
name: 'extract_archive',
|
|
533
|
+
description: 'Extract a zip archive in Nextcloud into a destination folder. Runs server-side.',
|
|
534
|
+
inputSchema: z.object({
|
|
535
|
+
archive: z.string().describe("Path to the .zip file (e.g., '/Documents/backup.zip')"),
|
|
536
|
+
destination: z.string().describe("Folder to extract into (e.g., '/Documents/restored')"),
|
|
537
|
+
overwrite: z
|
|
538
|
+
.boolean()
|
|
539
|
+
.optional()
|
|
540
|
+
.default(false)
|
|
541
|
+
.describe('Whether to overwrite entries that already exist'),
|
|
542
|
+
}),
|
|
543
|
+
handler: async (args) => {
|
|
544
|
+
try {
|
|
545
|
+
const result = await fetchAiquilaAPI('/files/unzip', {
|
|
546
|
+
method: 'POST',
|
|
547
|
+
body: {
|
|
548
|
+
archive: args.archive,
|
|
549
|
+
destination: args.destination,
|
|
550
|
+
overwrite: args.overwrite,
|
|
551
|
+
},
|
|
552
|
+
});
|
|
553
|
+
return {
|
|
554
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
return {
|
|
559
|
+
content: [
|
|
560
|
+
{
|
|
561
|
+
type: 'text',
|
|
562
|
+
text: `Error extracting archive: ${error instanceof Error ? error.message : String(error)}`,
|
|
563
|
+
},
|
|
564
|
+
],
|
|
565
|
+
isError: true,
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
};
|
|
570
|
+
/**
|
|
571
|
+
* List the contents of a zip archive without extracting
|
|
572
|
+
*/
|
|
573
|
+
export const listArchiveTool = {
|
|
574
|
+
name: 'list_archive',
|
|
575
|
+
description: 'List the contents of a zip archive in Nextcloud without extracting it.',
|
|
576
|
+
inputSchema: z.object({
|
|
577
|
+
archive: z.string().describe("Path to the .zip file (e.g., '/Documents/backup.zip')"),
|
|
578
|
+
}),
|
|
579
|
+
handler: async (args) => {
|
|
580
|
+
try {
|
|
581
|
+
const result = await fetchAiquilaAPI('/files/zip/list', {
|
|
582
|
+
queryParams: { archive: args.archive },
|
|
583
|
+
});
|
|
584
|
+
return {
|
|
585
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
return {
|
|
590
|
+
content: [
|
|
591
|
+
{
|
|
592
|
+
type: 'text',
|
|
593
|
+
text: `Error listing archive: ${error instanceof Error ? error.message : String(error)}`,
|
|
594
|
+
},
|
|
595
|
+
],
|
|
596
|
+
isError: true,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
};
|
|
481
601
|
/**
|
|
482
602
|
* Export all file system tools
|
|
483
603
|
*/
|
|
@@ -494,4 +614,7 @@ export const fileSystemTools = [
|
|
|
494
614
|
getFileContentTool,
|
|
495
615
|
analyzeImageTool,
|
|
496
616
|
bulkFileOperationsTool,
|
|
617
|
+
createArchiveTool,
|
|
618
|
+
extractArchiveTool,
|
|
619
|
+
listArchiveTool,
|
|
497
620
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiquila-mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.22",
|
|
4
4
|
"description": "Nextcloud MCP server — files, calendar, contacts, mail, maps, notes, tasks & 120+ more tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
],
|
|
44
44
|
"overrides": {
|
|
45
45
|
"brace-expansion": "^2.0.3",
|
|
46
|
+
"esbuild": ">=0.28.1",
|
|
46
47
|
"hono": "^4.12.14"
|
|
47
48
|
},
|
|
48
49
|
"dependencies": {
|