The CalDAV REPORT in `_search_events_by_date` unconditionally requested server-side `<C:expand>`. Per RFC 4791 §9.6.5 the server then normalizes every expanded DTSTART/DTEND to UTC `Z`, which destroyed two pieces of information on the read path: - RFC 5545 floating local times came back as fake-UTC (a `+00:00` suffix that did not match the stored value), so a 2:30 PM floating event was indistinguishable from a 14:30 UTC event in the MCP response. - TZID-bound events lost their IANA TZID context — a "10am America/New_York" event came back as `14:00:00+00:00`, making it impossible for callers to reconstruct DST-aware recurrence semantics. Replace `<C:expand>` with client-side recurrence expansion via the `recurring-ical-events` library (promoted from transitive to direct dep), so the wire response retains its original DTSTART format. Surface the TZID parameter as new `start_tz`/`end_tz` fields on `CalendarEventSummary`. Add an optional `timezone` (IANA name) parameter to `nc_calendar_create_event` and `nc_calendar_update_event` so callers can pin a TZID for naive input; the helper attaches `ZoneInfo(...)` and emits a paired `VTIMEZONE` component. Naive input without `timezone` continues to store as RFC 5545 floating local time (with a warning logged). Offset-aware input continues to store as UTC `Z`. Drive-by: switch the update path's DTSTART/DTEND assignment from raw `datetime` to `vDDDTypes(dt)` wrappers — the previous code produced invalid iCal like `DTSTART:2026-05-14 10:00:00+00:00` for any TZ-aware update. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
136 lines
4.7 KiB
TOML
136 lines
4.7 KiB
TOML
[project]
|
|
name = "nextcloud-mcp-server"
|
|
version = "0.85.0"
|
|
description = "Model Context Protocol (MCP) server for Nextcloud integration - enables AI assistants to interact with Nextcloud data"
|
|
authors = [
|
|
{name = "Chris Coutinho", email = "chris@coutinho.io"}
|
|
]
|
|
readme = "README.md"
|
|
license = {text = "AGPL-3.0-only"}
|
|
requires-python = ">=3.11"
|
|
keywords = ["nextcloud", "mcp", "model-context-protocol", "llm", "ai", "claude", "webdav", "caldav", "carddav"]
|
|
dependencies = [
|
|
"mcp[cli] (>=1.27,<1.28)",
|
|
"httpx (>=0.28.1,<0.29.0)",
|
|
"pillow (>=10.3.0,<12.0.0)", # Compatible with fastembed
|
|
"icalendar (>=7.1.0,<7.2.0)",
|
|
"pythonvcard4>=0.2.0",
|
|
"pydantic>=2.11.4",
|
|
"click>=8.1.8",
|
|
"caldav>=3.0.1,<4.0",
|
|
"recurring-ical-events>=3.8.0,<4.0", # Client-side recurrence expansion (preserves TZID/floating semantics)
|
|
"pyjwt[crypto]>=2.8.0",
|
|
"aiosqlite>=0.20.0", # Async SQLite for refresh token storage
|
|
"alembic>=1.14.0", # Database migrations
|
|
"authlib>=1.6.5",
|
|
"qdrant-client>=1.17.0",
|
|
"fastembed>=0.7.3", # BM25 sparse vector embeddings for hybrid search
|
|
"anthropic>=0.42.0", # For RAG evaluation with Anthropic LLMs
|
|
"boto3>=1.35.0", # For Amazon Bedrock provider (optional)
|
|
# Observability dependencies
|
|
"prometheus-client>=0.21.0", # Prometheus metrics
|
|
"opentelemetry-api>=1.28.2", # OpenTelemetry API
|
|
"opentelemetry-sdk>=1.28.2", # OpenTelemetry SDK
|
|
"starlette<1.0", # Pin until we address starlette 1.0 breaking changes (#648)
|
|
"opentelemetry-instrumentation-asgi>=0.49b2", # Auto-instrument ASGI/Starlette
|
|
"opentelemetry-instrumentation-httpx>=0.49b2", # Auto-instrument httpx client
|
|
"opentelemetry-instrumentation-logging>=0.49b2", # Logging integration
|
|
"opentelemetry-exporter-otlp-proto-grpc>=1.28.2", # OTLP gRPC exporter
|
|
"python-json-logger>=3.2.0", # Structured JSON logging
|
|
"jinja2>=3.1.6",
|
|
"langchain-text-splitters>=1.0.0",
|
|
"markdownify>=0.14.1", # HTML to Markdown conversion for News items
|
|
"pymupdf>=1.26.6",
|
|
"pymupdf4llm>=0.2.2",
|
|
"openai>=2.8.1",
|
|
"dynaconf>=3.2.13,<4.0",
|
|
"mistralai>=2.4.5",
|
|
]
|
|
classifiers = [
|
|
"Development Status :: 4 - Beta",
|
|
"Intended Audience :: Developers",
|
|
"License :: OSI Approved :: GNU Affero General Public License v3",
|
|
"Programming Language :: Python :: 3",
|
|
"Programming Language :: Python :: 3.11",
|
|
"Programming Language :: Python :: 3.12",
|
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
"Topic :: Communications",
|
|
"Topic :: Internet :: WWW/HTTP",
|
|
]
|
|
|
|
[project.urls]
|
|
Homepage = "https://github.com/cbcoutinho/nextcloud-mcp-server"
|
|
Documentation = "https://github.com/cbcoutinho/nextcloud-mcp-server#readme"
|
|
Repository = "https://github.com/cbcoutinho/nextcloud-mcp-server"
|
|
"Bug Tracker" = "https://github.com/cbcoutinho/nextcloud-mcp-server/issues"
|
|
Changelog = "https://github.com/cbcoutinho/nextcloud-mcp-server/blob/master/CHANGELOG.md"
|
|
|
|
[tool.pytest.ini_options]
|
|
anyio_mode = "auto"
|
|
addopts = "-p no:asyncio" # Disable pytest-asyncio plugin, use only anyio
|
|
log_cli = 1
|
|
log_cli_level = "ERROR"
|
|
log_level = "ERROR"
|
|
markers = [
|
|
"unit: Fast unit tests with mocked dependencies",
|
|
"integration: Integration tests requiring Docker containers",
|
|
"smoke: Critical path smoke tests for quick validation",
|
|
"keycloak: OAuth tests that utilize keycloak external identity provider",
|
|
"login_flow: Login Flow v2 integration tests (ADR-022)",
|
|
"multi_user_basic: Multi-user BasicAuth pass-through tests (ADR-020)",
|
|
]
|
|
testpaths = [
|
|
"tests",
|
|
]
|
|
# Timeout settings to prevent tests from hanging indefinitely
|
|
timeout = 180 # 3 minutes default timeout per test (includes fixture setup)
|
|
timeout_func_only = false # Timeout includes fixture setup/teardown
|
|
|
|
[tool.commitizen]
|
|
name = "cz_conventional_commits"
|
|
tag_format = "v$version"
|
|
version_scheme = "pep440"
|
|
version_provider = "uv"
|
|
update_changelog_on_bump = true
|
|
major_version_zero = true
|
|
|
|
[tool.ruff.lint]
|
|
extend-select = ["I", "PLC0415"]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
"tests/**" = ["PLC0415"]
|
|
|
|
[build-system]
|
|
requires = ["uv_build>=0.11.5,<0.12.0"]
|
|
build-backend = "uv_build"
|
|
|
|
[tool.uv.build-backend]
|
|
module-name = "nextcloud_mcp_server"
|
|
module-root = ""
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"commitizen>=4.8.2",
|
|
"datasets>=3.3.0", # For BeIR nfcorpus dataset loading
|
|
"ipython>=9.2.0",
|
|
"playwright>=1.49.1",
|
|
"pytest>=8.3.5",
|
|
"pytest-cov>=6.1.1",
|
|
"pytest-mock>=3.15.1",
|
|
"pytest-playwright-asyncio>=0.7.1",
|
|
"pytest-timeout>=2.3.1",
|
|
"ruff>=0.11.13",
|
|
"reportlab>=4.0.0",
|
|
"ty>=0.0.1a25",
|
|
"pytest-otel>=2.0.1",
|
|
]
|
|
|
|
[project.scripts]
|
|
nextcloud-mcp-server = "nextcloud_mcp_server.cli:cli"
|
|
|
|
[[tool.uv.index]]
|
|
name = "testpypi"
|
|
url = "https://test.pypi.org/simple/"
|
|
publish-url = "https://test.pypi.org/legacy/"
|
|
explicit = true
|