Skip to main content
Glama
homeserverhq

HedgeDoc MCP Multitenant Proxy Server

by homeserverhq
README.md
# HedgeDoc MCP Multitenant Proxy Server

This repository contains a Model Context Protocol (MCP) server that acts
as a secure, multi-tenant proxy between an AI Assistant and the HedgeDoc
backend API. It exposes **27 MCP tools** covering 5 resource domains
with full CRUD and permission management.

## โœจ Features

- **๐Ÿ”‘ Identity Passthrough** โ€” Extracts the `Authorization: Bearer <token>`
  header from incoming HTTP requests and forwards it to the HedgeDoc API
  without server-side authentication.
- **๐Ÿ‘ฅ Multi-Tenancy** โ€” Uses Python `contextvars` to maintain thread-safe
  user identity isolation, ensuring all AI-driven actions are scoped to
  the authenticated user's permissions.
- **๐Ÿ“Š Full HedgeDoc Coverage** โ€” 27 tools mapped to HedgeDoc API endpoints
  across 5 resource domains.
- **โšก TOON Optimization** โ€” Bulk list responses are automatically compressed
  using TOON (Token-Optimized Object Notation) to reduce token consumption
  and maximize context window efficiency.
- **๐Ÿš€ Efficient Gets** โ€” GET responses return only commonly used fields by
  default. Full objects are available via an `include_all_fields` flag.
- **๐Ÿงช Comprehensive Testing** โ€” 39 automated tests covering all tool
  domains, run via the test runner pipeline.
- **๐Ÿท๏ธ Tool Annotations** โ€” All tools expose standard MCP ToolAnnotations hints (readOnlyHint, destructiveHint, idempotentHint, openWorldHint). The tags field carries grouping metadata (basic/primary/advanced + hedgedoc).

## ๐Ÿ”ง Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `HEDGEDOC_BASE_URL` | Yes | Docker-internal URL of the HedgeDoc API (e.g. `http://hedgedoc-backend:3000`). |
| `MCP_SERVER_PORT` | Yes | Port number the MCP server listens on. |
| `ALLOW_ALL_AGGREGATE` | No | When `true`, aggregate listing tools honor the `include_all_fields` parameter. When `false` (default), the parameter is silently forced to `False` for aggregate list operations. |
| `IS_STATEFUL` | No | When `true`, uses stateful Streamable HTTP with session tracking. When `false` (default), uses stateless mode. |
| `HEDGEDOC_PUBLIC_URL` | No | Public-facing URL for `note_url` in responses. Defaults to `HEDGEDOC_BASE_URL` if not set. |

## ๐Ÿ“ฆ Installation & Local Development

1. Ensure you have Python 3.12+ installed.
2. Install dependencies:
    ```bash
    pip install fastmcp httpx pydantic uvicorn toon-mcp-server
    ```
3. Run the server:
    ```bash
    export HEDGEDOC_BASE_URL=http://localhost:7462
    export HEDGEDOC_PUBLIC_URL=https://hedgedoc.example.com
    export MCP_SERVER_PORT=80
    python -m src.main
    ```

## ๐Ÿณ Docker Deployment

Build and run the server using Docker:

```bash
docker build -t hedgedoc-mcp:latest .
docker run -d --name hedgedoc-mcp \
    -e HEDGEDOC_BASE_URL="http://hedgedoc-backend:3000" \
    -e HEDGEDOC_PUBLIC_URL="https://hedgedoc.example.com" \
    -e MCP_SERVER_PORT=80 \
    -e IS_STATEFUL=false \
    hedgedoc-mcp:latest

The MCP server serves at `http://hedgedoc-mcp:80/mcp` (Streamable HTTP).
```

## โš ๏ธ Important Notes

- **๐Ÿ“‹ `include_all_fields`** โ€” The `include_all_fields` parameter (available
  on all `get_*` and `list_*` tools) controls whether all available fields
  are included in responses. Defaults to `False` for performance; set to
  `True` only when additional fields are needed.
- **โšก TOON Compression** โ€” All bulk list responses are automatically
  compressed using TOON to reduce token consumption by 30โ€“60%.
- **๐Ÿ“ Required Fields & Defaults** โ€” Each `create_*` tool requires specific
  key fields. All other fields default to empty strings or reasonable values.
  The owner field is automatically set to the authenticated user for most
  resources.

## ๐Ÿ› ๏ธ API Tool Mapping

The server implements 27 MCP tools organized into the following categories:

### ๐Ÿ“„ Notes Core (6 tools)

- `create_note` โ€” Create a new note from markdown content
- `get_note_by_id` โ€” Get a single note by ID or alias
- `update_note` โ€” Update an existing note's content
- `delete_note_by_id` โ€” Delete a note by ID or alias
- `get_note_content` โ€” Get raw markdown content of a note
- `get_note_metadata` โ€” Get a note's metadata (title, tags, etc.)

### ๐Ÿ“Ž Notes Advanced (7 tools)

- `list_note_media` โ€” List all media files attached to a note
- `list_note_revisions` โ€” List all revisions of a note
- `get_note_revision_by_id` โ€” Get a specific revision by UUID
- `revert_to_revision` โ€” Revert a note to a previous revision
- `add_alias` โ€” Add an alias to a note
- `make_alias_primary` โ€” Set an alias as primary or non-primary
- `remove_alias` โ€” Remove an alias from a note

### ๐Ÿ” Notes Permissions (6 tools)

- `get_note_permissions` โ€” Get a note's permission settings
- `set_note_user_permission` โ€” Set a user's permission on a note
- `remove_note_user_permission` โ€” Remove a user's permission from a note
- `set_note_group_permission` โ€” Set a group's permission on a note
- `remove_note_group_permission` โ€” Remove a group's permission from a note
- `change_note_owner` โ€” Change the owner of a note

### ๐Ÿ“‹ Notes Listing (4 tools)

- `list_all_my_notes` โ€” List notes owned by the current user
- `list_all_shared_notes` โ€” List notes shared with the current user
- `list_all_public_notes` โ€” List public notes visible to the current user
- `list_all_pinned_notes` โ€” List notes pinned by the current user

### ๐Ÿ‘ค Profile & Media (4 tools)

- `get_my_profile` โ€” Get the current user's profile information
- `list_all_my_media` โ€” List all media files owned by the current user
- `get_media_by_id` โ€” Get a single media file by UUID
- `delete_media_by_id` โ€” Delete a media file by UUID

Maintenance

ActivitySlowing
ResponsivenessNo issues