Same pattern as the ENABLE_LOGIN_FLOW removal in the previous commit:
the deployment mode (MCP_DEPLOYMENT_MODE) is the single source of truth
for selecting an auth flow. The ENABLE_MULTI_USER_BASIC_AUTH env-var
alias is redundant with `MCP_DEPLOYMENT_MODE=multi_user_basic`.
Unlike the ENABLE_LOGIN_FLOW removal — where silent removal was safe
because Login Flow v2 is the auto-detection default — silent removal
here would be a surprise: a user with only ENABLE_MULTI_USER_BASIC_AUTH=true
in their .env would auto-detect into LOGIN_FLOW after upgrade (wrong
runtime mode). Mitigation: detect_auth_mode now reads os.environ
directly for both legacy aliases and raises ValueError with a one-line
migration message if either is set. Applied retroactively to
ENABLE_LOGIN_FLOW as well — loud is better than silent.
- nextcloud_mcp_server/config.py:
- Drop the dynaconf env-var alias entry for ENABLE_MULTI_USER_BASIC_AUTH.
- Update the `enable_multi_user_basic_auth` field docstring to mark it
as derived / not user-settable.
- `_is_multi_user_mode()` (early-config helper, runs before Settings
is built) switched to checking MCP_DEPLOYMENT_MODE directly. Now
consistent with the canonical detection in detect_auth_mode.
- nextcloud_mcp_server/config_validators.py:
- Drop the auto-detection branch (`if settings.enable_multi_user_basic_auth`).
Selection of MULTI_USER_BASIC is now exclusively via the explicit
MCP_DEPLOYMENT_MODE branch.
- Add `enable_multi_user_basic_auth` to `_sync_derived_flags` alongside
`enable_login_flow` — both flags are now derived from the resolved mode.
- Drop `enable_multi_user_basic_auth` from
`MODE_REQUIREMENTS[MULTI_USER_BASIC].required` and from the
`forbidden` lists of SINGLE_USER_BASIC and LOGIN_FLOW (no longer
user input → no meaningful forbidden check).
- Add loud-deprecation `ValueError` block at the top of detect_auth_mode
that errors with a clear migration message when ENABLE_MULTI_USER_BASIC_AUTH
or ENABLE_LOGIN_FLOW is found in os.environ.
- tests/unit/test_config_validators.py:
- Switch ~10 fixtures from `enable_multi_user_basic_auth=True` to
`deployment_mode="multi_user_basic"` (mirrors `enable_login_flow`
treatment from the previous commit).
- Switch two `patch.dict(os.environ, {"ENABLE_MULTI_USER_BASIC_AUTH": "true"})`
blocks to use MCP_DEPLOYMENT_MODE.
- Rename `test_forbidden_multi_user_basic_auth` to
`test_forbidden_multi_user_basic_when_credentials_present` — the
scenario is now an explicit-mode + credentials conflict, not an
env-var-flag conflict.
- Add `test_legacy_enable_multi_user_basic_auth_env_var_errors` and
`test_legacy_enable_login_flow_env_var_errors` to exercise the new
loud-deprecation ValueError path.
- docker-compose.yml: mcp-multi-user-basic profile switched to
`MCP_DEPLOYMENT_MODE=multi_user_basic`.
- env.sample: replaced `#ENABLE_MULTI_USER_BASIC_AUTH=true` example with
`#MCP_DEPLOYMENT_MODE=multi_user_basic`.
- docs/authentication.md, configuration.md, troubleshooting.md,
auth-flows.md, webhook-management-guide.md,
configuration-migration-v2.md, ADR-025: replaced env-var examples
with the canonical MCP_DEPLOYMENT_MODE form.
- docs/ADR-020: marked partly superseded by ADR-022.
- CLAUDE.md: Multi-User BasicAuth section updated to set
MCP_DEPLOYMENT_MODE.
- nextcloud_mcp_server/vector/oauth_sync.py: module docstring updated.
BREAKING CHANGE: ENABLE_MULTI_USER_BASIC_AUTH is no longer read from
the environment, and setting it now raises a startup ValueError with
a migration message. Replace `ENABLE_MULTI_USER_BASIC_AUTH=true` with
`MCP_DEPLOYMENT_MODE=multi_user_basic`. The same loud-deprecation
check is also applied to the recently-removed ENABLE_LOGIN_FLOW —
replace with `MCP_DEPLOYMENT_MODE=login_flow` (or drop both;
`login_flow` is the auto-detect default when no other auth env vars
are set).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
22 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Coding Conventions
async/await Patterns
- Use anyio for all async operations - Provides structured concurrency
- pytest runs in
anyiomode (anyio_mode = "auto"in pyproject.toml) - Use
anyio.create_task_group()for concurrent execution (NOTasyncio.gather()) - Use
anyio.Lock()for synchronization primitives (NOTasyncio.Lock()) - Use
anyio.run()for entry points (NOTasyncio.run()) - Prefer standard async/await syntax without explicit library imports when possible
- Examples: app.py, search/hybrid.py, search/verification.py, auth/token_broker.py
- pytest runs in
Type Hints
- Use Python 3.10+ union syntax:
str | Noneinstead ofOptional[str] - Use lowercase generics:
dict[str, Any]instead ofDict[str, Any] - Type all function signatures - Parameters and return types
- Type checker:
tyis configured for static type checkinguv run ty check -- nextcloud_mcp_server
Code Quality
- Before committing or pushing, invoke the
pre-push-reviewskill:- Runs
ruff check,ruff format --check,ty check, and unit tests, then audits the branch diff against this repo's recurring PR-review patterns (mined from PRs #733–#750). - Output is a labelled punch list (🔴 blocking / 🟡 important / 🟢 nit). The main loop fixes; the skill reports.
- Skill location:
.claude/skills/pre-push-review/SKILL.md. Invoke via theSkilltool withskill="pre-push-review", or when the user types/pre-push-review. - Skip only for tiny diffs (typo, README tweak, single-line dependency bump) or when the user explicitly says "just push it".
- Runs
- Manual fallback (if the skill is unavailable):
uv run ruff check uv run ruff format uv run ty check -- nextcloud_mcp_server uv run pytest tests/unit/ -x -q - Ruff configuration in pyproject.toml (extends select: ["I"] for import sorting)
Error Handling
- Use custom decorators:
@retry_on_429for rate limiting (see base_client.py) - Standard exceptions:
HTTPStatusErrorfrom httpx,McpErrorfor MCP-specific errors - Logging patterns:
logger.debug()for expected 404s and normal operationslogger.warning()for retries and non-critical issueslogger.error()for actual errors
Testing Patterns
- Use existing fixtures from
tests/conftest.py(2888 lines of test infrastructure) - Session-scoped fixtures handle anyio/pytest-asyncio incompatibility
- Mocked unit tests use
mocker.AsyncMock(spec=httpx.AsyncClient) - pytest-timeout: 180s default per test
- Mark tests appropriately:
@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.oauth,@pytest.mark.smoke
Architectural Patterns
- Base classes:
BaseNextcloudClientfor all API clients - Pydantic responses: All MCP tools return Pydantic models inheriting from
BaseResponse - Decorators:
@require_scopes,@require_provisioningfor access control - Context pattern:
await get_client(ctx)to access authenticated NextcloudClient (async!) - FastMCP decorators:
@mcp.tool(),@mcp.resource() - Token acquisition:
get_client()resolves credentials per deployment mode (see Deployment Modes below)
MCP Tool Annotations (ADR-017)
All tools MUST include annotations following these patterns:
from mcp.types import ToolAnnotations
# Read-only tools (list, search, get)
@mcp.tool(
title="Human Readable Name",
annotations=ToolAnnotations(
readOnlyHint=True,
openWorldHint=True, # Nextcloud is external to MCP server
),
)
# Create operations
@mcp.tool(
title="Create Resource",
annotations=ToolAnnotations(
idempotentHint=False, # Creates new resources each time
openWorldHint=True,
),
)
# Update operations (with etag/version control)
@mcp.tool(
title="Update Resource",
annotations=ToolAnnotations(
idempotentHint=False, # ETag changes = different inputs
openWorldHint=True,
),
)
# Delete operations
@mcp.tool(
title="Delete Resource",
annotations=ToolAnnotations(
destructiveHint=True, # Permanently deletes data
idempotentHint=True, # Same end state if called repeatedly
openWorldHint=True,
),
)
# HTTP PUT without version control (special case)
@mcp.tool(
title="Write File",
annotations=ToolAnnotations(
idempotentHint=True, # Same content = same end state
openWorldHint=True,
),
)
Key Principles:
- Idempotency: Same inputs → same result. ETags change after updates, making them non-idempotent
- Destructive: Operations that permanently delete/overwrite data
- Open World: All Nextcloud tools access external service (openWorldHint=True)
- Titles: Use human-readable names, not snake_case function names
See: docs/ADR-017-mcp-tool-annotations.md for detailed rationale and examples
Project Structure
nextcloud_mcp_server/client/- HTTP clients for Nextcloud APIsnextcloud_mcp_server/server/- MCP tool/resource definitionsnextcloud_mcp_server/auth/- OAuth/OIDC authenticationnextcloud_mcp_server/models/- Pydantic response modelsnextcloud_mcp_server/providers/- Unified LLM provider infrastructure (embeddings + generation)tests/- Layered test suite (unit, smoke, integration, load)
Provider Architecture (ADR-015)
Unified Provider System for embeddings and text generation:
Location: nextcloud_mcp_server/providers/
base.py-ProviderABC with optional capabilitiesregistry.py- Auto-detection and factory patternollama.py- Ollama provider (embeddings + generation)anthropic.py- Anthropic provider (generation only)bedrock.py- Amazon Bedrock provider (embeddings + generation)simple.py- Simple in-memory provider (embeddings only, fallback)
Usage:
from nextcloud_mcp_server.providers import get_provider
provider = get_provider() # Auto-detects from environment
# Check capabilities
if provider.supports_embeddings:
embeddings = await provider.embed_batch(texts)
if provider.supports_generation:
text = await provider.generate("prompt", max_tokens=500)
Environment Variables:
Bedrock:
AWS_REGION- AWS region (e.g., "us-east-1")BEDROCK_EMBEDDING_MODEL- Embedding model ID (e.g., "amazon.titan-embed-text-v2:0")BEDROCK_GENERATION_MODEL- Generation model ID (e.g., "anthropic.claude-3-sonnet-20240229-v1:0")AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY- Optional, uses AWS credential chain
Ollama:
OLLAMA_BASE_URL- API URL (e.g., "http://localhost:11434")OLLAMA_EMBEDDING_MODEL- Embedding model (default: "nomic-embed-text")OLLAMA_GENERATION_MODEL- Generation model (e.g., "llama3.2:1b")OLLAMA_VERIFY_SSL- SSL verification (default: "true")
Simple (fallback, no config needed):
SIMPLE_EMBEDDING_DIMENSION- Dimension (default: 384)
Auto-Detection Priority: Bedrock → Ollama → Simple
Backward Compatibility:
- Old code using
nextcloud_mcp_server.embedding.get_embedding_service()still works EmbeddingServicenow wrapsget_provider()internally
For Details: See docs/ADR-015-unified-provider-architecture.md
Development Commands (Quick Reference)
Testing
# Fast feedback (recommended)
uv run pytest tests/unit/ -v # Unit tests (~5s)
uv run pytest -m smoke -v # Smoke tests (~30-60s)
# Integration tests
uv run pytest -m "integration and not oauth" -v # Without OAuth (~2-3min)
uv run pytest -m oauth -v # OAuth only (~3min)
uv run pytest # Full suite (~4-5min)
# Coverage
uv run pytest --cov
# Specific tests after changes
uv run pytest tests/server/test_mcp.py -k "notes" -v
uv run pytest tests/client/notes/test_notes_api.py -v
Important: After code changes, rebuild the correct container:
- Single-user tests:
docker compose up --build -d mcp - Login Flow tests:
docker compose up --build -d mcp-login-flow - Keycloak tests:
docker compose up --build -d mcp-keycloak
Running the Server
# Local development
export $(grep -v '^#' .env | xargs)
uv run mcp run --transport sse nextcloud_mcp_server.app:mcp
# Docker development (rebuilds after code changes)
docker compose up --build -d mcp # Single-user (port 8000)
docker compose up --build -d mcp-login-flow # Login Flow v2 (port 8004)
docker compose up --build -d mcp-keycloak # Keycloak OAuth (port 8002)
Environment Setup
uv sync # Install dependencies
uv sync --group dev # Install with dev dependencies
Load Testing
# Quick test (default: 10 workers, 30 seconds)
uv run python -m tests.load.benchmark
# Custom concurrency and duration
uv run python -m tests.load.benchmark -c 20 -d 60
# Export results for analysis
uv run python -m tests.load.benchmark --output results.json --verbose
Expected Performance: 50-200 RPS for mixed workload, p50 <100ms, p95 <500ms, p99 <1000ms.
Database Inspection
Credentials: root/password, nextcloud/password, database: nextcloud
Do NOT use docker compose exec db mariadb or docker compose exec <service> sqlite3 directly. Use the wrapper scripts below instead -- they handle credentials, output formatting, and avoid repeated docker exec approvals.
MariaDB (Nextcloud)
Use scripts/dbquery.py for all MariaDB queries:
# Basic query
./scripts/dbquery.py "SELECT COUNT(*) FROM oc_users"
# Vertical output (one column per line) - useful for wide tables
./scripts/dbquery.py -E "SELECT * FROM oc_oidc_clients LIMIT 1"
# With different credentials
./scripts/dbquery.py -u nextcloud -p nextcloud "SHOW TABLES"
Important Tables:
oc_oidc_clients- OAuth client registrations (DCR)oc_oidc_client_scopes- Client allowed scopesoc_oidc_access_tokens- Issued access tokensoc_oidc_authorization_codes- Authorization codesoc_oidc_registration_tokens- RFC 7592 registration tokensoc_oidc_redirect_uris- Redirect URIs
SQLite (MCP Services)
Use scripts/sqlitequery.py for all SQLite queries:
# List tables
./scripts/sqlitequery.py ".tables"
# Query specific service
./scripts/sqlitequery.py -s oauth "SELECT * FROM refresh_tokens"
./scripts/sqlitequery.py -s keycloak "SELECT * FROM oauth_clients"
./scripts/sqlitequery.py -s basic "SELECT * FROM app_passwords"
# With column headers
./scripts/sqlitequery.py --column "SELECT * FROM audit_logs LIMIT 5"
# JSON output
./scripts/sqlitequery.py --json "SELECT * FROM oauth_sessions"
# View schema
./scripts/sqlitequery.py -s oauth ".schema refresh_tokens"
Services: mcp (default), oauth, keycloak, basic
SQLite Tables:
refresh_tokens- OAuth refresh tokens with user profilesaudit_logs- Security audit trailoauth_clients- DCR OAuth client credentialsoauth_sessions- OAuth flow session stateregistered_webhooks- Webhook registrationsapp_passwords- Multi-user BasicAuth passwordsalembic_version- Migration tracking
Architecture Quick Reference
For detailed architecture, see:
docs/comparison-context-agent.md- Overall architecturedocs/login-flow-v2.md- OAuth/OIDC integration patterns and architecturedocs/ADR-004-progressive-consent.md- Progressive consent implementation
Core Components:
nextcloud_mcp_server/app.py- FastMCP server entry pointnextcloud_mcp_server/client/- HTTP clients (Notes, Calendar, Contacts, Tables, WebDAV)nextcloud_mcp_server/server/- MCP tool/resource definitionsnextcloud_mcp_server/auth/- OAuth/OIDC authentication
Supported Apps: Notes, Calendar (CalDAV + VTODO tasks), Contacts (CardDAV), Tables, WebDAV, Deck, Cookbook
Key Patterns:
NextcloudClientorchestrates all app-specific clientsBaseNextcloudClientprovides common HTTP functionality + retry logic- MCP tools use context pattern:
get_client(ctx)→NextcloudClient - All operations are async using httpx
Deployment Modes
The server supports three deployment modes, controlled by environment variables and docker compose profiles:
1. Single-User (profile: single-user)
- Set
NEXTCLOUD_USERNAME+NEXTCLOUD_PASSWORD(app password) - One shared Nextcloud identity for all MCP requests
- Stateless, no persistent storage needed
- Best for: personal instances, local development
2. Multi-User BasicAuth (profile: multi-user-basic)
- Set
MCP_DEPLOYMENT_MODE=multi_user_basic - Each MCP client provides credentials via HTTP Authorization header
- Per-request client creation from extracted credentials
- Best for: internal deployments where users manage their own Nextcloud credentials
3. Login Flow v2 (profile: login-flow)
- Browser-based app password acquisition via Nextcloud's native Login Flow v2 API
- Per-user app passwords stored encrypted in SQLite
- Application-level scope enforcement (defense-in-depth)
- Works with any Nextcloud 16+ instance (no special apps required)
- Best for: production multi-user deployments, OAuth MCP integration
- See
docs/ADR-022-login-flow-v2.mdfor architecture details
MCP Response Patterns (CRITICAL)
Never return raw List[Dict] from MCP tools - FastMCP mangles them into dicts with numeric string keys.
Correct Pattern:
- Client methods return
List[Dict](raw data) - MCP tools convert to Pydantic models and wrap in response object
- Response models inherit from
BaseResponse, includeresultsfield + metadata
Reference implementations:
nextcloud_mcp_server/models/notes.py:80-SearchNotesResponsenextcloud_mcp_server/models/webdav.py:113-SearchFilesResponsenextcloud_mcp_server/server/{notes,webdav}.py- Tool examples
Testing: Extract data["results"] from MCP responses, not data directly.
MCP Sampling for RAG (ADR-008)
What is MCP Sampling? MCP sampling allows servers to request LLM completions from their clients. This enables Retrieval-Augmented Generation (RAG) patterns where the server retrieves context and the client's LLM generates answers.
When to use sampling:
- Generating natural language answers from retrieved documents
- Synthesizing information from multiple sources
- Creating summaries with citations
Implementation Pattern (see ADR-008 for details):
from mcp.types import ModelHint, ModelPreferences, SamplingMessage, TextContent
@mcp.tool()
@require_scopes("notes.read")
async def nc_notes_semantic_search_answer(
query: str, ctx: Context, limit: int = 5, max_answer_tokens: int = 500
) -> SamplingSearchResponse:
# 1. Retrieve documents
search_response = await nc_notes_semantic_search(query, ctx, limit)
# 2. Check for no results (don't waste sampling call)
if not search_response.results:
return SamplingSearchResponse(
query=query,
generated_answer="No relevant documents found.",
sources=[], total_found=0, success=True
)
# 3. Construct prompt with retrieved context
prompt = f"{query}\n\nDocuments:\n{format_sources(search_response.results)}\n\nProvide answer with citations."
# 4. Request LLM completion via sampling
try:
result = await ctx.session.create_message(
messages=[SamplingMessage(role="user", content=TextContent(type="text", text=prompt))],
max_tokens=max_answer_tokens,
temperature=0.7,
model_preferences=ModelPreferences(
hints=[ModelHint(name="claude-3-5-sonnet")],
intelligencePriority=0.8,
speedPriority=0.5,
),
include_context="thisServer",
)
return SamplingSearchResponse(
query=query,
generated_answer=result.content.text,
sources=search_response.results,
model_used=result.model,
stop_reason=result.stopReason,
success=True
)
except Exception as e:
# Fallback: Return documents without generated answer
return SamplingSearchResponse(
query=query,
generated_answer=f"[Sampling unavailable: {e}]\n\nFound {len(search_response.results)} documents.",
sources=search_response.results,
search_method="semantic_sampling_fallback",
success=True
)
Key Points:
- No server-side LLM: Server has no API keys, client controls which model is used
- Graceful degradation: Tool always returns useful results even if sampling fails
- User control: MCP clients SHOULD prompt users to approve sampling requests
- No results optimization: Skip sampling call when no documents found
- Fixed prompts: Prompts are not user-configurable to avoid injection risks
Reference: See nc_notes_semantic_search_answer in nextcloud_mcp_server/server/notes.py:517 and ADR-008 for complete implementation.
Testing Best Practices (MANDATORY)
Always Run Tests
- Run tests to completion before considering any task complete
- Rebuild the correct container after code changes (see Development Commands above)
- If tests require modifications, ask for permission before proceeding
Use Existing Fixtures
See tests/conftest.py for 2888 lines of test infrastructure:
nc_mcp_client- MCP client for tool/resource testing (usesmcpcontainer)nc_mcp_oauth_client- MCP client for OAuth testing (usesmcp-login-flowcontainer)nc_client- Direct NextcloudClient for setup/cleanuptemporary_note,temporary_addressbook,temporary_contact- Auto-cleanup
Writing Mocked Unit Tests
For client-layer response parsing tests, use mocked HTTP responses:
async def test_notes_api_get_note(mocker):
"""Test that get_note correctly parses the API response."""
mock_response = create_mock_note_response(
note_id=123, title="Test Note", content="Test content",
category="Test", etag="abc123"
)
mock_make_request = mocker.patch.object(
NotesClient, "_make_request", return_value=mock_response
)
client = NotesClient(mocker.AsyncMock(spec=httpx.AsyncClient), "testuser")
note = await client.get_note(note_id=123)
assert note["id"] == 123
mock_make_request.assert_called_once_with("GET", "/apps/notes/api/v1/notes/123")
Mock helpers in tests/conftest.py: create_mock_response(), create_mock_note_response(), create_mock_error_response()
When to use: Response parsing, error handling, request parameter building When NOT to use: CalDAV/CardDAV/WebDAV protocols, OAuth flows, end-to-end MCP testing
OAuth Testing
OAuth tests use Playwright browser automation to complete flows programmatically.
Test Environment:
- Three MCP containers:
mcp(single-user),mcp-login-flow(Login Flow v2),mcp-keycloak(external IdP) - OAuth tests require
NEXTCLOUD_HOST,NEXTCLOUD_USERNAME,NEXTCLOUD_PASSWORDenvironment variables - Playwright configuration:
--browser firefox --headedfor debugging - Install browsers:
uv run playwright install firefox
OAuth fixtures: nc_oauth_client, nc_mcp_oauth_client, alice_oauth_token, bob_oauth_token, etc.
Shared OAuth Client: All test users authenticate using a single OAuth client (created via DCR, deleted at session end via RFC 7592). Matches production behavior.
Run OAuth tests:
uv run pytest -m oauth -v # All OAuth tests
uv run pytest tests/server/oauth/ --browser firefox -v
uv run pytest tests/server/oauth/test_oauth_core.py --browser firefox --headed -v
Keycloak OAuth Testing
Validates ADR-002 architecture for external identity providers and offline access patterns.
Architecture: MCP Client → Keycloak (OAuth) → MCP Server → Nextcloud user_oidc (validates token) → APIs
Setup:
docker compose up -d keycloak app mcp-keycloak
curl http://localhost:8888/realms/nextcloud-mcp/.well-known/openid-configuration
docker compose exec app php occ user_oidc:provider keycloak
Credentials: admin/admin (Keycloak realm: nextcloud-mcp)
For detailed Keycloak setup, see:
docs/login-flow-v2.md- OAuth/OIDC configuration (setOIDC_DISCOVERY_URLto a Keycloak realm)docs/ADR-002-vector-sync-authentication.md- Offline access architecturedocs/keycloak-multi-client-validation.md- Realm-level validation
Integration Testing with Docker
Nextcloud: docker compose exec app php occ ... for occ commands
MariaDB: Use ./scripts/dbquery.py for queries (see Database Inspection above)
SQLite: Use ./scripts/sqlitequery.py for MCP service databases
Querying Nextcloud Application Logs
Use this pattern to inspect Nextcloud application logs during debugging:
# View recent log entries
docker compose exec app cat /var/www/html/data/nextcloud.log | jq | tail
# Filter by app
docker compose exec app cat /var/www/html/data/nextcloud.log | jq 'select(.app == "astrolabe")' | tail
# Filter by log level (0=DEBUG, 1=INFO, 2=WARN, 3=ERROR, 4=FATAL)
docker compose exec app cat /var/www/html/data/nextcloud.log | jq 'select(.level >= 3)' | tail
# Search for specific messages
docker compose exec app cat /var/www/html/data/nextcloud.log | jq 'select(.message | contains("OAuth"))' | tail -20
# View full exception traces
docker compose exec app cat /var/www/html/data/nextcloud.log | jq 'select(.exception != null)' | tail -5
Log Structure: Each entry is a JSON object with fields: reqId, level, time, remoteAddr, user, app, method, url, message, userAgent, version, exception
For detailed setup, see:
docs/installation.md- Installation guidedocs/configuration.md- Configuration optionsdocs/authentication.md- Authentication modesdocs/running.md- Running the server
For additional information regarding MCP during development, see:
../../Software/modelcontextprotocol/- MCP spec../../Software/python-sdk/- Python MCP SDK