xpeditis2.0/apps/backend/src/application/mcp/capabilities/bookings.capabilities.ts
David 177b70ea96 feat(api): registre de capacites et serveur MCP
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
2026-09-07 21:40:54 +02:00

114 lines
4.1 KiB
TypeScript

import { CsvBookingService } from '../../services/csv-booking.service';
import { Capability } from '../capability';
/**
* Reservations.
*
* Les lectures restent cantonnees a l'appelant, sauf `list_organization_bookings`
* qui demande un role d'encadrement — c'est la meme frontiere que dans
* l'interface, ou seuls ADMIN et MANAGER voient l'onglet organisation.
*
* Les ecritures sont volontairement limitees aux actions reversibles ou
* inoffensives : annuler, et supprimer une reservation impayee. Payer une
* commission, envoyer une demande a un transporteur ou televerser un document
* engagent un tiers ou de l'argent et restent hors de portee d'un agent.
*/
export function bookingsCapabilities(bookings: CsvBookingService): Capability[] {
return [
{
policy: { name: 'list_my_bookings', scope: 'read' },
description:
"Liste les réservations de l'utilisateur authentifié, de la plus récente à la plus ancienne.",
inputSchema: {
type: 'object',
properties: {
limit: {
type: 'integer',
description: 'Nombre maximum de réservations.',
minimum: 1,
maximum: 50,
default: 20,
},
},
additionalProperties: false,
},
handler: async (input, actor) =>
bookings.getUserBookings(actor.id, 1, (input.limit as number) ?? 20),
},
{
policy: { name: 'list_organization_bookings', scope: 'read', roles: ['ADMIN', 'MANAGER'] },
description:
"Liste les réservations de toute l'organisation. Réservé aux rôles ADMIN et MANAGER.",
inputSchema: {
type: 'object',
properties: {
limit: {
type: 'integer',
description: 'Nombre maximum de réservations.',
minimum: 1,
maximum: 50,
default: 20,
},
},
additionalProperties: false,
},
handler: async (input, actor) =>
bookings.getOrganizationBookings(actor.organizationId, 1, (input.limit as number) ?? 20),
},
{
policy: { name: 'get_booking', scope: 'read' },
description:
"Détail d'une réservation : route, marchandise, transporteur, statut, documents.",
inputSchema: {
type: 'object',
properties: {
bookingId: { type: 'string', description: 'Identifiant de la réservation (UUID).' },
},
required: ['bookingId'],
additionalProperties: false,
},
handler: async (input, actor) => bookings.getBookingById(input.bookingId as string, actor.id),
},
{
policy: { name: 'booking_statistics', scope: 'read' },
description:
"Répartition des réservations de l'utilisateur par statut (en attente de paiement, en attente, acceptées, refusées).",
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
handler: async (_input, actor) => bookings.getUserStats(actor.id),
},
{
policy: { name: 'cancel_booking', scope: 'write' },
description:
"Annule une réservation de l'utilisateur qui n'a pas encore été acceptée par le transporteur. La réservation est conservée avec le statut annulé.",
inputSchema: {
type: 'object',
properties: {
bookingId: { type: 'string', description: 'Identifiant de la réservation (UUID).' },
},
required: ['bookingId'],
additionalProperties: false,
},
handler: async (input, actor) => bookings.cancelBooking(input.bookingId as string, actor.id),
},
{
policy: { name: 'delete_unpaid_booking', scope: 'write' },
description:
"Supprime définitivement une réservation dont la commission n'a pas été payée. Sans effet sur une réservation payée, qui ne peut être qu'annulée.",
inputSchema: {
type: 'object',
properties: {
bookingId: { type: 'string', description: 'Identifiant de la réservation (UUID).' },
},
required: ['bookingId'],
additionalProperties: false,
},
handler: async (input, actor) => bookings.deleteBooking(input.bookingId as string, actor.id),
},
];
}