{
    "openapi": "3.1.0",
    "info": {
        "title": "ProjectForge API",
        "version": "1.0.0",
        "summary": "Envoyer des feedbacks dans un projet et piloter ProjectForge via MCP.",
        "description": "Surface HTTP publique de ProjectForge. `POST /api/feedback` reçoit les retours de vos applications ; `POST /mcp` est l'endpoint JSON-RPC du serveur MCP (outils, ressources, prompts). Toutes les erreurs sont en JSON avec un `code` stable et un `hint` de résolution.",
        "contact": {
            "name": "Kenny Lauret",
            "email": "tyu.lan@outlook.fr",
            "url": "https://projectforge.tyu.re/contact"
        }
    },
    "externalDocs": {
        "description": "Documentation du serveur MCP et de l'API de feedbacks",
        "url": "https://projectforge.tyu.re/docs/mcp"
    },
    "servers": [
        {
            "url": "https://projectforge.tyu.re"
        }
    ],
    "tags": [
        {
            "name": "Feedbacks",
            "description": "Retours envoyés par vos applications dans un projet."
        },
        {
            "name": "MCP",
            "description": "Serveur Model Context Protocol (transport HTTP streamable)."
        }
    ],
    "paths": {
        "/api/feedback": {
            "post": {
                "operationId": "submitFeedback",
                "tags": [
                    "Feedbacks"
                ],
                "summary": "Envoyer un feedback dans un projet",
                "description": "Crée un feedback (bug, idée ou autre) dans le projet auquel appartient le token. Le propriétaire du projet est notifié par e-mail et peut le transformer en tâche. Limité à 30 envois par heure.",
                "security": [
                    {
                        "feedbackToken": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/FeedbackSubmission"
                            }
                        },
                        "multipart/form-data": {
                            "schema": {
                                "$ref": "#/components/schemas/FeedbackSubmissionWithImage"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Feedback enregistré.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/FeedbackAccepted"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthenticated"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "413": {
                        "$ref": "#/components/responses/PayloadTooLarge"
                    },
                    "422": {
                        "$ref": "#/components/responses/ValidationFailed"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/mcp": {
            "post": {
                "operationId": "callMcpServer",
                "tags": [
                    "MCP"
                ],
                "summary": "Appeler le serveur MCP (JSON-RPC 2.0)",
                "description": "Endpoint unique du serveur MCP. Méthodes : `initialize`, `tools/list`, `tools/call`, `resources/list`, `resources/templates/list`, `resources/read`, `prompts/list`, `prompts/get`. Outils de lecture : list_workspaces, list_projects, get_project, list_tasks, list_notes, list_journal_entries, search_documents. Outils d'écriture (token read-write) : create_project, update_project, create_doc, update_doc, create_task, update_task, create_note, update_note, convert_note, create_journal_entry, delete_resource. Tout est limité aux projets dont le propriétaire du token est membre. Limité à 60 requêtes par minute. Le plus simple est de passer par un client MCP (Claude Code, Codex) plutôt que d'appeler cet endpoint à la main.",
                "externalDocs": {
                    "url": "https://projectforge.tyu.re/docs/mcp"
                },
                "security": [
                    {
                        "mcpToken": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/JsonRpcRequest"
                            },
                            "example": {
                                "jsonrpc": "2.0",
                                "id": 1,
                                "method": "tools/list"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Réponse JSON-RPC : `result` en cas de succès, `error` sinon (un refus d'accès à un projet y remonte).",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/JsonRpcResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthenticated"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "feedbackToken": {
                "type": "http",
                "scheme": "bearer",
                "description": "Token de feedback du projet, visible dans ses paramètres."
            },
            "mcpToken": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "Sanctum",
                "description": "Token personnel créé dans Settings → Accès MCP, en portée read-only ou read-write."
            }
        },
        "schemas": {
            "FeedbackSubmission": {
                "type": "object",
                "required": [
                    "type",
                    "title",
                    "description"
                ],
                "properties": {
                    "type": {
                        "type": "string",
                        "enum": [
                            "bug",
                            "feature",
                            "other"
                        ],
                        "description": "Nature du retour."
                    },
                    "title": {
                        "type": "string",
                        "maxLength": 200,
                        "description": "Résumé en une ligne."
                    },
                    "description": {
                        "type": "string",
                        "maxLength": 10000,
                        "description": "Détail du retour, étapes pour reproduire un bug."
                    },
                    "user_email": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "email",
                        "maxLength": 255,
                        "description": "E-mail de la personne qui envoie le retour, pour lui répondre."
                    },
                    "user_name": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "maxLength": 100,
                        "description": "Nom de la personne qui envoie le retour."
                    },
                    "context": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "additionalProperties": true,
                        "description": "Contexte technique libre : URL, version de l'app, navigateur…"
                    }
                }
            },
            "FeedbackSubmissionWithImage": {
                "type": "object",
                "required": [
                    "type",
                    "title",
                    "description"
                ],
                "properties": {
                    "type": {
                        "type": "string",
                        "enum": [
                            "bug",
                            "feature",
                            "other"
                        ],
                        "description": "Nature du retour."
                    },
                    "title": {
                        "type": "string",
                        "maxLength": 200,
                        "description": "Résumé en une ligne."
                    },
                    "description": {
                        "type": "string",
                        "maxLength": 10000,
                        "description": "Détail du retour, étapes pour reproduire un bug."
                    },
                    "user_email": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "email",
                        "maxLength": 255,
                        "description": "E-mail de la personne qui envoie le retour, pour lui répondre."
                    },
                    "user_name": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "maxLength": 100,
                        "description": "Nom de la personne qui envoie le retour."
                    },
                    "context": {
                        "type": [
                            "object",
                            "null"
                        ],
                        "additionalProperties": true,
                        "description": "Contexte technique libre : URL, version de l'app, navigateur…"
                    },
                    "image": {
                        "type": "string",
                        "format": "binary",
                        "description": "Capture d'écran jpg, png, gif ou webp, 5 Mo max."
                    }
                }
            },
            "FeedbackAccepted": {
                "type": "object",
                "required": [
                    "success",
                    "message",
                    "id"
                ],
                "properties": {
                    "success": {
                        "type": "boolean",
                        "const": true
                    },
                    "message": {
                        "type": "string"
                    },
                    "id": {
                        "type": "integer",
                        "description": "Identifiant du feedback créé."
                    }
                }
            },
            "Error": {
                "type": "object",
                "required": [
                    "message",
                    "code",
                    "hint"
                ],
                "properties": {
                    "message": {
                        "type": "string",
                        "description": "Message lisible, en français."
                    },
                    "code": {
                        "type": "string",
                        "enum": [
                            "bad_request",
                            "unauthenticated",
                            "missing_token",
                            "invalid_token",
                            "forbidden",
                            "feedback_disabled",
                            "not_found",
                            "method_not_allowed",
                            "payload_too_large",
                            "validation_failed",
                            "rate_limited",
                            "server_error"
                        ],
                        "description": "Code stable, à utiliser pour réagir à l'erreur."
                    },
                    "hint": {
                        "type": "string",
                        "description": "Piste de résolution."
                    },
                    "docs": {
                        "type": "string",
                        "format": "uri"
                    },
                    "error": {
                        "type": "string",
                        "deprecated": true,
                        "description": "Ancien champ des erreurs de token, conservé pour compatibilité : lire `message`."
                    }
                }
            },
            "ValidationError": {
                "allOf": [
                    {
                        "$ref": "#/components/schemas/Error"
                    },
                    {
                        "type": "object",
                        "required": [
                            "errors"
                        ],
                        "properties": {
                            "errors": {
                                "type": "object",
                                "description": "Messages d'erreur par champ.",
                                "additionalProperties": {
                                    "type": "array",
                                    "items": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                ]
            },
            "JsonRpcRequest": {
                "type": "object",
                "required": [
                    "jsonrpc",
                    "method"
                ],
                "properties": {
                    "jsonrpc": {
                        "type": "string",
                        "const": "2.0"
                    },
                    "id": {
                        "type": [
                            "string",
                            "integer"
                        ],
                        "description": "Absent pour une notification."
                    },
                    "method": {
                        "type": "string",
                        "examples": [
                            "tools/list",
                            "tools/call",
                            "resources/read"
                        ]
                    },
                    "params": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "Pour tools/call : `{\"name\": \"list_projects\", \"arguments\": {}}`."
                    }
                }
            },
            "JsonRpcResponse": {
                "type": "object",
                "required": [
                    "jsonrpc"
                ],
                "properties": {
                    "jsonrpc": {
                        "type": "string",
                        "const": "2.0"
                    },
                    "id": {
                        "type": [
                            "string",
                            "integer",
                            "null"
                        ]
                    },
                    "result": {
                        "type": "object",
                        "additionalProperties": true
                    },
                    "error": {
                        "type": "object",
                        "required": [
                            "code",
                            "message"
                        ],
                        "properties": {
                            "code": {
                                "type": "integer"
                            },
                            "message": {
                                "type": "string"
                            },
                            "data": []
                        }
                    }
                }
            }
        },
        "responses": {
            "Unauthenticated": {
                "description": "Token absent ou invalide (`missing_token`, `invalid_token`, `unauthenticated`).",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "Forbidden": {
                "description": "Action refusée pour ce token (`forbidden`, `feedback_disabled`).",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "PayloadTooLarge": {
                "description": "Envoi trop volumineux (`payload_too_large`).",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            },
            "ValidationFailed": {
                "description": "Champs invalides (`validation_failed`), détaillés dans `errors`.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/ValidationError"
                        }
                    }
                }
            },
            "RateLimited": {
                "description": "Limite de débit atteinte (`rate_limited`) ; voir l'en-tête Retry-After.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            }
        }
    }
}