Repository navigation
Releases
MCP for Cloud 1.4.0
Minor release on top of 1.3.5. It adds routine reads and writes around the calendar, Talk, notifications, absence, user status, file tags and comments, mail attachments and Deck labels, plus two write routines that combine apps after one confirmation and a read-only meeting preparation. Every new permission is denied by default: an upgrade grants no new access, saved policies, groups and levels keep their effective grants, and a permission missing from a saved policy counts as denied. No database migration; updating from 1.3.5 needs no extra step.
Added
- New permissions, all denied by default:
calendar.availability,activity.read,notifications.read,notifications.dismiss,availability.read,availability.absence,status.read,status.edit,files.tags,files.comments,mail.save,deck.labels,routines.prepare,routines.meetingandroutines.mailtask. Sensitive reads (Activity, notifications, absence and user status) start denied like every write. The permissions page keeps its layout; the new permissions are only new rows. - Calendar availability:
calendar_check_availabilitychecks one 15 to 240 minute window andcalendar_suggest_timessuggests at most 5 windows, both for up to 20 exact local account IDs and through the local free/busy data only. Suggestions check at most 32 candidates per call over at most 7 days. Busy, missing, external or unknown participants are never reported as free, and no appointment details are returned. Only calendar events count: the working hours configured in Nextcloud are not applied, so a free window can fall outside a person's working hours. Continuing a search is explicit and stateless: the returned continuation is passed back as the next call, never looped automatically. Nothing is reserved, written or sent. Requirescalendar.availability. - Activity (#38):
activity_list_eventsreads the caller's own Activity feed, newest first, returning at most 50 events after examining at most 500 per call. Only current, authorized Files and simple Calendar references are returned; historical subjects, names and paths are never copied, Deck, task and sharing activities are omitted, and an event with any reference the caller cannot see is omitted without a count. An empty page never proves there was no activity. Requiresactivity.read. - Talk unread:
talk_list_unreadlists the conversations with unread messages (50 per page) andtalk_read_unreadreads the messages after the caller's read marker (200 per page). Both are read-only: they never advance the read marker, open a lobby or rename a conversation. A conversation hosted on another server is refused. Uses the existingtalk.read. - Notifications (#42):
notifications_listlists the caller's latest notifications as id, app and time only (50 by default, at most 200), and says when the list is not the whole inbox; it never marks anything read.notifications_dismissdismisses one own notification after a confirmed plan, through the Notifications app's own dismiss path; a partial or uncertain result is reported and not retried. Requiresnotifications.readandnotifications.dismiss. - Out-of-office absence:
availability_read_absencereads andavailability_set_absencecreates or changes the caller's own absence, the same way the absence settings do, including the calendar event and background jobs Nextcloud creates for it. Clearing an absence is not offered: in Nextcloud 34 and 35,AbsenceService::clearAbsenceremoves the absence dispatcher jobs without naming a user, so clearing one person's absence can remove the scheduled absence jobs of every user. The tool stays out until that is fixed upstream. The native absence settings call the sameclearAbsenceand have the same upstream limitation. Requiresavailability.readandavailability.absence. - User status:
status_read_ownreads the caller's own configured status (a stored snapshot, not live presence),status_set_typechanges the status type andstatus_set_messagesets a custom message, one of the predefined messages with a translated label, or clears it. No custom emoji or icon is accepted or returned. Only the caller's own status can be read or changed. Requiresstatus.readandstatus.edit. - File tags (#39):
files_list_tagslists the collaborative tags of one file or folder andfiles_set_tagassigns or removes one existing tag. Both run through Nextcloud's own DAV server, so the same app plugins and checks as the web interface apply. Exclusion tags are never listed, assigned or removed, and are checked again at the write. Listing usesfiles.read; changing requiresfiles.tags. - File comments (#40):
files_read_commentsreads the comments of a file or folder without marking them read, andfiles_add_commentadds one comment as the caller after a confirmed plan. Mentions resolve only accounts the caller can find through people search; a hidden and a missing account get the same refusal, so the answer does not reveal whether an account exists. Comments are never edited or deleted. Reading usesfiles.read; adding requiresfiles.comments. - Save mail attachments to Files (#36):
mail_save_attachmentsaves one attachment, byte for byte, as a new file in Files. It reads the message withBODY.PEEK, so the message stays unread. The size limit is the smaller of the existing file checkout limit and the memory available to PHP; there is no new setting. An occupied name is refused unlessreplaceis passed with the file's ETag; replacing always requires native versioning for the caller and keeps a verified pre-write copy unless the administrator has suspended copies. Requiresmail.saveand Mail 5.10.11 or later. - Deck label management (#37):
deck_create_label,deck_update_labelanddeck_delete_labelmanage the labels of a board the caller manages, anddeck_create_cardaccepts optionallabels(titles or IDs, at most 20) that assigns existing labels and creates missing ones in the same confirmation. Any use of labels requiresdeck.labels, including existing IDs. Titles that differ only by letter case are refused as duplicates. Deleting a label removes it from every card that carries it, archived and deleted cards included; Deck has no trash for labels, so the plan shows how many cards are affected and a change in between needs a new approval. Thanks to @Roman991 (#37) for the label management code. - Meeting routine:
routines_create_meetingcreates, after one confirmation, a private Talk group room with local participants, posts the agenda as its first message and creates the calendar event with the room link, in that order. It stops at the first step that fails or is uncertain; what was created stays and the receipt lists it. There is no rollback and no retry, so calling again creates another room. External or federated rooms are not supported. Invitations are not sent unlesssend_invitationsis true, and then delivery is not confirmed. It appears only for users with both Talk and Calendar enabled. Requiresroutines.meeting, plustalk.create,talk.replyandcalendar.create. - Mail card routine:
routines_create_mail_cardturns one email into one Deck card after one confirmation: one attachment is saved as a new file in the caller's Files (an occupied name is refused, never overwritten), then a card is created with a private reference to the email and the file. The file is not shared with the board: sharing discloses it to every board member and needs its own decision, so other readers of the board may not be able to open it. The plan names who reads the board, groups and teams by name as current and future members; an audience that cannot be read is treated as shared. The board is read again just before the card is created. If the card fails, the saved file stays. The message is not marked read, forwarded or answered. Requiresroutines.mailtask, plusmail.messages,mail.saveanddeck.create. - Meeting preparation:
routines_prepare_meetingbuilds a read-only dossier of one event the caller can already see: up to 10 participants, emails from the last 30 days, at most 20 emails, 20 cards and 20 files, searching at most 20 Mail folders with the inbox first. Each source needs its own permission; a source without it is named as missing, without counts. A participant address becomes a local account only withpeople.readand one exact match. Only links to this instance are followed, each read as a whole token. Cards found by assignment come from the first 20 assignment rows per participant, in no defined order, and the answer states that scope. A Mail folder that fails is skipped and the coverage is marked partial. The dossier is marked as untrusted data written by other people. It is not a complete record of each person. Requiresroutines.prepare.
Changed
- Files that a failed write leaves behind: when
files_create, thefiles_checkoutupload link ormail_save_attachmentfails after the new file was created, the answer now names the empty or incomplete file left at that path and suggests another name or replacing it. Before, the answer looked like a clean refusal and the next attempt failed because the name was taken. If the file became hidden in the meantime, the answer gives only a generic reason, without a path or id. A failure inside the creation itself reports that the file at that name may come from this write or from another client. Nothing is deleted; answers befo...
MCP for Cloud 1.3.5
Patch release on top of 1.3.4. No database change and no permission change; updating from 1.3.4 needs no extra step. This release admits the verified Forms 5.4.2 API for private form creation and structure edits, refuses unsupported question type changes before a native call, and separates version refusals from creation-permission denials.
Fixed
- Forms 5.4.2 compatibility (#35): creation and structure edits admit the explicitly verified 5.4.2 pin. A shared version profile selects the native constructor, including
userConfigfor 5.4.x, and applies the existing upload-folder precheck and warning to both 5.4.1 and 5.4.2. Unknown versions remain refused; 5.4.0 keeps its behavior without title-driven folder synchronization. Question type changes on 5.4.2 are refused while preparing the preview or revalidating confirmation, before the native controller can acquire its 15-minute lock. Preview, confirmation, native permissions and readback checks remain required. - Forms refusal messages: distinguish version refusals for creation, point editing and structure changes from denied creation permission, and name the Forms version when a question type change is unavailable. Classified refusals are preserved; technical policy failures keep the generic refusal and are not described as a user-permission denial. The messages and the existing forms.edit help text are translated into Portuguese, Spanish and German. The permissions page layout is unchanged.
- Isolated tests without pcntl: the test bootstrap no longer writes an optional timeout warning to stderr when pcntl is absent, which made six isolated Talk contract tests fail. The development-test timeout and its CI requirement are unchanged; production code is unaffected.
Validation and limits
The Forms contracts pin the official 5.4.2 archive and the native method bodies, including the type refusal after the native lock. Unit tests cover the exact version profiles, constructor arguments, folder prechecks and warnings, classified refusals in four languages, and rejection of a type change without entering the native controller or marking an attempt. The existing NC34 integration cell now pins Forms 5.4.2; NC32 remains on 5.3.7, and NC33/NC35 remain on 5.4.1. No extra workflow or runner is introduced. Local checks cover the focal Forms tests, translations, store/version contracts, architecture and API/effect contracts; the complete suite and live integration run in GitHub Actions. Creation remains sequential: failures can leave a partial form, and concurrent native folder effects remain possible after a precheck. No rollback, retry or deduplication is added.
Community feedback
Forms 5.4.2 support follows the report and request by @matt-ek in #35.
MCP for Cloud 1.3.4
The administrator sets the largest file loaded by files_read, files_edit, files_replace, a stored version and resources/read, on the MCP management page, in whole MiB. The default stays 20 MiB. A configured value at or below that default is used as stored, so a small memory_limit cannot make this release read less than 1.3.3 did. A higher setting is capped by a heuristic from memory_limit (six copies of the file, with 32 MiB left aside; neither figure is measured) and never below the default. A memory_limit of -1 applies the configured value. A directive PHP cannot read falls back to the default and never to zero bytes. The checkout upload limit now reads post_max_size the same way PHP does, so a value outside the usual form, such as 256MB or a hexadecimal size, takes the size PHP itself would use. At the default, the PDF decode allowance stays 80 MiB and the parser applies it per stream. Each inflated DOCX or ODT entry stays capped at 20 MiB. files_search returns excerpts from the full-text index and does not load the file through this ceiling. No database change and no permission change; the page saves the value, so occ is not required.
Changed
- Configurable file read limit: the management page shows the configured limit, the effective limit and why
memory_limitdoes or does not cap the read. Thanks to @johkoenig (#33) for raising the read limit; it is now configurable instead of fixed.
Fixed
- Changelog in the Nextcloud app manager (#32): the package now carries
CHANGELOG.mdin the app folder, where the Nextcloud app store reads the notes of each release and the app manager shows them in the Changelog tab. The file is copied bymcp/scripts/package.shfrom the repository root, which stays its only source; the version headings already follow the format the store reads.
Validation and limits
- Note read coverage (#34): the reported empty
notes_readresponse was not reproduced on 1.3.3. New unit protocol and live HTTP tests cover create → list → read → edit → read, checking content, path and a changed ETag in Notes and Notizen. The live checks used Nextcloud 35.0.1 with Notes 6.0.2 and both Basic and bearer authentication. This release adds regression coverage, not a claimed fix for the report.
The read-limit tests cover the default, a lower setting, a higher setting capped by PHP, unlimited and rejected memory directives, per-stream PDF decode allowances and the fixed 20 MiB inflated ZIP-entry cap. The PHP memory ceiling is a heuristic, not a guarantee that a file fits in memory. The package check verifies that the app-folder changelog matches the repository source and carries its own SPDX header.
Community feedback
Thanks to @johkoenig (#33) for raising the read limit, now configurable instead of fixed, and for asking that the changelog appear in the app overview (#32).
The empty notes_read response reported by @matt-ek (#34) was not reproduced on 1.3.3. This release adds regression coverage for create, list, read, edit and read in Notes and Notizen. No fix for that report is claimed.
MCP for Cloud 1.3.3
Fix release on top of 1.3.2. No database change and no permission change; updating from 1.3.2 needs no step. This release fixes extraction of an existing ocrmypdf text layer, makes failure diagnostics visible in the Nextcloud log, and fixes calendar warning formatting and invitation-setting compatibility.
Fixed
- Failure diagnostics in the Nextcloud log: unexpected tool failures now include the tool name and exception class and code in the log message itself, so they remain visible when a host does not expose structured log context. Other failure logs that kept their diagnostic only in context now interpolate it too. Logs that passed a complete exception now record only its class and integer code, never its message or trace, which may contain private file names and paths. Client responses and log levels are unchanged.
- Calendar plans on servers without Intl: a conflicting event could turn
calendar_create_eventor a timing change incalendar_update_eventinto an unexpected error while formatting the overlap warning. Warnings now reuse the calendar formatter, preserving localized dates with Intl and readable local ISO dates and times without it. A zero-duration overlap shows its start once, like the plan renderer; an interval that cannot be formatted still shows its start instead of an empty time. Planning still writes nothing. - Native DAV invitation setting compatibility: calendar plans and invitation checks now read
sendInvitationsusing its native configuration type, supporting both legacyyes/nostrings and the boolean setting declared by newer upstream DAV code. A boolean setting no longer raises a typed configuration conflict, and a disabled setting stored lazily is no longer mistaken for an absent key. - Scans made searchable by ocrmypdf were reported as having no text layer (#31):
files_read,resources/readand the reading of a stored version returned the "no text layer" notice, and a PDF mail attachment gave no text, for PDFs whose Tesseract text was added by ocrmypdf with--output-type pdf, with any--optimizelevel and either PDF renderer, on ocrmypdf 13 with Tesseract 4.1 and on ocrmypdf 15 with Tesseract 5.3. ocrmypdf draws the text layer as a Form XObject named/OCR-…, and pikepdf stores the page that draws it in a compressed object stream. smalot/pdfparser reads objects in object streams with its own grammar, which stops a dictionary key at a hyphen (and a name value at an underscore), so the page lost its XObjects and its text; up to 2.13.1, the latest release, the library still does this. The app now writes every such name in its#xxescape before the library reads an object stream, which the library decodes back to the same name, and keeps the offsets of the stream; strings, objects outside object streams and streams without such names are read exactly as before. Files that went through Ghostscript (ocrmypdf's default PDF/A output) or that Tesseract wrote directly were not affected, which is why only part of a collection failed. Defect present since PDF reading arrived.
Validation and limits
The fix is covered by unit tests with two small scans of synthetic text made by ocrmypdf 15.2 with Tesseract 5.3 and ocrmypdf 13.4 with Tesseract 4.1, read through files_read, through a mail attachment and through the parser, which fail on 1.3.2 and pass now, and by tests of the escaping itself (keys and values, strings, hexadecimal strings, comments and offsets). Thirty-two PDFs made with Tesseract directly, with ocrmypdf (PDF and PDF/A output, every --optimize level, both renderers, several pages, rotated pages), and rewritten by qpdf (object streams, linearization, uncompressed, without ToUnicode) and Ghostscript all return their text now; the thirteen that returned none on 1.3.2 are the ocrmypdf --output-type pdf ones that qpdf or Ghostscript did not rewrite. Text that Tesseract itself recognized wrongly, such as a rotated scan OCRed without page rotation, is returned as Tesseract wrote it.
Calendar collision warnings are covered with and without Intl, including zero-duration events and rejected intervals. Legacy string, boolean and lazy invitation settings are covered in both create and update plans; the embedded DAV factory uses the same reader. A real Nextcloud 34.0.5 test with a mixed-case account, German language, a second calendar, a subscription and a writable share returned all 24 create and description-only update plans through Basic and bearer authentication without confirming a write. The cause of a calendar plan failure reported by email remains under investigation; it was not reproduced in the tested environment. The improved failure log now provides the exception class and code needed to investigate it without exposing private exception content.
Community feedback
The ocrmypdf text-layer fix follows the report by @johkoenig in #31.
MCP for Cloud 1.3.2
Fix release on top of 1.3.1. No database change and no permission change; updating from 1.3.1 needs no step. Only the file writes that keep a pre-write copy change (files_version_restore, files_edit, files_replace and the files_checkout upload) and the receipt of collectives_remove_member, as listed below. Every other tool behaves as in 1.3.1.
Fixed
files_version_restoreof the current version failed: files_versions lists the current content of a file as one of its versions (its revision is the file's modification time) but keeps no stored copy of it. Where the files_lock app is installed, as in the Nextcloud 34 and 35 images, restoring that listed version answered "Resource not found in Nextcloud." after the pre-write copy had already been taken. Without files_lock, the core rollback first copies the file into the versions folder without creating the folders it needs, so for a file in a folder with no stored version yet it ended in "Failed to write file". That version is now written back through the file on every supported Nextcloud. Defect present since 1.0.5 with files_lock and since 0.7.0, when the tool arrived, without it.- A version restore that fails after the pre-write copy says where the copy is: a version that could not be read, a rollback that reported failure, a file that can no longer be read once restored or any other error after the copy now ends with where the original is kept, or says that no copy was created when copies are suspended. Before, the copy was left in the backup folder without being mentioned.
files_edit,files_replaceand thefiles_checkoutupload say where the copy is when the file cannot be read back: when Nextcloud takes the read permission away during the write, or cannot describe the file for the receipt, the refusal now ends with where the original is kept, as every other failure after the copy already did. A file that disappeared after the copy now gets "Resource not found in Nextcloud." with that note (HTTP 404 for the checkout upload) instead of a generic write failure, and a checkout upload stopped by a lock after the copy (HTTP 423) also carries that note. A refusal of the backup folder after the copy now gives its own reason followed by that note infiles_editandfiles_replace, asfiles_checkoutandfiles_version_restoredo.- No failure names the copy of a file that became hidden: for
files_edit,files_replace,files_version_restoreand thefiles_checkoutupload, a file hidden by an exclusion tag or the Files scope while it was being written now gets only "Resource not found in Nextcloud." (HTTP 404 for the checkout upload), the answer of a file hidden from the start. Before, a write that failed at that moment named the copy, and with it the hidden file's folder and name. When the scope cannot be read to decide, the checkout upload answers HTTP 503 and asks for a newfiles_checkoutonce access is back, since its link is already spent. collectives_remove_memberreportedpartialfor a removal that worked: Teams removes a member in the background, as it does for its own interface: within a web request it answers before it deletes the member. The receipt read the team right away and, when the member was still listed, answeredpartialwith "another result than the one requested", although nothing else had changed and the member was gone a moment later. After an accepted removal the receipt now reads the team again until the member is gone, for at most 5 seconds (the time Teams gives its own background request), and isapplied; a member still listed after that is reported asuncertain, saying that Teams applies the change in the background, never aspartial. The removal itself is still sent once. Adding a member and changing a level need no wait, since Teams stores them during the request. Defect present since 1.3.0, when the tool arrived.
Validation and limits
Each fix is covered by unit tests that fail on 1.3.1 and pass now, and the restore of the current version is also covered by two integration tests that now restore it on every run, on Nextcloud 32 to 35; before, CI hit the defect only now and then on Nextcloud 34 and 35, when the last write crossed a second boundary, and the deterministic tests then showed it on 32 and 33 as well. The full unit suite passes on PHP 8.2, 8.3, 8.4 and 8.5; an upgrade from 1.3.1 on Nextcloud 35 with PostgreSQL and the optional Deck, Mail, Tables, Forms, Polls and Collectives apps passes the integration suite, which also passes in CI on Nextcloud 32, 33, 34 and 35 with MariaDB, PostgreSQL and SQLite, fresh and upgraded. The hidden-file and lost-read paths are exercised with test doubles, since a file cannot be hidden or have its read permission taken away at a chosen moment of a write on a live server. The collectives_remove_member receipt is covered by unit tests of the wait and of each receipt status, and on Nextcloud 35 with MariaDB by the Collectives integration tests with the background removal of Teams held back on purpose: 1 and 3 seconds give applied, 7 seconds uncertain (or partial when Teams itself changed the member meanwhile), and 30 runs under CPU load stay applied, where 1 in 15 failed before.
MCP for Cloud 1.3.1
Fix release on top of 1.3.0. No database change and no permission change; updating from 1.3.0 needs no step. Only HTML mail drafts (mail_create_draft with html: true) change: they now work on servers with libxml2 2.14 or later (1.3.0 refused them there), a loose & no longer refuses a draft, and some HTML that 1.3.0 accepted on older libxml2 is now refused with the same message on every server, as listed below. Plain-text drafts and every other tool behave as in 1.3.0.
Fixed
- HTML mail drafts were always refused on servers with libxml2 2.14 or later (#30): on Ubuntu 25.10 and 26.04, Debian testing and other systems whose libxml2 parses HTML with the HTML5 tokenizer, every
mail_create_draftcall withhtml: truefailed with "The HTML draft could not be safely parsed and sanitized; nothing was saved." The UTF-8 hint the sanitizer adds before parsing became an extra comment there, which was counted as content removed by the engine. Nothing unsafe was ever saved, but the feature did not work. The hint no longer reaches the sanitizer on any libxml version. Servers with older libxml2 (Ubuntu 24.04, Debian 12 and 13, the official Nextcloud Docker images) were not affected. The newer parser does not wrap bare top-level text in a paragraph, so a draft such as<a>ok</a>is saved asokthere and as<p>ok</p>on older libxml2; both are safe. - HTML that libxml versions read differently is refused on all of them: the newer parser silently recovers constructs the older one reported, such as a repeated attribute or a numeric reference without its semicolon, and it ends comments, raw text and some elements in other places. Instead of following either parser, the sanitizer now accepts only plain HTML that both read alike, and refuses the draft, on every libxml version, when the source contains: a malformed or unclosed comment (for example one ending in
--!>or containing--); any element other than the ones a draft keeps, thehtml,head,body,meta,title,baseandlinkelements, the elements the sanitizer removes (script,style,iframe,object,form,template,noscript,textarea,img) and the elements it unwraps while keeping their content, as in 1.3.0 (font,center,h4,h5,h6,sup,sub,small,mark,abbr,cite,q,ins,kbd,dl,dt,dd,section,article,header,footer,figure,figcaption,acronym,big,strike,tt,samp,var,addressanddfn), so obsolete and unknown elements such asbgsound,svg,math,source,navorcaptionare refused;/>on an element that is not void (onlybr,hr,img,meta,linkandbasemay use it) and an end tag of a void element; attributes not separated by spaces or written with another syntax; a repeated attribute name anywhere, in any letter case; an attribute value that holds<or>, also written as a reference; an end tag with attributes;<that does not start a tag or comment; a script, style, title, textarea, iframe or noscript with markup inside or without its end tag;html,headorbodyrepeated or out of order (htmlmust come before any content,headandbodybefore any body content, andheadmust hold only head elements and be closed), a revealed conditional comment delimiter insidehead, and anything but whitespace after</body>or</html>, which both versions silently dropped while reporting that nothing was removed; control characters other than tab and line break, or noncharacters; and numeric character references without their semicolon or to anything but tab, line feed and visible characters (carriage return, other controls, DEL, C1 controls, surrogates, noncharacters and values beyond Unicode are refused). Before, some of these were removed with a count on older libxml2. Line breaks are saved as line feeds, and whitespace before the first content or after</body>or</html>is dropped. The removal counts no longer report an implied document head that held only whitespace as a removed subtree. - A loose
&no longer refuses an HTML draft: text such as "A & B", "AT&T" or "R&D", and a link such ashttps://example.com/?a=1&b=2, was refused on most servers. An ampersand that starts no character reference is now kept as text (saved as&), so the preview, the link destinations and the saved draft show exactly what was written. A named reference is decoded only with its semicolon and a name every libxml version knows (the HTML 4.01 names and'); any other name, and a name without its semicolon such as© 2026or&, is kept as literal text, as shown in the preview.
Validation and limits
The sanitizer is covered by unit tests run against both HTML parsers: a corpus of drafts and a parity fixture whose cases must give the same result, or the same refusal, on legacy libxml2 and on libxml2 2.14 or later; the only accepted difference is the implied paragraph described above. The Mail HTML draft tests pass on libxml2 2.9.14 (PHP 8.2 to 8.5) and on libxml2 2.15.2 (PHP 8.5, Ubuntu 26.04), and CI now also runs them on Ubuntu 24.04 to keep the legacy parser covered. The full unit suite passes on PHP 8.2, 8.3, 8.4 and 8.5, and an upgrade from 1.3.0 on Nextcloud 35 with PostgreSQL and the optional Deck, Mail, Tables, Forms, Polls and Collectives apps passes the integration suite.
Community feedback
- HTML mail drafts follow the request of @Beluluz in #30; this release makes them work on servers with newer libxml2.
Feedback credits name who requested, reported or suggested a change.
MCP for Cloud 1.3.0
This release reorganizes the app into capability modules, following the architecture proposed by @marcjoancr in #14, and adds writing for Forms structure, Polls, Tables schema and sharing, Collectives, Calendar reminders, Deck completion and archiving, HTML mail drafts and task status. Existing permissions, the admin and personal pages, endpoints and client connections stay the same.
Database migration on update: this version includes Version001205Date20261106000000 for CIMD host trust and credential generations (#28), the first database migration since 1.1.0. Nextcloud runs the migration and the CimdHostsSeedStep post-migration repair step. The effective CIMD host allowlist is preserved. Credentials issued to hosts that remain allowed survive with client_gen = 1. Credentials from a host that was never allowed no longer authenticate or refresh; an administrator must allow the intended host and the client must sign in again.
Added
-
Calendar reminders (#24): optional
alarmson create/update, with up to five DISPLAY reminders relative to the master event start, 0–40320 minutes before it. Omission preserves existing update alarms; an explicit list replaces only valid DISPLAY/start alarms, including removal with[]. Other alarms and recurrence overrides remain. Plans show the change and receipts observe the server result, without claiming notification delivery. Existing grants and confirmation remain.calendar_list_eventsnow returns the DISPLAY reminders relative to the start managed by MCP underalarmswith the same keys accepted by create/update (minutesBeforeanddescription), and counts other reminders inotherAlarms; confidential events and read-only shares expose none. Existing negative, non-integer or greater than 40320 offsets remain visible and must be adjusted before reuse in create/update.calendar_update_eventnow reports a replaced reminder with the sameindexin the plan (alarmChange.after) and inalarmReceipt.requestedas inalarmReceipt.observed: its position among the event's reminders, counted from 0.calendar_create_eventwas not affected. -
Forms structure writes (#1): seven operations (
forms_add_question,forms_reorder_questions,forms_add_option,forms_reorder_options,forms_delete_question,forms_delete_option,forms_change_question_type) add/reorder/delete questions/options and change a question type under the existingforms.editand native EDIT permission. Checked native APIs are Forms 5.3.0, 5.3.7, 5.4.0 and 5.4.1. Question deletion is logical and can orphan answers; option deletion is irreversible; deleting an upload question can schedule native asynchronous folder cleanup whose completion is not observed. Type changes do not convert answers and refuse grid/datetime targets. Plans bind state; effects may be partial or uncertain, without retry/rollback. Administrators should review existingforms.editgrants, which include these operations. The existing Edit forms help lists them; there is no grant rewrite or layout change. -
Polls writing (#1): ten operations configure/change state, manage/reorder/remove/restore options, manage shares and send one individual invitation, under separate default-denied configure/options/options.remove/share/invite grants and native editing rights. Public sharing follows the actor's native policy and never returns tokens/URLs. No automatic vote or retry; an invitation marker does not prove delivery. Existing 1.2.5 presets, levels and labels are preserved.
-
Tables schema and sharing (#1): eight operations create/update tables, columns and views with
tables.schema, and share/revoke user/group access withtables.share. Both grants start denied; the Full level includes them. Initial native API ranges are[1.0.10,1.0.11)on NC32 and[2.2.2,2.2.3),[2.3.1,2.3.3)or exactly2.4.0on NC33–35, subject to shape checks. Existing reading/row-writing compatibility stays separate. No schema deletion, owner transfer or public link; native manage rights still apply. Partial/unverified outcomes, concurrent overwrite and duplicate creation remain possible without automatic retry/rollback or guaranteed Undo. Add an exact schema/sharing profile on Nextcloud 33–35 with native API introspection. Preserve view layouts/card sources; refusetables_update_viewandtables_create_columnwithview_idswhen their column changes would clear a card source. Additional official-source contracts cover read/row APIs and column parsers, while a SHA-pinned NC35 archive opt-in keeps the existing matrix pins. -
Collectives pages (#22): list/pages/read/search, create, edit Markdown, move and rename inside an existing collective. The independent read grant starts allowed; create/edit/move start denied, with native permissions and confirmed plans. Content search is refused before 4.4.0 while title search remains. The approved native API ranges are 4.0.0–<4.8.0 on NC32–33, 4.4.0–<4.8.0 on NC34 and 4.5.0–<4.8.0 on NC35, subject to shape checks. No page deletion, attachments, templates or sharing management; Text editing has no cross-service CAS guarantee. Collective creation and members are below.
-
Create a collective (#22): on NC33–35 only,
collectives_create_collectivecreates a collective with its own new team, owned by the user and with no other member, through Collectives itself. The newcollectives.collective.creategrant starts denied and is included only in the Full level. A name already used by a collective or team the user sees is refused before writing, guest accounts are refused, and an account Teams has not used yet is asked to open Collectives once. Limitation: a team of that name administered by the user that appears between confirmation and creation is adopted by Collectives and its members get access; the plan warns and the receipt reports it, and after a failure halfway adoption may be unknown. On NC35 Teams may add a team folder. No collective is deleted; nothing is retried, undone or repaired. -
Collective members (#22): on NC33–35 only,
collectives_list_members,collectives_add_member,collectives_set_member_levelandcollectives_remove_membermanage members through Teams, as the Collectives interface does. The newcollectives.membersgrant (listing included) starts denied and is included only in the Full level. Only collective admins change members, and Teams checks its own rules again. Accounts and groups of the server can be added; Teams sends no email of its own. Limitation: in a team that requires invitations, an account gets an invitation and a Nextcloud notification, unless it had already asked to join: then the request is accepted and it becomes a member at once; groups are refused. Levels are member, moderator and admin; ownership is never transferred, and the owner and the user's own membership are never changed or removed. Receipts read the team again and report Teams' internal records apart. -
HTML mail drafts (#30):
mail_create_draftaccepts optionalhtml: truewhile keeping plain text as the default. The confirmation shows the complete sanitized HTML, a whitespace-preserving text preview, removal counts, names of unwrapped elements and all link destinations. Images and active content are forbidden; unavailable sanitization or a nonempty body left without readable content is refused before saving. No signature is added and nothing is sent. -
Deck completion and archiving (#27):
deck_set_card_donecompletes/reopens anddeck_set_card_archivedarchives/unarchives through the native Deck service, using the existing deck.edit grant. Each requires preview and state-bound confirmation; received boards also require confirm_shared. Completion preserves the native Unix UTC timestamp and can move to the configured done column on Deck 1.19, as shown in the plan. Reopening does not return the card to the previous list, and an already completed card is a no-op, not a retry of a partial move. Unarchive before completing/reopening. Completing and archiving remain separate, non-atomic calls. -
Task status on create and edit:
statusaccepts NEEDS-ACTION, IN-PROCESS, COMPLETED and CANCELLED, following the Tasks app. Status alone completes at 100% with a timestamp (preserving an existing time when already completed), reopens 100% at 99%, and starts IN-PROCESS at 1% from zero or absent progress. CANCELLED preserves progress and the completion date and remains listed. Explicit progress takes precedence when consistent; contradictory pairs are refused before writing. Progress alone retains its previous behavior, including reopening cancelled tasks. Confirmation plans show status and completion-date changes in the plan timezone. Thanks to @mturgeonferland (#29), Marc-Olivier Turgeon-Ferland, for the code that served as the basis for status/progress support.
Changed
- Permission catalog and presets (F00): Tables schema/share and Polls configure/options/options.remove/share/invite are independent, default-denied operations. In the permission matrix, Full now also covers Tables structure (
tables.schema) and sharing (tables.share), and creating collectives (collectives.collective.create) and managing m...
MCP for Cloud 1.2.5
Fix release on top of 1.2.4. No database change and no permission change; updating from 1.2.4 needs no step.
Fixed
- Reading PDF, DOCX and ODT files could fail with "Malformed UTF-8" (#25): the text extracted from these documents was returned without a UTF-8 check, so some files made the whole answer fail with a server error (HTTP 500). This affected
files_read,files_version_readand reading a file throughresources/read; Mail attachments (mail_read_attachment) did not fail, but lost the affected characters. Two causes were found: the PDF parser writes characters outside the Basic Multilingual Plane, such as emoji, as two separately encoded halves (CESU-8), and the XML of a DOCX or ODT file that declares a legacy charset (for exampleencoding="Windows-1252") was passed on without conversion. Extracted text is now always valid UTF-8: the two halves are joined back into the real character; XML that declares a known text charset is converted before its entities are decoded; any other invalid byte becomes the replacement character U+FFFD; and control characters other than tab and line breaks are removed. No charset is guessed, so text that does not declare its charset is never reinterpreted. As a safety net, the MCP response itself now replaces invalid UTF-8 instead of failing, as suggested in #25.
Validation and limits
The fix is covered by unit tests built from generated documents (a PDF with an emoji in its text map, DOCX and ODT files declaring Windows-1252); the full unit suite was run locally in a PHP 8.3 container, with no failure other than the known ones of that environment, the same set as in 1.2.4, and the front-end tests passed with Node. Two of the new tests need the PHP zip extension and are skipped where it is missing; they pass with it. Not tested on a live Nextcloud, and not with the file of the report, which we do not have. No remote CI run exists for this release. A DOCX or ODT file whose XML uses a legacy charset without declaring it still comes back with U+FFFD in place of those characters, not with the original letters.
Community feedback
- The fix follows the report of @RoggerTan in #25, who shared the server log of the failure and suggested replacing invalid UTF-8 in the response (
JSON_INVALID_UTF8_SUBSTITUTE), which this release adopts as a safety net. This is feedback credit, not code authorship.
MCP for Cloud 1.2.4
MCP for Cloud 1.2.4
Fix release on top of 1.2.3. No database change and no new permission; updating from 1.2.3 needs no step. Two changes affect existing setups: a group grant this version does not recognize no longer blocks the members of that group (see below), and confirming contacts_create_contact now requires the plan_state of its plan.
Fixed
- Notes folder with a translated name (#23): the Notes tools read the folder from a separate
notesPathuser value that the Notes app never writes, so the folder chosen in the app was ignored, and an account whose Notes folder has a translated name (for exampleNotizenin German) saw an empty list. The folder now comes from thenotesPathfield of the Notes app'ssettingsJSON; an emptynotesPathis the root of the user's files; without it, the app's default folder name is used and, when only the translated folder exists, its translation in the user's language. With no folder at all,notes_listandnotes_searchstill return[]and add a second text block saying the folder was not found. Nothing is written, moved or migrated, andnotes_createwrites into the folder that is found instead of creating a second one. - Image search no longer fails silently:
files_image_searchreturned an empty list when the tag did not exist or the search itself failed, which looked like "no image matches". An unknown or hidden tag now answers "Tag not found in Nextcloud." and a failed search "Failed to search images in Nextcloud." (logged without private text); a visible tag with no matching image still returns[]. A node that cannot be resolved is skipped and the others stay; policy and lock errors are passed on as before. - Bounded wait before the OAuth consent page: the download of a CIMD client's metadata document now has one total budget of 5 seconds for connection and reading together. When it runs out the existing error page says the client did not send its metadata document in time (translated), and that client is refused without a new download for 30 seconds. Allowlist, document validation, size limit and the one-hour cache of successful documents are unchanged. DNS resolution stays outside this budget.
- Fewer small admin and personal scripts (continuation of #21): the four remaining scripts below 5 KB were merged into scripts their pages already loaded:
admin-polls-grants.jsandadmin-mail-grants.jsintoadmin-levels.js,admin-backups.jsintoadmin-file-write-policy.jsandpersonal-scope.jsintoconnections.js. Behavior, grants and access levels are unchanged; the Administration page requests three scripts fewer and the personal page one fewer. Whether this removes the blank page on the host of #21 is not confirmed: we could not reproduce it. - Unknown group grants no longer block a whole group (#20): a stored group grant whose module or operation this version does not recognize made the permission check of the group's members fail, so their MCP requests were refused. Such a grant now grants nothing and the other permissions of the group keep applying; damaged or ambiguous grant data still blocks, as before. In Administration → MCP for Cloud, the group shows "Unknown grants: denied", and its editor lists each unknown key with its stored value (Allow) and its effect (denied). An administrator can clear one key at a time after a confirmation naming the group and the key; the cleanup is refused when the group's grants changed in the meantime, and it is recorded in the policy audit log with the administrator, the time and the removed keys. Nothing is cleared automatically: saving a group, clearing a policy and the cleanup after a group is deleted remove only grants this version knows, so a deleted group whose only stored grants are unknown stays listed until an administrator clears them.
- File deletion says "try the trash bin first" and reports what it saw (#20): what
files_deletedeletes and refuses is unchanged. The tool, the plan and the admin warning now say what Nextcloud does: it tries to move the item to the trash bin and may permanently delete it if that fails; recovery is not guaranteed. After removing a file or folder of the acting user, the receipt checks that user's own trash bin once: when the item is found there, it says it was confirmed in the trash bin at the time of the check (for a folder, only the folder itself) and that the trash bin keeps items only for a limited time. An item inside a share the user received goes to the owner's trash bin, which MCP never checks: the plan and the receipt say its destination is not confirmed. When the check cannot run or does not find the item, the receipt is the one of 1.2.3. - One
DTSTAMPper calendar event: events created through MCP carried twoDTSTAMPlines, one added by the calendar library and one by MCP. Creation now writes exactly one, in UTC; an update through MCP already replaced everyDTSTAMPwith one and still does, so an event created with two is left with one at its next MCP update. - The contact UID of the plan is the one saved:
contacts_create_contactshowed one UID in its plan and saved the contact under another. Confirmation now saves the UID of the plan, carried in the plan'splan_state, as Mail, Notes, Tables and Forms already do. A confirmation without thatplan_state, or with one from another plan, is refused and writes nothing; when the contact fields changed, a new plan with the same UID is returned instead of writing. talk_attach_fileno longer shows the storage path: the plan and the result showed the file's internal path, which starts with the user's account ID (/<account>/files/...). They now show the path inside the user's files (/Reports/Q3.pdf), as the Files tools do, and only the file name when that path cannot be determined.- "The plan changed" warnings say what changed: when a confirmed Polls creation, Tables row write or Forms creation or edit finds that its plan no longer matches, the new plan's warning now names the data behind the change: the installed Polls version, the upper limit of each numeric column of the table, the installed Forms version, or the current value of the form field that changed. The warning about upload folders in a Forms creation plan names the installed Forms version, and outside Forms 5.4.1 it says that setting the title does not rename or merge upload folders.
Validation and limits
Each fix is covered by unit tests; the full unit suite and the front-end tests were run locally in a PHP 8.3 container, with no failure other than the known ones of that environment, the same set as in 1.2.3. No remote CI run exists for this release. Not tested on a live Nextcloud: the trash bin receipt (the source of Nextcloud 32–35 was checked for the call it uses), the cleanup of unknown group grants in a browser, the Notes folder change on the host of #23, and the smaller admin pages on the host of #21.
Community feedback
- The Notes folder fix follows the report of @Beluluz in #23, who found that the Notes tools returned nothing when the Notes folder had a translated name.
- The "try the trash bin first" wording and the trash bin receipt follow the request of @leonbollerup in #20 for a deletion that only moves items to the trash bin. Nextcloud offers no way to guarantee that, so this release says what it does and reports what it saw instead.
- The smaller admin pages continue the diagnosis of @Wenerus in #21.
These are feedback credits, not code authorship.
MCP for Cloud 1.2.3
MCP for Cloud 1.2.3
Fix release for the Administration page. No database change; updating from 1.2.2 needs no step and changes no permission.
Fixed
- Administration → MCP for Cloud could stay blank for minutes (#21): on some managed hosts the request for one of the page's scripts,
admin-level-names.js, received no answer. Nextcloud shows the page only after its deferred scripts have loaded, so the page appeared when that request finally timed out. The names of the Mail and Forms access levels now live inadmin-levels.js, which the page already loaded, and the separate script no longer exists; the levels and their names are unchanged and the page requests one script fewer. We could not reproduce the missing answer outside that host and do not know what held the request there.
Validation and boundaries
The change is covered by the front-end tests and by the unit tests of the admin assets and the store descriptions, run locally in a PHP 8.3 container, and the page was rendered on Nextcloud 34.0.4 and 34.0.5 test instances, where the missing answer did not occur. The fix has not yet been confirmed on the host that reported the problem. No remote CI run exists for this release. The package contains only this fix, the version and the release texts compared with 1.2.2.