Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Parley Core

FreeNot checked

Parley core: the transport-agnostic seam, normalized Message, cursor/dedup engine, and dual-role MCP channel server. Zero backend dependencies.

GitHubEmbed

About

Parley core: the transport-agnostic seam, normalized Message, cursor/dedup engine, and dual-role MCP channel server. Zero backend dependencies.

README

Plan a task in your Claude chat, hand it to a Claude Code session that catches up on the shared thread and does the work, then read the result back in a chat app you already keep open.

CI last commit npm backends conformance license

Parley is an MCP server — the standard way Claude connects to outside tools — that turns your Claude chat, your Claude Code sessions, and you into one shared, durable thread over a messaging backend you choose.

Contents: The loop · Two ways in · Quickstart A — local, 5 min · Quickstart B — chat→Code, 15 min · Today vs. preview · How it works · Backends · Tools · Remote / chat mode · Compare · Status · Docs


The loop

Your Claude chat and your Claude Code sessions can't talk to each other. Right now you are the cable: you copy a plan out of one window, paste it into the other, wait, then ferry the answer back. Parley is the shared thread that lets them confer over a durable backend while you watch from an ordinary chat app.

Keep Matrix open on your phone, run a Claude Code session on your acme project, and wire a Claude chat to the same topic — project-acme:

  1. In your Claude chat, post a self-contained brief to project-acme: "Add retry-with-backoff to the S3 upload path in uploader.ts; cap at 5 attempts; add a unit test." (Under the hood: parley_post.)
  2. You copy nothing. Go back to what you were doing.
  3. A Claude Code session on acme starts (as claude-code-acme) and, on start, calls parley_fetch_recent on project-acme — catch-up-on-start over the durable backend, not a live push — reading your brief as if it had been in the room the whole time, though it wasn't running when you posted. (The chat-handoff skill is what wires this fetch to fire automatically on session start.)
  4. It does the work — edits uploader.ts, adds the test, runs it — then posts the result back into the same topic (threaded via in_reply_to): "Done — added withBackoff(), capped at 5, test retries on 503 passing."
  5. You read the answer where you asked it — the whole plan → hand-off → result thread lands in your ordinary Matrix app. No terminal, no copy-paste.

Two ways in

The full chat→Code loop above needs remote mode: a Claude chat lives in Anthropic's cloud and can't reach a stdio server on your laptop, so chat joins the thread over an HTTP connector, not a local file. Two on-ramps — price the gap before you start:

  • Kick the tires in 5 minutesno accounts, no domain, no server; Node ≥ 22. A local taste on one SQLite file, Code↔Code. It proves the whole shared-thread spine end to end; the only thing left off is the chat seat.
  • The real hand-offneeds a public HTTPS URL + a claude.ai connector; ~15 min. chat→Code over remote/OAuth mode. The maintainer runs this remote-mode hand-off in production, in a hardened Keycloak/OIDC + Zulip variant (see Remote / chat mode); Quickstart B is the lighter built-in-OAuth + SQLite on-ramp to the same mode.

About the phone. The hero's "read it back in a chat app you already keep open" is the full loop — remote mode plus a chat backend — so it's Quickstart B with one swap, not the 5-minute path. Today the honest, live-proven phone pick is Matrix (or XMPP with a MAM server); the consumer apps most people keep open are still fake-conformance (details). Quickstart B lands chat→Code and reads back in Claude chat; its Land it on your phone step swaps SQLite for Matrix to also drop the thread onto your phone.


Quickstart A: the local taste (5 min, zero infra)

The deliberate local taste — two Claude Code sessions, two directories, one SQLite file — the shared-thread spine with the chat seat left off (that's Quickstart B). No broker, no server, no flags.

Prereqs: Node ≥ 22, two Claude Code sessions, ~5 min — no accounts, no domain, no server.

Give each session its own directory so their configs and .mcp.json don't collide; both point at the same db_path, which is the bus.

1. Install core plus the SQLite backend (in each directory):

npm install @sharptrick/parley-core @sharptrick/parley-sqlite

2. Write a parley.config.yaml in each directory — identical except the handle:

# planner/parley.config.yaml   (coder/ is identical but handle: "coder")
backend: local-sqlite            # selects the @sharptrick/parley-sqlite plugin
identity:
  handle: "planner"              # session B uses "coder"
topics:
  - "ctx-demo"
backend_config:
  db_path: "../parley-demo.db"   # BOTH sessions point at this one file — it is the bus

Defaults are catch-up-only and safe (no live push, skip_permissions: false). The rest of the knobs live in DESIGN.md; you need none of them for the aha.

3. Point a .mcp.json in each directory at the stdio server:

{
  "mcpServers": {
    "parley": {
      "command": "npx",
      "args": ["parley-sqlite", "--config", "parley.config.yaml"]
    }
  }
}

parley-sqlite is the bin shipped by @sharptrick/parley-sqlite — the only runnable binary Parley ships; npx resolves it from your local install.

4. Run the hand-off. Open two Claude Code sessions, one per directory (planner and coder), both resolving to the same parley-demo.db:

  • In planner: "post plan: add retry-with-backoff to uploader.ts, cap at 5 to ctx-demo" → it runs parley_post.
  • In coder: "fetch recent on ctx-demo, then reply with a result" → it runs parley_fetch_recent, reads planner's brief even though it wasn't running when planner posted, and replies with parley_post + in_reply_to.
  • Back in planner: "fetch recent on ctx-demo" → the coder's threaded reply is right there.

Here you trigger each fetch by hand to watch it work; in a wired session the on-start catch-up from The loop fires automatically.

That's the round-trip. parley_fetch_recent hands back the message plus the bookmark that makes catch-up durable:

{
  "messages": [
    {
      "topic": "ctx-demo",
      "senderHandle": "planner",
      "content": "plan: add retry-with-backoff to uploader.ts, cap at 5",
      "backendMsgId": "1",
      "cursor": "1",
      "mentions": [],
      "timestamp": "2026-07-12T18:03:11.204Z"
    }
  ],
  "nextCursor": "1"
}
Why this survives a restart (optional)

nextCursor is the whole trick: persist it, pass it back as since, and the next catch-up returns only messages newer than it — never re-sending one you already read. Kill coder mid-hand-off and start it again: it catches up from its bookmark, re-reads the thread, and loses nothing. Then swap backend: local-sqlite for Redis / NATS / Matrix and the same participants run across machines with zero code changes — correctness lives in the seam, not in any one host. The mechanics (cursor, backendMsgId) are spelled out in How it works.


Quickstart B: the chat-to-Code hand-off (remote mode, ~15 min)

The loop the hero promised — a Claude chat plans, a Claude Code session does the work — over the built-in OAuth front door. No external identity provider for this light path.

Prereqs: a public HTTPS URL (a one-command tunnel works) and a claude.ai connector, ~15 min. Node ≥ 22 for the server; no external IdP on this light path.

  1. Run the self-host-remote reference server as an HTTP server (same seam, a local SQLite backend, just spoken over HTTP). Set PARLEY_OWNER_PASSPHRASE locally and start it on 127.0.0.1:3000.

  2. Put it behind HTTPS. Claude requires a public HTTPS endpoint. The fastest way is one tunnel command (install cloudflared first):

    cloudflared tunnel --url http://127.0.0.1:3000
    

    Set PARLEY_ISSUER_URL to the https://…trycloudflare.com URL it prints. That URL is ephemeral — for a stable one, front the server with Caddy/nginx instead (there's a ready Caddyfile in the self-host README).

  3. Add a claude.ai connector pointed at https://parley.example.com/mcp (your HTTPS URL + /mcp). Claude runs discovery → dynamic client registration → PKCE; on the consent page, enter your owner passphrase to authorize. Backend creds never leave the server — Claude holds only a consented token.

  4. Hand off. In your Claude chat, parley_post a brief to project-acme. A Claude Code session on that project catches up on start with parley_fetch_recent and does the work — so the brief is waiting whether or not any session was live when you posted.

Step 4 is the real, in-production shape: chat→Code lands over the durable catch-up path. The other half — an already-running session reacting the instant you post — is the live-push preview (below), not needed here. Full recipe + connector gotchas + the optional Keycloak/OIDC front door: examples/self-host-remote/README.md.

Optional — land it on your phone. Quickstart B reads back in Claude chat. To get the hero's phone experience, swap the SQLite backend for Matrix — the live-proven phone pick (backends): point backend: at the Matrix plugin, keep the same topics, and the plan → hand-off → result thread also shows up in an ordinary Matrix app on your phone. Same seam, zero participant-code changes; config is in the bridge-matrix README.


What runs today, and what's still preview

Two delivery paths — one is the product, one is a preview. Stated once, here:

Tier 1 — durable catch-up (the product, works today). parley_post + parley_fetch_recent over standard MCP: a session catches up on start from the backend's own store, with ordering and dedup from the cursor + backendMsgId, never timestamps. Live-tested on 6 of 10 backends against real implementations — SQLite as a real on-disk file, plus real Redis/NATS/Postgres servers and live Matrix/XMPP homeservers — with no subscription, no flag, and on the SQLite path no server at all. This is what buys restart-survival, a swappable backend, and a human reading in a real chat app — and it's what the remote hand-off in Quickstart B rides.

Tier 2 — live <channel> push (research preview). Proactive events into an already-running Code session, riding Claude Code's native claude/channel capability. Verified headless-only — an InMemoryTransport harness plus a human manual checklist — and not yet demonstrated in a real interactive Claude Code session. It needs Claude Code v2.1.80+, a claude.ai subscription or a Console API key (not Bedrock, Vertex, or Foundry), and the --dangerously-load-development-channels flag against the fakechat loopback (--channels plugin:fakechat@claude-plugins-official). It rides Anthropic's research preview and may break on any Claude Code release — treat it as experimental, never load-bearing: a dropped or duplicated push is reconciled by the next parley_fetch_recent, and Tier 1 stands on its own.

Reading on your phone. Of the 6 live backends, Matrix is a mainstream chat app with clients on every phone — the honest end-to-end pick right now (XMPP is the other, given a MAM-enabled server). The consumer apps most people already keep open — Zulip, Slack, Discord, Telegram — are fake-conformance today (see the table below; Zulip is additionally the maintainer's operator-run production backend). So for the loop end-to-end on live-proven code, reach for Matrix.


How it works

Three numbers: 10 backends, each implementing the same 6 seam methods; Claude itself calls just 4 MCP tools.

The seam is that one small interface every backend implements. All the hard, backend-independent logic — live push, catch-up, dedup, ordering, presence, reply fan-out, OAuth mode — is written once above it in @sharptrick/parley-core. Dependencies point one way: a plugin depends on core, never the reverse.

 Messaging backend   SQLite · Redis · Postgres · Matrix · XMPP · NATS · Zulip · Discord · Slack · Telegram
        │  backend-native protocol
 Backend plugin      implements the seam (the six methods below)
        │  normalized Message (topic · sender · content · backendMsgId · cursor · mentions)
 @sharptrick/parley-core   reactive tools (post · fetchRecent)   +   proactive push (<channel> events)
        │            dedup + ordering via cursor · reply fan-out · presence · topic allowlist · OAuth mode
   ┌────┴───────────────────────────┐
 Claude Code                     Claude chat  ·  you, in a normal chat client
 catch-up + live push (preview)  post + fetchRecent

The whole product is these six seam methods:

interface BackendPlugin {
  connect(config): Promise<void>;
  disconnect(): Promise<void>;
  subscribe(topic, handler): Promise<void>;                       // live path (push)
  post(topic, identity, content, opts?): Promise<BackendMsgId>;   // the single durable write
  fetchRecent({ topic, since?, limit? }): Promise<{ messages, nextCursor }>; // catch-up
  resolveIdentity(handle): Promise<BackendIdentity>;
}

Two invariants make a backend correct: a stable, unique backendMsgId (the dedup key — how core throws away a message it has already seen) and a monotonic cursor (an opaque, ever-advancing per-topic position). Catch-up is exclusive on since: it returns only messages newer than the cursor you pass, never re-sending it (ordering is the plugin's job; core never compares cursor values, so the cursor stays opaque). The one absent-topic escape hatch is NoSuchTopicError; every other rejection is a real failure and propagates.

Adding every backend after SQLite changed zero lines of @sharptrick/parley-core — check the git log — and one shared conformance suite proves each against the same contract.

Add a backend. Implement the six methods, point the shared conformance suite (@sharptrick/parley-conformance) at your plugin, and ship it as its own package — core never has to know it exists. The Redis backend is a complete, conformant example in one small file: post = XADD, fetchRecent = XRANGE (stream id as the cursor), subscribe = a blocking XREAD loop. Every transport maps its native position onto the cursor (SQLite rowid, Redis stream id, Postgres BIGSERIAL, … — full mapping in the table below). → packages/bridge-redis/src/index.ts


Backends

Ten backends — six live-tested against real implementations, four proven against in-process API fakes of the vendor protocol. The Verified column tells you which is which, so "10 backends" never reads as "10 live-tested backends."

Backend Transport / mechanism Cursor Verified
SQLite local file, polling-only (WAL, no broker) rowid live
Redis Redis Streams, blocking XREAD push stream id live
Postgres table + LISTEN / NOTIFY push BIGSERIAL live
Matrix hand-rolled Client-Server HTTP + /sync sync token live
XMPP MUC + MAM archive (server must enable MAM) MAM id live
NATS JetStream (persistent streams) stream sequence live
Zulip event-queue long-poll (raw fetch) message id fake-conformance + operator-run
Discord gateway websocket (raw ws) snowflake (Discord's time-sortable id) fake-conformance
Slack Socket Mode websocket (raw ws) ts fake-conformance
Telegram Bot API getUpdates + local observed store per-chat message_id fake-conformance
  • live — exercised against real implementations in CI/dev: SQLite on a real on-disk file, real Redis/NATS binaries, live Synapse/Prosody homeservers, and Postgres 16 (SQLite, Redis, Matrix, NATS, XMPP, Postgres — 6 of 10).
  • fake-conformance — the same shared suite run against in-process fakes of the vendor API, not live vendor accounts: Zulip, Discord, Slack, Telegram (4 of 10). Zulip is additionally operator-run (the maintainer's own instance, below) and ships an env-gated real-credential suite (PARLEY_ZULIP_URL / _EMAIL / _API_KEY) that runs the identical seam suite against a live Zulip server. Telegram's Bot API has no pre-join history endpoint, so that backend documents the gap rather than faking backfill.

Every backend clears the identical seam contract — cursor monotonicity, exclusive-since catch-up, and double-delivery dedup — plus multi-process write safety wherever the transport allows concurrent writers (Telegram is single-writer by design: one bridge per bot token, since a second getUpdates poller gets HTTP 409, so that one conformance case is deliberately skipped). No vendor SDKs: Matrix is a hand-rolled HTTP client (not matrix-js-sdk), Discord and Slack use raw ws, Telegram and Zulip use raw fetch.


The four MCP tools

Distinct from the six seam methods a backend implements, these are the 4 tools Claude actually calls:

  • parley_fetch_recent — catch a topic up from the durable backend. {topic, since?, limit?}{messages, nextCursor}.
  • parley_post — publish or hand off into a topic (thread with optional in_reply_to). {topic, content, in_reply_to?}{backendMsgId}.
  • parley_reply — reply into the topic an inbound <channel> event arrived from. Like parley_post, it's written durably to the backend — the live channel is only the fast inbound hop, so a reply survives restart and shows up in the next catch-up.
  • parley_list_users — the reachability roster for hand-off: who's online now plus recently-seen-but-offline peers, derived above the seam (bridges beat hello/heartbeat/goodbye on one shared presence topic) so it works identically on every backend, with no new seam method.

Remote / chat mode

The same codebase runs as a public HTTP MCP server so a Claude chat connector can reach it directly — the mechanism behind Quickstart B. It sits behind a standards-compliant built-in OAuth 2.1 + PKCE front door (dynamic client registration; standards-based discovery (RFC 9728) and audience binding (RFC 8707) — details in the docs), or delegates to an external IdP in OIDC mode (Keycloak is the tested target). It's single-tenant: backend credentials stay server-side, and Claude only ever holds a consented, audience-bound token.

For a hardened setup, the maintainer runs Parley as their own single-tenant instance behind a Keycloak/OIDC front door (issuer at auth.example.com) fronting a self-hosted Zulip backend (zulip.example.com), TLS terminated at a reverse proxy and reachable over a private tailnet — shipped infrastructure, not a roadmap slide. That's optional hardening; the built-in OAuth front door in Quickstart B is the light path.

Security posture: secrets live in backend_config / .env, never in core and never committed. A client-side topic allowlist plus anchored post_topics regexes bound each instance (e.g. general + project-.*). Inbound messages are treated as untrusted data, never as privileged instructions. SQLite uses WAL for safe concurrent multi-process writes, and retention_days pruning is opt-in on SQLite/Redis/NATS.


How it compares

  • vs. localhost agent buses (xats, claude-peers-mcp, AgentBus): the one thing they fundamentally can't do is put a human reading and replying in an ordinary chat app — on their phone, off the machine. Those buses are localhost-only and agents-only. Parley is the same primitives — topics, presence, claude/channel push — but off one machine and carrying a human plus a Claude chat too. Should you switch from xats today? Not for live push: xats rides the same experimental claude/channel capability, and Parley's is still preview-grade (above) — concede that. Switch when you want a human in a real chat app, cross-host sessions, or a durable backend you can swap: the same participants run on a local SQLite file today and Redis / NATS / Matrix / Zulip across machines tomorrow with zero participant-code changes.
  • vs. a plain MCP server: Parley is dual-role. A request/response server can answer post / fetch_recent; it can't proactively push a <channel> event into a session that's already running. Parley does both (the push half is the research preview above).
  • What it isn't: not an agent framework (A2A, AutoGen, MindRoom) — deliberately just the seam. No built-in chat UI.

Project status & maintenance

  • Single maintainer, MIT, no hosted service. It runs on infrastructure you operate — no lock-in, deliberately just the seam.
  • Pre-1.0 (v0.8.0), actively developed. See the GitHub Releases for cadence; the last-commit badge up top is the live pulse. Per project convention, early breaking changes land as feat: until 1.0 is deliberately cut.
  • The one risk to price in. The product (Tier 1 durable catch-up) stands alone and depends on nothing unstable — six methods, ten backends, and zero core changes are the evidence the seam is stable. The only experimental dependency is Tier 2 live push, which rides Anthropic's claude/channel research preview and may break on any Claude Code release — scoped above, never load-bearing.
  • Releases are automated. A merge to main is a release: CI runs the test gate, then semantic-release picks the version bump from the PR title and publishes every package to npm in lockstep with provenance (OIDC — no tokens). See CONTRIBUTING.md.
  • Contributing. Dev setup, running the shared conformance suite, and the pre-PR checklist are in CONTRIBUTING.md; extending Parley with a new backend is the Add a backend callout above.
  • Support: open a GitHub issue; report vulnerabilities via SECURITY.md.

Docs & license

A parley is a conference between parties to reach an understanding — which is all Parley is: one durable thread where your Claude chat, your Claude Code sessions, and you confer.

MIT licensed.

from github.com/sharpTrick/parley

Install Parley Core in Claude Desktop, Claude Code & Cursor

Recommended · one command, every IDE
unyly install parley-core

Installs 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 parley-core -- npx -y @sharptrick/parley-core

FAQ

Is Parley Core MCP free?

Yes, Parley Core MCP is free — one-click install via Unyly at no cost.

Does Parley Core need an API key?

No, Parley Core runs without API keys or environment variables.

Is Parley Core hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Parley Core in Claude Desktop, Claude Code or Cursor?

Open Parley Core on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Parley Core with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All communication MCPs