Skip to main content
Glama
README.md
# huddle-mcp

> An MCP server that lets your parallel coding agents **schedule briefings** on your real calendar instead of interrupting you. They queue their plans and questions; the queue batches them by urgency into time-blocks booked when you're free.

When you run a fleet of agents, the bottleneck is you. Three plans and two questions land at once, on their schedule, and the context-switching wrecks your decisions. `huddle-mcp` is the fix real employees already use: don't barge in — book a meeting.

```
auth-agent  → request_meeting("JWT vs sessions", type=plan, urgency=blocker)
ui-agent    → request_meeting("dark-mode default?", type=question)
db-agent    → request_meeting("migration applied", type=fyi)

You: "book my agent briefings"
  → 🚨 Blocker · auth-agent: JWT vs sessions      11:05–11:15
  → 📋 Agent briefing · 2 items                    14:00–14:20  (the question + the FYI)
```

You review at the booked time; the agents wait and unblock once you've decided.

## The idea: a broker, not another calendar wrapper

Wrapping a calendar is a solved problem (Google ships an official Calendar MCP; there are mature OSS ones). The value here is the **coordination layer** in front of it:

- a shared **queue** any agent can drop a request into,
- an **urgency-aware batching** policy (blockers get booked ASAP; routine items bundle into the next briefing; FYIs ride along and reserve no time),
- a **briefing renderer** that aggregates everything into one agenda you skim.

So huddle-mcp is **auth-free and provider-agnostic** — it never touches your calendar. It emits the agenda + a desired time window, and the actual freebusy lookup + event creation is delegated to whatever calendar MCP you already have connected (Google, Outlook, …). **Install = one line, zero new credentials.**

## Tools

| Tool              | Who calls it  | What it does                                                            |
| ----------------- | ------------- | ---------------------------------------------------------------------- |
| `request_meeting` | worker agent  | Queue a plan/question/decision/fyi. Returns a `ticketId`. No calendar. |
| `check_response`  | worker agent  | Poll a `ticketId`; returns the user's decision once answered.          |
| `cancel_request`  | worker agent  | Withdraw a ticket you resolved on your own.                            |
| `plan_meetings`   | orchestrator  | Batch the queue into proposed meetings + rendered briefings.           |
| `confirm_meeting` | orchestrator  | Record a booked calendar event; marks its tickets scheduled.           |
| `get_briefing`    | orchestrator  | The rendered agenda + full tickets for a meeting.                      |
| `resolve`         | orchestrator  | Record the user's decision for one ticket → unblocks the worker.       |
| `ingest_answers`  | orchestrator  | Parse the user's answers out of the **event description** and resolve every answered ticket at once. |
| `reopen`          | orchestrator  | Undo a decision and re-open the ticket (you changed your mind).        |
| `discard_meeting` | orchestrator  | Drop a meeting, re-queue its open tickets (stale proposals auto-expire after 24h). |
| `list_agenda`     | orchestrator  | What's queued / scheduled / answered.                                  |

## How it fits together

The calendar event is **two-way**: huddle writes the briefing into it, and the user answers right there.

```
worker agents ──request_meeting──▶ [ huddle-mcp queue ]   (local JSON, file-locked)
   (auth, ui, db…)                       │ batch by urgency + render briefing
                                         ▼
                          plan_meetings → proposed meetings (window + duration + briefing)
                                         │
your orchestrator ──▶ your Calendar MCP: freebusy → create_event(description = briefing + answer block)
                                         │
                          confirm_meeting(meetingId, eventId, start, end)
                                         ▼
        ┌── you answer in the event:  "tkt_3f9a → use server sessions"
        │
        └─▶ orchestrator reads the event back ──▶ ingest_answers(description)
                                         ▲              (or resolve(ticketId, decision) in chat)
worker agents ──check_response(ticketId)─┘  poll, unblock, proceed
```

The briefing that lands in the event ends with a writable answer block:

```
— ✍️ Your answers —
Reply after each → then tell your agent "read my huddle answers".
tkt_3f9a2c1d → use server sessions
tkt_8b7e0a44 → ship dark mode as the default
```

When you say *"read my huddle answers"*, the bundled **`huddle` skill** (in `skills/huddle/`) fetches the event via your calendar MCP, passes its description to `ingest_answers`, and every answered ticket resolves — the agents unblock. The same skill drives the booking side: *"book my agent briefings"* runs `plan_meetings`, finds free slots, and creates the events. huddle itself never touches the calendar — the skill bridges it.

## Requirements

- Node.js ≥ 20
- A calendar MCP connected in the same client (e.g. Google Calendar) for the actual booking. huddle-mcp itself needs no account and no auth.

## Install — Claude Code

```json
{
  "mcpServers": {
    "huddle": {
      "command": "npx",
      "args": ["-y", "huddle-mcp"]
    }
  }
}
```

That's it — no env vars. (Prefer the bleeding edge? Use `"github:marcoloco23/huddle-mcp"` instead of `"huddle-mcp"`.) Connect a Google Calendar MCP alongside it for booking, and (optionally) copy `skills/huddle/` into `~/.claude/skills/` so *"book my agent briefings"* and *"read my huddle answers"* just work.

### From source

```bash
git clone https://github.com/marcoloco23/huddle-mcp.git
cd huddle-mcp
pnpm install   # builds via the prepare hook
node dist/index.js
```

## For agent authors

Worker agents should **queue and wait**, never block the user inline:

```
request_meeting({ agent, title, body, type, urgency?, estimated_minutes? })
  → { ticketId }
# do other work, then:
check_response({ ticket_id })  → { answered, response }   # proceed once answered
```

- `type`: `question` (needs an answer) · `plan` (review a plan) · `decision` (pick an option) · `fyi` (no action).
- `urgency`: `blocker` (book ASAP) · `normal` / `low` (bundle into the next briefing). Default `normal`.

## Storage

The queue is a single JSON file shared by every agent process, at
`~/.config/huddle-mcp/queue.json` (honors `XDG_CONFIG_HOME`; override with
`HUDDLE_HOME`). Concurrent writes from parallel agents are guarded by an atomic
cross-process lock.

## Developing & releasing

```bash
npm install        # builds via the prepare hook
npm test           # node:test — pure units + a stdio server integration suite
```

CI runs the suite on every push/PR. Releases are automated: bump the version in
`package.json`, then cut a **GitHub Release** — the publish workflow builds, tests,
and pushes to npm. It needs an npm **automation** token (which bypasses 2FA) saved
once as the `NPM_TOKEN` repo secret (Settings → Secrets and variables → Actions).

## Roadmap

Where this is headed — autonomy (agents book themselves), closing the last mile
(answered → agent actually resumes), notifications, smarter briefings, Outlook, and
more — is in [ROADMAP.md](ROADMAP.md), ranked by how much it protects your attention.

## License

MIT

TDQS

A3.7/5.0

Scored across 8 tools

Disambiguation5/5

Each tool serves a distinct purpose: cancel_request, check_response, confirm_meeting, get_briefing, list_agenda, plan_meetings, request_meeting, and resolve all have clearly different functions with no overlap. An agent can easily select the correct tool.

Naming Consistency4/5

Most tools follow a verb_noun pattern (e.g., cancel_request, confirm_meeting), but 'resolve' is a single verb, slightly breaking the pattern. The naming is otherwise consistent and readable.

Tool Count5/5

With 8 tools, the server covers the ticket-based meeting workflow without being too few or excessive. Each tool earns its place in the domain.

Completeness5/5

The tool set covers the full lifecycle: requesting, cancelling, planning, confirming, polling, resolving, and listing. External calendar integration is explicitly delegated, leaving no dead ends.

Maintenance

ActivityStale
ResponsivenessNo issues