Twin Slack
FreeMaintainedDeterministic Slack Web API twin for agent testing — REST + MCP, SQLite-backed state.
About
Deterministic Slack Web API twin for agent testing — REST + MCP, SQLite-backed state.
README
Pome Digital Twins
Test mode for your integrations, built for the way agents build.
Pome gives you local, stateful twins of GitHub, Slack, Stripe, Gmail and Linear. Your agent, or your own code, calls a twin the way it calls the real API, over REST or MCP. There is no test account, no OAuth app and no API key. Every request lands on a tape: the twin's own record of every call and what it changed. A step the agent only claimed appears on the tape as a step that did not happen. Stripe has a test mode. GitHub and Slack do not, and none of them hands you the tape.
One command
You need Node.js 24 or newer. The twins run on Node's built-in SQLite.
npx @pome-sh/cli@latest twin start github
It prints:
Pome github twin listening at http://127.0.0.1:3333/s/standalone
Seed: the github twin's default (pass --seed <path>, or write one with `pome twin new-seed github`).
POME_GITHUB_REST_URL=http://127.0.0.1:3333/s/standalone
POME_GITHUB_MCP_URL=http://127.0.0.1:3333/s/standalone/mcp
POME_AUTH_TOKEN=eyJ…
Health check (no auth): curl http://127.0.0.1:3333/healthz
Connect your agent (pick one):
Claude Code:
claude mcp add --transport http pome-github http://127.0.0.1:3333/s/standalone/mcp --header "Authorization: Bearer eyJ…"
Codex, .mcp.json and the SDK line below read the token from your shell. Export it once:
export POME_GITHUB_REST_URL=http://127.0.0.1:3333/s/standalone POME_GITHUB_MCP_URL=http://127.0.0.1:3333/s/standalone/mcp POME_AUTH_TOKEN=eyJ…
Codex (append to ~/.codex/config.toml):
[mcp_servers.pome-github]
url = "http://127.0.0.1:3333/s/standalone/mcp"
bearer_token_env_var = "POME_AUTH_TOKEN"
…
The output continues with a .mcp.json stanza and the line for the vendor's own SDK. The twin mints the token when it starts. It is not an account credential. The twin listens on port 3333, and --port picks another. It serves everything under /s/standalone, the one session a standalone twin has.
The twin starts with one repository, acme/api, and one open issue, so your agent has something to act on before you write a seed. State lives in the twin's process and is gone when you stop it. --seed decides where it starts. The twin also writes these values to .pome/twin-status.json in the folder you ran it from, readable only by you. .pome/ git-ignores itself.
Several twins at once is one command: twin start github slack linear. Each twin takes its own port. One connect block covers all of them. .pome/twin-status.json lists them under twins, and its top-level fields describe the twin you started last.
Connect your agent
Paste the block for your client, exactly as printed. Claude Code takes the one-liner. Codex takes the table. Any client that reads .mcp.json takes the stanza. The stanza reads the token from POME_AUTH_TOKEN in your shell, so run the printed export line first. Your own code takes the SDK line. GitHub's Octokit, Slack's WebClient, Stripe's client, googleapis for Gmail and Linear's SDK each get their own. The connect guide covers Cursor and the other clients.
If your client already has a real GitHub MCP server, the twin registers under its own name, pome-github. Disable the real one while you test. Otherwise the agent picks whichever it likes.
Then ask the agent for something small: "Open an issue in acme/api for the login page returning 500 after the deploy."
See what it actually did
npx @pome-sh/cli@latest twin tape --diff
github twin at http://127.0.0.1:3333/s/standalone — 3 requests
TIME REQUEST STATUS FIDELITY STATE
19:04:49.743 list_issues 200 semantic read
19:04:53.762 create_issue 200 semantic changed
19:05:00.780 add_issue_comment 404 semantic no change ← write did not land (404); error: Issue not found
3 requests: 1 changed state · 1 write did not land · 1 read
State diff since boot (seed → now):
repositories ~1 changed: acme/api
repositories[acme/api].issues +1 added: #2
One line per request, REST or MCP. A call through Octokit shows as POST /repos/acme/api/issues. In the STATE column, changed means the write landed in the twin's state, and no change on a write is a step that did not happen. In the FIDELITY column, unsupported is a route the twin does not model, answered with 501 rather than a guess.
Here the agent listed the issues, created #2, then commented on issue #17, which does not exist. An agent that reports "commented" after that is what the tape is for. The diff is what the run left behind, measured against the state the twin booted with. --json prints the same as one object.
Run it in CI
The same command works in a GitHub Actions job. The job below starts the twin, waits for it, hands its address and token to your tests, and prints the tape at the end. Pin the version you tested. The CLI is pre-1.0 and @latest moves.
- uses: actions/setup-node@v4
with:
node-version: 24
- name: Start the GitHub twin
run: |
npx @pome-sh/[email protected] twin start github > twin.log 2>&1 &
for i in $(seq 1 60); do curl -fsS http://127.0.0.1:3333/healthz >/dev/null 2>&1 && break; sleep 1; done
echo "POME_GITHUB_REST_URL=$(jq -r .rest_url .pome/twin-status.json)" >> "$GITHUB_ENV"
echo "POME_GITHUB_MCP_URL=$(jq -r .mcp_url .pome/twin-status.json)" >> "$GITHUB_ENV"
echo "POME_AUTH_TOKEN=$(jq -r .auth_token .pome/twin-status.json)" >> "$GITHUB_ENV"
- run: npm test
- name: What the tests did
if: always()
run: npx @pome-sh/[email protected] twin tape --diff
The twin from the first step keeps running for the rest of the job. This repository's own CI starts its twins the same way. twin tape finds the twin through .pome/twin-status.json. There is no account and no secret in the repository: the twin mints the token on the runner, and the token dies with the twin. Your tests read POME_GITHUB_REST_URL and POME_AUTH_TOKEN the way they would read a real base URL and token.
Supported twins
Pome includes 5 digital twins and 115 MCP tools. Each twin publishes a route-by-route fidelity record. Every day Pome replays the same requests against the real vendor API, with its own accounts, and publishes the result per route at status.pome.sh.
| Twin | MCP tools | Main API coverage | Details |
|---|---|---|---|
| GitHub | 36 | Repositories, issues, pull requests, reviews, and merges | Fidelity |
| Stripe | 26 | PaymentIntents, refunds, charges, balances, events, and x402 payments | Fidelity |
| Slack | 18 | Channels, messages, threads, reactions, and search | Fidelity |
| Gmail | 13 | Messages, drafts, threads, labels, and uploads | Fidelity |
| Linear | 22 | GraphQL, OAuth with PKCE, and webhook registration | Fidelity |
Each route has one of these fidelity levels:
semantic: The route implements and tests provider behavior.shape: The response has the provider's shape.unsupported: The twin returns501.
The twins answer requests. They do not call your app. No twin delivers webhooks today. Stripe's twin exposes /v1/events to poll. Linear's twin records webhook registrations and does not deliver them. A flow that starts from a vendor event needs you to post that event to your app yourself.
Swap to the real API
Three things change, and nothing else in your code should:
- The base URL.
POME_GITHUB_REST_URLbecomeshttps://api.github.com. The MCP URL becomes the vendor's own MCP server, or yours. - The credential. The twin's bearer becomes a real token with real scopes.
- Vendor-side setup the twin never asked for: OAuth apps, app installation, webhook registration. Gmail needs a Google OAuth client. The twin does not.
A green run on a twin says your integration behaves against the API as far as the fidelity record covers it. It does not say the vendor will behave the same tomorrow. Run one smoke test against the real API after the swap.
Why not mocks, why not Emulate
| Hand-written mocks | Vercel Emulate | Pome twins | |
|---|---|---|---|
| State that persists across calls | Whatever you wrote by hand | Yes | Yes |
| A tape of every request your code made | Only if you wrote one | No | Yes |
| Fidelity measured against the vendor and published | No | No | Yes, daily |
| MCP surface for agents | No | No | Yes, 115 tools |
The Emulate column comes from its README as of 2026-09-16. Emulate targets application code in a dev loop, and it is Apache-2.0 like this repo.
Going further
pome below is the same CLI. npm install -g @pome-sh/cli puts it on your PATH, or keep using npx @pome-sh/cli@latest. Everything above runs locally with no account. Hosted grading is optional. Nothing leaves your machine unless you use it, apart from the daily usage event (see Telemetry).
- Your own world:
pome twin new-seed github --out seed.json, edit it, thenpome twin start github --seed seed.json. Several twins from one file:pome twin new-seed github slack --out seed.json, thenpome twin start github slack --seed seed.json. See the local twin guide. - Graded tasks, locally:
npx @pome-sh/cli@latest initscaffolds a project.pome run --local tasks/01-bug-happy-path.mdrecords a run.pome inspect latestreads it. A local run records evidence and does not score. - Scoring:
pome login, thenpome run tasks/01-bug-happy-path.mdrecords and grades in one hosted workflow. Or score a local tape with Braintrust or LangSmith, see integration-examples/.
Examples
- agent-examples/ contains complete agents and graded tasks.
- integration-examples/ connects Pome to Braintrust and LangSmith.
- showcases/ demonstrates individual twin behaviors without an agent or grading.
- skills/ contains skills that help coding agents author and run Pome tasks.
Repository layout
@pome-sh/cli contains the CLI and the twin runtimes. Users do not install the twin packages separately.
The shared runtime provides HTTP routing, bearer authentication, MCP dispatch, recording, and SQLite state. Each twin adds its provider-specific domain behavior.
See packages/README.md for the package map. See CONTRACT.md for the twin runtime contract.
Contributions are welcome, and the easiest first ones are seeds and showcases. CONTRIBUTING.md says where to start and what a pull request needs. A new twin is a package that satisfies the runtime contract in CONTRACT.md. A bug report is most useful with the tape attached (pome twin tape --json). A security problem goes to SECURITY.md, not to a public issue.
Telemetry
The CLI sends one anonymous usage event per day, at most. The event carries a random id, the CLI version, the OS, the Node major, and the command's name (twin start, init). The CLI mints the id once and keeps it in ~/.pome/telemetry.json. The event never carries an argument, a path, a repo name, a seed, a token or anything from a tape. The first send prints a one-line notice.
Turn it off with POME_TELEMETRY=0. The CLI also honours DO_NOT_TRACK=1. It sends nothing when CI is set, and nothing from a build made without an ingest key. The code is cli/src/cli/usageTick.ts.
Status and license
Pome is in beta. CLI behavior and dependencies can change before version 1.0.
This repository uses the Apache-2.0 license.
Install Twin Slack in Claude Desktop, Claude Code & Cursor
unyly install twin-slackInstalls into Claude Desktop, Claude Code, Cursor & VS Code — handles npx, uvx and build-from-source repos for you.
First time? Get the CLI: curl -fsSL https://unyly.org/install | sh
Or configure manually
Run in your terminal:
claude mcp add twin-slack --env ANTHROPIC_API_KEY="" --env TOKEN="" --env TWIN_AUTH_SECRET="" -- npx -y @pome-sh/twin-slackStep-by-step: how to install Twin Slack
FAQ
Is Twin Slack MCP free?
Yes, Twin Slack MCP is free — one-click install via Unyly at no cost.
Does Twin Slack need an API key?
Yes, it requires environment variables: ANTHROPIC_API_KEY, TOKEN, TWIN_AUTH_SECRET. Unyly injects them into the config during install.
Is Twin Slack hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install Twin Slack in Claude Desktop, Claude Code or Cursor?
Open Twin Slack on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Changes
Versions and requested access over time.
- New version published
- New version published
- New version published
- New version published
- New version published
Related MCPs
Gmail
Read, send and search emails from Claude
by GoogleSlack
Send, search and summarize Slack messages
by SlackRunbear
No-code MCP client for team chat platforms, such as Slack, Microsoft Teams, and Discord.
Discord Server
A community discord server dedicated to MCP by [Frank Fiegel](https://github.com/punkpeye)
Klavis AI
Open Source MCP Infra. Hosted MCP servers and MCP clients on Slack and Discord.
Work90210/APIFold
Turn any REST API into a hosted MCP server. 18 free public servers (GitHub, Stripe, Slack, OpenAI, Notion, and more) — no setup required, bring your own API key
by Work90210arikusi/deepseek-mcp-server
MCP server for DeepSeek AI with chat, reasoning, multi-turn sessions, function calling, thinking mode, and cost tracking.
by arikusihashgraph-online/hashnet-mcp-js
MCP server for the Registry Broker. Discover, register, and chat with AI agents on the Hashgraph network.
by hashgraph-onlineprofullstack/mcp-server
A comprehensive MCP server aggregating 20+ tools including SEO optimization, document conversion, domain lookup, email validation, QR generation, weather data,
by profullstackWayStation-ai/mcp
Seamlessly and securely connect Claude Desktop and other MCP hosts to your favorite apps (Notion, Slack, Monday, Airtable, etc.). Takes less than 90 secs.
by waystation-aiCompare Twin Slack with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All communication MCPs
