Skip to main content
Glama
data.ts8.66 kB
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z, ZodTypeAny } from 'zod'; import { get } from '../support/http.js'; import { t } from '../i18n/index.js'; import { type Ctx, resolveAuth } from '../context.js'; /** Util pour typer un handler depuis un "shape" */ type InferFromShape<S extends Record<string, ZodTypeAny>> = z.infer<z.ZodObject<S>>; /** Entrées communes à toutes les listes */ const CommonShape = { format: z.enum(['json', 'csv', 'html']).default('json'), } satisfies Record<string, ZodTypeAny>; const getOrderShape = { order_id: z.number().int(), } satisfies Record<string, ZodTypeAny>; const getOrdersShape = { validatedOrders: z.boolean(), from_date_ISO8601: z.string().datetime(), to_date_ISO8601: z.string().datetime(), filterDeliveryMethod: z.union([ z.number().int().min(0).max(6), z.enum(['0', '1', '2', '3', '4', '5', '6']) ]).transform((v) => Number(v)).optional(), } satisfies Record<string, ZodTypeAny>; type CommonArgs = InferFromShape<typeof CommonShape>; type getOrderArgs = InferFromShape<typeof getOrderShape>; type getOrdersArgs = InferFromShape<typeof getOrdersShape>; function safeStringify(value: any, space = 2, maxLen = 4000) { try { const cache = new WeakSet(); const s = JSON.stringify( value, (k, v) => { if (typeof v === 'bigint') return v.toString(); // éviter l’erreur BigInt if (typeof v === 'object' && v !== null) { if (cache.has(v)) return '[Circular]'; cache.add(v); } return v; }, space ); return s.length > maxLen ? s.slice(0, maxLen) + '…(truncated)' : s; } catch (e) { return `[unstringifiable: ${(e as Error).message}]`; } } function structData(data: any) { // on ne touche PAS à structuredContent (c’est ce que ChatGPT utilise) const light = Array.isArray(data) ? data.slice(0, 5000)//.map(({ id, nom, email, tel, ...r }) => ({ id, nom, email, tel })) : data; const maxLength = 40000; const preview = typeof light === 'string' ? (light.length > maxLength ? light.slice(0, maxLength) + '…(truncated)' : light) : safeStringify(light, 2, maxLength); // <-- aperçu court et “safe” const wrapped = Array.isArray(data) ? { data: data } : data && typeof data === 'object' ? data : { data: data }; return { content: [{ type: 'text', text: preview }], structuredContent: wrapped, }; } /** Fabrique un tool "liste" minimaliste */ function registerSimple( server: McpServer | any, toolName: string, path: string, title: string, entityLabel: string ) { server.registerTool( toolName, { title, description: `Liste des ${entityLabel}`, inputSchema: CommonShape, // ZodRawShape, annotations: { readOnlyHint: true } }, async ({ format }: CommonArgs, ctx: Ctx) => { try { const { shopId, apiKey } = resolveAuth(undefined, ctx); const data = await get(path, { idboutique: shopId, key: apiKey, format }); process.stderr.write( `[caisse][tool:${toolName}] ok type=${Array.isArray(data) ? 'array' : typeof data}` + (Array.isArray(data) ? ` len=${data.length}` : '') + '\n' ); //Array.isArray(data) ? data.slice(0, 50) : data const funcResult = structData( data); process.stderr.write(`[caisse][RES] ${JSON.stringify(data)} \n`); process.stderr.write(`[caisse][RES] funcResult ${JSON.stringify(funcResult)} \n`); return funcResult; //return { content, structuredContent: isText ? undefined : data }; } catch (e) { process.stderr.write(`[caisse][tool:${toolName}][error]\n`); process.stderr.write(`[caisse][tool:${toolName}][error] ${(e as Error).message}\n`); // renvoyer un message "propre" plutôt que laisser l’exception devenir un 424 return { content: [{ type: 'text', text: `Erreur pendant la préparation de la réponse: ${(e as Error).message}` }], is_error: true, }; } } ); } export function registerDataTools(server: McpServer | any) { // Garde les mêmes endpoints que ton backend PHP // Noms de tools avec UNDERSCORE (comme vus dans tes logs) t('tools.sale_create.title') registerSimple(server, 'data_list_products', '/workers/getPlus.php', t('tools.data_list_products.description'), t('tools.data_list_products.title')); registerSimple(server, 'data_list_departments', '/workers/getDepartments.php', t('tools.data_list_departments.description'), t('tools.data_list_departments.title')); registerSimple(server, 'data_list_department_groups', '/workers/getDepartmentsGroups.php', t('tools.data_list_department_groups.description'), t('tools.data_list_department_groups.title')); registerSimple(server, 'data_list_clients', '/workers/getClients.php', t('tools.data_list_clients.description'), t('tools.data_list_clients.title')); registerSimple(server, 'data_list_variations', '/workers/getDeclinaisons.php', t('tools.data_list_variations.description'), t('tools.data_list_variations.title')); registerSimple(server, 'data_list_delivery_men', '/workers/getLivreurs.php', t('tools.data_list_delivery_men.description'), t('tools.data_list_delivery_men.title')); registerSimple(server, 'data_list_payments_modes', '/workers/getPaymentModes.php', t('tools.data_list_payments.description'), t('tools.data_list_payments.title')); registerSimple(server, 'data_list_cashboxes', '/workers/getCashbox.php', t('tools.data_list_cashboxes.description'), t('tools.data_list_cashboxes.title')); registerSimple(server, 'data_list_delivery_zones', '/workers/getDeliveryZones.php', t('tools.data_list_delivery_zones.description'), t('tools.data_list_delivery_zones.title')); registerSimple(server, 'data_list_relay_points', '/workers/getRelayDeposit.php', t('tools.data_list_relay_points.description'), t('tools.data_list_relay_points.title')); registerSimple(server, 'data_list_discounts', '/workers/getDiscounts.php', t('tools.data_list_discounts.description'), t('tools.data_list_discounts.title')); registerSimple(server, 'data_list_users', '/workers/getUsers.php', t('tools.data_list_users.description'), t('tools.data_list_users.title')); registerSimple(server, 'data_list_tables', '/workers/getTables.php', t('tools.data_list_tables.description'), t('tools.data_list_tables.title')); //registerSimple(server, 'data_list_orders', '/workers/getPending.php', t('tools.data_list_orders.description'), t('tools.data_list_orders.title'),); server.registerTool( 'data_list_orders', { title: t('tools.data_list_orders.title'), description: t('tools.data_list_orders.description'), inputSchema: getOrdersShape, // ZodRawShape, annotations: { readOnlyHint: true } }, async ({ validatedOrders, from_date_ISO8601, to_date_ISO8601, filterDeliveryMethod }: getOrdersArgs, ctx: Ctx) => { const { shopId, apiKey } = resolveAuth(undefined, ctx); const data = await get('/workers/getOrders.php', { idboutique: shopId, key: apiKey, validatedOrders, from_date_ISO8601, to_date_ISO8601, filterDeliveryMethod }); return structData(data); //return { content, structuredContent: isText ? undefined : data }; } ); server.registerTool( 'order_detail', { title: t('tools.order_detail.title'), description: t('tools.order_detail.description'), inputSchema: getOrderShape, // ZodRawShape, annotations: { readOnlyHint: true } }, async ({ order_id }: getOrderArgs, ctx: Ctx) => { const { shopId, apiKey } = resolveAuth(undefined, ctx); const data = await get('/workers/getOrder.php', { idboutique: shopId, key: apiKey, order_id }); return structData(data); //return { content, structuredContent: isText ? undefined : data }; } ); // je pourrais ajouter getReport : obtenir les rapports de vente et getOrders : liste des commandes }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/paracetamol951/caisse-enregistreuse-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server