Step 10: Code review, testing & documentation
Code Review (REVIEW.md): - Fixed parseSearchResponse skipping first result (critical bug) - Fixed trashbin/versions/chunked paths missing /remote.php/dav prefix - Fixed trash_restore to use original file location instead of /restore endpoint - Fixed createTask/updateTask missing iCal text escaping - Added 409 handling for createFolder (parent missing) - Extracted duplicate decodeXmlText to utils.ts - Extracted duplicate generateUID to utils.ts (shared with calendar/tasks) - Removed 5 dead code functions (parseVEVENT, extractVEventBlocks, unfoldICalLines, getCalDAVXmlHeaders, local decodeXmlText) - Cleaned unused imports across all tool files Testing (RESULTS.md): - 35 tests passed, 1 skipped (trash_empty), 1 server limitation (bulk_upload) - Tested all 21+ file tools, edge cases (spaces, unicode, overwrite, empty folders) - Verified chunked upload end-to-end Documentation (README.md): - Complete tool reference (21 file + 10 other tools) - Quick start, CLI usage, size limits, troubleshooting - Architecture overview
This commit is contained in:
@@ -1,285 +1,273 @@
|
||||
# Nextcloud MCP Server
|
||||
# nextcloud-mcp
|
||||
|
||||
A Model Context Protocol (MCP) server that integrates with Nextcloud to provide access to:
|
||||
- **Tasks** (via CalDAV)
|
||||
- **Calendar Events** (via CalDAV)
|
||||
- **Notes** (via Notes API)
|
||||
- **Emails** (via Mail API)
|
||||
MCP (Model Context Protocol) server for Nextcloud — browse, read, write, and manage files, calendars, tasks, notes, and email via WebDAV/CalDAV/REST APIs.
|
||||
|
||||
## Features
|
||||
|
||||
### Tasks
|
||||
- ✅ Get tasks (filter by status: all/open/completed)
|
||||
- ✅ Create new tasks with due dates and priorities
|
||||
- ✅ Update tasks (mark complete, change summary, etc.)
|
||||
- **21 file management tools** — browse, search, read, upload, move, copy, delete, trashbin, favorites, versions
|
||||
- **Calendar tools** — list calendars, get/create events (CalDAV)
|
||||
- **Task tools** — get/create/update tasks (CalDAV VTODO)
|
||||
- **Note tools** — get/create notes (Nextcloud Notes API)
|
||||
- **Email tool** — get inbox emails (Nextcloud Mail API)
|
||||
- **Smart size routing** — small files inline (≤10MB), large files via direct URL or chunked upload
|
||||
- **CLI wrapper** — `ncmcp` command for quick testing and scripting
|
||||
|
||||
### Calendar
|
||||
- ✅ Get calendar events with date range filtering
|
||||
- ✅ Query one, many, or all VEVENT calendars automatically
|
||||
- ✅ Discover available calendars and supported components
|
||||
- ✅ Create new calendar events with details and location
|
||||
## Quick Start
|
||||
|
||||
### Notes
|
||||
- ✅ Get all notes
|
||||
- ✅ Create new notes with markdown support
|
||||
- ✅ Get specific note content by ID
|
||||
### 1. Configure
|
||||
|
||||
### Email
|
||||
- ✅ Get emails from inbox
|
||||
- 📧 Requires Nextcloud Mail app configured
|
||||
Create `.env` in the project root:
|
||||
|
||||
## Prerequisites
|
||||
```env
|
||||
NEXTCLOUD_URL=https://your-nextcloud.example.com
|
||||
NEXTCLOUD_USERNAME=your_username
|
||||
NEXTCLOUD_PASSWORD=your_app_password
|
||||
```
|
||||
|
||||
1. **Nextcloud Instance** (any recent version with CalDAV support)
|
||||
2. **Required Nextcloud Apps**:
|
||||
- Tasks (for task management)
|
||||
- Calendar (for events)
|
||||
- Notes (for note-taking)
|
||||
- Mail (optional, for email access)
|
||||
> Use an **app password** (Settings → Security → App passwords) instead of your main password.
|
||||
|
||||
3. **App Password**: Generate in Nextcloud Settings > Security > Devices & sessions
|
||||
|
||||
## Installation
|
||||
### 2. Build
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
1. Copy the environment template:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Edit `.env` with your Nextcloud credentials:
|
||||
```env
|
||||
NEXTCLOUD_URL=https://your-nextcloud.com
|
||||
NEXTCLOUD_USERNAME=your-username
|
||||
NEXTCLOUD_PASSWORD=your-app-password
|
||||
```
|
||||
|
||||
⚠️ **Important**: Always use an app password, never your main Nextcloud password!
|
||||
|
||||
## Usage
|
||||
|
||||
### Testing Locally
|
||||
### 3. Test Connection
|
||||
|
||||
```bash
|
||||
# Development mode (auto-reload)
|
||||
npm run dev
|
||||
|
||||
# Production mode
|
||||
npm run start
|
||||
node ncmcp.mjs list_files path=/
|
||||
node ncmcp.mjs get_quota
|
||||
```
|
||||
|
||||
### Using with Claude Desktop
|
||||
### 4. Use with MCP (mcporter)
|
||||
|
||||
Add this to your Claude Desktop configuration file:
|
||||
|
||||
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
||||
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
||||
Add to your `mcporter.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"nextcloud": {
|
||||
"command": "node",
|
||||
"args": ["/path/to/nextcloud-mcp/build/index.js"],
|
||||
"env": {
|
||||
"NEXTCLOUD_URL": "https://your-nextcloud.com",
|
||||
"NEXTCLOUD_USERNAME": "your-username",
|
||||
"NEXTCLOUD_PASSWORD": "your-app-password"
|
||||
}
|
||||
"nextcloud": {
|
||||
"command": "node",
|
||||
"args": ["path/to/nextcloud-mcp/build/index.js"],
|
||||
"env": {
|
||||
"NEXTCLOUD_URL": "https://your-nextcloud.example.com",
|
||||
"NEXTCLOUD_USERNAME": "your_username",
|
||||
"NEXTCLOUD_PASSWORD": "your_app_password"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or use the development version with tsx:
|
||||
Then use: `mcporter call nextcloud.list_files --args '{"path":"/"}'`
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"nextcloud": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "tsx", "/path/to/nextcloud-mcp/src/index.ts"],
|
||||
"env": {
|
||||
"NEXTCLOUD_URL": "https://your-nextcloud.com",
|
||||
"NEXTCLOUD_USERNAME": "your-username",
|
||||
"NEXTCLOUD_PASSWORD": "your-app-password"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
## CLI Usage (ncmcp)
|
||||
|
||||
```bash
|
||||
ncmcp <tool> [key=value ...] [positionalPath] [@file] [--curl]
|
||||
```
|
||||
|
||||
Restart Claude Desktop to load the MCP server.
|
||||
**Examples:**
|
||||
|
||||
## Available Tools
|
||||
```bash
|
||||
# List files
|
||||
ncmcp list_files path=/Documents
|
||||
|
||||
Once connected, Claude can use these tools:
|
||||
# Read a file
|
||||
ncmcp read_file path=/Documents/notes.txt
|
||||
|
||||
### Tasks
|
||||
- `get_tasks` - Retrieve tasks (filter by status, limit results)
|
||||
- `create_task` - Create new task with summary, description, due date, priority
|
||||
- `update_task` - Update existing task (mark complete, change details)
|
||||
# Upload a file
|
||||
ncmcp upload_file path=/Documents/new.txt content="Hello World"
|
||||
ncmcp upload_file path=/Documents/photo.jpg @./local-photo.jpg
|
||||
|
||||
### Calendar
|
||||
- `list_calendars` - List available calendars (`name`, `href`, `components`)
|
||||
- `get_calendar_events` - Get events in date range across selected calendars
|
||||
- `create_calendar_event` - Create new event with details
|
||||
# Download (get URL)
|
||||
ncmcp download_file path=/Documents/video.mp4
|
||||
ncmcp download_file path=/Documents/video.mp4 --curl # prints curl command
|
||||
|
||||
`get_calendar_events` supports:
|
||||
- `startDate` / `endDate` (YYYY-MM-DD)
|
||||
- `calendar` (single calendar name or href)
|
||||
- `calendars` (array of names/hrefs)
|
||||
- `includeAllCalendars` (default `true`, queries all VEVENT calendars when no selectors are provided)
|
||||
- `limit`
|
||||
# Search
|
||||
ncmcp search_files query="report" mimeType="application/pdf" limit=10
|
||||
|
||||
### Notes
|
||||
- `get_notes` - List all notes
|
||||
- `create_note` - Create new note with markdown
|
||||
- `get_note_content` - Get full content of specific note
|
||||
# Pipe content
|
||||
echo "file contents" | ncmcp upload_file path=/test.txt
|
||||
```
|
||||
|
||||
### Email
|
||||
- `get_emails` - Retrieve recent emails from inbox
|
||||
## Tool Reference
|
||||
|
||||
## Example Prompts for Claude
|
||||
### 🔷 Browsing & Discovery
|
||||
|
||||
Once the MCP server is connected, you can ask Claude:
|
||||
| Tool | Description | Key Parameters |
|
||||
|------|-------------|----------------|
|
||||
| `list_files` | List directory contents | `path` (default: `/`), `depth` (`0`/`1`/`infinity`) |
|
||||
| `get_file_info` | Get detailed file/folder metadata | `path` (required) |
|
||||
| `search_files` | Search by name, mime, size, date | `query`, `mimeType`, `minSize`, `maxSize`, `sortBy`, `limit` |
|
||||
| `list_favorites` | List favorited files | `path` (scope) |
|
||||
| `get_quota` | Get storage usage | — |
|
||||
|
||||
### 🔷 Read & Download
|
||||
|
||||
| Tool | Description | Key Parameters |
|
||||
|------|-------------|----------------|
|
||||
| `read_file` | Read file content (text or base64) | `path`, `encoding` (`utf8`/`base64`), `maxSize` (default 10MB) |
|
||||
| `download_file` | Get direct download URL | `path`, `metadata` (default: true) |
|
||||
| `download_folder` | Download folder as ZIP/TAR | `path`, `format` (`zip`/`tar`), `files` (subset), `maxSize` (default 50MB) |
|
||||
|
||||
### 🔷 Write & Upload
|
||||
|
||||
| Tool | Description | Key Parameters |
|
||||
|------|-------------|----------------|
|
||||
| `upload_file` | Upload file via PUT | `path`, `content`, `encoding`, `contentType`, `mtime` |
|
||||
| `create_folder` | Create a new folder | `path` |
|
||||
| `bulk_upload` | Upload multiple files at once | `files[]` (path, content, encoding, contentType) |
|
||||
|
||||
### 🔷 Chunked Upload (Large Files)
|
||||
|
||||
For files too large for a single `upload_file` call (>10MB content in parameter).
|
||||
|
||||
| Tool | Description | Key Parameters |
|
||||
|------|-------------|----------------|
|
||||
| `chunked_upload_start` | Start upload session | `path`, `totalSize`, `chunkSize` (default 10MB) |
|
||||
| `chunked_upload_chunk` | Upload one chunk (base64) | `uploadId`, `chunkIndex` (1-based), `content` |
|
||||
| `chunked_upload_finish` | Assemble final file | `uploadId`, `mtime` |
|
||||
|
||||
**Flow:** Start → upload chunks (1..N) → finish. All steps must be in the same session (in-memory state).
|
||||
|
||||
### 🔷 Move, Copy, Delete
|
||||
|
||||
| Tool | Description | Key Parameters |
|
||||
|------|-------------|----------------|
|
||||
| `move_file` | Move/rename file or folder | `source`, `destination`, `overwrite` |
|
||||
| `copy_file` | Copy file or folder | `source`, `destination`, `overwrite` |
|
||||
| `delete_file` | Delete (moves to trashbin) | `path` |
|
||||
|
||||
### 🔷 Trashbin
|
||||
|
||||
| Tool | Description | Key Parameters |
|
||||
|------|-------------|----------------|
|
||||
| `trash_list` | List deleted items | — |
|
||||
| `trash_restore` | Restore from trash | `trashPath` (from `trash_list`) |
|
||||
| `trash_delete` | Permanently delete | `trashPath` |
|
||||
| `trash_empty` | Empty entire trashbin | — (⚠️ destructive) |
|
||||
|
||||
### 🔷 Favorites & Versions
|
||||
|
||||
| Tool | Description | Key Parameters |
|
||||
|------|-------------|----------------|
|
||||
| `set_favorite` | Toggle favorite status | `path`, `favorite` (boolean) |
|
||||
| `get_file_versions` | List file versions | `fileId` (oc:fileid) |
|
||||
| `restore_file_version` | Restore a version | `fileId`, `versionName` |
|
||||
|
||||
### 📅 Calendar
|
||||
|
||||
| Tool | Description | Key Parameters |
|
||||
|------|-------------|----------------|
|
||||
| `list_calendars` | List CalDAV calendars | — |
|
||||
| `get_calendar_events` | Get events in date range | `startDate`, `endDate`, `calendar`, `limit` |
|
||||
| `create_calendar_event` | Create event | `summary`, `startDateTime`/`endDateTime`, `allDay`, `location`, `reminderMinutesBefore` |
|
||||
|
||||
### ✅ Tasks
|
||||
|
||||
| Tool | Description | Key Parameters |
|
||||
|------|-------------|----------------|
|
||||
| `get_tasks` | Get tasks (VTODO) | `status` (`all`/`open`/`completed`), `limit` |
|
||||
| `create_task` | Create task | `summary`, `description`, `due`, `priority` |
|
||||
| `update_task` | Update task | `taskId`, `summary`, `status`, `percentComplete` |
|
||||
|
||||
### 📝 Notes
|
||||
|
||||
| Tool | Description | Key Parameters |
|
||||
|------|-------------|----------------|
|
||||
| `get_notes` | List notes | `limit` |
|
||||
| `create_note` | Create note | `content`, `title`, `category` |
|
||||
| `get_note_content` | Get note by ID | `noteId` |
|
||||
|
||||
### 📧 Email
|
||||
|
||||
| Tool | Description | Key Parameters |
|
||||
|------|-------------|----------------|
|
||||
| `get_emails` | Get inbox emails | `accountId` (default 0), `limit` |
|
||||
|
||||
## Size Limits & Routing
|
||||
|
||||
MCP transports data as JSON over stdio. Large files need special handling:
|
||||
|
||||
```
|
||||
"Show me my open tasks for this week"
|
||||
"Create a task to review the Q4 report, due next Friday"
|
||||
"What meetings do I have tomorrow?"
|
||||
"Create a calendar event for team standup tomorrow at 10am"
|
||||
"Show me my recent notes"
|
||||
"Create a note about the meeting outcomes"
|
||||
"What are my latest emails?"
|
||||
UPLOAD
|
||||
├─ Content ≤ ~10MB in param → upload_file (PUT)
|
||||
├─ Content > 10MB → chunked_upload_start/chunk/finish
|
||||
└─ Many small files → bulk_upload (multipart/related)
|
||||
|
||||
READ
|
||||
├─ File ≤ maxSize (10MB) → read_file (inline content)
|
||||
└─ File > maxSize → download_file (direct URL)
|
||||
|
||||
DOWNLOAD FOLDER
|
||||
├─ Archive ≤ 50MB → download_folder (inline base64)
|
||||
└─ Archive > 50MB → download_folder (direct URL)
|
||||
```
|
||||
|
||||
The limits are MCP transport constraints, not Nextcloud limits. Nextcloud handles files of any size.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `NEXTCLOUD_URL` | Yes | Nextcloud base URL (e.g. `https://cloud.example.com`) |
|
||||
| `NEXTCLOUD_USERNAME` | Yes | Nextcloud username |
|
||||
| `NEXTCLOUD_PASSWORD` | Yes | App password (recommended) or account password |
|
||||
| `DEBUG_NEXTCLOUD_MCP` | No | Set to `1` for debug logging (calendar operations) |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Issues
|
||||
- Verify your Nextcloud URL (use HTTPS, no trailing slash)
|
||||
- Ensure app password is correct
|
||||
- Check that required apps (Tasks, Calendar, Notes) are installed
|
||||
### Connection errors
|
||||
- Verify `NEXTCLOUD_URL` is accessible (no trailing slash)
|
||||
- Check credentials — use an app password, not your main password
|
||||
- Test: `curl -u user:pass https://your-nextcloud.example.com/remote.php/dav/`
|
||||
|
||||
### CalDAV Issues
|
||||
- Use `list_calendars` to discover calendar names/hrefs from your server
|
||||
- Set `DEBUG_NEXTCLOUD_MCP=1` to log CalDAV requests and parsing details
|
||||
- Default task list name is still `tasks` for task operations
|
||||
### "File too large" errors
|
||||
- Use `download_file` to get a direct URL for large files
|
||||
- Use `chunked_upload_*` tools for large uploads
|
||||
- Adjust `maxSize` parameter if needed
|
||||
|
||||
### Debugging with curl
|
||||
List calendars with PROPFIND:
|
||||
```bash
|
||||
curl -u "$NEXTCLOUD_USERNAME:$NEXTCLOUD_PASSWORD" \
|
||||
-X PROPFIND \
|
||||
-H "Depth: 1" \
|
||||
-H "Accept: application/xml" \
|
||||
-H "Content-Type: application/xml; charset=utf-8" \
|
||||
--data '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
|
||||
<d:prop>
|
||||
<d:displayname />
|
||||
<d:resourcetype />
|
||||
<c:supported-calendar-component-set />
|
||||
</d:prop>
|
||||
</d:propfind>' \
|
||||
"$NEXTCLOUD_URL/remote.php/dav/calendars/$NEXTCLOUD_USERNAME/"
|
||||
```
|
||||
### Search returns empty
|
||||
- The server may not have Full Text Search enabled
|
||||
- The tool falls back to PROPFIND with depth infinity — works but slower on large directories
|
||||
- Narrow the search scope with `path` parameter
|
||||
|
||||
Query events with REPORT:
|
||||
```bash
|
||||
curl -u "$NEXTCLOUD_USERNAME:$NEXTCLOUD_PASSWORD" \
|
||||
-X REPORT \
|
||||
-H "Depth: 1" \
|
||||
-H "Accept: application/xml" \
|
||||
-H "Content-Type: application/xml; charset=utf-8" \
|
||||
--data '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<c:calendar-query xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:d="DAV:">
|
||||
<d:prop>
|
||||
<d:getetag />
|
||||
<c:calendar-data />
|
||||
</d:prop>
|
||||
<c:filter>
|
||||
<c:comp-filter name="VCALENDAR">
|
||||
<c:comp-filter name="VEVENT">
|
||||
<c:time-range start="20260201T000000Z" end="20260301T000000Z"/>
|
||||
</c:comp-filter>
|
||||
</c:comp-filter>
|
||||
</c:filter>
|
||||
</c:calendar-query>' \
|
||||
"$NEXTCLOUD_URL/remote.php/dav/calendars/$NEXTCLOUD_USERNAME/personal/"
|
||||
```
|
||||
### Calendar/Task errors
|
||||
- Ensure CalDAV is enabled on the server
|
||||
- Check that the calendar/task list exists (default: `personal` calendar, `tasks` list)
|
||||
- Set `DEBUG_NEXTCLOUD_MCP=1` for detailed CalDAV logging
|
||||
|
||||
### Email Issues
|
||||
- Ensure Nextcloud Mail app is installed and configured
|
||||
- Check that at least one email account is set up
|
||||
- Account ID defaults to 0 (first account)
|
||||
|
||||
### Debug Mode
|
||||
Check the MCP server logs in Claude Desktop:
|
||||
- **macOS**: `~/Library/Logs/Claude/mcp*.log`
|
||||
- **Windows**: `%APPDATA%\Claude\logs\mcp*.log`
|
||||
|
||||
## API Endpoints Used
|
||||
|
||||
- CalDAV: `/remote.php/dav/calendars/{username}/`
|
||||
- Notes: `/index.php/apps/notes/api/v1/notes`
|
||||
- Mail: `/index.php/apps/mail/api/`
|
||||
|
||||
## Security Notes
|
||||
|
||||
- 🔐 Always use app passwords, never your main password
|
||||
- 🔒 Store credentials securely (environment variables, not in code)
|
||||
- 🛡️ Use HTTPS for your Nextcloud instance
|
||||
- 🔑 Limit app password scopes if possible in Nextcloud
|
||||
|
||||
## Customization
|
||||
|
||||
### Calendar Selection
|
||||
Use `list_calendars` and pass `calendar` / `calendars` to `get_calendar_events` to target specific calendars by name or href.
|
||||
|
||||
### Changing Task List Names
|
||||
```typescript
|
||||
// Default tasks list
|
||||
const caldavPath = `/remote.php/dav/calendars/${this.config.username}/tasks/`;
|
||||
|
||||
// For a different task list
|
||||
const caldavPath = `/remote.php/dav/calendars/${this.config.username}/personal-tasks/`;
|
||||
```
|
||||
### Bulk upload fails
|
||||
- The `/remote.php/dav/bulk` endpoint may not be available on all Nextcloud versions
|
||||
- Fall back to individual `upload_file` calls
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
npm run build # Compile TypeScript
|
||||
npm run watch # Watch mode
|
||||
npm run dev # Run with tsx (no build needed)
|
||||
```
|
||||
|
||||
# Run in development mode with auto-reload
|
||||
npm run watch # In one terminal
|
||||
npm run dev # In another terminal
|
||||
## Architecture
|
||||
|
||||
# Build for production
|
||||
npm run build
|
||||
```
|
||||
src/
|
||||
├── index.ts — MCP server entry point
|
||||
├── types.ts — Shared TypeScript interfaces
|
||||
├── client.ts — Nextcloud HTTP client (axios, WebDAV methods)
|
||||
├── webdav.ts — WebDAV XML builders & parsers
|
||||
├── caldav.ts — CalDAV XML builders & iCal parsers
|
||||
├── utils.ts — Shared utilities (path, mime, formatting)
|
||||
└── tools/
|
||||
├── index.ts — Tool registry & routing
|
||||
├── files.ts — 24 file management tools
|
||||
├── calendar.ts — 3 calendar tools
|
||||
├── tasks.ts — 3 task tools
|
||||
├── notes.ts — 3 note tools
|
||||
└── email.ts — 1 email tool
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Contributing
|
||||
|
||||
Feel free to submit issues and pull requests for:
|
||||
- Additional Nextcloud integrations
|
||||
- Bug fixes
|
||||
- Documentation improvements
|
||||
- Feature enhancements
|
||||
|
||||
## Resources
|
||||
|
||||
- [MCP Documentation](https://docs.anthropic.com/mcp)
|
||||
- [Nextcloud API Documentation](https://docs.nextcloud.com/server/latest/developer_manual/)
|
||||
- [CalDAV Documentation](https://www.rfc-editor.org/rfc/rfc4791)
|
||||
|
||||
Reference in New Issue
Block a user