Skip to main content
Glama
coder-mil

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. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

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/coder-mil/ask-agent'

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