# Cellaria API + MCP comercial

Fecha: 2026-07-14  
Estado: demo local ejecutable, sin credenciales reales y sin facturación.

## 1. Qué entrega este paquete

Cellaria expone dos superficies complementarias:

- **REST API** para integraciones servidor a servidor, CRM, BI, exportaciones y operaciones persistentes.
- **MCP stdio** para que Claude, Codex u otro host compatible use herramientas de inteligencia comercial en lenguaje natural.

El servidor MCP local usa `data/cellaria_workspace_demo.json`. No consulta datos live, no escribe en el ledger, no persiste solicitudes y no contiene secretos.

```mermaid
flowchart LR
  A["Claude / Codex"] -->|"MCP stdio"| B["Cellaria MCP demo"]
  B --> C["Datos sintéticos locales"]
  D["CRM / BI / backend"] -->|"HTTPS + X-API-Key"| E["Cellaria REST API"]
  E --> F["Agregados autorizados"]
  B -. "borrador revisable" .-> E
```

## 2. Arranque rápido

### Portal developer

```bash
node server.mjs
```

Abrir `http://localhost:4181/developers.html`.

El explorer del portal ejecuta únicamente estos recursos públicos:

| Recurso | Finalidad | Credencial |
| --- | --- | --- |
| `GET /v1/health` | Estado básico | Ninguna |
| `GET /v1/ready` | Readiness local | Ninguna |
| `GET /v1/openapi.json` | Contrato REST | Ninguna |
| `GET /v1/billing/plans` | Catálogo de planes | Ninguna |
| `GET /data/cellaria_mcp_tools.json` | Catálogo MCP demo | Ninguna |

El identificador `static_demo_pages` sirve para describir el tráfico de demo. **No es un secreto, no autoriza endpoints y no debe enviarse como `X-API-Key`.**

### Test MCP

```bash
node scripts/test-cellaria-mcp.mjs
```

El test levanta el proceso stdio, negocia protocolo, descubre herramientas, ejecuta las cuatro operaciones y comprueba errores y guardrails.

## 3. REST API

Base local:

```txt
http://localhost:4181
```

Base productiva prevista en OpenAPI:

```txt
https://api.market.winerim.wine
```

### Recurso público

Request:

```bash
curl --silent http://localhost:4181/v1/health
```

Response:

```json
{
  "ok": true,
  "service": "market-winerim-api",
  "mode": "local-prototype",
  "timestamp": "2026-07-14T10:15:00.000Z"
}
```

### Pregunta comercial autenticada

La API key se inyecta en servidor o CI. Nunca debe incrustarse en `developers.html`, JavaScript público, capturas o repositorio.

```bash
curl --request POST "https://api.market.winerim.wine/v1/market/ask" \
  --header "Content-Type: application/json" \
  --header "X-API-Key: $CELLARIA_API_KEY" \
  --data '{
    "question": "¿Qué PVP defiende El Anejón en Madrid?",
    "buyer_type": "winery",
    "region": "madrid",
    "period": "90d"
  }'
```

Response abreviada:

```json
{
  "schema_version": "market_answer_v0.21",
  "intent": "pricing_benchmark",
  "answer": "Para Madrid, Cellaria usaría el rango p50/p75 como defensa de precio...",
  "confidence": 82,
  "privacy_gate": {
    "status": "aggregate_publishable",
    "minimum_restaurant_count": 5
  },
  "billing": {
    "unit": "market_answer",
    "units": 1
  }
}
```

El contrato íntegro vive en `docs/openapi.json` y se sirve en `GET /v1/openapi.json`.

## 4. Servidor MCP stdio

Ejecutable:

```bash
node scripts/cellaria-mcp-server.mjs
```

El transporte sigue JSON-RPC 2.0 con un mensaje UTF-8 por línea. Implementa:

- `initialize`
- `notifications/initialized`
- `ping`
- `tools/list`
- `tools/call`

Versiones aceptadas: `2025-11-25`, `2025-06-18`, `2025-03-26` y `2024-11-05`. Si el cliente propone otra, el servidor responde con `2025-11-25` para que el host decida si continúa.

### Herramientas

| Herramienta | Resultado demo | Endpoint REST equivalente | Unidad |
| --- | --- | --- | --- |
| `cellaria_ask` | Respuesta con evidencia y caveats | `POST /v1/market/ask` | `market_answer` |
| `cellaria_compare_wines` | Comparativa de 2 o 3 referencias | `GET /v1/market/compare` | `wine_comparison` |
| `cellaria_list_products` | Catálogo comercial filtrable | `GET /v1/market/report-packages` | `product_catalog_read` |
| `cellaria_generate_report_request` | Borrador no persistente | `POST /v1/market/report-requests` | `report_request_draft` |

Todas declaran `readOnlyHint=true`, `destructiveHint=false` y `openWorldHint=false` en esta demo local. El borrador de informe requiere revisión humana y una llamada REST autenticada posterior para convertirse en solicitud real.

### Ejemplo JSON-RPC

Request:

```json
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"cellaria_compare_wines","arguments":{"wines":["El Anejón","Alion"],"region":"Madrid"}}}
```

Response abreviada:

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{ ... respuesta JSON serializada ... }"
      }
    ],
    "structuredContent": {
      "schema_version": "cellaria_mcp_wine_comparison_v1",
      "rows": [
        {
          "name": "El Anejón",
          "presence_observed_pct": 18,
          "pvp_p50_eur": 89,
          "opportunity_index": 91
        }
      ],
      "sample": {
        "n": null,
        "minimum_required": 5,
        "status": "synthetic_demo_no_live_sample"
      },
      "metering": {
        "unit": "wine_comparison",
        "units": 1,
        "billed": false
      }
    },
    "isError": false
  }
}
```

Se devuelve el mismo objeto como texto y como `structuredContent` para compatibilidad con hosts antiguos y modernos.

## 5. Claude

El archivo `examples/cellaria-mcp-config.example.json` tiene formato `.mcp.json` de Claude Code:

```json
{
  "mcpServers": {
    "cellaria": {
      "type": "stdio",
      "command": "node",
      "args": [
        "${CELLARIA_REPO:-/Users/GOIKO/Documents/Playground/market-winerim}/scripts/cellaria-mcp-server.mjs"
      ],
      "env": {
        "CELLARIA_MODE": "demo",
        "CELLARIA_DEMO_KEY_ID": "static_demo_pages"
      }
    }
  }
}
```

Alternativa CLI:

```bash
claude mcp add --transport stdio cellaria -- node /Users/GOIKO/Documents/Playground/market-winerim/scripts/cellaria-mcp-server.mjs
claude mcp get cellaria
```

Caso recomendado: preparar un benchmark o una solicitud de informe desde una conversación, revisar evidencia y caveats y aprobar manualmente cualquier paso comercial.

## 6. Codex

Añadir a `~/.codex/config.toml` o a `.codex/config.toml` de un proyecto confiable:

```toml
[mcp_servers.cellaria]
command = "node"
args = ["/Users/GOIKO/Documents/Playground/market-winerim/scripts/cellaria-mcp-server.mjs"]
cwd = "/Users/GOIKO/Documents/Playground/market-winerim"
startup_timeout_sec = 10
tool_timeout_sec = 30
enabled_tools = [
  "cellaria_ask",
  "cellaria_compare_wines",
  "cellaria_list_products",
  "cellaria_generate_report_request"
]
default_tools_approval_mode = "prompt"

[mcp_servers.cellaria.env]
CELLARIA_MODE = "demo"
CELLARIA_DEMO_KEY_ID = "static_demo_pages"
```

Alternativa CLI:

```bash
codex mcp add cellaria -- node /Users/GOIKO/Documents/Playground/market-winerim/scripts/cellaria-mcp-server.mjs
codex mcp list
```

Caso recomendado: analizar un catálogo dentro de un flujo de trabajo técnico, generar fixtures o preparar un payload REST revisable sin dar al agente una credencial productiva.

## 7. CRM

Para Salesforce, HubSpot u otro CRM, la superficie recomendada es REST servidor a servidor:

1. El CRM crea una pregunta o evento de cuenta.
2. Un backend autorizado llama a `/v1/market/ask` o `/v1/market/compare`.
3. Guarda solo respuesta agregada, confianza, caveats, `answer_id` y unidad de uso.
4. Una persona aprueba la creación de informe o la activación comercial.

```js
const response = await fetch(`${process.env.CELLARIA_BASE_URL}/v1/market/ask`, {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-api-key": process.env.CELLARIA_API_KEY
  },
  body: JSON.stringify({
    question: "¿Qué referencias de Ribera priorizar para Madrid?",
    buyer_type: "distributor",
    region: "madrid",
    period: "90d"
  })
});

if (!response.ok) throw new Error(`Cellaria API ${response.status}`);
const insight = await response.json();
```

No guardar claves, identidad de restaurantes, costes, stocks o ventas exactas en notas de CRM.

## 8. BI

Para Power BI, Tableau, Looker o notebooks:

- usar endpoints agregados como `pricing-benchmark`, `indices`, `trends` o `compare`;
- usar `GET /v1/market/export?scope=...` cuando el contrato permita CSV;
- conservar `generated_at`, filtros, `n`, cobertura, confianza y `privacy_status`;
- no mezclar snapshots con periodos o coberturas distintas sin etiquetarlos;
- mantener la API key en gateway, backend o secret manager, nunca en un dashboard público.

Ejemplo:

```bash
curl --silent \
  --header "X-API-Key: $CELLARIA_API_KEY" \
  "https://api.market.winerim.wine/v1/market/export?scope=pricing&region=madrid&period=90d" \
  --output cellaria-pricing-madrid.csv
```

## 9. Acceso

| Nivel | Superficie | Autenticación | Uso |
| --- | --- | --- | --- |
| Público demo | Health, readiness, OpenAPI, planes, catálogo MCP | Ninguna | Exploración y documentación |
| Contratado | `/v1/market/*` | `X-API-Key` hasheada en servidor | Inteligencia, export, watchlists, informes |
| Interno | `/v1/ingest/*`, `/v1/ops/*` | Key interna y scopes específicos | Ingesta, operaciones y billing |
| MCP local demo | Cuatro herramientas Cellaria | Proceso local | Evaluación con datos sintéticos |

Una API key tiene cliente, buyer type, plan, scopes, cuota mensual y rate limit. La respuesta puede incluir `X-RateLimit-Limit`, `X-RateLimit-Remaining` y `X-RateLimit-Reset`.

## 10. Privacidad y guardrails

Reglas aplicadas en el catálogo y en runtime:

- publicación agregada solo con `N>=5` en datos reales;
- identidad y contacto de restaurantes ocultos sin permiso explícito;
- ventas, margen, coste y stock exactos por restaurante bloqueados;
- cuota total, ranking absoluto y predicción garantizada bloqueados;
- presencia no equivale a venta;
- PVP no equivale a margen;
- toda salida declara modo demo/live, muestra, caveat y unidad de metering;
- una solicitud comercial requiere revisión humana.

Una petición bloqueada devuelve `isError=true`, `error=privacy_guardrail`, categorías afectadas y una alternativa segura en términos agregados.

## 11. Metering

### Demo local

- `key_id`: `static_demo_pages`
- `credential`: `null`
- `billable`: `false`
- `ledger`: `disabled`
- no escribe `data/usage_events.jsonl`

### API contratada

- cada endpoint registra uso según scope y condición facturable;
- las respuestas preparadas declaran su unidad, por ejemplo `market_answer`;
- la cuota efectiva depende del plan y cliente provisionado;
- `GET /v1/market/usage` permite consultar uso con scope `usage:read`;
- los límites de minuto y mes pueden responder `429`.

No inferir precio ni cuota a partir de la demo. El contrato comercial manda sobre los ejemplos.

## 12. Errores

JSON-RPC de protocolo:

| Código | Significado |
| --- | --- |
| `-32700` | JSON no parseable |
| `-32600` | Request JSON-RPC inválido |
| `-32601` | Método no implementado |
| `-32602` | Herramienta desconocida o params de protocolo inválidos |
| `-32603` | Error interno |

Errores de uso de herramienta se devuelven dentro de `result` con `isError=true`, de modo que el modelo pueda corregir argumentos o reformular una petición sensible.

## 13. Fuentes de compatibilidad

- [MCP 2025-11-25: lifecycle](https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle)
- [MCP 2025-11-25: stdio transport](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports)
- [MCP 2025-11-25: tools](https://modelcontextprotocol.io/specification/2025-11-25/server/tools)
- [Claude Code: MCP](https://code.claude.com/docs/en/mcp)
- [Codex: Model Context Protocol](https://learn.chatgpt.com/docs/extend/mcp)

## 14. Archivos

```txt
developers.html
cellaria-developers.js
cellaria-developers.css
data/cellaria_mcp_tools.json
scripts/cellaria-mcp-server.mjs
scripts/test-cellaria-mcp.mjs
examples/cellaria-mcp-config.example.json
docs/CELLARIA_API_MCP_COMMERCIAL.md
```
