Skip to main content
Glama
Ask Agent

Ask Agent

by coder-mil

Ask Agent — MCP ↔ PWA bridge

A local-first system that lets any MCP-compatible AI agent (OpenCode, Claude Code, Hermes Agent, etc.) ask the human questions in real time via a mobile-friendly PWA with browser push notifications. No Telegram, no Discord, no third-party middlemen.

┌─────────────┐    ask_user()    ┌──────────────┐    Socket.IO    ┌────────────┐
│  Agent /    │ ───────────────► │  MCP server  │ ──────────────► │   PWA in   │
│  OpenCode   │ ◄─────────────── │  (Node.js)   │ ◄────────────── │  your phone│
└─────────────┘   user's reply   └──────────────┘    answer       └────────────┘
                                       │
                                       ▼   Web Push (VAPID)
                                  ┌──────────────┐
                                  │  Browser     │
                                  │  Notification│
                                  └──────────────┘

Why?

When an agent runs for hours, it shouldn't have to guess — it should ask. This lets you stay in flow on your phone/PC while the agent waits, without being glued to a terminal or a Telegram session.

Related MCP server: @zeph-to/mcp-server

Features

  • 5 question types: free text, yes/no, multiple choice, 1–5 scale, confirm

  • Multiple concurrent questions (each with a UUID, separate timeout)

  • Real-time delivery via Socket.IO with automatic reconnect

  • Push notifications even when the PWA tab is in the background (VAPID/web-push)

  • Installable PWA (manifest + service worker + maskable icons)

  • Stateless MCP — each agent session gets its own server, scales horizontally

  • One command to run: npm start on port 3010

Quick start

npm install
npm start
# open http://localhost:3010

That's it. VAPID keys auto-generate on first boot (printed in the console — set VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY / VAPID_SUBJECT env vars to persist them).

MCP client config

OpenCode / Claude Code / Hermes Agent

Add to your MCP config (~/.config/opencode/mcp.json or equivalent):

{
  "mcpServers": {
    "ask-user": {
      "type": "http",
      "url": "http://localhost:3010/mcp"
    }
  }
}

The agent will see one tool: ask_user(question, type, options?, timeoutMs?).

GitHub Actions

Expose the server with ngrok/cloudflared and use the same URL:

- run: npm install
- run: npm start &
- run: npx ngrok http 3010 --authtoken=$NGROK_TOKEN > ngrok.log &
- run: sleep 5 && cat ngrok.log  # extract https URL

Then point your MCP client at the ngrok URL.

Question types

type

UI

Response shape

text

Textarea + Enter to submit

string

yesno

Two big buttons (Sim / Não)

true or false

multiple_choice

Grid of buttons (requires options)

string (selected option)

scale

Slider 1–5

integer 1–5

confirm

Single "OK, confirmado" button

true

Default timeout: 5 minutes. Override per call with timeoutMs.

Endpoints

Path

Purpose

GET /

The PWA

GET /health

Liveness probe

POST /mcp

MCP Streamable HTTP

GET /api/vapid-public-key

VAPID public key (for push subscribe)

POST /api/push/subscribe

Register a push subscription

POST /api/push/unsubscribe

Unregister

GET /api/push/test

Send a test push

Architecture

src/
├── server.js         Entry point: Express + Socket.IO + MCP, all on 3010
├── questionManager.js  In-memory store + EventEmitter; UUID-keyed Promises
├── mcp.js            Streamable HTTP MCP, stateless (new McpServer per session)
├── socket.js         Socket.IO ↔ QuestionManager glue
├── push.js           Web-push init + VAPID key handling
└── pushRoutes.js     REST endpoints for push subscription
public/
├── index.html        PWA shell
├── app.js            Vanilla JS frontend (Socket.IO client, push subscribe)
├── styles.css        Dark-mode mobile-first styles
├── manifest.webmanifest  PWA manifest (installable, maskable icons)
├── sw.js             Service worker (push handler + asset cache)
└── icons/            192/512/maskable SVG icons

Key design choices

  • Stateless MCP sessions: each agent that calls /mcp gets its own McpServer instance. The McpServer SDK enforces one transport per server — this is the cleanest workaround that still allows multiple concurrent agents.

  • Shared state via QuestionManager singleton: questions and answers flow through a single in-memory Map. Restart loses pending questions (intentional — these are short-lived conversational exchanges).

  • Push is best-effort: if no one is subscribed, the question still arrives via Socket.IO. Push is only for when the PWA tab isn't active.

Running in production

This is a local-first tool by design. If you want 24/7 availability:

  1. Run on a VPS with HTTPS (Cloudflare Tunnel, Caddy, or nginx + Let's Encrypt)

  2. Set VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT env vars to persist VAPID across restarts

  3. Persist subscriptions somewhere (the current in-memory Map dies on restart) — easy swap to SQLite/Redis

  4. Add auth to /api/push/* and /mcp if exposed beyond localhost

License

MIT

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      B
      maintenance
      MCP server for AI agents to send notifications, copy to clipboard, request confirmations, and collect text input from users across their devices.
      10
      35 npm
      Apache 2.0
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI agents to ask questions to users asynchronously via a local macOS app, allowing users to respond by text or voice.
      1
      MIT
    • A
      license
      Not graded
      quality
      A
      maintenance
      MCP server that provides an ask_human tool so AI agents can request decisions from users via chat apps, with deadlines, defaults, and an append-only audit log.
      93 npm
      Apache 2.0