Skip to main content
Glama
MartinCley

SharePoint-Cleyrop MCP

by MartinCley
README.md
# SharePoint → Cleyrop MCP

Serveur **MCP** qui récupère des fichiers depuis **SharePoint** (via Microsoft
Graph, authentification **Entra ID** déléguée et **multi-tenant**) et les dépose
dans les **données de travail** d'un projet **Cleyrop** (via `cleyrop-sdk`).

Conçu pour un usage **multi-organisations en self-service** : chaque utilisateur
se connecte avec le compte de **son** organisation et accède au SharePoint de
celle-ci, avec **ses propres droits**. L'isolation par utilisateur se fait via
un **refresh token propre à chaque personne** (variable utilisateur du Tool).

## Fonctionnement

```
SharePoint  ──(Microsoft Graph, token utilisateur Entra ID)──▶  tool MCP
                                                                   │
                                                                   ▼
                                          cleyrop-sdk.upload_file()  ──▶  Données de travail (projet Cleyrop)
```

Les fichiers transitent par des fichiers temporaires locaux (pas de limite
mémoire ; compatible gros fichiers jusqu'à la limite Cleyrop de 5 Go).

## Tools exposés

| Tool | Rôle |
|------|------|
| `sharepoint_login_start()` | Démarrer la connexion (renvoie URL + code) |
| `sharepoint_login_finish(login_id)` | Finaliser et obtenir son refresh token |
| `sharepoint_search_sites(query)` | Trouver l'identifiant d'un site SharePoint |
| `sharepoint_list_folder(site, path="", drive=None)` | Explorer une bibliothèque / un dossier |
| `sharepoint_read_file(site, path, drive=None, max_chars=20000)` | Lire le texte d'un fichier (PDF/Word/Excel/texte) **sans import** |
| `cleyrop_list_projects()` | Lister les projets Cleyrop (id / slug) |
| `transfer_sharepoint_to_cleyrop(site, path, project, drive=None, cleyrop_folder=None, recursive=True)` | Copier fichier(s) SharePoint → Cleyrop |
| `sharepoint_check_auth()` | Vérifier que le refresh token configuré donne accès à SharePoint |
| `sharepoint_debug_headers()` | Diagnostic : headers reçus (identifier la variable utilisateur) |

`site` accepte trois formes : un **id Graph**, un chemin
`contoso.sharepoint.com:/sites/MonSite`, ou une **URL** complète.

---

## 1. Configuration côté Entra ID (à faire par toi)

> Une seule application Entra ID, en **multi-tenant** et **client public**
> (device code) : **aucun secret client**. Chaque utilisateur s'authentifie avec
> son compte et n'accède qu'à ce qu'il peut déjà voir dans son SharePoint.

1. **Portail Entra ID** → *Identity* → *Applications* → *App registrations* →
   **New registration**.
   - *Name* : `sharepoint-cleyrop-mcp` (au choix).
   - *Supported account types* : **Accounts in any organizational directory
     (Multitenant)** ← important pour le multi-organisations.
   - *Redirect URI* : laisser vide.
   - → **Register**. Note l'**Application (client) ID**.

2. **Authentication** → active **Allow public client flows** = **Yes**
   (indispensable pour le device code flow). → *Save*.

3. **API permissions** → **Add a permission** → **Microsoft Graph** →
   **Delegated permissions** → ajoute :
   - `Sites.Read.All`
   - `Files.Read.All`
   - `offline_access` (indispensable : c'est ce qui délivre le **refresh token**)
   - `User.Read` (généralement déjà présent)
   - **Grant admin consent** pour TON tenant.

4. Pour la config : `ENTRA_CLIENT_ID` = Application (client) ID.
   (`ENTRA_TENANT_ID` reste à `organizations` — valeur par défaut multi-tenant.)

> **Consentement des autres organisations** : tu ne peux consentir que pour ton
> tenant. Pour chaque autre organisation, **un admin (ou l'utilisateur) de cette
> org devra approuver l'application à sa première connexion**. `Sites.Read.All`
> est souvent « admin-consent required » → certaines orgs demanderont une
> validation par leur admin. Faire la *Publisher verification* de l'app réduit
> la friction du consentement.

---

## 2. Configuration côté Cleyrop (à faire par toi)

Le `cleyrop-sdk` s'authentifie via `ClientConfig.from_env()` +
`login_client_credentials()`. Tu dois fournir les **client credentials** de ton
service/projet Cleyrop sous forme de variables d'environnement.

> Les noms exacts des variables dépendent de ton instance — vérifie-les dans la
> [doc cleyrop-sdk](https://cleyrop.gitbook.io/docs/support-and-ressources/references-techniques-api-sdk/sdk-cleyrop-sdk).
> Renseigne-les dans `.env` (voir `.env.example`).

Le package `cleyrop-sdk` étant privé (registre GitLab), l'installation nécessite
`UV_INDEX_CLEYROP_SDK_GITLAB_USERNAME` / `..._PASSWORD` (voir ci-dessous).

---

## 3. Installation

```bash
cd /home/cleyrop/tool-sharepoint
uv venv && source .venv/bin/activate

# cleyrop-sdk vient d'un registre privé GitLab :
export UV_INDEX_CLEYROP_SDK_GITLAB_USERNAME=...
export UV_INDEX_CLEYROP_SDK_GITLAB_PASSWORD=...

uv pip install -e . \
  --extra-index-url "https://${UV_INDEX_CLEYROP_SDK_GITLAB_USERNAME}:${UV_INDEX_CLEYROP_SDK_GITLAB_PASSWORD}@gitlab.com/api/v4/projects/79823225/packages/pypi/simple"
```

Copie `.env.example` → `.env` et renseigne les variables.

## 4. Connexion de chaque utilisateur (une fois par personne)

**Depuis l'assistant, en langage naturel** — l'utilisateur dit simplement
« je veux me connecter à SharePoint » et l'assistant enchaîne :

1. **`sharepoint_login_start`** → URL + code à saisir dans le navigateur
   (connexion avec le compte de l'organisation de l'utilisateur).
2. L'utilisateur s'authentifie, puis dit « c'est bon ».
3. **`sharepoint_login_finish`** (appelé **sans argument** — le serveur retrouve
   la connexion en cours via `x-user-id`) → la connexion est **mémorisée côté
   serveur** pour cet utilisateur. **Rien à copier, aucun `login_id` à fournir** :
   il peut enchaîner directement (recherche de sites, transfert…).

> Le token est gardé **en mémoire**, par utilisateur. Il est donc perdu si le
> conteneur redémarre : il suffit alors de relancer la connexion. Pour survivre
> aux redémarrages, l'utilisateur peut (optionnel) coller le token fourni par
> `login_finish` dans sa variable utilisateur `SHAREPOINT_REFRESH_TOKEN` ; le
> serveur la lit en repli si la mémoire est vide.

> **Alternative CLI** (tests, en DevSpace où `cleyrop-sdk` est disponible) :
> `ENTRA_CLIENT_ID=<client-id> sharepoint-cleyrop-login`.

> Le serveur déployé ne fait jamais de connexion interactive « invisible » : il
> échange silencieusement le refresh token contre des access tokens, **par
> utilisateur**.

## 5. Utilisation en local (client MCP de bureau, stdio)

Pour tester en local sans déploiement :

```json
{
  "mcpServers": {
    "sharepoint-cleyrop": {
      "command": "/home/cleyrop/tool-sharepoint/.venv/bin/sharepoint-cleyrop-mcp",
      "env": {
        "ENTRA_CLIENT_ID": "...",
        "SHAREPOINT_REFRESH_TOKEN": "..."
      }
    }
  }
}
```

Ce mode utilise le transport **stdio**. En conteneur / Tool Cleyrop, c'est
`main.py` qui démarre le serveur en **HTTP** (streamable-http, voir section Build),
et `SHAREPOINT_REFRESH_TOKEN` provient de la variable utilisateur de chaque
personne.

---

## Exemple d'usage (depuis un client MCP)

1. `sharepoint_search_sites("marketing")` → récupère le `site`.
2. `sharepoint_list_folder(site, "Documents partages/2026")` → repère les fichiers.
3. `cleyrop_list_projects()` → récupère l'id/slug du projet.
4. `transfer_sharepoint_to_cleyrop(site, "Documents partages/2026/rapport.pdf", "mon-projet")`
   ou un dossier entier :
   `transfer_sharepoint_to_cleyrop(site, "Documents partages/2026", "mon-projet", cleyrop_folder="sharepoint/2026")`.

## Build de l'image (Docker / La Fabrique)

Le [Dockerfile](Dockerfile) suit la convention des tools MCP Cleyrop :
base `astral/uv`, `uv sync`, serveur exposé en **HTTP** (streamable-http) sur le
port `MCP_PORT` (défaut **8000**, endpoint `/mcp`).

`cleyrop-sdk` (registre GitLab privé) est résolu par **uv** via l'index
`cleyrop-sdk-gitlab` déclaré dans `pyproject.toml`. uv s'authentifie tout seul
avec les variables `UV_INDEX_CLEYROP_SDK_GITLAB_USERNAME` /
`UV_INDEX_CLEYROP_SDK_GITLAB_PASSWORD` — **injectées par l'environnement de build
Cleyrop**, donc aucun secret à passer au build.

```bash
# build local (hors Cleyrop) : exporter d'abord les credentials du registre
export UV_INDEX_CLEYROP_SDK_GITLAB_USERNAME=...
export UV_INDEX_CLEYROP_SDK_GITLAB_PASSWORD=...
docker build -t sharepoint-cleyrop-mcp .
docker run --rm -p 8000:8000 --env-file .env sharepoint-cleyrop-mcp
```

> Dans **La Fabrique** : *Registre interne → Créer une image → Dépôt interne →*
> dépôt `tool-sharepoint`, branche `v1`, chemin du Dockerfile `Dockerfile`.

### Variables d'environnement de l'instance (Cleyrop)

| Variable | Portée | Valeur |
|----------|--------|--------|
| `ENTRA_CLIENT_ID` | globale (instance) | Application (client) ID de l'app Entra ID |
| `CLEYROP_DOMAIN` | globale (instance) | domaine Cleyrop (cible de l'API données de travail) |
| `MCP_PORT` | globale | `8000` (= « port exposé » de l'instance) |
| `SHAREPOINT_REFRESH_TOKEN` | **par utilisateur** | refresh token perso (via `sharepoint_login_start/finish`) |
| `ENTRA_TENANT_ID` | globale (option) | `organizations` par défaut ; un GUID pour restreindre à une seule org |
| `CLEYROP_USER_TOKEN_HEADER` | globale (option) | nom exact du header portant le token utilisateur Cleyrop (si la détection auto échoue) |

**Authentification Cleyrop** : l'upload se fait **au nom de l'utilisateur**, via
le **token utilisateur transmis par Cleyrop dans un header** de la requête (lu
automatiquement, comme `SHAREPOINT_REFRESH_TOKEN`). Le SDK l'utilise comme bearer
(`ClientConfig.token`) — **pas besoin de client_secret**. Repli possible sur un
compte de service (`CLEYROP_CLIENT_ID` / `CLEYROP_CLIENT_SECRET`) si aucun token
utilisateur n'est disponible.

> `CLEYROP_DOMAIN` est requis pour que le SDK sache quelle API contacter. Si la
> détection du header de token Cleyrop échoue, l'outil `sharepoint_debug_headers`
> liste les headers reçus → fixe alors `CLEYROP_USER_TOKEN_HEADER`.

## Notes & limites

- **Multi-tenant & droits** : chaque utilisateur accède au SharePoint de **son**
  organisation, avec **ses propres droits** (flux délégué). L'isolation est
  assurée par le refresh token personnel — ne jamais mettre `SHAREPOINT_REFRESH_TOKEN`
  en variable globale.
- **Variable utilisateur transmise en header** : sur Cleyrop, les variables
  *utilisateur* arrivent dans les **headers de la requête HTTP** (et non comme
  variables d'environnement du process). Le serveur lit donc `SHAREPOINT_REFRESH_TOKEN`
  dans les headers (détection tolérante casse/tirets/préfixe). Si le nom de header
  diffère, le fixer via `REFRESH_TOKEN_HEADER`. L'outil `sharepoint_debug_headers`
  aide à identifier le nom exact.
- **Expiration du refresh token** : un refresh token Entra ID est valable ~90
  jours en usage glissant. S'il expire (ou en cas de changement de mot de passe /
  révocation), les tools renvoient une erreur invitant à relancer la connexion.
- **« Token expiré » immédiat = mauvaise valeur, pas une vraie expiration** : un
  refresh token ne peut pas expirer en quelques minutes. Si Entra ID le refuse
  juste après l'avoir généré, c'est que la variable contient un token d'une
  connexion *précédente* (chaque nouvelle connexion invalide les précédentes) ou
  abîmé au copier-coller (espaces, tirets Unicode). Pour fiabiliser le transport,
  `sharepoint_login_finish` renvoie le token **encodé en hexadécimal** (aucun
  caractère altérable par le chat) ; le serveur le décode automatiquement. Le
  tool nettoie aussi espaces et tirets Unicode. Recopie toujours le token du
  **dernier** `sharepoint_login_finish`, et valide avec `sharepoint_check_auth`.
- **Sécurité** : le refresh token vaut accès au SharePoint de l'utilisateur.
  Il est stocké côté Cleyrop comme variable utilisateur (secret) ; le serveur ne
  le journalise jamais.
- **Arborescence** : un transfert de dossier recrée les sous-dossiers côté Cleyrop
  (désactivable avec `recursive=False`).
- **Limite Cleyrop** : 5 Go par fichier.

TDQS

A4/5.0

Scored across 9 tools

Disambiguation5/5

Each tool targets a distinct action: authentication flow (start/finish), diagnostics (check_auth, debug_headers), browsing (list_folder, search_sites, list_projects), reading files, and transferring. No two tools have overlapping purposes.

Naming Consistency5/5

All tools follow a consistent verb_noun pattern (e.g., sharepoint_list_folder, sharepoint_login_start, cleyrop_list_projects) with snake_case and clear prefixes for SharePoint vs Cleyrop. No mixing of conventions.

Tool Count5/5

With 9 tools covering authentication, browsing, reading, and transfer, the count is well-scoped for the integration domain. Each tool serves a clear need without excess or deficiency.

Completeness4/5

The surface covers the primary workflow (authenticate, browse, search, read, and transfer from SharePoint to Cleyrop). Minor gaps exist, such as no write operations to SharePoint (e.g., upload or delete), but these are outside the core purpose.

Maintenance

ActivityInactive
ResponsivenessNo issues