Documentation développeur
Piloter NoNote par programme : API REST & serveur MCP
NoNote expose deux surfaces protégées par OAuth2 pour les intégrations tierces :
une API REST sous /api/v1/* et un
serveur MCP distant sous /mcp. Elles partagent
exactement les mêmes gestionnaires de routes que l'application web — un outil MCP
create_task et un POST /api/v1/tasks exécutent le même code.
L'API REST s'adresse aux développeurs qui intègrent NoNote dans leurs propres
outils (scripts, back-office, automatisations). Le serveur MCP
(Model Context Protocol) s'adresse aux agents IA — Claude Desktop,
Claude.ai, ChatGPT et tout client compatible MCP — qui peuvent découvrir le
serveur, s'authentifier via OAuth2 et appeler les outils listés plus bas pour
agir sur les tâches, canvas, Drive et profil de l'utilisateur en son nom.
Chaque appel est autorisé par un jeton d'accès OAuth2 (JWT) portant
un ensemble de scopes précis. Un jeton ne peut jamais faire plus que
ce que ses scopes autorisent : un jeton tasks:read ne peut pas créer de
tâche, et un jeton profil ne peut pas toucher au Drive.
Base de production : https://www.nonote.fr — tous les
chemins ci-dessous sont relatifs à cette origine (ex. https://www.nonote.fr/api/v1/tasks).
Les chemins REST sont abrégés en /tasks, /canvases… dans le
sommaire ; leur préfixe complet est toujours /api/v1.
Flux OAuth2 : Authorization Code + PKCE
NoNote implémente Authorization Code avec PKCE (obligatoire — le
serveur refuse tout échange sans code_verifier). Aucun secret client
n'est nécessaire : les clients publics s'authentifient avec
token_endpoint_auth_method: none + PKCE.
| Étape | Endpoint | Détail |
| Découverte | GET /oauth/.well-known/oauth-authorization-server | Métadonnées RFC 8414 (endpoints, algorithmes, scopes). |
| Enregistrement dynamique | POST /oauth/register | RFC 7591, public (aucun jeton initial requis). Renvoie un client_id. |
| Autorisation | GET /oauth/authorize | Redirige l'utilisateur vers l'écran de connexion / consentement. |
| Jeton | POST /oauth/token | Échange le code contre un access_token (JWT, 15 min) + refresh_token (30 j). |
| Révocation | POST /oauth/revoke | RFC 7009. |
Le jeton d'accès est un JWT d'une durée de vie de 15 minutes ;
rafraîchissez-le avec le refresh_token (valable 30 jours). Le JWKS de
vérification est publié à GET /oauth/jwks.
Scopes disponibles
Un scope suit la forme domaine:action (read ou write).
Demandez uniquement ceux dont vous avez besoin — l'utilisateur voit la liste exacte
sur l'écran de consentement.
| Domaine | Scopes |
| Profil | profile:read · profile:write |
| Canvas | canvas:read · canvas:write |
| Drive | drive:read · drive:write |
| Tâches | tasks:read · tasks:write |
| Équipes | teams:read · teams:write |
| Drive d'équipe | teamdrive:read · teamdrive:write |
| Messages | messages:read · messages:write |
| Mail | mail:read · mail:write |
| Agenda | calendar:read · calendar:write |
| Appels | calls:read · calls:write |
| Factures | invoices:read · invoices:write |
| Produits | products:read · products:write |
| Dépenses | expenses:read · expenses:write |
| Facturation | billing:read |
| Modèles | templates:read · templates:write |
| IA | ai:use |
| Support | support:read · support:write |
Par conception, certains scopes n'existent pas. Il n'y a
aucun scope admin:* et pas de billing:write
(seul billing:read existe). Ils sont structurellement impossibles à demander :
le serveur d'autorisation rejette tout scope hors de la liste ci-dessus. Aucun jeton
délivré à une intégration ne peut donc administrer l'instance ni modifier la facturation.
Quickstart
Trois étapes : enregistrer un client, obtenir un jeton, appeler un endpoint.
1 · Enregistrer un client (public + PKCE)
POST/oauth/register
Requête
curl -X POST https://www.nonote.fr/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "Mon intégration",
"redirect_uris": ["https://mon-app.example/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"scope": "tasks:read tasks:write canvas:read"
}'
Réponse (201)
{
"client_id": "s6BhdRkqt3",
"client_id_issued_at": 1784200000,
"client_name": "Mon intégration",
"redirect_uris": ["https://mon-app.example/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"scope": "tasks:read tasks:write canvas:read"
}
2 · Autoriser puis échanger le code
Redirigez l'utilisateur vers l'URL d'autorisation (PKCE : code_challenge_method=S256) :
GET/oauth/authorize
https://www.nonote.fr/oauth/authorize
?response_type=code
&client_id=s6BhdRkqt3
&redirect_uri=https://mon-app.example/callback
&scope=tasks:read%20tasks:write
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&state=af0ifjsldkj
Après consentement, l'utilisateur est renvoyé vers redirect_uri avec ?code=…&state=…. Échangez ce code :
Requête
curl -X POST https://www.nonote.fr/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=authorization_code \
-d code=SplxlOBeZQQYbYS6WxSbIA \
-d redirect_uri=https://mon-app.example/callback \
-d client_id=s6BhdRkqt3 \
-d code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
Réponse (200)
{
"access_token": "eyJhbGciOiJSUzI1Ni…",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "GEbRxBN…",
"scope": "tasks:read tasks:write"
}
3 · Appeler un endpoint
GET/api/v1/tasks
curl https://www.nonote.fr/api/v1/tasks \
-H "Authorization: Bearer eyJhbGciOiJSUzI1Ni…"
En JavaScript : await fetch("https://www.nonote.fr/api/v1/tasks", { headers: { Authorization: `Bearer ${token}` } }).then(r => r.json())
Section 2
API REST · /api/v1/*
Chaque endpoint indique sa méthode, son chemin, le scope requis, un exemple de corps
de requête (le cas échéant) et un exemple de réponse réel, obtenu en
exécutant le gestionnaire de route contre une base de développement. Sur GET/HEAD le
scope requis est domaine:read ; sur toute écriture, domaine:write.
Profil
GET/api/v1/auth/me
Renvoie le profil de l'utilisateur authentifié, ses fonctionnalités de plan et les feature flags de l'app.
Scope requis profile:read
Requête
GET /api/v1/auth/me
Authorization: Bearer <token>
Réponse (200)
{
"user": {
"id": "caa35a21-d7b7-4599-812c-35080160b6fe",
"name": "Ada Lovelace",
"email": "ada@example.com",
"plan": "free",
"role": "user",
"features": ["canvas", "drive", "chat", "mail",
"tasks", "tickets", "billing", "priority_support"],
"avatarUrl": null,
"avatarColor": "#0B40C7",
"bio": "Développeuse — pilote NoNote via API.",
"status": "offline",
"actif": "actif",
"confirmedAt": null,
"onboardingStep": 0,
"onboardingCompleted": false,
"persona": null,
"sousProfil": null,
"firstCanvasId": null,
"totpEnabled": false,
"featureFlags": {
"templatesMarketplaceVisible": true,
"callsEnabled": true,
"mailboxEnabled": true
}
}
}
PUT/api/v1/auth/profile
Met à jour le profil. Champs acceptés : name, bio, avatarColor (format #RRGGBB), avatarUrl. Les champs omis restent inchangés.
Scope requis profile:write
Requête
{
"name": "Ada Lovelace",
"bio": "Développeuse — pilote NoNote via API.",
"avatarColor": "#0B40C7"
}
Réponse (200)
{
"user": {
"id": "caa35a21-d7b7-4599-812c-35080160b6fe",
"name": "Ada Lovelace",
"email": "ada@example.com",
"plan": "free",
"role": "user",
"features": [],
"avatarUrl": null,
"avatarColor": "#0B40C7",
"bio": "Développeuse — pilote NoNote via API.",
"status": "offline",
"actif": "actif",
"confirmedAt": null,
"onboardingStep": 0,
"onboardingCompleted": false,
"persona": null,
"sousProfil": null,
"firstCanvasId": null,
"totpEnabled": false
},
"message": "Profil mis a jour."
}
Tâches
Une tâche peut être en mode quick (par défaut — jeu de champs minimal) ou
full (champs étendus : status, priority,
labels, subtasks…). La réponse ne contient les champs étendus
que pour une tâche full.
GET/api/v1/tasks
Liste les tâches de l'utilisateur. Filtres via query : mode, done, status, priority, canvas_id, search, sort, order, limit (≤ 200), offset.
Scope requis tasks:read
Requête
GET /api/v1/tasks?limit=50
Authorization: Bearer <token>
Réponse (200)
{
"tasks": [
{
"id": "2367d366-d66a-40d0-a086-5523f6a953b7",
"key": null,
"mode": "quick",
"title": "Préparer la démo API",
"done": false,
"dueDate": "2026-07-20",
"canvasId": null,
"postitId": null,
"createdAt": "2026-07-16T13:57:18.570Z",
"updatedAt": "2026-07-16T13:57:18.570Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}
POST/api/v1/tasks
Crée une tâche. Seul title est requis. Passez "mode": "full" pour renseigner status, priority, labels, estimatedHours, etc.
Scope requis tasks:write
Requête
{
"title": "Migrer le webhook de facturation",
"mode": "full",
"status": "in_progress",
"priority": "high",
"type": "task",
"description": "Basculer vers la v1 signee.",
"labels": ["backend", "api"],
"estimatedHours": 4,
"dueDate": "2026-07-22"
}
Réponse (201)
{
"id": "5f96ffc7-f346-4dbc-9080-cad415892da3",
"key": null,
"mode": "full",
"title": "Migrer le webhook de facturation",
"done": false,
"dueDate": "2026-07-22",
"canvasId": null,
"postitId": null,
"createdAt": "2026-07-16T14:02:12.535Z",
"updatedAt": "2026-07-16T14:02:12.535Z",
"description": "Basculer vers la v1 signee.",
"status": "in_progress",
"priority": "high",
"type": "task",
"assignee": "",
"assigneeId": null,
"reporter": "",
"labels": ["backend", "api"],
"estimatedHours": 4,
"subtasks": [],
"activity": [],
"comments": []
}
PUT/api/v1/tasks/:id
Met à jour une tâche. Envoyez uniquement les champs à modifier. Passer status: "done" coche automatiquement done.
Scope requis tasks:write
Requête
PUT /api/v1/tasks/2367d366-d66a-40d0-a086-5523f6a953b7
{
"status": "in_progress",
"description": "Script rédigé, relecture en cours"
}
Réponse (200)
{
"id": "2367d366-d66a-40d0-a086-5523f6a953b7",
"key": null,
"mode": "quick",
"title": "Préparer la démo API",
"done": false,
"dueDate": "2026-07-20",
"canvasId": null,
"postitId": null,
"createdAt": "2026-07-16T13:57:18.570Z",
"updatedAt": "2026-07-16T13:57:18.612Z"
}
DELETE/api/v1/tasks/:id
Supprime définitivement une tâche. Action irréversible.
Scope requis tasks:write
Requête
DELETE /api/v1/tasks/2367d366-d66a-40d0-a086-5523f6a953b7
Authorization: Bearer <token>
Réponse (200)
{ "ok": true }
Canvas
GET/api/v1/canvases
Liste paginée des canvas accessibles (possédés ou partagés) avec leurs dossiers. Query : page, limit (≤ 100).
Scope requis canvas:read
Requête
GET /api/v1/canvases?page=1&limit=20
Authorization: Bearer <token>
Réponse (200)
{
"data": [
{
"id": "007819e6-fad3-4bdc-a8c8-1ccc5820b3b0",
"name": "Feuille de route API",
"createdAt": "2026-07-16T13:57:18.640Z",
"updatedAt": "2026-07-16T13:57:18.640Z",
"thumbnail": null,
"isShared": false,
"publicShareToken": null,
"isPublicShare": false,
"ownerId": "caa35a21-d7b7-4599-812c-35080160b6fe",
"folderId": null,
"ownerName": "Ada Lovelace",
"collaborators": [],
"postits": [],
"drawings": [],
"events": [],
"connections": [],
"publishedTemplate": null
}
],
"folders": [],
"pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }
}
GET/api/v1/canvases/:id
Détail complet d'un canvas, avec ses post-its, dessins, événements et connexions déchiffrés.
Scope requis canvas:read
Requête
GET /api/v1/canvases/016dd03b-7cc3-44db-9a96-0721159abfd2
Authorization: Bearer <token>
Réponse (200)
{
"id": "016dd03b-7cc3-44db-9a96-0721159abfd2",
"name": "Kanban Sprint 12",
"createdAt": "2026-07-16T14:02:12.610Z",
"updatedAt": "2026-07-16T14:02:12.846Z",
"thumbnail": null,
"isShared": false,
"publicShareToken": null,
"isPublicShare": false,
"ownerId": "85d832a6-9b1b-496d-abba-49ec07f43bc5",
"folderId": null,
"ownerName": "Ada Lovelace",
"collaborators": [],
"postits": [
{
"id": "f96dc5b7-bfaa-49c6-a3e8-c9936c0b0fa3",
"type": "sticky",
"x": 160, "y": 120, "w": 320, "h": 200,
"data": { "text": "Revue de code", "color": "#E8EEFC" }
}
],
"drawings": [],
"events": [],
"connections": [],
"publishedTemplate": null
}
POST/api/v1/canvases
Crée un canvas. Champs : name (≤ 200 car.), folderId et postits[] initiaux optionnels.
Scope requis canvas:write
Requête
{ "name": "Feuille de route API" }
Réponse (201)
{
"id": "007819e6-fad3-4bdc-a8c8-1ccc5820b3b0",
"name": "Feuille de route API",
"createdAt": "2026-07-16T13:57:18.640Z",
"updatedAt": "2026-07-16T13:57:18.640Z",
"thumbnail": null,
"isShared": false,
"publicShareToken": null,
"isPublicShare": false,
"ownerId": "caa35a21-d7b7-4599-812c-35080160b6fe",
"folderId": null,
"ownerName": null,
"collaborators": [],
"postits": [],
"drawings": [],
"events": [],
"connections": [],
"publishedTemplate": null
}
POST/api/v1/canvases/:id/postits
Ajoute un bloc (post-it) au canvas. Champs : type, x, y (numériques requis), w, h, data. Renvoie l'identifiant du bloc créé.
Scope requis canvas:write
Requête
{
"type": "sticky",
"x": 160, "y": 120, "w": 320, "h": 200,
"data": { "text": "Revue de code", "color": "#E8EEFC" }
}
Réponse (201)
{ "id": "f96dc5b7-bfaa-49c6-a3e8-c9936c0b0fa3" }
DELETE/api/v1/canvases/:id/postits/:pid
Supprime définitivement un bloc du canvas.
Scope requis canvas:write
Requête
DELETE /api/v1/canvases/016dd03b-…/postits/f96dc5b7-…
Authorization: Bearer <token>
Réponse (200)
{ "ok": true }
Drive
GET/api/v1/drive
Liste les fichiers et dossiers du Drive personnel (racine par défaut ; ?parentId= pour un sous-dossier). Les dossiers portent le nombre et la taille cumulée de leurs enfants.
Scope requis drive:read
Requête
GET /api/v1/drive
Authorization: Bearer <token>
Réponse (200)
[
{
"id": "982a5c77-c673-4d08-b6a3-c79b655f3e5d",
"parentId": null,
"canvasId": null,
"name": "Exports API",
"type": "folder",
"mimeType": null,
"size": 0,
"createdAt": "2026-07-16T13:57:18.924Z",
"childFileCount": 0,
"childTotalSize": 0
}
]
POST/api/v1/drive/folder
Crée un dossier. Champs : name (requis), parentId (optionnel).
Scope requis drive:write
Requête
{ "name": "Exports API" }
Réponse (201)
{
"id": "982a5c77-c673-4d08-b6a3-c79b655f3e5d",
"parentId": null,
"canvasId": null,
"name": "Exports API",
"type": "folder",
"mimeType": null,
"size": 0,
"createdAt": "2026-07-16T13:57:18.924Z"
}
DELETE/api/v1/drive/:fileId
Supprime définitivement un fichier ou un dossier (et son contenu). Action irréversible.
Scope requis drive:write
Requête
DELETE /api/v1/drive/982a5c77-c673-4d08-b6a3-c79b655f3e5d
Authorization: Bearer <token>
Réponse (200)
{ "ok": true }
Section 3
Outils MCP · /mcp
Le serveur MCP distant expose 13 outils via JSON-RPC 2.0 sur
POST /mcp (transport HTTP streamable). Le serveur s'annonce comme
{ "name": "nonote", "version": "1.0.0" }. Chaque appel porte l'en-tête
Authorization: Bearer <token> ; tools/list ne renvoie
que les outils couverts par les scopes du jeton.
Enveloppe de réponse. Le résultat d'un outil est renvoyé dans un bloc
content de type text, dont le champ text contient
le JSON du résultat (sérialisé) :
ENVELOPPEresult.content[0]
tools/call — requête
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_tasks",
"arguments": {}
}
}
tools/call — réponse
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\"tasks\":[…],\"total\":1,\"limit\":50,\"offset\":0}"
}
]
}
}
Pour la lisibilité, les exemples ci-dessous montrent le JSON déjà désérialisé du champ text, sans répéter l'enveloppe content.
mutatingSensitive — confirmation en deux temps.
Les outils destructifs (delete_task, delete_postit,
delete_drive_file) sont marqués mutatingSensitive. Un
premier appel n'exécute rien : il renvoie
{ "needsConfirmation": true, "actionId": "…" }. Un
second appel avec { "actionId": "…" } redéclenche l'outil
avec les arguments d'origine (les arguments renvoyés avec l'actionId
sont ignorés). Voir delete_task pour un exemple complet.
Profil
TOOLget_profile
Récupère le profil de l'utilisateur authentifié (nom, bio, avatar, plan).
Scope profile:readmutatingSensitive false
Input schema
{ "type": "object", "properties": {} }
tools/call
{
"jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": { "name": "get_profile", "arguments": {} }
}
Résultat (texte JSON)
{
"user": {
"id": "caa35a21-d7b7-4599-812c-35080160b6fe",
"name": "Ada Lovelace",
"email": "ada@example.com",
"plan": "free",
"role": "user",
"features": ["canvas", "drive", "chat", "mail",
"tasks", "tickets", "billing", "priority_support"],
"avatarColor": "#0B40C7",
"bio": "Développeuse — pilote NoNote via API.",
"totpEnabled": false,
"featureFlags": {
"templatesMarketplaceVisible": true,
"callsEnabled": true, "mailboxEnabled": true
}
}
}
TOOLupdate_profile
Met à jour le profil (name, bio, avatarColor, avatarUrl). Les champs omis restent inchangés.
Scope profile:writemutatingSensitive false
Input schema
{
"type": "object",
"properties": {
"name": { "type": "string" },
"bio": { "type": "string" },
"avatarColor": { "type": "string" },
"avatarUrl": { "type": "string" }
}
}
tools/call
{
"jsonrpc": "2.0", "id": 3, "method": "tools/call",
"params": {
"name": "update_profile",
"arguments": {
"name": "Ada Lovelace",
"bio": "Développeuse — pilote NoNote via API.",
"avatarColor": "#0B40C7"
}
}
}
Résultat (texte JSON)
{
"user": {
"id": "caa35a21-d7b7-4599-812c-35080160b6fe",
"name": "Ada Lovelace",
"avatarColor": "#0B40C7",
"bio": "Développeuse — pilote NoNote via API.",
"plan": "free", "role": "user"
},
"message": "Profil mis a jour."
}
Tâches
TOOLlist_tasks
Liste les tâches de l'utilisateur (Kanban / todo).
Scope tasks:readmutatingSensitive false
Input schema
{ "type": "object", "properties": {} }
tools/call
{
"jsonrpc": "2.0", "id": 4, "method": "tools/call",
"params": { "name": "list_tasks", "arguments": {} }
}
Résultat (texte JSON)
{
"tasks": [
{
"id": "2367d366-d66a-40d0-a086-5523f6a953b7",
"mode": "quick",
"title": "Préparer la démo API",
"done": false,
"dueDate": "2026-07-20",
"createdAt": "2026-07-16T13:57:18.570Z",
"updatedAt": "2026-07-16T13:57:18.570Z"
}
],
"total": 1, "limit": 50, "offset": 0
}
TOOLcreate_task
Crée une tâche. Champs : title (obligatoire), description, status, dueDate.
Scope tasks:writemutatingSensitive false
Input schema
{
"type": "object",
"properties": {
"title": { "type": "string" },
"description": { "type": "string" },
"status": { "type": "string" },
"dueDate": { "type": "string" }
},
"required": ["title"]
}
tools/call
{
"jsonrpc": "2.0", "id": 5, "method": "tools/call",
"params": {
"name": "create_task",
"arguments": {
"title": "Préparer la démo API",
"description": "Rédiger le script de démonstration",
"status": "todo",
"dueDate": "2026-07-20"
}
}
}
Résultat (texte JSON)
{
"id": "2367d366-d66a-40d0-a086-5523f6a953b7",
"key": null,
"mode": "quick",
"title": "Préparer la démo API",
"done": false,
"dueDate": "2026-07-20",
"canvasId": null,
"postitId": null,
"createdAt": "2026-07-16T13:57:18.570Z",
"updatedAt": "2026-07-16T13:57:18.570Z"
}
TOOLupdate_task
Met à jour une tâche existante par id.
Scope tasks:writemutatingSensitive false
Input schema
{
"type": "object",
"properties": {
"id": { "type": "string" },
"title": { "type": "string" },
"description": { "type": "string" },
"status": { "type": "string" }
},
"required": ["id"]
}
tools/call
{
"jsonrpc": "2.0", "id": 6, "method": "tools/call",
"params": {
"name": "update_task",
"arguments": {
"id": "2367d366-d66a-40d0-a086-5523f6a953b7",
"status": "in_progress",
"description": "Script rédigé, relecture en cours"
}
}
}
Résultat (texte JSON)
{
"id": "2367d366-d66a-40d0-a086-5523f6a953b7",
"mode": "quick",
"title": "Préparer la démo API",
"done": false,
"dueDate": "2026-07-20",
"createdAt": "2026-07-16T13:57:18.570Z",
"updatedAt": "2026-07-16T13:57:18.612Z"
}
TOOLdelete_task
Supprime définitivement une tâche par id. Action irréversible.
Scope tasks:writemutatingSensitive true
Input schema
{
"type": "object",
"properties": { "id": { "type": "string" } },
"required": ["id"]
}
1ᵉʳ appel — demande de confirmation
// requête
{
"jsonrpc": "2.0", "id": 7, "method": "tools/call",
"params": {
"name": "delete_task",
"arguments": { "id": "2367d366-…-5523f6a953b7" }
}
}
// résultat (texte JSON) — RIEN n'est supprimé
{
"needsConfirmation": true,
"message": "Confirmation requise pour delete_task. Rappeler ce tool avec { actionId: \"a1b2c3…\" } pour executer.",
"actionId": "a1b2c3d4e5f6"
}
2ᵉ appel — exécution
// requête
{
"jsonrpc": "2.0", "id": 8, "method": "tools/call",
"params": {
"name": "delete_task",
"arguments": { "actionId": "a1b2c3d4e5f6" }
}
}
// résultat (texte JSON)
{ "ok": true }
Canvas
TOOLlist_canvases
Liste les canvas (tableaux blancs infinis) de l'utilisateur.
Scope canvas:readmutatingSensitive false
Input schema
{ "type": "object", "properties": {} }
tools/call
{
"jsonrpc": "2.0", "id": 9, "method": "tools/call",
"params": { "name": "list_canvases", "arguments": {} }
}
Résultat (texte JSON)
{
"data": [
{
"id": "007819e6-fad3-4bdc-a8c8-1ccc5820b3b0",
"name": "Feuille de route API",
"ownerId": "caa35a21-…-35080160b6fe",
"ownerName": "Ada Lovelace",
"folderId": null,
"postits": [],
"collaborators": [],
"createdAt": "2026-07-16T13:57:18.640Z",
"updatedAt": "2026-07-16T13:57:18.640Z"
}
],
"folders": [],
"pagination": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }
}
TOOLcreate_canvas
Crée un nouveau canvas. Champ : name (obligatoire).
Scope canvas:writemutatingSensitive false
Input schema
{
"type": "object",
"properties": { "name": { "type": "string" } },
"required": ["name"]
}
tools/call
{
"jsonrpc": "2.0", "id": 10, "method": "tools/call",
"params": {
"name": "create_canvas",
"arguments": { "name": "Feuille de route API" }
}
}
Résultat (texte JSON)
{
"id": "007819e6-fad3-4bdc-a8c8-1ccc5820b3b0",
"name": "Feuille de route API",
"ownerId": "caa35a21-…-35080160b6fe",
"folderId": null,
"postits": [],
"collaborators": [],
"createdAt": "2026-07-16T13:57:18.640Z",
"updatedAt": "2026-07-16T13:57:18.640Z"
}
TOOLcreate_postit
Crée un bloc dans un canvas. Types : sticky, page, todo, kanban, table… Champs : canvasId, type, data (obligatoires), x, y (optionnels, défaut 0 ; les dimensions w/h par défaut dépendent du type).
Scope canvas:writemutatingSensitive false
Input schema
{
"type": "object",
"properties": {
"canvasId": { "type": "string" },
"type": { "type": "string" },
"data": { "type": "object" },
"x": { "type": "number" },
"y": { "type": "number" }
},
"required": ["canvasId", "type", "data"]
}
tools/call
{
"jsonrpc": "2.0", "id": 11, "method": "tools/call",
"params": {
"name": "create_postit",
"arguments": {
"canvasId": "007819e6-…-1ccc5820b3b0",
"type": "sticky",
"data": { "text": "Publier la doc développeur", "color": "#E8EEFC" },
"x": 120, "y": 80
}
}
}
Résultat (texte JSON)
{
"id": "621bd876-c479-445b-9fbc-d653ccb655a9",
"canvas_id": "007819e6-fad3-4bdc-a8c8-1ccc5820b3b0"
}
TOOLdelete_postit
Supprime définitivement un bloc d'un canvas. Action irréversible (confirmation en deux temps).
Scope canvas:writemutatingSensitive true
Input schema
{
"type": "object",
"properties": {
"canvasId": { "type": "string" },
"postitId": { "type": "string" }
},
"required": ["canvasId", "postitId"]
}
2ᵉ appel — avec actionId
{
"jsonrpc": "2.0", "id": 12, "method": "tools/call",
"params": {
"name": "delete_postit",
"arguments": { "actionId": "b7c8d9…" }
}
}
Résultat (texte JSON)
{ "ok": true }
Drive
TOOLlist_drive_files
Liste les fichiers et dossiers du Drive personnel.
Scope drive:readmutatingSensitive false
Input schema
{ "type": "object", "properties": {} }
tools/call
{
"jsonrpc": "2.0", "id": 13, "method": "tools/call",
"params": { "name": "list_drive_files", "arguments": {} }
}
Résultat (texte JSON)
[
{
"id": "982a5c77-c673-4d08-b6a3-c79b655f3e5d",
"parentId": null,
"canvasId": null,
"name": "Exports API",
"type": "folder",
"mimeType": null,
"size": 0,
"createdAt": "2026-07-16T13:57:18.924Z",
"childFileCount": 0,
"childTotalSize": 0
}
]
TOOLcreate_drive_folder
Crée un dossier dans le Drive personnel. Champ : name (obligatoire), parentId (optionnel).
Scope drive:writemutatingSensitive false
Input schema
{
"type": "object",
"properties": {
"name": { "type": "string" },
"parentId": { "type": "string" }
},
"required": ["name"]
}
tools/call
{
"jsonrpc": "2.0", "id": 14, "method": "tools/call",
"params": {
"name": "create_drive_folder",
"arguments": { "name": "Exports API" }
}
}
Résultat (texte JSON)
{
"id": "982a5c77-c673-4d08-b6a3-c79b655f3e5d",
"parentId": null,
"canvasId": null,
"name": "Exports API",
"type": "folder",
"mimeType": null,
"size": 0,
"createdAt": "2026-07-16T13:57:18.924Z"
}
TOOLdelete_drive_file
Supprime définitivement un fichier ou dossier du Drive. Action irréversible (confirmation en deux temps).
Scope drive:writemutatingSensitive true
Input schema
{
"type": "object",
"properties": { "fileId": { "type": "string" } },
"required": ["fileId"]
}
2ᵉ appel — avec actionId
{
"jsonrpc": "2.0", "id": 15, "method": "tools/call",
"params": {
"name": "delete_drive_file",
"arguments": { "actionId": "c9d0e1…" }
}
}
Résultat (texte JSON)
{ "ok": true }
Section 4
Connecter un agent IA
Tout client compatible MCP (Claude Desktop, Claude.ai, ChatGPT, etc.) peut se
connecter au serveur /mcp en suivant le flux d'autorisation
standard MCP : le client détecte que la ressource exige un jeton, découvre le
serveur d'autorisation, obtient le consentement de l'utilisateur, puis appelle les
outils.
Concrètement, le client réutilise exactement la brique OAuth2 décrite plus haut :
- La ressource protégée
/mcp annonce, via les métadonnées de ressource
protégée (/.well-known/oauth-protected-resource, RFC 9728), l'emplacement
de son serveur d'autorisation.
- Le client lit les métadonnées du serveur d'autorisation à
/oauth/.well-known/oauth-authorization-server pour trouver les endpoints
(autorisation, jeton, enregistrement).
- Il s'enregistre dynamiquement (
POST /oauth/register), puis lance
Authorization Code + PKCE — l'utilisateur choisit les scopes sur
l'écran de consentement de NoNote.
- Muni du jeton, le client appelle
tools/list (qui ne renvoie que les
outils autorisés par les scopes) puis tools/call.
Aucune configuration propriétaire n'est nécessaire : l'utilisateur colle l'URL du
serveur MCP (https://www.nonote.fr/mcp) dans son client, s'authentifie via
l'écran NoNote, et les 13 outils deviennent disponibles dans la limite des scopes
accordés. La révocation se fait comme n'importe quel jeton OAuth2
(POST /oauth/revoke ou depuis les applications connectées du compte).