nc-mcp-server
# Nextcloud MCP Server
[](https://github.com/cloud-py-api/nc_mcp_server/actions/workflows/lint.yml)
[](https://github.com/cloud-py-api/nc_mcp_server/actions/workflows/tests-unit.yml)
[](https://github.com/cloud-py-api/nc_mcp_server/actions/workflows/tests-integration.yml)
[](https://codecov.io/gh/cloud-py-api/nc_mcp_server)


[](https://pypi.org/project/nc-mcp-server/)
[](https://pypi.org/project/nc-mcp-server/)
[](https://github.com/cloud-py-api/nc_mcp_server/blob/main/LICENSE)
> **Experimental** — This repository is fully maintained by AI (Claude). It serves as an experiment in autonomous AI-driven open-source development.
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that exposes Nextcloud APIs as tools for AI assistants. Connect any MCP-compatible client (Claude Desktop, Claude Code, etc.) to your Nextcloud instance and let AI manage your files, calendar, contacts, conversations, and more.
## Quick Start
```bash
pip install nc-mcp-server
```
Set environment variables and connect:
```bash
export NEXTCLOUD_URL=https://your-nextcloud.example.com
export NEXTCLOUD_USER=your-username
export NEXTCLOUD_PASSWORD=your-app-password
nc-mcp-server
```
## 222 Tools Across 24 Nextcloud Apps
A 223rd tool, `upload_file_from_path`, is registered only when the operator sets
`NEXTCLOUD_MCP_UPLOAD_ROOT`. See [Files](#files) for details.
| Category | Tools | Protocol |
|----------|-------|----------|
| [Files](#files) | list, read, search, upload (text / binary / from path), copy, move, delete | WebDAV |
| [File Sharing](#file-sharing) | list, get, create, update, delete shares; accept, decline and leave shares from others | OCS |
| [Trashbin](#trashbin) | list, restore, delete item, empty trash | WebDAV |
| [File Versions](#file-versions) | list, restore versions | WebDAV |
| [File Comments](#file-comments) | list, add, edit, delete comments | WebDAV |
| [File Reminders](#file-reminders) | get, set, remove per-file reminders | OCS |
| [System Tags](#system-tags) | list, create, assign, unassign, delete tags | WebDAV |
| [Users](#users) | get current, list, get, create, update, enable/disable, delete users | OCS |
| [Groups](#groups) | list groups and members, create, delete groups | OCS |
| [User Status](#user-status) | get, set, clear status | OCS |
| [Notifications](#notifications) | list, dismiss one, dismiss all | OCS |
| [Activity](#activity) | activity feed with filters, search by file, time and user, daily counts | OCS |
| [Talk](#talk) | conversations, messages, threads, participants, edits, reactions, read state, shared items, pins, reminders, personal settings and tags, participant and conversation management | OCS |
| [Talk Polls](#talk-polls) | get, create, vote, close polls | OCS |
| [Announcements](#announcements) | list, create, delete announcements | OCS |
| [Calendar](#calendar) | list calendars, CRUD events | CalDAV |
| [Contacts](#contacts) | list address books, CRUD contacts | CardDAV |
| [Tasks](#tasks) | list lists, CRUD tasks, complete | CalDAV |
| [Mail](#mail) | accounts, mailboxes, messages, send, move, flags, tags | OCS + REST |
| [Collectives](#collectives) | list, pages, create, edit, move and copy, search, tags, attachments, public links, trash, restore | OCS |
| [Forms](#forms) | CRUD forms, questions, options, shares, submissions + export | OCS |
| [Circles (Teams)](#circles-teams) | list, CRUD, members (add/remove/promote), join/leave, search | OCS |
| [Cospend](#cospend) | shared expense tracking — projects, members, bills | OCS |
| [Unified Search](#unified-search) | list providers, search across apps | OCS |
| [App Management](#app-management) | list, info, enable, disable apps | OCS |
| [Flow](#flow) | list, create, update, delete automation rules; list what they can be built from | OCS |
## Security: Permission Model
Every tool has a required permission level. You control what the AI is allowed to do:
| Level | What it can do | Environment variable |
|-------|---------------|---------------------|
| `read` (default) | List files, read files, get users, view notifications | `NEXTCLOUD_MCP_PERMISSIONS=read` |
| `write` | Everything in `read` + upload files, send messages, create events | `NEXTCLOUD_MCP_PERMISSIONS=write` |
| `destructive` | Everything in `write` + delete files, remove shares, empty trash | `NEXTCLOUD_MCP_PERMISSIONS=destructive` |
If a tool is called without sufficient permission, it returns a clear error explaining what permission is needed — no silent failures, no accidental deletions.
## Installation
```bash
pip install nc-mcp-server
```
Or with `pipx` / `uvx` for isolated installation:
```bash
pipx install nc-mcp-server
# or
uvx nc-mcp-server
```
Or from source:
```bash
git clone https://github.com/cloud-py-api/nc_mcp_server.git
cd nc_mcp_server
pip install -e .
```
## Configuration
Set these environment variables:
```bash
# Required
export NEXTCLOUD_URL=https://your-nextcloud.example.com
export NEXTCLOUD_USER=your-username
export NEXTCLOUD_PASSWORD=your-app-password # Use an app password, not your main password!
# Optional
export NEXTCLOUD_MCP_PERMISSIONS=read # read (default), write, or destructive
export NEXTCLOUD_MCP_RETRY_MAX=3 # max retries on 429/503 (default: 3, 0 to disable)
export NEXTCLOUD_MCP_UPLOAD_ROOT= # unset (default). If set to an absolute directory,
# enables upload_file_from_path, restricted to files
# inside that directory (symlinks resolved).
```
### Getting an App Password
1. Log into your Nextcloud instance
2. Go to **Settings** > **Security**
3. Under "Devices & sessions", create a new app password
4. Use this password for `NEXTCLOUD_PASSWORD`
Since Nextcloud 34.0.1 an app-password session never counts as password-confirmed, so with an app password the
admin tools Nextcloud guards with password confirmation (`create_user`, `update_user`, `set_user_enabled`,
`delete_user`, `create_group`, `delete_group`, `enable_app`, `disable_app`) fail with "Password confirmation is
required". To use them, allow the MCP server's IP address in `config.php` (Nextcloud 34.0.3 and newer), e.g.
`'allowed_no_password_confirmation_ranges' => ['192.0.2.10/32']`. With the account's login password they work
without that: when Nextcloud asks for a confirmation, the server repeats the request as a fresh login. Accounts
with two-factor authentication cannot log in with their password here, so they need an app password and the
exemption.
## Usage
### With Claude Desktop
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"nextcloud": {
"command": "nc-mcp-server",
"env": {
"NEXTCLOUD_URL": "https://your-nextcloud.example.com",
"NEXTCLOUD_USER": "your-username",
"NEXTCLOUD_PASSWORD": "your-app-password",
"NEXTCLOUD_MCP_PERMISSIONS": "read"
}
}
}
}
```
### With Claude Code
```bash
claude mcp add nextcloud \
-e NEXTCLOUD_URL=https://your-nextcloud.example.com \
-e NEXTCLOUD_USER=your-username \
-e NEXTCLOUD_PASSWORD=your-app-password \
-e NEXTCLOUD_MCP_PERMISSIONS=read \
-- nc-mcp-server
```
### As HTTP Server (for containers/remote)
```bash
nc-mcp-server --transport http
# Listens on http://0.0.0.0:8100 by default
```
### Stdio Mode (default)
```bash
nc-mcp-server
# Communicates via stdin/stdout — used by MCP clients like Claude Desktop
```
## Available Tools
### Files
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_directory` | read | List files and folders in a directory |
| `get_file` | read | Read a file's content (returns images as MCP ImageContent) |
| `search_files` | read | Search files by name, MIME type, or path pattern |
| `upload_file` | write | Upload or overwrite a text file |
| `upload_file_binary` | write | Upload or overwrite a binary file (images, PDFs, archives) from base64-encoded content |
| `upload_file_from_path` | write | Stream a local file from the server's filesystem — only registered when `NEXTCLOUD_MCP_UPLOAD_ROOT` is set |
| `create_directory` | write | Create a new directory |
| `copy_file` | write | Copy a file or directory |
| `move_file` | destructive | Move or rename a file |
| `delete_file` | destructive | Delete a file or directory (moves to trash) |
`upload_file_from_path` is off by default because it gives the AI read access
to the local filesystem. To enable it, set `NEXTCLOUD_MCP_UPLOAD_ROOT` to an
absolute directory — only files resolving inside that directory (after symlink
resolution) can be uploaded. This is the right choice when you need to upload
multi-GB files that would blow past the size limit of an inline `base64` tool
call; the body is streamed in chunks rather than loaded into memory.
### File Sharing
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_shares` | read | List shares for a file/folder, all your shares, or the shares others gave you (federated included) |
| `get_share` | read | Get details of a specific share |
| `list_pending_shares` | read | List shares offered to you that wait to be accepted, from this server and federated |
| `create_share` | write | Share a file/folder (user, group, public link, email) |
| `update_share` | write | Update share permissions, expiration, password, etc. |
| `accept_share` | write | Accept a pending share |
| `delete_share` | destructive | Remove a share, or leave one you received |
| `decline_share` | destructive | Decline a pending share |
### Trashbin
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_trash` | read | List deleted files in the trash bin |
| `restore_trash_item` | write | Restore a file from trash to its original location |
| `delete_trash_item` | destructive | Permanently delete a single item from trash |
| `empty_trash` | destructive | Permanently delete all items in trash |
### File Versions
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_versions` | read | List version history of a file |
| `restore_version` | write | Restore a previous version of a file |
### File Comments
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_comments` | read | List comments on a file |
| `add_comment` | write | Add a comment to a file |
| `edit_comment` | write | Edit an existing comment |
| `delete_comment` | destructive | Delete a comment |
### File Reminders
| Tool | Permission | Description |
|------|-----------|-------------|
| `get_file_reminder` | read | Get the reminder set on a file (null if none) |
| `set_file_reminder` | write | Set or replace a reminder due date (ISO 8601, must be in the future) |
| `remove_file_reminder` | destructive | Remove the reminder from a file |
### System Tags
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_tags` | read | List all available tags |
| `get_file_tags` | read | Get tags assigned to a file |
| `create_tag` | write | Create a new tag |
| `assign_tag` | write | Assign a tag to a file |
| `unassign_tag` | destructive | Remove a tag from a file |
| `delete_tag` | destructive | Delete a tag |
### Users
| Tool | Permission | Description |
|------|-----------|-------------|
| `get_current_user` | read | Get current authenticated user info |
| `list_users` | read | List or search users |
| `get_user` | read | Get specific user details |
| `create_user` | write | Create a new user (admin only) |
| `update_user` | write | Change display name, email, password, quota, language, manager, groups and sub-admin groups in one call (Nextcloud 34+) |
| `set_user_enabled` | write | Enable or disable a user account (admin or sub-admin); disabling needs `destructive` |
| `delete_user` | destructive | Delete a user (admin only) |
`update_user` has Nextcloud validate every field before applying any of them. Users can change their own
display name, email, language and password with it; Nextcloud 34 and 35 only accept the underlying call from
admins and sub-admins, so for a regular user's own account the tool sets those fields one at a time instead,
without that all-or-nothing check. Passing `password`, `groups` or `subadmin_groups` needs the `destructive`
level, as does disabling an account with `set_user_enabled`.
### Groups
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_groups` | read | List or search groups with member counts (admin) |
| `list_group_members` | read | List the users in a group (admins, the group's sub-admins and members) |
| `create_group` | write | Create a group (admin only) |
| `delete_group` | destructive | Delete a group (admin only) |
### User Status
| Tool | Permission | Description |
|------|-----------|-------------|
| `get_user_status` | read | Get a user's status (online, away, dnd, etc.) |
| `set_user_status` | write | Set your status and custom message |
| `clear_user_status` | destructive | Clear your status |
### Notifications
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_notifications` | read | List all notifications |
| `dismiss_notification` | write | Dismiss a single notification |
| `dismiss_all_notifications` | write | Dismiss all notifications |
### Activity
| Tool | Permission | Description |
|------|-----------|-------------|
| `get_activity` | read | View recent activity with filtering, sorting, and pagination; search by file path, time range and user (Nextcloud 35) |
| `list_activity_filters` | read | List the activity filters this server offers |
| `get_activity_counts` | read | Count activities per day over the last days (Nextcloud 35) |
### Talk
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_conversations` | read | List all Talk conversations |
| `get_conversation` | read | Get conversation details |
| `get_messages` | read | Get messages from a conversation, or from one thread |
| `get_participants` | read | List participants in a conversation |
| `list_threads` | read | List the most recently active threads in a conversation |
| `get_thread` | read | Get a thread's title, reply count and first/last message |
| `list_subscribed_threads` | read | List the threads you follow across all conversations |
| `get_message_context` | read | Get the messages before and after one message |
| `get_reactions` | read | List who reacted to a message, and with what |
| `list_shared_items` | read | List files, media, polls, locations and more shared in a conversation |
| `search_mentions` | read | Find who can be mentioned, with the text to put in a message |
| `list_message_reminders` | read | List your upcoming message reminders |
| `list_conversation_tags` | read | List your personal conversation tags |
| `list_conversation_presets` | read | List the presets create_conversation can start from |
| `send_message` | write | Send a message; can start a thread or post into one |
| `create_conversation` | write | Create a one-to-one, group or public conversation, optionally from a preset |
| `update_conversation` | write | Rename, describe, lock (read-only) or open (public) a conversation (moderators); making it private needs `destructive`; owners can preserve it (Talk 25+) |
| `add_participant` | write | Add a user, group, team, email guest or federated user (moderators) |
| `set_participant_role` | write | Make a participant owner (Talk 25+), moderator or user |
| `rename_thread` | write | Rename a thread |
| `set_thread_notification_level` | write | Set your notification level for a thread |
| `edit_message` | write | Edit a message (own ones, or any as a moderator of a group conversation; within 24 hours) |
| `add_reaction` | write | React to a message with an emoji |
| `mark_conversation_read` | write | Mark a conversation read, fully or up to a message |
| `mark_conversation_unread` | write | Mark the last message unread again |
| `set_conversation_preferences` | write | Your own settings: favorite, archived, important, sensitive, message and call notifications |
| `pin_message` | write | Pin a message for everyone, optionally until a time (moderators) |
| `set_message_reminder` | write | Get a notification about a message later |
| `create_conversation_tag` | write | Create a personal conversation tag |
| `rename_conversation_tag` | write | Rename a conversation tag |
| `set_conversation_tags` | write | Set which of your tags a conversation has |
| `delete_message` | destructive | Delete a message |
| `leave_conversation` | destructive | Leave a conversation |
| `remove_participant` | destructive | Remove someone from a conversation (moderators) |
| `delete_conversation` | destructive | Delete a conversation for everyone (moderators; one-to-one ones can only be left) |
| `remove_reaction` | destructive | Take back your reaction to a message |
| `unpin_message` | destructive | Unpin a message for everyone, or hide it only for you |
| `remove_message_reminder` | destructive | Cancel a message reminder |
| `delete_conversation_tag` | destructive | Delete a conversation tag |
The thread tools need a Talk version that advertises the `threads` capability (Talk 22, which
ships with Nextcloud 32, and newer), so every Nextcloud release supported here has them.
A thread ID is the message ID of the thread's first message.
Messages read back with their mentions and shared objects filled in ("@Jane Doe", "report.pdf")
instead of the placeholders Talk stores (`{mention-user1}`, `{file}`).
### Talk Polls
| Tool | Permission | Description |
|------|-----------|-------------|
| `get_poll` | read | Get poll details and results |
| `create_poll` | write | Create a poll in a conversation |
| `vote_poll` | write | Vote on a poll |
| `close_poll` | write | Close a poll |
### Announcements
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_announcements` | read | List announcements |
| `create_announcement` | write | Create an announcement |
| `delete_announcement` | destructive | Delete an announcement |
### Calendar
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_calendars` | read | List user's calendars |
| `get_events` | read | Get events from a calendar (with date filtering) |
| `get_event` | read | Get a single event by UID |
| `create_event` | write | Create a calendar event |
| `update_event` | write | Update an event (partial updates supported) |
| `delete_event` | destructive | Delete a calendar event |
### Contacts
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_addressbooks` | read | List user's address books |
| `get_contacts` | read | Get contacts with pagination |
| `get_contact` | read | Get a single contact by UID |
| `create_contact` | write | Create a contact (multi-value email/phone supported) |
| `update_contact` | write | Update a contact (ETag concurrency control) |
| `delete_contact` | destructive | Delete a contact |
### Tasks
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_task_lists` | read | List task lists (CalDAV VTODO collections) |
| `get_tasks` | read | List tasks in a list (with status/completed filters) |
| `get_task` | read | Get a single task by UID |
| `create_task` | write | Create a task (due date, priority, categories, etc.) |
| `update_task` | write | Update a task (partial updates supported) |
| `complete_task` | write | Mark a task as completed |
| `delete_task` | destructive | Delete a task |
### Mail
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_mail_accounts` | read | List mail accounts |
| `list_mailboxes` | read | List mailboxes (folders) for an account |
| `list_mail_messages` | read | List messages in a mailbox |
| `get_mail_message` | read | Get full message content |
| `send_mail` | write | Send an email |
| `move_mail_message` | write | Move a message to another mailbox of the same account (its ID changes) |
| `set_mail_message_flags` | write | Mark as read/unread, starred, answered |
| `create_mail_tag` | write | Create a tag, or get the existing one with the same label |
| `add_mail_message_tag` | write | Tag a message |
| `remove_mail_message_tag` | write | Remove a tag from a message |
### Collectives
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_collectives` | read | List all collectives |
| `get_collective_pages` | read | List pages in a collective |
| `get_collective_page` | read | Get a page's content |
| `search_collective_pages` | read | Search the text of a collective's pages |
| `list_recent_collective_pages` | read | List the most recently changed pages across collectives |
| `list_collective_tags` | read | List a collective's page tags |
| `list_collective_page_attachments` | read | List the files attached to a page |
| `list_collective_shares` | read | List your public links to a collective and its pages |
| `create_collective` | write | Create a new collective |
| `create_collective_page` | write | Create a page in a collective, optionally with its text |
| `update_collective_page` | write | Change a page's text, title or emoji |
| `move_collective_page` | write | Move or copy a page under another page, also into another collective |
| `create_collective_tag` | write | Create a page tag |
| `update_collective_tag` | write | Rename a tag or change its color |
| `set_collective_page_tags` | write | Set which tags a page has |
| `share_collective` | write | Create a public link to a collective or one page, optionally editable and with a password |
| `update_collective_share` | write | Change a public link's editing and password |
| `trash_collective` | destructive | Move a collective to trash |
| `delete_collective` | destructive | Permanently delete a trashed collective, optionally with its team (and, when told, the team's folder) |
| `trash_collective_page` | destructive | Move a page to trash |
| `delete_collective_page` | destructive | Permanently delete a trashed page |
| `delete_collective_tag` | destructive | Delete a tag, taking it off its pages |
| `delete_collective_share` | destructive | Remove a public link |
| `restore_collective` | write | Restore a collective from trash |
| `restore_collective_page` | write | Restore a page from trash |
### Forms
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_forms` | read | List forms (filter by ownership: "owned" or "shared"; omit to merge both) |
| `get_form` | read | Get a form with questions, options, shares |
| `list_questions` | read | List questions on a form |
| `get_question` | read | Get a single question |
| `list_submissions` | read | List submissions (owner only), with pagination and text filter |
| `get_submission` | read | Get a single submission with answers |
| `create_form` | write | Create an empty form or clone from an existing form |
| `update_form` | write | Update form properties (title, access, state, maxSubmissions, etc.) |
| `create_question` | write | Add a question (short, long, multiple, dropdown, date, file, grid, …) |
| `update_question` | write | Update question properties |
| `reorder_questions` | write | Reorder all questions on a form |
| `create_options` | write | Add answer options to a choice question |
| `update_option` | write | Update option text |
| `reorder_options` | write | Reorder options within a question |
| `create_form_share` | write | Share a form with user, group, circle, or link |
| `update_form_share` | write | Update share permissions |
| `submit_form` | write | Submit answers to a form |
| `update_submission` | write | Edit an existing submission (requires allowEditSubmissions) |
| `export_submissions` | write | Export submissions as a spreadsheet to a Nextcloud folder |
| `delete_form` | destructive | Delete a form and all its content |
| `delete_question` | destructive | Delete a question |
| `delete_option` | destructive | Delete an option |
| `delete_form_share` | destructive | Revoke a share |
| `delete_submission` | destructive | Delete one submission |
| `delete_all_submissions` | destructive | Delete every submission on a form |
### Circles (Teams)
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_circles` | read | List circles the current user can see |
| `get_circle` | read | Get a single circle including the current user's membership |
| `list_circle_members` | read | List members of a circle |
| `search_circles` | read | Search circles and candidate members (users/groups/mail) by term |
| `create_circle` | write | Create a circle; caller becomes owner. Optionally with a team folder (Nextcloud 35 + Team folders app) |
| `update_circle_name` | write | Rename a circle |
| `update_circle_description` | write | Update description |
| `update_circle_config` | write | Update config bitmask (VISIBLE, OPEN, INVITE, HIDDEN, etc.) |
| `add_circle_member` | write | Add a user, group, email, or nested circle as a member |
| `update_circle_member_level` | write | Promote/demote a member (member/moderator/admin/owner) |
| `join_circle` | write | Join an open circle |
| `leave_circle` | destructive | Leave a circle. The owner's leave passes ownership to any other member (pending invitations count), or destroys the circle when no one else is left; refuses to lose a team folder unless told |
| `delete_circle` | destructive | Delete a circle; refuses to delete its team folder and files unless told |
| `remove_circle_member` | destructive | Kick a member |
### Cospend
Shared expense tracking ("who paid for what"). Requires the [Cospend](https://apps.nextcloud.com/apps/cospend) app to be installed and enabled. All routes are OCS at `/ocs/v2.php/apps/cospend/api/v1/`.
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_cospend_projects` | read | List projects the user can access |
| `get_cospend_project` | read | Get full project info (members, balance, shares, settings) |
| `get_cospend_project_statistics` | read | Per-member spending stats (paid/spent/balance) with filters |
| `get_cospend_project_settlement` | read | Suggested reimbursement transactions to settle a project |
| `list_cospend_members` | read | List members of a project |
| `list_cospend_bills` | read | List bills with filters (payer, category, search, pagination) |
| `get_cospend_bill` | read | Get a single bill |
| `create_cospend_project` | write | Create a project (caller becomes ADMIN) |
| `update_cospend_project` | write | Update project name, currency, sort, archive, etc. |
| `create_cospend_member` | write | Add a member (free-form name or linked to a Nextcloud user) |
| `update_cospend_member` | write | Update name/weight/color/activated/userid |
| `create_cospend_bill` | write | Create a bill (defaults date to today if neither date nor timestamp set) |
| `update_cospend_bill` | write | Update any bill field |
| `delete_cospend_project` | destructive | Delete a project and all its data |
| `delete_cospend_member` | destructive | Delete (or soft-disable if member has bills) |
| `delete_cospend_bill` | destructive | Delete a bill (default: trash; pass `move_to_trash=False` to purge) |
### Unified Search
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_search_providers` | read | List available search providers (files, mail, talk, etc.) |
| `unified_search` | read | Search across one or more providers with pagination |
### App Management
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_apps` | read | List installed apps |
| `get_app_info` | read | Get detailed app information |
| `enable_app` | write | Enable an app (admin only) |
| `disable_app` | destructive | Disable an app (admin only) |
### Flow
| Tool | Permission | Description |
|------|-----------|-------------|
| `list_flows` | read | List Flow rules of the user or (admin) global scope |
| `get_flow_options` | read | List the operations, entities, events and checks a rule can use, with operators and value formats for the built-in checks |
| `create_flow` | write | Create a rule; global rules need `destructive` |
| `update_flow` | write | Change a rule's name, checks, settings or events; global rules need `destructive` |
| `delete_flow` | destructive | Delete a rule |
The available operations depend on the installed apps (Talk adds "Write to conversation", for example), and
Nextcloud has no API that lists them, so `get_flow_options` reads them from the Flow settings page. Global
rules act on every user's files, which is why they need the `destructive` level. Rules that would run a command
or command-line arguments of the agent's choosing on the server (the workflow_script app's operation, or
workflow_ocr with custom ocrmypdf arguments) are refused at any level.
## Development
```bash
# Clone and install
git clone https://github.com/cloud-py-api/nc_mcp_server.git
cd nc_mcp_server
python3 -m venv venv && source venv/bin/activate
pip install -e ".[dev]"
# Run tests
pytest # Unit tests
pytest tests/integration/ -v # Integration tests (needs running Nextcloud)
# Lint & type check
ruff check . && ruff format --check .
pyright
```
### Integration Tests
Integration tests run against a real Nextcloud instance. Set the environment variables and run:
```bash
export NEXTCLOUD_URL=http://localhost:8080
export NEXTCLOUD_USER=admin
export NEXTCLOUD_PASSWORD=admin
pytest tests/integration/ -v
```
CI runs the integration tests against Nextcloud 34 and 35 using the official Docker images.
## About This Project
This project is an experiment in AI-autonomous open-source development. The entire codebase — including this README — is written and maintained by Claude (Anthropic's AI assistant). Human oversight is limited to:
- High-level design decisions
- Code review of pull requests
- Resolving architectural questions
The goal is to explore how far autonomous AI development can go in building production-quality, well-tested software.
TDQS
Scored across 222 tools
Across 222 tools spanning many Nextcloud apps, most names and descriptions are carefully differentiated (e.g. get_event vs get_events, list_shares vs list_pending_shares vs list_shared_items, create_tag vs create_collective_tag vs create_mail_tag). A few generic names such as create_tag, assign_tag, and list_tags could be confused with app-specific tag tools, and the sheer breadth makes selection harder, but the detailed descriptions largely disambiguate.
Nearly all tools follow a snake_case verb_noun pattern: list_*, get_*, create_*, update_*, delete_*, set_*, remove_*, add_*, etc. App-specific prefixes (collective_, cospend_, conversation_, mail_) are applied predictably. No mixed camelCase or wildly inconsistent verb styles appear.
222 tools is an extreme mismatch for an MCP server set, far beyond the 50+ threshold for 'extreme mismatch.' Even if each app area is covered, the total surface is too heavy for an agent to navigate efficiently without specialized routing.
The surface provides deep CRUD/lifecycle coverage across files, shares, tags, comments, versions, trash, calendars, tasks, contacts, Talk, forms, collectives, circles, Cospend, mail, announcements, users/groups, flows, activity, and search. Only minor optional operations (e.g. update_announcement, hard delete_mail_message) are absent, which are not dead ends for core workflows.