Skip to main content
Glama

mcp-hedgedoc

Serveur MCP pour HedgeDoc 1.x — lit et crée des notes via un compte de service unique, avec un vrai mode lecture seule pour la paire de variantes ro/rw de LibreChat.

Ce que ce serveur peut et ne peut pas faire

HedgeDoc 1.x n'a aucune API à jetons : toute action passe par un cookie de session obtenu via un vrai login. Ce serveur agit donc toujours sous un compte de service partagé — jamais sous l'identité réelle de la personne qui pose la question dans LibreChat. Toute note créée, toute action listée dans /history, apparaît sous ce compte dans l'historique HedgeDoc.

Il n'y a pas d'outil update_note ni delete_note, et ce n'est pas un oubli : POST /new/{alias} sur un alias déjà existant renvoie 409 Conflict (vérifié sur le code source de HedgeDoc 1.11.1 et 1.12.0, identique dans les deux). La modification du contenu d'une note existante ne se fait que via le canal Socket.IO temps réel de l'éditeur web, que ce serveur n'implémente pas.

Related MCP server: HackMD MCP Server

Outils exposés

Outil

Lecture seule

Description

read_note

oui

Contenu Markdown brut d'une note

get_note_info

oui

Titre, description, vues, dates

get_note_revisions

oui

Liste des révisions sauvegardées

get_note_revision_content

oui

Contenu à une révision donnée

whoami

oui

Identité de la session actuelle (toujours le compte de service)

list_history

oui

Notes récemment consultées/épinglées par le compte de service

create_note

non

Crée une nouvelle note (jamais une modification)

Variables d'environnement

Variable

Obligatoire

Défaut

Description

HEDGEDOC_URL

oui

—

URL de base, ex. https://hedgedoc.example.org

HEDGEDOC_AUTH_MODE

non

local

local, oidc, ou cookie

HEDGEDOC_READ_ONLY

non

false

true pour la variante ro

MCP_SERVER_PORT

non

8080

Port d'écoute HTTP

HEDGEDOC_TIMEOUT_SECONDS

non

15

Timeout des requêtes HTTP

REQUESTS_CA_BUNDLE

non

—

CA interne, si nécessaire

Selon HEDGEDOC_AUTH_MODE :

Mode

Variables requises

local

HEDGEDOC_EMAIL, HEDGEDOC_PASSWORD (compte HedgeDoc local, ex. créé via bin/manage_users)

oidc

HEDGEDOC_OIDC_USERNAME, HEDGEDOC_OIDC_PASSWORD (compte IdP dédié, pas celui d'une vraie personne), et éventuellement HEDGEDOC_OIDC_LOGIN_PATH (défaut /auth/oauth2)

cookie

HEDGEDOC_SESSION_COOKIE (cookie de session déjà valide — surtout utile en debug)

Choisir le mode d'auth

Le bon mode dépend de la façon dont les comptes existent sur l'instance cible, pas du serveur MCP :

  • local — l'instance possède des comptes natifs HedgeDoc (config.email / CMD_EMAIL activés côté serveur, comptes créés par exemple via bin/manage_users). C'est le mode le plus simple : un seul POST /login, rien de fragile. Il ne peut pas authentifier un compte créé par un fournisseur OAuth2/Keycloak — même avec les bonnes identités, POST /login est rejeté.

  • oidc — les utilisateurs de l'instance proviennent d'un IdP externe (ex. Keycloak, CMD_OAUTH2_* activés). Le serveur scripte la danse /auth/oauth2 → page de login Keycloak → callback. Voir la docstring de auth.py pour le détail et les limites (thème de login personnalisé, 2FA, actions requises sur le compte).

  • cookie — contournement pour le debug : on fournit directement un connect.sid déjà valide (ex. copié d'un navigateur). Le serveur vérifie quand même que le cookie est utilisable via GET /me.

Comment savoir quel mode utiliser sur une instance inconnue ? Le symptôme typique d'un mauvais mode est trompeur : en local sur une instance Keycloak, le login « réussit » (HTTP 200, cookie posé) mais la session est anonyme, et c'est GET /me qui renvoie {"status": "forbidden"} — le serveur MCP le détecte et lève AuthenticationError, mais si on ne passe pas par le MCP, on peut longtemps chercher un problème de credentials. Pour trancher vite :

  • GET /auth/oauth2 renvoie une redirection vers l'IdP → l'instance est en mode IdP, utiliser oidc.

  • POST /login avec de bonnes identités renvoie « Invalid email or password » → il n'y a pas de compte local correspondant, utiliser oidc.

  • les deux se négocient → local est plus simple, l'utiliser.

Utilisation avec podman-compose

Une seule image, les deux variantes changent uniquement HEDGEDOC_READ_ONLY (exemple de composition, à adapter à votre registre) :

{% for v in librechat_mcp_servers.hedgedoc.variants %}
  mcp-hedgedoc-{{ v }}:
    image: ghcr.io/votre-org/mcp-hedgedoc:latest
    container_name: mcp-hedgedoc-{{ v }}
    environment:
      - HEDGEDOC_URL={{ hedgedoc_addr }}
      - HEDGEDOC_AUTH_MODE=local
      - HEDGEDOC_EMAIL={{ mcp_hedgedoc_bot_email }}
      - HEDGEDOC_PASSWORD={{ mcp_hedgedoc_bot_password }}
      - HEDGEDOC_READ_ONLY={{ 'true' if v == 'ro' else 'false' }}
      - REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-bundle.crt
    volumes:
      - /etc/ssl/certs/ca-bundle.crt:/etc/ssl/certs/ca-bundle.crt:ro
    restart: unless-stopped

{% endfor %}

Construire et publier l'image

L'image est publiée sur GHCR par la CI GitHub Actions (voir .github/workflows/ci.yml) : tag immuable par commit, plus latest sur la branche par défaut. Pour construire et pousser manuellement :

podman build -t ghcr.io/votre-org/mcp-hedgedoc:latest -f Containerfile .
podman login ghcr.io
podman push ghcr.io/votre-org/mcp-hedgedoc:latest

Vérifier une image après construction

scripts/smoke_test.sh ghcr.io/votre-org/mcp-hedgedoc:latest

Lance le conteneur en mode lecture seule contre une URL bidon, vérifie que les 6 outils de lecture apparaissent et que create_note est bien rejeté — sans jamais toucher à une vraie instance HedgeDoc. Voir le script pour le détail.

Tests

pip install -e ".[dev]"
pytest

24 tests, tous hors-réseau (mocks HTTP via responses, page IdP synthétique pour le scraping OIDC) sauf le smoke test du conteneur ci-dessus, qui est volontairement séparé du reste.

Voir aussi

AGENTS.md pour les conventions du dépôt (comment ajouter un outil, pourquoi certains choix ont été faits) et les limitations connues de HedgeDoc 1.x sur lesquelles ce projet s'appuie.

Licence

GPL-3.0-or-later — voir LICENSE.

Related MCP Connectors

Related MCP Servers