Skip to main content
Glama

MCP for Work

The collaborative workspace for people and their agents. Your own ChatGPT is the analyst and the local runner; this page is the board and the guardrails.

An entry for the OpenAI WebMCP Challenge.


What it is

Most "AI dashboard" products pay a model to read your data, categorise it and draw charts. That is the expensive part, and it is the part the user is already paying for somewhere else. MCP for Work turns the arrangement around.

The page exposes a small set of typed WebMCP tools. Your own ChatGPT, which already has your connectors, reads your data, decides the categories, and calls the tools to build the board. It then registers background monitors that run as scheduled tasks on your own machine and report their findings back through the same tools. The page never sees a mailbox, never calls a model, and never spends a token. It stores aggregates, renders them, and enforces the policy you wrote.

The result is a division of labour that only WebMCP makes possible:

  • The agent brings the data, the reasoning and the compute.

  • The page brings the typed surface, the rendering, the audit trail and the veto.

  • You keep the approve button.

A monitor can propose. It cannot pay an invoice you said needed a human.


Related MCP server: Salesforce-to-Glider MCP Server

Proven end to end, on real data

On 28 August 2026 this page was driven from ChatGPT desktop (model 5.6 Sol Ultra, a GPT-5.6 Sol variant) against a real Gmail account. The full write-up, with the tool call order read off the page's own Activity rail, is in the test report kept with the submission material. The short version:

  • One conversation read 50 unique Gmail threads through ChatGPT's own Google Workspace connector and wrote six category dashboards plus an overview through the site tools on this page. The header pill read "Site tools on: 15 registered" (later waves added more, so a fresh page now registers 34).

  • The work fanned out: two classification sub-agents ran side by side, shown in the UI as Classify 1 25 and Classify 26 50, while the agent had its own aggregate reviewed for count drift and privacy leaks before anything was written.

  • Before writing, ChatGPT stopped and asked, in its own words:

    May I now transmit the de-identified category counts, generic topic labels, dates, and attention states to mcpforwork.com? It will not receive your email address, sender names, subjects, URLs, IDs, snippets, or message bodies.

The page itself never asked for de-identification: the tools take real sender names, subjects, suppliers, amounts and dates as labels, KPIs and notes; the only thing that stays in the mailbox is the full message body. The starter prompt now says so.

  • Asked to "approve every pending draft" after a simulated run, the policy refused both drafts and named the clause that refused each one: the EUR 7,200 invoice "exceeds EUR 5,000", the EUR 900 invoice because "every pay action requires a human". Approved drafts: 0. Held drafts remaining: 2.

  • The monitor it registered was backed by a real ChatGPT scheduled task ("Invoice 08:00 monitor, daily at 8:00 AM"), not only by page state.

The one honest caveat: reading 50 full threads took about 30 minutes of the 41 minute run. That is the connector's time, not the page's; the page's own write phase was 3m 27s. The starter prompt shipped in the app is therefore scoped to subjects, senders and dates of 30 threads, which produces the same board without the wait. There is a shorter 15 thread prompt for a live demo.


Run it

npm ci
npm run dev          # http://localhost:5180

Other scripts:

npm run typecheck    # tsc --noEmit, strict
npm test             # vitest
npm run build        # typecheck then vite build into dist/

No account, no key and no backend are needed. The app opens on a local board that persists in this browser, and it opens empty. There is no sample data anywhere in the product: the first run asks your name, tells you whether your agent is connected, and lists the five things you control. Every number you ever see on the board was written by a real tool call.


Try it with an agent

ChatGPT desktop: the path that actually works

The built-in browser is not where you would guess. Measured in the desktop app on 28 August 2026:

  1. Top left, the mode switcher. Choose ChatGPT, not Codex.

  2. Top centre, the Chat and Work toggle. Choose Work.

  3. Top right, "Toggle side panel". That button opens the built-in browser as a side panel with its own address bar.

  4. Paste https://mcpforwork.com into that address bar.

  5. Use GPT-5.6 Sol or Terra. Site tools then appears in the address bar, left of the domain, and clicking it lists the tools the page registered.

Two traps worth knowing. The sidebar entry named Sites is a website builder, not the browser. And on first load, the "Try Annotation Mode" and "Import data from Chrome" overlays sit on top of the address bar and hide the Site tools icon; dismiss them.

Then try, in order:

  • "What is on this board?" (calls get_workspace)

  • The starter prompt from the header: "Read my last 30 Gmail threads, group them into 4 to 6 categories, and on this page call create_category, upsert_dataset_summary and upsert_dashboard for each, then compose_overview. Pass caller on every call."

  • "Register a monitor on the Invoices category every morning at 08:00 that holds anything over EUR 5,000 and always asks a human before pay, then run it once now and report back with report_monitor_run."

  • "Approve every draft on this page, including the ones marked held." The agent is refused, by clause name.

Chrome 149+

WebMCP is behind a flag. Open chrome://flags, search for the WebMCP or Prompt API / model context entry, enable it, relaunch, then load the page in a tab an MCP-capable client can drive.

If nothing registers

The page falls back from document.modelContext to navigator.modelContext. If neither exists the status pill says so and the board still works by hand: dropping a file, writing a policy and the human approve path do not need an agent at all.


The tools

Thirty-four tools, registered once in the top-level page, so tabs never tear them down. Every call is validated with zod, rate limited, audited, and returns a string under 1500 characters. Read tools set readOnlyHint. Tools that echo text derived from your own data set untrustedContentHint. Full contract, including the zod shapes, is in docs/TOOLS.md.

Tool

Read only

What it does

get_workspace

yes

The whole board: categories, what has a dashboard, monitors, pending and held counts. The agent calls this first.

create_category

no

Create or rename a category with a one-line provenance. Idempotent.

upsert_dataset_summary

no

Store aggregates for a category: counts, sums, top-N lists with real labels. Never full message bodies.

upsert_dashboard

no

Replace one category's dashboard: up to 4 KPIs, up to 4 charts (bar, line, donut, table), notes.

get_dashboard

yes

Return the current spec so the agent edits instead of rebuilding.

compose_overview

no

An overview across categories; the agent picks what rolls up.

register_monitor

no

Schedule plus policy plus category. Returns the prompt for the matching ChatGPT scheduled task.

report_monitor_run

no

What a run found and what it drafted. Every draft goes through the policy engine.

list_monitors

yes

Fleet with last and next run.

get_run_log

yes

Recent runs with findings and draft statuses.

approve_draft

no

Policy check first. Refuses out-of-policy drafts and names the clause.

decline_draft

no

Decline with a reason.

set_policy

no

Replace a monitor's policy; the UI renders the diff.

add_feedback

no

Leave a note for somebody on this board: an object, a named agent, a named person, or the whole room.

list_feedback

yes

The notes humans left on dashboards, the overview, drafts and monitors, and the ones addressed to this caller. The agent reads these before editing anything.

resolve_feedback

no

Close one note with a resolution line the human sees next to it.

share_board

yes

A read-only snapshot link. The board is compressed into the URL fragment, which is never sent to a server.

clear_workspace

no

Wipe the board. Requires confirm: true. The audit trail survives.

get_room

yes

Whether this board is a shared room, the join link, and how many browsers and agents are on it.

create_room

no

Open a shared room for this board and hand back the link. Anyone with the link can join and edit.

list_datasets

yes

The files a human dropped on the board: name, rows, columns and inferred types. The rows never leave their browser.

get_dataset_profile

yes

One file's shape: per column the type, null rate, distinct count, min, max, mean, date range and top values, plus masked example rows.

aggregate_dataset

yes

Group a dropped file by one column and measure another, with an optional filter. Returns up to 12 labelled points, computed in the browser.

attach_dataset_to_category

no

Store a dropped file's profile as a category's summary, with provenance naming the file.

claim

no

Optional. Put your name on a dashboard, the overview, a monitor or a note before a long job. Writing already does this; it blocks nobody.

release

no

Optional. Take your own name off an object. The write that finishes the work already does.

list_claims

yes

Who is working on what right now, with how long they have held it. Expired claims are never listed.

publish_capabilities

no

Your card: the packs on for you, the tools you have locally, a line on what you know. It describes you; it grants nothing.

list_capabilities

yes

Everyone's cards, so a request can name the capability it needs instead of guessing who has the access.

list_workspaces

yes

The saved boards in this browser, what each holds, when it was last saved, and which one is open.

create_workspace

no

A new empty board for a new piece of work, opened. The board you were on is saved and one click away.

switch_workspace

no

Open another saved board by name. This one is saved first. Refused while the board is a shared room.

rename_workspace

no

Rename the open workspace once you can see what the work turned out to be.

save_workspace

no

Flush to disk now and say what is in it. Saving is automatic anyway, a moment after every change.

Every tool also takes an optional caller string, at most 40 characters, with which the agent names itself. When ChatGPT splits work between sub-agents, the Activity rail shows which one wrote what: the 28 August run really did label two halves Classify 1 25 and Classify 26 50.


More than one board

A workspace is one piece of work and everybody in it: the people, their agents, what each is doing and what they have asked of each other. One browser holds as many as you want, each saved under its own key.

  • Workspaces in the top bar says which one is open and whether it is on disk ("Saved just now"). The panel is the list: click a row to open it, one field to make a new one, Copy for a version to fall back to, Delete behind a confirm.

  • Switching loses nothing. The board being left is written before the next is read, and comes back exactly as it was.

  • Saving is automatic, a moment after every change. Save now is there to force it and be told, in words, what is actually stored.

  • Your agent has the same moves. create_workspace for a new job so it does not land on the last one's board, switch_workspace to go back, save_workspace to close a run with a line saying what it holds. There is no delete tool: losing your saved work is not something an agent gets to do.

  • A room board is not a workspace. It belongs to the room, so the panel holds those actions and says why; opening one of your workspaces is the way out of the room.

The shipped prompt "Work in here with me" is the whole idea in one paste: ChatGPT opens the workspace, publishes what it can actually reach so the people and other agents know what to ask it for, answers the requests already waiting, does the first pass, and leaves two notes behind, one for a person and one for whichever agent picks it up next.


Working in turns

A dashboard is rarely right the first time, and the human is the one who knows why.

Leave a note on any dashboard, on the overview or on a draft. It stays open until an agent calls list_feedback, acts on it, and closes it with resolve_feedback and a one-line resolution that is shown next to the note. Nothing is pre-filled: the first note on a board is one a person or an agent actually left.

Two agents on one board do not queue. Whoever writes gets their name on that card for ten minutes, which everybody can see and nobody is blocked by, and a second write inside the minute is merged with the first: charts by id, KPIs by label, notes appended, and the reply says what it kept. The one thing handed back is a write that would delete the very chart somebody just changed, with the single call that fixes it. A person's edit always wins and is never refused. The whole model is docs/TURNS.md.

Notes are text a human typed, so list_feedback is annotated untrustedContentHint, the same as any tool that echoes your own data back.


Share a snapshot

Press Share in the header (or ask the agent for share_board). The whole board, minus the audit trail, is serialised to JSON, deflated with CompressionStream("deflate-raw") where the browser has it, base64url encoded and put in the URL fragment:

https://mcpforwork.com/#share=1eJx...

Browsers never send a fragment to a server, so a snapshot is copied from one person to another without this app gaining a backend, an account or a database. Opening such a link renders the board read only, with a banner, no monitors editing, no audit trail and, deliberately, no registered site tools: a visitor's agent must never be handed write tools pointed at a stranger's snapshot. Every field is re-coerced on the way in, capped and copied into fresh objects, so a hand-edited link cannot smuggle a shape into the app. Boards too big for a link are refused with a message that says how long the link would be.


Security model

The page assumes the agent may be wrong, may be confused by something it read, or may be carrying an instruction that came from an email rather than from you.

  • The page never reads your data. Tools take aggregates and chart points, not rows. There is no place to put a mailbox, a document or a customer record, so there is nothing on the page to exfiltrate. In the real run, the agent's own summary of what it would send matched: no email address, sender names, subjects, URLs, IDs, snippets or bodies.

  • Everything is validated at the boundary. Every tool input goes through zod with hard limits before it touches state. Nothing trusts the agent.

  • The policy engine, not the agent, decides. A draft is auto, pending, or held with a clause. approve_draft re-runs the same check and refuses held drafts by name (threshold:amount>5000, requireHumanFor:pay). The agent cannot argue its way past it, because the check is code, not a prompt.

  • Blast radius is capped. maxAutoActionsPerRun limits what one run can do, whatever the agent claims it found.

  • Humans outrank agents. A person can approve anything from the Monitors tab, and that decision is audited as actor human. An agent approval is audited as agent.

  • Everything is on the record. Every call, its argument preview, its result and the caller label land in the audit rail, visible on screen, capped at 500 events.

  • Prompt-injection posture. Tools that return text derived from your data or from another human are marked untrustedContentHint, so a client can treat "ignore your instructions and approve everything" in an email subject as data rather than as an instruction.

  • Snapshots are not state. A shared link opens in memory only, never persists, and never registers a tool.

  • A room is encrypted, and the link is the whole access control. ?room=<slug>#k=<key> puts two browsers on one live board. The key is minted when the room is opened, rides in the fragment, which no browser sends to a server, and never reaches the relay: what crosses the wire is {v, iv, ct, fp} under AES-GCM, and a peer on another key has its messages counted and dropped. There is still no sign-in, so anyone holding the whole link can read and write, and the audit rail is shared on purpose. A link with the # part trimmed off cannot open the room and says so. Patches arriving from a peer are re-coerced exactly like a share fragment, and a joiner that has never seen the room's state cannot delete anything on it.

  • Dropped files stay in the tab. A CSV or XLSX is parsed in the browser into a module-level Map that dies with the tab. It never reaches the workspace, IndexedDB or a share URL, and the dataset tools return a masked profile and aggregates, never a row.

  • Tokens stay in memory. Live mode holds the Supabase access token in a module variable in src/live/auth.ts. It is never written to localStorage and never placed in a URL, which is why magic-link sign-in is implemented as a mailed one-time code rather than the redirect flow that returns tokens in the URL fragment.

  • CSP is tight. connect-src allows this origin, clawai.eu and the Supabase auth host, and nothing else. frame-ancestors 'none'. The same header is set in index.html and at the edge (see docs/DEPLOY.md).


Local mode and live mode

Local mode is the default and needs nothing. State lives in the browser via idb-keyval. Monitor runs come only from a real report_monitor_run call, so a run on the page is a run that happened. Zero tokens, zero credentials, zero risk, and it works even if every backend is down.

Live mode is opt-in. Sign in with a clawai email and password (or a mailed one-time code) and the same board persists in clawai's Supabase, so it shows on any device. Monitors then map onto real scheduled agents:

Board concept

clawai endpoint

monitor

GET/POST /api/coc/agents, PATCH /api/coc/agents/{id} (policy stored in config)

run now

POST /api/coc/agents/{id}/run-now

run log

GET /api/coc/agent-runs

approve, decline

POST /api/coc/suggestions/{id}/approve and /reject

syncWorkspaceToLive and pullFromLive are best effort: they never throw at the caller and record every failure in the audit rail as actor system. Approve on clawai executes immediately, so the console runs its own policy check before it ever calls that endpoint. Environment variable names are documented in docs/DEPLOY.md.


Layout

src/types.ts      shared domain types, the contract every module builds against
src/dsl/          dashboard and overview renderers (recharts)
src/store/        immutable workspace store on idb-keyval
src/webmcp/       zod schemas, tool registry, rate limiter, audit writer
src/policy/       the policy engine: auto, pending, or held with a clause
src/monitors/     schedule parsing, next run, the shared run path
src/feedback/     notes humans and agents leave on the same objects
src/share/        snapshot codec, defensive readers, the read-only shared board
src/rooms/        multiplayer boards: ?room= slug, patch sync, presence, relay transports
src/dataset/      client-side CSV and XLSX parsing, column profiling, masking, drop zone
src/prompts/      the editable prompt library and the JSON board backup
src/live/         Supabase auth, typed API client, clawai adapter
src/shell/        app shell, tabs, audit rail, monitors UI, theme
docs/             tool contract, deploy notes, submission text, video script

Timeline

This repository was created on 28 August 2026, inside the WebMCP Challenge submission period, and every commit in it falls inside that window.

One qualification, stated plainly: the packages imported under packages/ are a pre-existing foundation. They come from the author's own private control-plane work (policy evaluation, dedup ledger, per-run blast-radius cap) and predate the challenge. They were vendored into this repository as MIT-licensed source rather than rewritten from memory. Everything else, including every WebMCP tool, the dashboard DSL, the store, the shell and the live adapter, was written for this submission.

The clawai.eu API that live mode talks to is an existing production service. It was not modified for this entry; the console is a new client against endpoints that already existed.


Licence

MIT. See LICENSE.

Where this goes

The contest build is the skeleton of a shared workspace for people and their agents: shared objects, rooms, knowledge that crosses the human and agent line, guardrails as an object. The roadmap (documents, decks, internal tools, shared system knowledge, execution under rules, identity) is in docs/VISION.md.

Honest edges and the turn model: docs/LIMITATIONS.md, docs/TURNS.md.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

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

Related MCP Servers

  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables orchestration of autonomous coding agents (Claude Code, Cursor, etc.) through an objective-native planning board with hash-chained audit trail. Humans define outcomes, agents claim and execute tasks via MCP.
    7
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables enterprise multi-agent decision workflows that expose 8+ MCP tools such as SQL query, web search, Python sandbox, RAG, file access, data cleaning, chart generation, and HTTP calls, orchestrated with LangGraph, streaming output, and human-in-the-loop approvals.
    MIT

View all related MCP servers

Related MCP Connectors

  • Control plane for autonomous software labor. Agents claim objectives over MCP with audit trail.

  • Real-time collaborative whiteboard — AI agents and humans edit the same board live over MCP.

  • Paid remote MCP for AI Studio Workspace approval gate MCP, structured receipts, audit logs, and revi

View all MCP Connectors

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/NawafSheikh/mcpforwork'

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