aiquila-mcp 0.3.21 → 0.3.23

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,10 @@ 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';
37
+ import { announcementTools } from './tools/apps/announcements.js';
38
+ import { registrationTools } from './tools/apps/registration.js';
39
+ import { termsOfServiceTools } from './tools/apps/terms-of-service.js';
36
40
  import { trashTools } from './tools/apps/trash.js';
37
41
  import { versionsTools } from './tools/apps/versions.js';
38
42
  import { projectsTools } from './tools/apps/projects.js';
@@ -86,6 +90,14 @@ export const TOOL_REGISTRY = [
86
90
  { category: 'translate', appIds: ['text_translate', 'translate'], tools: translateTools },
87
91
  { category: 'user_status', appIds: ['user_status'], tools: userStatusTools },
88
92
  { category: 'notifications', appIds: ['notifications'], tools: notificationsTools },
93
+ { category: 'activity', appIds: ['activity'], tools: activityTools },
94
+ { category: 'announcements', appIds: ['announcementcenter'], tools: announcementTools },
95
+ { category: 'registration', appIds: ['registration'], tools: registrationTools },
96
+ {
97
+ category: 'terms_of_service',
98
+ appIds: ['terms_of_service'],
99
+ tools: termsOfServiceTools,
100
+ },
89
101
  ];
90
102
  const ALL_CATEGORIES = new Set(TOOL_REGISTRY.map((e) => e.category));
91
103
  /**
@@ -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];
@@ -0,0 +1,178 @@
1
+ // SPDX-License-Identifier: MIT
2
+ import { z } from 'zod';
3
+ import { fetchOCS } from '../../client/ocs.js';
4
+ function formatTime(ts) {
5
+ if (!ts)
6
+ return '';
7
+ return new Date(ts * 1000).toISOString();
8
+ }
9
+ function formatAnnouncements(items) {
10
+ return items
11
+ .map((a) => {
12
+ const time = a.time ? ` (${formatTime(a.time)})` : '';
13
+ const schedule = a.schedule_time ? `\n scheduled: ${formatTime(a.schedule_time)}` : '';
14
+ const del = a.delete_time ? `\n deletes: ${formatTime(a.delete_time)}` : '';
15
+ return `- #${a.id} [${a.author}] ${a.subject}${time}${schedule}${del}`;
16
+ })
17
+ .join('\n');
18
+ }
19
+ // ---------------------------------------------------------------------------
20
+ // list_announcements
21
+ // ---------------------------------------------------------------------------
22
+ export const listAnnouncementsTool = {
23
+ name: 'list_announcements',
24
+ description: 'List announcements from the Nextcloud Announcement Center (org-wide notices such as ' +
25
+ 'maintenance windows, events, or news). Returns newest first.',
26
+ inputSchema: z.object({
27
+ offset: z
28
+ .number()
29
+ .optional()
30
+ .describe('Return announcements before this offset (for pagination, default 0)'),
31
+ }),
32
+ handler: async (args) => {
33
+ try {
34
+ const queryParams = {
35
+ offset: String(args.offset ?? 0),
36
+ };
37
+ const result = await fetchOCS('/ocs/v2.php/apps/announcementcenter/api/v1/announcements', { queryParams });
38
+ const items = result.ocs.data;
39
+ if (items.length === 0) {
40
+ return {
41
+ content: [{ type: 'text', text: 'No announcements.' }],
42
+ };
43
+ }
44
+ const lastId = items[items.length - 1].id;
45
+ return {
46
+ content: [
47
+ {
48
+ type: 'text',
49
+ text: `Announcements (${items.length}):\n${formatAnnouncements(items)}\n\n` +
50
+ `Last id: ${lastId} (pass as 'offset' to page further).`,
51
+ },
52
+ ],
53
+ };
54
+ }
55
+ catch (error) {
56
+ return {
57
+ content: [
58
+ {
59
+ type: 'text',
60
+ text: `Error listing announcements: ${error instanceof Error ? error.message : String(error)}`,
61
+ },
62
+ ],
63
+ isError: true,
64
+ };
65
+ }
66
+ },
67
+ };
68
+ // ---------------------------------------------------------------------------
69
+ // create_announcement
70
+ // ---------------------------------------------------------------------------
71
+ export const createAnnouncementTool = {
72
+ name: 'create_announcement',
73
+ description: 'Create a new announcement in the Announcement Center. This is visible to all or ' +
74
+ 'selected groups and can trigger notifications/emails — use deliberately. Requires the ' +
75
+ 'configured Nextcloud user to be an admin.',
76
+ inputSchema: z.object({
77
+ subject: z.string().describe('The announcement title (kept short)'),
78
+ message: z.string().describe('The announcement body (Markdown supported)'),
79
+ plainMessage: z
80
+ .string()
81
+ .optional()
82
+ .describe('Plain-text version of the message (defaults to message)'),
83
+ groups: z
84
+ .array(z.string())
85
+ .optional()
86
+ .describe("Group IDs to target, or ['everyone'] for all users (default)"),
87
+ activities: z.boolean().optional().describe('Publish to the activity feed (default true)'),
88
+ notifications: z.boolean().optional().describe('Send notifications (default true)'),
89
+ emails: z.boolean().optional().describe('Send emails (default false)'),
90
+ comments: z.boolean().optional().describe('Allow comments (default true)'),
91
+ scheduleTime: z
92
+ .number()
93
+ .optional()
94
+ .describe('Unix timestamp (seconds) to publish the announcement later'),
95
+ deleteTime: z
96
+ .number()
97
+ .optional()
98
+ .describe('Unix timestamp (seconds) to automatically delete the announcement'),
99
+ }),
100
+ handler: async (args) => {
101
+ try {
102
+ const jsonBody = {
103
+ subject: args.subject,
104
+ message: args.message,
105
+ plainMessage: args.plainMessage ?? args.message,
106
+ groups: args.groups ?? ['everyone'],
107
+ activities: args.activities ?? true,
108
+ notifications: args.notifications ?? true,
109
+ emails: args.emails ?? false,
110
+ comments: args.comments ?? true,
111
+ };
112
+ if (args.scheduleTime !== undefined)
113
+ jsonBody.scheduleTime = args.scheduleTime;
114
+ if (args.deleteTime !== undefined)
115
+ jsonBody.deleteTime = args.deleteTime;
116
+ const result = await fetchOCS('/ocs/v2.php/apps/announcementcenter/api/v1/announcements', { method: 'POST', jsonBody });
117
+ const created = result.ocs.data;
118
+ return {
119
+ content: [
120
+ {
121
+ type: 'text',
122
+ text: `Created announcement #${created.id}: "${created.subject}"`,
123
+ },
124
+ ],
125
+ };
126
+ }
127
+ catch (error) {
128
+ return {
129
+ content: [
130
+ {
131
+ type: 'text',
132
+ text: `Error creating announcement: ${error instanceof Error ? error.message : String(error)}`,
133
+ },
134
+ ],
135
+ isError: true,
136
+ };
137
+ }
138
+ },
139
+ };
140
+ // ---------------------------------------------------------------------------
141
+ // delete_announcement
142
+ // ---------------------------------------------------------------------------
143
+ export const deleteAnnouncementTool = {
144
+ name: 'delete_announcement',
145
+ description: 'Delete an announcement by its ID. Requires the configured Nextcloud user to be an admin.',
146
+ inputSchema: z.object({
147
+ id: z.number().describe('The announcement ID to delete'),
148
+ }),
149
+ handler: async (args) => {
150
+ try {
151
+ await fetchOCS(`/ocs/v2.php/apps/announcementcenter/api/v1/announcements/${args.id}`, {
152
+ method: 'DELETE',
153
+ });
154
+ return {
155
+ content: [{ type: 'text', text: `Deleted announcement #${args.id}.` }],
156
+ };
157
+ }
158
+ catch (error) {
159
+ return {
160
+ content: [
161
+ {
162
+ type: 'text',
163
+ text: `Error deleting announcement: ${error instanceof Error ? error.message : String(error)}`,
164
+ },
165
+ ],
166
+ isError: true,
167
+ };
168
+ }
169
+ },
170
+ };
171
+ // ---------------------------------------------------------------------------
172
+ // Export
173
+ // ---------------------------------------------------------------------------
174
+ export const announcementTools = [
175
+ listAnnouncementsTool,
176
+ createAnnouncementTool,
177
+ deleteAnnouncementTool,
178
+ ];
@@ -0,0 +1,164 @@
1
+ // SPDX-License-Identifier: MIT
2
+ import { z } from 'zod';
3
+ import { fetchOCS } from '../../client/ocs.js';
4
+ /**
5
+ * Nextcloud Registration Tools
6
+ *
7
+ * The Registration app (https://github.com/nextcloud/registration) lets visitors
8
+ * self-register accounts via an email-verification flow. It exposes no API of its
9
+ * own — all admin behaviour is driven by app config keys stored under the
10
+ * `registration` app id. These tools read and write those keys through core's
11
+ * provisioning_api appconfig OCS endpoints.
12
+ *
13
+ * Pending registrations are not exposed by any API. When `admin_approval_required`
14
+ * is enabled, registrants become disabled Nextcloud users — approve or reject them
15
+ * with the standard user tools (enable_user / delete_user).
16
+ */
17
+ const APPCONFIG_BASE = '/ocs/v2.php/apps/provisioning_api/api/v1/config/apps/registration';
18
+ /** Known Registration app config keys, with short descriptions. */
19
+ const REGISTRATION_KEYS = {
20
+ allowed_domains: 'Email domains allowed (or, with domains_is_blocklist, blocked) for registration (JSON array string)',
21
+ domains_is_blocklist: 'Treat allowed_domains as a blocklist instead of an allowlist (yes/no)',
22
+ show_domains: 'Show the email domain list to users (yes/no)',
23
+ admin_approval_required: 'Newly registered users must be validated by an admin (yes/no)',
24
+ registered_user_group: 'Group id newly registered users are added to',
25
+ email_is_optional: 'Email address is optional during registration (yes/no)',
26
+ email_is_login: 'Force the email address as the user id / login (yes/no)',
27
+ disable_email_verification: 'Skip the email verification step (yes/no)',
28
+ email_verification_hint: 'Text embedded in the verification email',
29
+ additional_hint: 'Text displayed on the account creation form',
30
+ username_policy_regex: 'Optional regex the chosen username must match',
31
+ show_fullname: 'Show the full name field on the registration form (yes/no)',
32
+ enforce_fullname: 'Make the full name field mandatory (yes/no)',
33
+ show_phone: 'Show the phone field on the registration form (yes/no)',
34
+ enforce_phone: 'Make the phone field mandatory (yes/no)',
35
+ };
36
+ const KNOWN_KEYS = Object.keys(REGISTRATION_KEYS);
37
+ // ---------------------------------------------------------------------------
38
+ // get_registration_settings
39
+ // ---------------------------------------------------------------------------
40
+ export const getRegistrationSettingsTool = {
41
+ name: 'get_registration_settings',
42
+ description: 'Read the Nextcloud Registration app settings (self-service signup configuration), such as ' +
43
+ 'allowed email domains, whether admin approval is required, and the default group for new ' +
44
+ 'users. Requires the configured Nextcloud user to be an admin.',
45
+ inputSchema: z.object({}),
46
+ handler: async () => {
47
+ try {
48
+ const entries = await Promise.all(KNOWN_KEYS.map(async (key) => {
49
+ const result = await fetchOCS(`${APPCONFIG_BASE}/${key}`);
50
+ return [key, result.ocs.data];
51
+ }));
52
+ const text = entries
53
+ .map(([key, value]) => `- ${key}: ${value === '' ? '(default)' : value}`)
54
+ .join('\n');
55
+ return {
56
+ content: [{ type: 'text', text: `Registration settings:\n${text}` }],
57
+ };
58
+ }
59
+ catch (error) {
60
+ return {
61
+ content: [
62
+ {
63
+ type: 'text',
64
+ text: `Error reading registration settings: ${error instanceof Error ? error.message : String(error)}`,
65
+ },
66
+ ],
67
+ isError: true,
68
+ };
69
+ }
70
+ },
71
+ };
72
+ // ---------------------------------------------------------------------------
73
+ // update_registration_settings
74
+ // ---------------------------------------------------------------------------
75
+ export const updateRegistrationSettingsTool = {
76
+ name: 'update_registration_settings',
77
+ description: 'Update one or more Nextcloud Registration app settings. Boolean settings use the strings ' +
78
+ "'yes'/'no'; allowed_domains takes a JSON array string (e.g. '[\"example.com\"]'). Requires " +
79
+ 'the configured Nextcloud user to be an admin.',
80
+ inputSchema: z.object({
81
+ settings: z
82
+ .array(z.object({
83
+ key: z
84
+ .enum(KNOWN_KEYS)
85
+ .describe('The Registration config key to set'),
86
+ value: z.string().describe('The value to store (boolean keys: "yes"/"no")'),
87
+ }))
88
+ .min(1)
89
+ .describe('One or more key/value pairs to update'),
90
+ }),
91
+ handler: async (args) => {
92
+ try {
93
+ const updated = [];
94
+ for (const { key, value } of args.settings) {
95
+ await fetchOCS(`${APPCONFIG_BASE}/${key}`, {
96
+ method: 'POST',
97
+ body: { value },
98
+ });
99
+ updated.push(`${key} = ${value}`);
100
+ }
101
+ return {
102
+ content: [
103
+ {
104
+ type: 'text',
105
+ text: `Updated registration settings:\n${updated.map((u) => `- ${u}`).join('\n')}`,
106
+ },
107
+ ],
108
+ };
109
+ }
110
+ catch (error) {
111
+ return {
112
+ content: [
113
+ {
114
+ type: 'text',
115
+ text: `Error updating registration settings: ${error instanceof Error ? error.message : String(error)}`,
116
+ },
117
+ ],
118
+ isError: true,
119
+ };
120
+ }
121
+ },
122
+ };
123
+ // ---------------------------------------------------------------------------
124
+ // reset_registration_setting
125
+ // ---------------------------------------------------------------------------
126
+ export const resetRegistrationSettingTool = {
127
+ name: 'reset_registration_setting',
128
+ description: 'Reset a Nextcloud Registration app setting to its default by deleting the stored value. ' +
129
+ 'Requires the configured Nextcloud user to be an admin.',
130
+ inputSchema: z.object({
131
+ key: z
132
+ .enum(KNOWN_KEYS)
133
+ .describe('The Registration config key to reset to its default'),
134
+ }),
135
+ handler: async (args) => {
136
+ try {
137
+ await fetchOCS(`${APPCONFIG_BASE}/${args.key}`, { method: 'DELETE' });
138
+ return {
139
+ content: [
140
+ { type: 'text', text: `Reset registration setting '${args.key}' to default.` },
141
+ ],
142
+ };
143
+ }
144
+ catch (error) {
145
+ return {
146
+ content: [
147
+ {
148
+ type: 'text',
149
+ text: `Error resetting registration setting: ${error instanceof Error ? error.message : String(error)}`,
150
+ },
151
+ ],
152
+ isError: true,
153
+ };
154
+ }
155
+ },
156
+ };
157
+ // ---------------------------------------------------------------------------
158
+ // Export
159
+ // ---------------------------------------------------------------------------
160
+ export const registrationTools = [
161
+ getRegistrationSettingsTool,
162
+ updateRegistrationSettingsTool,
163
+ resetRegistrationSettingTool,
164
+ ];
@@ -0,0 +1,254 @@
1
+ // SPDX-License-Identifier: MIT
2
+ import { z } from 'zod';
3
+ import { fetchOCS } from '../../client/ocs.js';
4
+ /**
5
+ * Nextcloud Terms of Service Tools
6
+ *
7
+ * The Terms of Service app (https://github.com/nextcloud/terms_of_service) lets admins
8
+ * publish per-country / per-language terms that users must accept. It exposes its own OCS
9
+ * API under `/ocs/v2.php/apps/terms_of_service`, so most tools call those endpoints
10
+ * directly.
11
+ *
12
+ * Two enforcement flags (`tos_for_users`, `tos_on_public_shares`) are plain app config
13
+ * keys: they are read via the admin form endpoint and written through core's
14
+ * provisioning_api appconfig endpoints.
15
+ */
16
+ const OCS_BASE = '/ocs/v2.php/apps/terms_of_service';
17
+ const APPCONFIG_BASE = '/ocs/v2.php/apps/provisioning_api/api/v1/config/apps/terms_of_service';
18
+ function preview(body) {
19
+ const oneLine = body.replace(/\s+/g, ' ').trim();
20
+ return oneLine.length > 80 ? `${oneLine.slice(0, 80)}…` : oneLine;
21
+ }
22
+ // ---------------------------------------------------------------------------
23
+ // get_terms_of_service
24
+ // ---------------------------------------------------------------------------
25
+ export const getTermsOfServiceTool = {
26
+ name: 'get_terms_of_service',
27
+ description: 'Read the Nextcloud Terms of Service admin configuration: all published terms (by ' +
28
+ 'country/language), the enforcement settings (tos_for_users, tos_on_public_shares), and ' +
29
+ 'the valid country and language codes that can be used when setting terms. Requires the ' +
30
+ 'configured Nextcloud user to be an admin.',
31
+ inputSchema: z.object({}),
32
+ handler: async () => {
33
+ try {
34
+ const result = await fetchOCS(`${OCS_BASE}/terms/admin`);
35
+ const data = result.ocs.data;
36
+ const termsText = data.terms.length === 0
37
+ ? '(none)'
38
+ : data.terms
39
+ .map((t) => `- #${t.id} [${t.countryCode}/${t.languageCode}] ${preview(t.body)}`)
40
+ .join('\n');
41
+ const settings = [
42
+ `- tos_for_users: ${data.tos_for_users}`,
43
+ `- tos_on_public_shares: ${data.tos_on_public_shares}`,
44
+ ].join('\n');
45
+ const countries = Object.keys(data.countries).join(', ');
46
+ const languages = Object.keys(data.languages).join(', ');
47
+ const text = `Terms of Service:\n${termsText}\n\n` +
48
+ `Settings:\n${settings}\n\n` +
49
+ `Valid country codes (use "--" for global): ${countries}\n` +
50
+ `Valid language codes: ${languages}`;
51
+ return {
52
+ content: [{ type: 'text', text }],
53
+ };
54
+ }
55
+ catch (error) {
56
+ return {
57
+ content: [
58
+ {
59
+ type: 'text',
60
+ text: `Error reading terms of service: ${error instanceof Error ? error.message : String(error)}`,
61
+ },
62
+ ],
63
+ isError: true,
64
+ };
65
+ }
66
+ },
67
+ };
68
+ // ---------------------------------------------------------------------------
69
+ // set_terms_of_service
70
+ // ---------------------------------------------------------------------------
71
+ export const setTermsOfServiceTool = {
72
+ name: 'set_terms_of_service',
73
+ description: 'Create or update the terms of service for a given country/language pair. If terms ' +
74
+ 'already exist for that pair they are replaced, otherwise new terms are created. Use ' +
75
+ 'get_terms_of_service to discover valid country/language codes; an invalid code returns ' +
76
+ 'an error (HTTP 417). Requires the configured Nextcloud user to be an admin.',
77
+ inputSchema: z.object({
78
+ countryCode: z
79
+ .string()
80
+ .default('--')
81
+ .describe('2-letter region code, or "--" for global (default)'),
82
+ languageCode: z.string().describe('2-letter language code (e.g. "en")'),
83
+ body: z.string().describe('The terms text (markdown: headers, formatting, lists, links)'),
84
+ }),
85
+ handler: async (args) => {
86
+ const countryCode = args.countryCode ?? '--';
87
+ try {
88
+ const result = await fetchOCS(`${OCS_BASE}/terms`, {
89
+ method: 'POST',
90
+ body: {
91
+ countryCode,
92
+ languageCode: args.languageCode,
93
+ body: args.body,
94
+ },
95
+ });
96
+ const t = result.ocs.data;
97
+ return {
98
+ content: [
99
+ {
100
+ type: 'text',
101
+ text: `Saved terms #${t.id} for [${t.countryCode}/${t.languageCode}].`,
102
+ },
103
+ ],
104
+ };
105
+ }
106
+ catch (error) {
107
+ return {
108
+ content: [
109
+ {
110
+ type: 'text',
111
+ text: `Error saving terms of service: ${error instanceof Error ? error.message : String(error)}`,
112
+ },
113
+ ],
114
+ isError: true,
115
+ };
116
+ }
117
+ },
118
+ };
119
+ // ---------------------------------------------------------------------------
120
+ // delete_terms_of_service
121
+ // ---------------------------------------------------------------------------
122
+ export const deleteTermsOfServiceTool = {
123
+ name: 'delete_terms_of_service',
124
+ description: 'Delete a single terms of service entry by its id (see get_terms_of_service). Requires ' +
125
+ 'the configured Nextcloud user to be an admin.',
126
+ inputSchema: z.object({
127
+ id: z.number().describe('The terms id to delete'),
128
+ }),
129
+ handler: async (args) => {
130
+ try {
131
+ await fetchOCS(`${OCS_BASE}/terms/${args.id}`, { method: 'DELETE' });
132
+ return {
133
+ content: [{ type: 'text', text: `Deleted terms #${args.id}.` }],
134
+ };
135
+ }
136
+ catch (error) {
137
+ return {
138
+ content: [
139
+ {
140
+ type: 'text',
141
+ text: `Error deleting terms of service: ${error instanceof Error ? error.message : String(error)}`,
142
+ },
143
+ ],
144
+ isError: true,
145
+ };
146
+ }
147
+ },
148
+ };
149
+ // ---------------------------------------------------------------------------
150
+ // reset_terms_signatures
151
+ // ---------------------------------------------------------------------------
152
+ export const resetTermsSignaturesTool = {
153
+ name: 'reset_terms_signatures',
154
+ description: "Reset ALL users' terms of service signatures org-wide, forcing every user to accept " +
155
+ 'the terms again on next login. This is destructive and cannot be undone. Requires the ' +
156
+ 'configured Nextcloud user to be an admin.',
157
+ inputSchema: z.object({}),
158
+ handler: async () => {
159
+ try {
160
+ await fetchOCS(`${OCS_BASE}/sign`, { method: 'DELETE' });
161
+ return {
162
+ content: [
163
+ {
164
+ type: 'text',
165
+ text: 'Reset all terms of service signatures. All users must accept the terms again.',
166
+ },
167
+ ],
168
+ };
169
+ }
170
+ catch (error) {
171
+ return {
172
+ content: [
173
+ {
174
+ type: 'text',
175
+ text: `Error resetting terms signatures: ${error instanceof Error ? error.message : String(error)}`,
176
+ },
177
+ ],
178
+ isError: true,
179
+ };
180
+ }
181
+ },
182
+ };
183
+ // ---------------------------------------------------------------------------
184
+ // update_terms_settings
185
+ // ---------------------------------------------------------------------------
186
+ export const updateTermsSettingsTool = {
187
+ name: 'update_terms_settings',
188
+ description: 'Update the Terms of Service enforcement settings: whether logged-in users must accept ' +
189
+ 'the terms (tos_for_users) and whether the terms apply to public shares ' +
190
+ '(tos_on_public_shares). Requires the configured Nextcloud user to be an admin.',
191
+ inputSchema: z.object({
192
+ tosForUsers: z.boolean().optional().describe('Require logged-in users to accept the terms'),
193
+ tosOnPublicShares: z.boolean().optional().describe('Apply the terms to public shares'),
194
+ }),
195
+ handler: async (args) => {
196
+ const updates = [];
197
+ if (args.tosForUsers !== undefined)
198
+ updates.push(['tos_for_users', args.tosForUsers]);
199
+ if (args.tosOnPublicShares !== undefined) {
200
+ updates.push(['tos_on_public_shares', args.tosOnPublicShares]);
201
+ }
202
+ if (updates.length === 0) {
203
+ return {
204
+ content: [
205
+ {
206
+ type: 'text',
207
+ text: 'No settings provided. Specify tosForUsers and/or tosOnPublicShares.',
208
+ },
209
+ ],
210
+ isError: true,
211
+ };
212
+ }
213
+ try {
214
+ const applied = [];
215
+ for (const [key, enabled] of updates) {
216
+ const value = enabled ? 'yes' : 'no';
217
+ await fetchOCS(`${APPCONFIG_BASE}/${key}`, {
218
+ method: 'POST',
219
+ body: { value },
220
+ });
221
+ applied.push(`${key} = ${value}`);
222
+ }
223
+ return {
224
+ content: [
225
+ {
226
+ type: 'text',
227
+ text: `Updated terms of service settings:\n${applied.map((u) => `- ${u}`).join('\n')}`,
228
+ },
229
+ ],
230
+ };
231
+ }
232
+ catch (error) {
233
+ return {
234
+ content: [
235
+ {
236
+ type: 'text',
237
+ text: `Error updating terms settings: ${error instanceof Error ? error.message : String(error)}`,
238
+ },
239
+ ],
240
+ isError: true,
241
+ };
242
+ }
243
+ },
244
+ };
245
+ // ---------------------------------------------------------------------------
246
+ // Export
247
+ // ---------------------------------------------------------------------------
248
+ export const termsOfServiceTools = [
249
+ getTermsOfServiceTool,
250
+ setTermsOfServiceTool,
251
+ deleteTermsOfServiceTool,
252
+ resetTermsSignaturesTool,
253
+ updateTermsSettingsTool,
254
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiquila-mcp",
3
- "version": "0.3.21",
3
+ "version": "0.3.23",
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",