WebDAV Notes MCP

A Dockerized MCP server that lets ChatGPT browse, search, and read an Obsidian vault stored on WebDAV. The server is fixed to one vault root (/Notes/Personal) and uses HTTPS Basic authentication to access dav.ryankegel.com from inside the container.

It uses Streamable HTTP for remote MCP connections. ChatGPT's custom MCP connection supports OAuth authentication; this project includes a small, single-user OAuth login flow so the MCP endpoint does not have to be public and anonymous. The SDK serves the MCP authorization discovery and token routes. See ChatGPT custom MCP server setup and the MCP Python SDK authorization guide.

The local OAuth login is intended for one trusted user and has password protection, CSRF checks, and rate limiting. It does not provide MFA or identity-provider federation. The MCP login password must be at least 16 bytes; use a unique passphrase.

Tools

Read-only tools are available by default:

  • list_directory(path) lists the immediate children of a vault folder. Use an empty path for the vault root.
  • read_file(path) reads a UTF-8 text or Markdown file and returns its ETag. Files are limited to 256 KiB.
  • search_files(query, path, limit) searches file and folder paths recursively.
  • search_notes(query, path, limit) searches text in Markdown and .txt files recursively.

When MCP_READ_ONLY=false, the server also exposes:

  • write_file(path, content) creates a new note. It will not replace an existing file by default.
  • write_file(path, content, overwrite=true, expected_etag=...) replaces a note only when its current ETag matches the value returned by read_file.
  • create_directory(path) creates one folder; its parent must already exist.

There is no delete or move tool. Read and search calls are capped to keep large vaults and responses bounded. Recursive searches visit at most 500 directories and inspect at most 1,500 text files.

Deploy

Use a dedicated public hostname such as mcp.ryankegel.com and terminate HTTPS at your existing reverse proxy. Docker binds the app to 127.0.0.1 by default, so port 8000 is not directly exposed to the internet.

  1. Copy .env.example to .env. Set MCP_PUBLIC_URL, MCP_ALLOWED_HOSTS, WEBDAV_USERNAME, and MCP_AUTH_USERNAME. The public URL must be the HTTPS origin only, without /mcp.

    For example:

    MCP_PUBLIC_URL=https://mcp.ryankegel.com
    MCP_ALLOWED_HOSTS=mcp.ryankegel.com,mcp.ryankegel.com:*
    WEBDAV_USERNAME=your-webdav-username
    MCP_AUTH_USERNAME=ryan
    
  2. Create the Docker secret files. Keep both passwords out of .env and source control. On the server:

    mkdir -p secrets
    chmod 700 secrets
    
    read -rsp 'WebDAV password: ' DAV_SECRET
    printf '%s' "$DAV_SECRET" > secrets/webdav_password
    unset DAV_SECRET
    printf '\n'
    
    read -rsp 'New MCP login password: ' MCP_SECRET
    printf '%s' "$MCP_SECRET" > secrets/mcp_auth_password
    unset MCP_SECRET
    printf '\n'
    
    # Compose file-backed secrets keep the host file's group and permissions.
    # The container runs as uid/gid 10001, so give that group read access.
    sudo chgrp 10001 secrets/webdav_password secrets/mcp_auth_password
    chmod 640 secrets/webdav_password secrets/mcp_auth_password
    

    Docker Compose mounts file-backed secrets without remapping their ownership and mode, so the group-readable permission above lets the non-root container user read them while keeping the files private from other users on the server. See the Compose secrets reference.

    The MCP login password is separate from your WebDAV password. ChatGPT users enter the MCP login credentials on the OAuth consent page. The WebDAV credential remains server-side.

  3. Start the container:

    docker compose up -d --build
    docker compose logs -f dav-mcp
    
  4. Point your reverse proxy to 127.0.0.1:8000, preserving the incoming Host header. Example Caddy site:

    mcp.ryankegel.com {
        reverse_proxy 127.0.0.1:8000
    }
    

    The MCP endpoint is https://mcp.ryankegel.com/mcp. The OAuth discovery and login endpoints share that hostname. The MCP allowed-host setting also prevents requests to unexpected hostnames.

  5. In ChatGPT on the web, add a custom MCP server with that /mcp URL and choose OAuth authentication. The server supports Dynamic Client Registration (DCR). On the first authorization, sign in with MCP_AUTH_USERNAME and the password stored in secrets/mcp_auth_password, approve the requested vault access, then click Continue to return to ChatGPT.

Enable writes

Keep MCP_READ_ONLY=true until you want write tools. To enable them:

  1. Confirm the WebDAV account has only the permissions you want ChatGPT to have. Restrict it to the notes vault if your WebDAV provider supports that.
  2. Set MCP_READ_ONLY=false in .env and restart with docker compose up -d --build.
  3. Refresh or recreate the MCP connection in ChatGPT so it discovers the new tools, then reconnect and grant the write scope.

The write tools still require an OAuth token carrying notes:write. Replacing an existing file also requires overwrite=true and the exact ETag returned by read_file; this reduces the chance of replacing a newer edit.

Configuration

Variable Default Purpose
WEBDAV_URL https://dav.ryankegel.com WebDAV origin or base path
WEBDAV_ROOT /Notes/Personal The only subtree exposed to MCP tools
WEBDAV_USERNAME — WebDAV username
WEBDAV_PASSWORD_FILE Docker secret Path to WebDAV password file
MCP_PUBLIC_URL — Public HTTPS origin serving the MCP endpoint and OAuth login
MCP_ALLOWED_HOSTS — Comma-separated Host values allowed by MCP transport security
MCP_ALLOWED_ORIGINS empty Optional comma-separated browser origins allowed by MCP transport security
MCP_AUTH_USERNAME — Username for the local OAuth login page
MCP_AUTH_PASSWORD_FILE Docker secret Path to the local OAuth login password file
MCP_READ_ONLY true Hides write tools and disallows the write scope when true
MCP_BIND_ADDRESS 127.0.0.1 Host interface for the published Docker port
MCP_HOST_PORT 8000 Host port forwarded to the container

OAuth clients, authorization codes, and refresh tokens are stored in the oauth-data Docker volume. The volume contains credentials and should be backed up and protected like other application data.

ChatGPT availability

Custom MCP servers are connected from ChatGPT on the web. OpenAI's current guide says Pro accounts can use custom servers for read/fetch, while full MCP support including write actions is available on Business, Enterprise, and Edu. Workspace settings also control who can add them. Check OpenAI's current connection guide for current account requirements.

S
Description
WebDAV mcp
Readme
60 KiB
Languages
Python 98.7%
Dockerfile 1.3%