npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

madrid-avisos-mcp

v0.1.7

Published

Servidor MCP y CLI para la API de Avisos del Ayuntamiento de Madrid (AVSICAPI)

Readme

madrid-avisos-mcp

Servidor MCP (y CLI de apoyo) para la API de Avisos del Ayuntamiento de Madrid. Permite a un agente listar categorías, resolver una ubicación, consultar avisos y crear avisos con inteligencia artificial— incluso desde una foto.

La finalidad de este proyecto es hacer más fácil que los ciudadanos puedan reportar problemas al Ayuntamiento de Madrid. Saca una foto de la incidencia (por ejemplo, basura tirada en la calle, una farola que no funciona…) pásasela al agente pidiéndole que genere un aviso para que de forma autónoma describa el problema, seleccione la categoría más adecuada, añada la ubicación (la foto tiene que estar geolocalizada) y lance el aviso al Ayuntamiento.

Inicio rápido

  1. Inicia sesión en https://avisos.madrid.es con tu usuario.
  2. Abre las DevTools del navegador (F12) → pestaña Application o Aplicación, y ahí en la barra lateral ve a Almacenamiento > Almacenamiento local y selecciona https://avisos.madrid.es. Copia el valor de la clave token (empieza por ey…). Ese es tu MADRID_AVISOS_TOKEN (caduca al mes; repite el paso cuando deje de funcionar).
  3. Añade el servidor a tu cliente MCP (ejemplos) o configúralo a mano:
{
  "mcpServers": {
    "madrid-avisos": {
      "command": "npx",
      "args": ["-y", "madrid-avisos-mcp"],
      "env": { "MADRID_AVISOS_TOKEN": "<tu-token>" }
    }
  }
}
  1. Flujo del agente: list_categories → resolve_location → create_aviso en dry-run → enseña el preview al humano → confirm: true solo con su "sí" → attach_photo. O directamente create_aviso_from_photo con la imagen.

Todo corre en tu máquina y el token no sale de ella: cada aviso se crea como tu usuario.

Fotos demasiado grandes para el modelo

Algunos modelos rechazan fotos muy grandes (image decode limit exceeded). El servidor reduce en TypeScript (sin dependencias) conservando el GPS, así que el modelo nunca necesita procesar la original:

  • Remoto (HTTP): sube la foto con curl y usa el file_id (los bytes no pasan por el modelo). Requiere el secreto del servidor:
    curl -X PUT --data-binary @foto.jpg \
      -H "Authorization: Bearer <secreto>" \
      'http://127.0.0.1:3000/upload?filename=foto.jpg'
    # → {"file_id":"…","bytes":…}
    El preview devuelve preview_image_base64 (copia reducida) para visión y adjunta siempre la original.
  • Local (stdio/CLI): pasa image_path; el servidor lee y reduce sin que el modelo abra el fichero. O reduce tú con node dist/cli.js prep-photo foto.jpg [foto-ligera.jpg].

¿Eres un agente IA? Lee esto primero

La skill (skill/SKILL.md) no se autoinstala: cópiala al directorio de tu cliente y abre sesión nueva.

cp -r skill ~/.hermes/skills/madrid-avisos        # Hermes
cp -r skill ~/.claude/skills/madrid-avisos        # Claude Code
# o descárgala: https://github.com/Naroh091/madrid-avisos-mcp/blob/main/skill/SKILL.md

Instálalo así (requiere Node 18+):

  1. Token: pide al humano su token de https://avisos.madrid.es (DevTools → Application → Almacenamiento local → https://avisos.madrid.es → clave token). Sin token no hay nada que hacer.
  2. Instalación según tu cliente (comandos exactos): Claude Code (claude mcp add … -- npx -y madrid-avisos-mcp), Hermes (hermes mcp add … --command npx … --args -y madrid-avisos-mcp) u OpenClaw (openclaw mcp add … --command npx --arg -y --arg madrid-avisos-mcp). El token viaja en la variable MADRID_AVISOS_TOKEN de tu config.
  3. Verifica (mcp list / test / doctor --probe según cliente): debes ver 11 tools.
  4. Uso: hay skill completa en skill/SKILL.md. Lo esencial: solo incidencias genuinas; create_aviso_from_photo en 2 fases (preview → mostrar al humano → envío solo con su "sí" + confirm + human_confirmed + preview_token); foto por image_base64; sin GPS no adivines la ubicación.

Añadir el MCP vía npx

Requiere Node 18+. <tu-token> es tu bearer (paso 2 del inicio rápido).

Claude Code

claude mcp add madrid-avisos -e MADRID_AVISOS_TOKEN=<tu-token> -- npx -y madrid-avisos-mcp
claude mcp list   # verificar

Hermes

hermes mcp add madrid-avisos --command npx --env MADRID_AVISOS_TOKEN=<tu-token> --args -y madrid-avisos-mcp
hermes mcp test madrid-avisos   # verificar (lista las 11 tools)

OpenClaw

openclaw mcp add madrid-avisos \
  --command npx \
  --arg -y \
  --arg madrid-avisos-mcp \
  --env MADRID_AVISOS_TOKEN=<tu-token>
openclaw mcp doctor madrid-avisos --probe   # verificar

Desde código

npm install
npm run build
npx -y -p madrid-avisos-mcp madrid-avisos-mcp-http   # HTTP en 127.0.0.1:3000/mcp

Herramientas MCP

| Tool | Qué hace | |---|---| | whoami | Perfil del usuario autenticado (valida el token). | | refresh_session | Refresca el access token con el refresh token (también automático ante 401). | | list_categories | Categorías/servicios (id, flags de formulario). | | get_category | Detalle de una categoría (formulario, obligatorios, tipología). | | resolve_location | Valida zona del servicio + dirección municipal + preguntas de ubicación + duplicados. | | resolve_address | Geocodificación inversa propia: coords → dirección municipal + respuestas pre-rellenadas. | | create_aviso | Crea un aviso. Dry-run por defecto; confirm: true para enviar de verdad. | | create_aviso_from_photo | Aviso desde foto en 2 fases: preview (GPS EXIF + categoría + ubicación) y envío solo con confirm: true + human_confirmed: true + preview_token. Acepta image_base64 o image_path. | | attach_photo | Adjunta una foto al aviso (image_base64 o image_path). Dry-run por defecto. | | get_aviso | Detalle de un aviso por su id interno. | | list_my_avisos | Avisos propios (own: true por defecto). |

Seguridad de envío

create_aviso es dry-run por defecto: devuelve el payload sin crear nada. Solo con confirm: true hace el POST real — un aviso real que revisa personal municipal. Envía únicamente incidencias reales.

create_aviso_from_photo exige confirmación humana en dos fases:

  1. Preview (confirm ausente/false): extrae el GPS EXIF (o usa lat/lng manuales), sugiere categoría desde category_hint si falta service_id, resuelve ubicación (validación + duplicados) y devuelve el payload + un preview_token. No envía nada.
  2. Envío: el agente muestra el preview al humano y espera su "sí"; solo entonces repite la llamada con los MISMOS campos + confirm: true + human_confirmed: true + preview_token. Si cambió cualquier campo, hay que repetir el preview.

Uso como CLI

export MADRID_AVISOS_TOKEN=<tu-token>
node dist/cli.js categories
node dist/cli.js category 591b39e24e4ea83a018b46ad
node dist/cli.js resolve 591b39e24e4ea83a018b46ad 40.4168 -3.7038
node dist/cli.js create 591b39e24e4ea83a018b46ad 40.4168 -3.7038 "Farola apagada"          # dry-run
node dist/cli.js create 591b39e24e4ea83a018b46ad 40.4168 -3.7038 "Farola apagada" --send   # ENVÍA de verdad
node dist/cli.js from-photo foto.jpg 591b126d4e4ea840018b45b6 "Cartones en la acera"      # preview desde foto
node dist/cli.js aviso <id>
node dist/cli.js prep-photo foto.jpg [foto-ligera.jpg]   # reduce para el modelo, conserva EXIF/GPS

Servidor HTTP (opcional)

Por stdio cada uno corre su copia con su token. La entrada HTTP sirve para el caso contrario: exponer el servidor que corre en TU máquina (con TU token) para que un agente en OTRA máquina lo use — en ese caso actúa como tú, no como el dueño del agente remoto. Para uso personal normal no la necesitas.

export MADRID_AVISOS_TOKEN=<tu-token>
export MADRID_AVISOS_MCP_SECRET=<un-secreto-largo>            # exige x-mcp-secret o Bearer
export MADRID_AVISOS_ALLOWED_HOSTS=tu-host.tu-tailnet.ts.net  # anti DNS-rebinding
npm run start:http     # 127.0.0.1:3000/mcp

Variables: MADRID_AVISOS_HTTP_PORT (3000), MADRID_AVISOS_HTTP_HOST (127.0.0.1), MADRID_AVISOS_HTTP_PATH (/mcp). Expón solo en red privada (p.ej. tailscale serve, nunca funnel): quien llegue a la URL actúa como tu usuario. Para persistencia, launchd/pm2/tmux o similar.

Arquitectura

  • src/client.ts — HTTP: cabeceras de app + bearer, GET/POST/multipart, auto-refresh ante 401.
  • src/avisos.ts — núcleo de negocio (reutilizado por MCP y CLI).
  • src/photo.ts — foto: EXIF/GPS, subida a tmp, token de preview.
  • src/types.ts — esquemas zod de entrada + payload de creación.
  • src/mcp.ts — buildServer(): registra las 11 tools (compartido por stdio y HTTP).
  • src/server.ts — entrada stdio · src/http.ts — entrada HTTP (/mcp + PUT /upload) · src/cli.ts — CLI.

Notas

  • Algunas categorías exigen usuario registrado; con login-anonymous no se envían.