Skip to main content
Glama
martinsantos

Avisos Abiertos MCP

by martinsantos

Avisos Abiertos · Última Milla

MVP público bajo MIT, preparado en VM para Sites y autoalojamiento. Build listo para revisión; Site y DNS no publicados por esta entrega. Iniciativa https://www.ultimamilla.com.ar/.

UI móvil en /; REST /api/v1; OpenAPI /api/v1/openapi.json; MCP stateless POST /mcp. D1 guarda avisos y hashes de credenciales; R2 guarda fotos privadas. No hay feed global ni importación del piloto C3. No requiere IA ni servicios pagos para el flujo básico; el hosting sí requiere disponibilidad y cuotas de D1/R2/Workers.

Ejecutar en una VM

Node 22+; npm ci, npm run typecheck, npm run build, npm test. Build ESM dist/server/index.js exporta default.fetch. Activos dist/client. Esquema db/schema.ts; migraciones Drizzle drizzle/, schema-only, inspeccionadas. npm run db:generate solo cuando cambie el esquema. No cambiar migraciones aplicadas.

Para Cloudflare propio: instalar Wrangler, crear una base D1 y un bucket R2 privado, configurar DB, EVIDENCE y assets ASSETS con directorio dist/client, main=dist/server/index.js, fecha de compatibilidad 2026-08-06. Aplicar cada archivo SQL antes del Worker; wrangler d1 migrations apply requiere configurar migrations_dir="drizzle". En Sites el helper oficial administra recursos; .openai/hosting.json contiene solo bindings lógicos y el Site autorizado. El padre coordina helper, publicación y DNS. No usar el manifiesto de C3.

Por defecto TRUST_SITES_IDENTITY está ausente y las cabeceras de identidad se ignoran. El padre debe configurar el valor sites-dispatcher exclusivamente en el entorno protegido Sites antes de habilitar herramientas MCP privadas. Autoalojamiento no debe confiar en cabeceras oai-authenticated-user-* enviadas por Internet: eliminarlas en el proxy salvo identidad verificada por un gateway confiable. REST funciona con credenciales propias sin OAuth. MCP con datos privados necesita identidad de gateway confiable; sin Sites no se anuncia integración OAuth operativa.

Related MCP server: fintaro-mcp

REST sin cuenta ChatGPT

Todos los POST llevan X-Avisos-Client: 1; browser: mismo origen. No hay CORS abierto. JSON como máximo 16.000 bytes. Pedir sesión POST /api/v1/session; devuelve una credencial aleatoria de 32 bytes, válida una hora, almacenada como SHA-256. Crear con Bearer de sesión, una nueva credencial aleatoria por aviso y clave de idempotencia. Guardar ambas antes del intento permite repetir sin pérdida. El hash de entrada incluye credencial; misma clave con otro contenido devuelve 409. La clave está aislada por sesión y nunca permite recuperar avisos de otra sesión.

BASE=https://tu-host
SESSION=$(curl -s -X POST "$BASE/api/v1/session" -H 'X-Avisos-Client: 1' -d '{}' | jq -r .credential)
TOKEN=$(openssl rand -hex 32)
# Crear el JSON sin incluir tokens en URLs. Estas variables solo viven en tu terminal.
jq -n --arg credential "$TOKEN" '{place:"Lugar de prueba",department:"Maipú",description:"Observación de prueba",occurredAt:"2026-10-05T12:00:00-03:00",credential:$credential,idempotencyKey:"ejemplo-1"}' > aviso.json
curl -s -X POST "$BASE/api/v1/notices" -H 'X-Avisos-Client: 1' -H "Authorization: Bearer $SESSION" -H 'Content-Type: application/json' --data-binary @aviso.json
# Usar el id devuelto y el TOKEN del aviso, nunca el de sesión:
curl -s "$BASE/api/v1/notices/ID" -H "Authorization: Bearer $TOKEN"
curl -s -X POST "$BASE/api/v1/notices/ID/evidence" -H 'X-Avisos-Client: 1' -H "Authorization: Bearer $TOKEN" -H 'Content-Type: image/png' --data-binary @foto.png
rm aviso.json

El archivo temporal contiene la credencial: no subirlo a repositorios. En producción, los clientes y proxies deben evitar registrar Authorization/cuerpos; no hay analytics ni logs de credenciales en la aplicación. Seguimiento UI por fragmento /#ID:TOKEN, limpiado inmediatamente y guardado en almacenamiento del dispositivo. El fragmento no viaja al servidor. Solo se guarda allí la credencial: datos y fotos siguen en D1/R2. Este dispositivo puede recuperar el aviso; otro necesita credencial/enlace. Pérdida de credencial de aviso anónimo no tiene recuperación administrativa en este MVP.

Límites: 30 sesiones por IP/hora (si no hay IP de gateway, cupo compartido); 5 creaciones por sesión o identidad/hora; 3 fotos por aviso; 3 MiB por foto; 25 millones de píxeles declarados por foto. JSON MCP máximo 4.250.000 bytes para base64. Cuotas de hosting pueden ser inferiores. No hay captcha ni defensa distribuida frente a múltiples IP; monitorear consumo antes de ampliar audiencia. Los contadores son persistentes y atómicos; configurar mantenimiento periódico para eliminar sesiones vencidas y contadores de ventanas antiguas. No hay borrado ni retención automática de avisos en esta versión; definir política antes de uso sostenido.

Fotos: verifica firma, tamaño y estructura acotada, dimensiones y SHA-256; PNG valida CRC, IHDR, orden, IEND y PLTE consistente obligatorio para indexados; JPEG valida marcadores, segmentos, frame, scan y cierre. No decodifica exhaustivamente ni inspecciona contenido/malware ni elimina EXIF. Rechaza GIF/WebP/SVG. Fotos se descargan como adjunto con permisos del aviso y no-store, sin URLs R2 públicas. El browser mantiene el formulario ante errores; si falló una foto después de crear, informa registro exitoso y permite reintentar el adjunto.

MCP y permisos

Discovery initialize, ping, tools/list sin datos privados. Operaciones channels, context_search, create_notice, read_notice, add_evidence, prepare_referral, add_receipt comparten el servicio REST. Creación requiere identidad Sites; lectura/modificación requiere además propiedad del aviso o su credencial Bearer. No hay listar avisos. Bypass no genera identidad. En MCP se puede omitir credencial en creación: se devuelve una aleatoria una sola vez; para reintento idempotente proporcionar la misma credencial desde el inicio. Las fotos MCP son base64 y privadas. La identidad no puede apropiarse de avisos anónimos por conocer su id.

Sites administra OAuth y la conexión de su plugin. Clientes externos solo son compatibles cuando completan el OAuth soportado por Sites y el gateway entrega identidad; conexión externa y OAuth extremo a extremo aún no comprobados en esta entrega sin publicar. No se implementa OAuth propio. REST curl con credenciales por objeto sí está implementado y probado en VM. La cabecera de identidad en pruebas simula la frontera confiable, no acredita una conexión real de cliente.

Derivación manual y constancias

Catálogo revisado el 2026-10-05: Ambiente Mendoza desde https://www.mendoza.gob.ar/energiayambiente/ a https://ticketsform.mendoza.gov.ar/ticketsform/servlet/responderformulario?PORTAL_AMBIENTE (opción anónima sin seguimiento oficial); DIRCAS desde https://dircas.cloud.irrigacion.gov.ar/ a https://reclamos.irrigacion.gov.ar/autogestion/web/site/login (cuenta requerida; agua potable/saneamiento). La persona elige competencia; no se deriva automáticamente por palabras clave. No se encontraron APIs oficiales de escritura documentadas, lo cual no prueba que no existan.

registrado: guardado interno. preparado: texto creado y enlace disponible, sin envío. constancia_aportada: referencia declarada por usuario, sin verificación institucional. Ninguna ruta envía mensajes ni llama a organismos. Las pruebas usan información sintética.

Proyección documental publicable

Hoy hay cero documentos aprobados. La búsqueda responde sin antecedentes; no importa los 99 pasajes ni los 100 activos privados ni providers C3. Contrato src/documents.ts: URL HTTPS, hash de fuente, fechas de decisión/publicación/hecho separadas y anulables, cobertura, localizador, rol territorial y revisión aprobada con condiciones de reutilización. reviewBatch genera recibo de lote con IDs; el responsable debe comprobar el hash contra la fuente y conservar recibo antes de insertar documentos revisados en la tabla pública mediante herramienta administrativa offline. No hay endpoint de ingesta ni aprobación pública. La búsqueda solo lee esa tabla, devuelve citas y máximo 20 coincidencias textuales; no hay vectores, correlaciones, culpables ni probabilidades. El contrato está preparado, no se presenta como pipeline editorial automático.

Entrega

delivery/receipt.json, lista de SHA-256 y archivo comprimido de fuente/build. Los hashes finales del paquete se calculan después de comprimir; no incluir el paquete dentro de sí mismo. Dependencias de desarrollo no se empaquetan; package-lock.json permite reproducir. LICENSE del proyecto MIT; dependencias conservan sus licencias en npm.

WebMCP complementario: feature-detect document.modelContext?.registerTool, herramientas para crear el aviso visible, leer el comprobante y preparar derivación, registradas una vez con AbortSignal. Reutiliza API/credenciales y actualiza la pantalla antes del JSON de resultado. No autentica ni reemplaza /mcp. API browser experimental; no comprobado en un navegador compatible en esta VM. UI y GPS opt-in requieren revisión visual final; pruebas de integración se ejecutaron en workerd/Miniflare con D1 y R2 persistentes locales, no sobre recursos reales Sites aún sin publicar.

Related MCP Connectors

Related MCP Servers

  • A
    license
    C
    quality
    A
    maintenance
    Enables searching, tagging, uploading, and reading documents in Paperless-NGX, with management of tags, correspondents, document types, and custom fields via MCP tools and resources.
    50
    1
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables MCP-capable agents to read Fintaro invoices and transactions, and upload receipts, via a scoped API key with PII-safe projections.
    6
    Apache 2.0
  • A
    license
    Not graded
    quality
    A
    maintenance
    Access EPA environmental data — facility compliance (ECHO), toxic releases (TRI), Superfund sites, drinking water systems, environmental justice screening (EJScreen), and real-time air quality (AirNow) via MCP.
    201 npm
    1
    Apache 2.0