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.
@@ -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.20",
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": {