Skip to content

Commit 9cc5300

Browse files
authored
Merge pull request #96 from cbcoutinho/refactor/server
Refactor server tools and resources
2 parents 3cab343 + be466ab commit 9cc5300

31 files changed

+2265
-1382
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
repos:
22
- repo: https://github.com/commitizen-tools/commitizen
3-
rev: v4.8.2
3+
rev: v4.8.3
44
hooks:
55
- id: commitizen
66
- id: commitizen-branch
77
stages:
88
- pre-push
99
- repo: https://github.com/astral-sh/ruff-pre-commit
10-
rev: v0.12.2
10+
rev: v0.12.5
1111
hooks:
1212
- id: ruff-check
1313
- id: ruff-format

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ COPY . .
66

77
RUN uv sync --locked --no-dev
88

9-
CMD ["/app/.venv/bin/mcp", "run", "--transport", "sse", "/app/nextcloud_mcp_server/server.py:mcp"]
9+
CMD ["/app/.venv/bin/mcp", "run", "--transport", "sse", "/app/nextcloud_mcp_server/app.py:mcp"]

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ NEXTCLOUD_PASSWORD=your_nextcloud_app_password_or_login_password
243243
* `NEXTCLOUD_HOST`: The full URL of your Nextcloud instance.
244244
* `NEXTCLOUD_USERNAME`: Your Nextcloud username.
245245
* `NEXTCLOUD_PASSWORD`: **Important:** It is highly recommended to use a dedicated Nextcloud App Password for security. You can generate one in your Nextcloud Security settings. Alternatively, you can use your regular login password, but this is less secure.
246+
* `FASTMCP_HOST`: _Optional:_ By default FastMCP binds to localhost. Use this variable to set a different binding address (e.g. `0.0.0.0`)
246247
247248
## Running the Server
248249
@@ -255,10 +256,12 @@ Ensure your environment variables are loaded, then run the server using `mcp run
255256
export $(grep -v '^#' .env | xargs)
256257

257258
# Run the server
258-
mcp run --transport sse nextcloud_mcp_server.server:mcp
259+
mcp run --transport sse nextcloud_mcp_server.app:mcp
259260
```
260261

261-
The server will start, typically listening on `http://0.0.0.0:8000`.
262+
The server will start, typically listening on `http://localhost:8000`.
263+
264+
> NOTE: To make the server bind to a different address, use the FASTMCP_HOST environmental variable
262265
263266
### Using Docker
264267

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/bash
2+
3+
set -e # Exit on any error
4+
5+
echo "Installing and configuring Calendar app..."
6+
7+
# Enable calendar app
8+
php /var/www/html/occ app:enable calendar
9+
10+
# Wait for calendar app to be fully initialized
11+
echo "Waiting for calendar app to initialize..."
12+
sleep 5
13+
14+
# Ensure maintenance mode is off before calendar operations
15+
php /var/www/html/occ maintenance:mode --off
16+
17+
# Sync DAV system to ensure proper initialization
18+
echo "Syncing DAV system..."
19+
php /var/www/html/occ dav:sync-system-addressbook
20+
21+
# Repair calendar app to ensure proper setup
22+
echo "Repairing calendar app..."
23+
php /var/www/html/occ maintenance:repair --include-expensive
24+
25+
# Final wait to ensure CalDAV service is fully ready
26+
echo "Final CalDAV initialization wait..."
27+
sleep 5
28+
29+
echo "Calendar app installation complete!"

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ services:
5252
- NEXTCLOUD_HOST=http://app:80
5353
- NEXTCLOUD_USERNAME=admin
5454
- NEXTCLOUD_PASSWORD=admin
55+
- FASTMCP_HOST=0.0.0.0
5556

5657
volumes:
5758
nextcloud:

nextcloud_mcp_server/app.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import logging
2+
from collections.abc import AsyncIterator
3+
from contextlib import asynccontextmanager
4+
from dataclasses import dataclass
5+
6+
from mcp.server.fastmcp import Context, FastMCP
7+
8+
from nextcloud_mcp_server.client import NextcloudClient
9+
from nextcloud_mcp_server.config import setup_logging
10+
from nextcloud_mcp_server.server import (
11+
configure_calendar_tools,
12+
configure_notes_tools,
13+
configure_tables_tools,
14+
configure_webdav_tools,
15+
)
16+
17+
setup_logging()
18+
19+
20+
@dataclass
21+
class AppContext:
22+
client: NextcloudClient
23+
24+
25+
@asynccontextmanager
26+
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
27+
"""Manage application lifecycle with type-safe context"""
28+
# Initialize on startup
29+
logging.info("Creating Nextcloud client")
30+
client = NextcloudClient.from_env()
31+
logging.info("Client initialization wait complete.")
32+
try:
33+
yield AppContext(client=client)
34+
finally:
35+
# Cleanup on shutdown
36+
await client.close()
37+
38+
39+
# Create an MCP server
40+
mcp = FastMCP("Nextcloud MCP", lifespan=app_lifespan)
41+
42+
logger = logging.getLogger(__name__)
43+
44+
45+
@mcp.resource("nc://capabilities")
46+
async def nc_get_capabilities():
47+
"""Get the Nextcloud Host capabilities"""
48+
ctx: Context = (
49+
mcp.get_context()
50+
) # https://github.com/modelcontextprotocol/python-sdk/issues/244
51+
client: NextcloudClient = ctx.request_context.lifespan_context.client
52+
return await client.capabilities()
53+
54+
55+
configure_notes_tools(mcp)
56+
configure_tables_tools(mcp)
57+
configure_webdav_tools(mcp)
58+
configure_calendar_tools(mcp)
59+
60+
61+
def run():
62+
mcp.run()

nextcloud_mcp_server/client/__init__.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
1-
import os
2-
from httpx import (
3-
AsyncClient,
4-
Auth,
5-
BasicAuth,
6-
Request,
7-
Response,
8-
)
91
import logging
2+
import os
3+
4+
from httpx import AsyncClient, Auth, BasicAuth, Request, Response
105

6+
from ..controllers.notes_search import NotesSearchController
7+
from .calendar import CalendarClient
118
from .notes import NotesClient
12-
from .webdav import WebDAVClient
139
from .tables import TablesClient
14-
from .calendar import CalendarClient
15-
from ..controllers.notes_search import NotesSearchController
10+
from .webdav import WebDAVClient
1611

1712
logger = logging.getLogger(__name__)
1813

nextcloud_mcp_server/client/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
"""Base client for Nextcloud operations with shared authentication."""
22

3+
import logging
34
from abc import ABC
5+
46
from httpx import AsyncClient
5-
import logging
67

78
logger = logging.getLogger(__name__)
89

0 commit comments

Comments
 (0)