npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

crosstalk-mcp

v1.2.0

Published

Local MCP server for shared AI agent collaboration sessions

Readme

Crosstalk

A local MCP server that lets multiple AI agents collaborate through shared sessions. Use it when a frontend project agent and a backend project agent need to coordinate on features that touch both codebases.

Sessions are stored in a local JSON file (~/.crosstalk/sessions.json by default).

Setup

From npm (recommended)

No clone required — use with Cursor via npx:

{
  "mcpServers": {
    "crosstalk": {
      "command": "npx",
      "args": ["-y", "crosstalk-mcp"],
      "env": {
        "CROSSTALK_AGENT_ID": "frontend"
      }
    }
  }
}

CLI commands also work globally after install, or via npx:

npx crosstalk-mcp wait --session-id <uuid> --agent-id frontend --agent-key <your-key>
npx crosstalk-mcp export <session-id> ./transcript.md

Note: The npm package name is crosstalk-mcp because crosstalk is taken by an unrelated legacy package. Use npx crosstalk-mcp, not npx crosstalk.

From source

git clone <repo-url>
cd crosstalk
npm install
npm run build

Cursor configuration

Add to your Cursor MCP settings (.cursor/mcp.json in a project, or global Cursor settings). Use the same config in both the frontend and backend projects — they share the same session store.

Frontend project — set a distinct agent ID:

{
  "mcpServers": {
    "crosstalk": {
      "command": "npx",
      "args": ["-y", "crosstalk-mcp"],
      "env": {
        "CROSSTALK_AGENT_ID": "frontend"
      }
    }
  }
}

Backend project:

{
  "mcpServers": {
    "crosstalk": {
      "command": "npx",
      "args": ["-y", "crosstalk-mcp"],
      "env": {
        "CROSSTALK_AGENT_ID": "backend"
      }
    }
  }
}

For local development from a clone, use "command": "node" and "args": ["/absolute/path/to/crosstalk/dist/index.js"] instead.

Optional: set CROSSTALK_DATA_DIR to change where sessions are stored (both projects must use the same directory). Set CROSSTALK_DEFAULT_TTL_HOURS to override the default idle auto-close window (default 72).

Tools

| Tool | Description | |------|-------------| | create_session | Create a session with name, description; creator is auto-joined. Optional tags, ttl_hours. | | list_sessions | List active sessions (newest first). Optional include_closed, include_archived. | | search_sessions | Search by name, description, ID, or tags. Optional include_closed, include_archived. | | get_session | Get session details (works on archived sessions). | | join_session | Join a session as a participant. | | send_message | Post a message. Optional kind (proposal/decision/blocker/question/contract), attachments ({path, description?}[]), reply_to. | | read_messages | Read messages; use unread_only: true to poll for new ones. | | mark_read | Mark messages as read. This is the ack — there is no separate ack/reaction tool. | | set_tags | Add and/or remove tags on a session. | | archive_session | Archive a session (closes it first if open). Hidden from list/search by default. | | unarchive_session | Reverse archiving. Session stays closed if it was closed. | | get_wait_command | Get the background wait command — exits on each message so Cursor resumes the agent. | | get_watch_command | Deprecated alias for get_wait_command. | | close_session | Close a session when done. |

MCP prompts

The server exposes reusable prompt templates (if your client supports MCP prompts):

| Prompt | When to use | |--------|-------------| | collaboration-guide | Starting cross-project collaboration | | handle-message-alert | A CROSSTALK_NEW_MESSAGE notification arrived | | join-session | Joining a session with a known ID |

Server instructions also include the full workflow and are injected into the agent context when Crosstalk MCP is connected.

Data model

Sessions are stored as JSON at ~/.crosstalk/sessions.json. The store is schema-versioned (currently 2); older files migrate automatically on first read. All new fields are optional, so existing session files keep working unchanged.

Concurrency

Writes are serialized with a cross-process lockfile (sessions.json.lock via proper-lockfile) and the JSON is written via temp-file + atomic rename, so concurrent agents can't clobber each other or produce a torn file. If the store is busy you'll get a clear "Session store is busy; retry" error.

Typed messages

send_message accepts an optional kind to signal intent:

| Kind | Use for | |------|---------| | message | Default freeform message | | proposal | A proposed approach or contract | | decision | A settled decision | | blocker | Something blocking progress | | question | A question for the other agent | | contract | An API/data contract |

The kind appears in the wait alert JSON and the exported transcript.

Attachments

send_message accepts an optional attachments array of { path, description? }. These are filesystem references the receiving agent can read directly — useful for pointing at code instead of pasting large blocks into content.

Tags

create_session accepts optional tags, and set_tags adds/removes tags on an existing session. search_sessions matches tags in addition to name, description, and ID.

Archive

archive_session hides a finished session from list_sessions / search_sessions without deleting it (it closes the session first if still open). Archived sessions remain readable via get_session and export. Passing include_archived: true surfaces archived sessions in list/search regardless of closed state. unarchive_session reverses the archive AND reopens the session (clears both archivedAt and closedAt).

Idle auto-close

Sessions auto-close after their TTL of inactivity. The TTL is ttl_hours from create_session, falling back to CROSSTALK_DEFAULT_TTL_HOURS (default 72). The sweep is lazy: it runs on the next write to the store, so there's no daemon. When a session is auto-closed, Crosstalk stamps a system decision message (_Session auto-closed after Nh idle._) so the close is visible in the transcript.

mark_read is the acknowledgment mechanism — calling it tells the other agent you've seen the message.

Cursor skill

Install the skill in each collaborating project by copying or linking .cursor/skills/crosstalk/ from this repo. The skill auto-triggers when agents work with Crosstalk or receive message alerts.

Background message alerts

MCP cannot push notifications to agents. In Cursor, background tasks wake the agent when they complete — not when they print output mid-run.

Use the wait command (not watch). It blocks until the other agent sends a message, prints CROSSTALK_NEW_MESSAGE, then exits. Cursor resumes the agent; the agent handles the message and starts a new wait.

After joining a session, call get_wait_command (or use the wait field from create_session / join_session):

node /Users/dean.ward/projects/crosstalk/dist/index.js wait \
  --session-id <uuid> \
  --agent-id frontend \
  --agent-key <your-key> \
  --interval 2

Agent loop:

  1. Run wait in a background shell (block_until_ms: 0)
  2. Continue local work
  3. When the other agent messages, wait exits → Cursor resumes you
  4. Read CROSSTALK_NEW_MESSAGE from the terminal output
  5. Handle via read_messages / send_message / mark_read
  6. Start a new background wait — repeat from step 1

Do not use long-running watch for Cursor agents — it never exits, so Cursor will not wake you.

Sentinel lines:

| Line prefix | Meaning | |-------------|---------| | CROSSTALK_NEW_MESSAGE | New message from the other agent (wait exits after this) | | CROSSTALK_SESSION_CLOSED | Session was closed (wait exits) | | CROSSTALK_WAIT_TIMEOUT | Wait timed out — restart wait to keep listening |

Typical workflow

  1. Backend agent creates a session: create_session(name: "User auth", description: "OAuth flow API contract", agent_id: "backend")
  2. Frontend agent finds and joins: search_sessions(query: "User auth")join_session(session_id, agent_id: "frontend")
  3. Each agent starts a background wait (get_wait_command) — it exits when the other agent replies, waking Cursor
  4. After handling each message, agents restart wait and use send_message / read_messages / mark_read as needed
  5. Either agent calls close_session when the feature is done

Export transcript

Export a session to markdown:

node /Users/dean.ward/projects/crosstalk/dist/index.js export \
  --session-id <uuid> \
  --output ./transcript.md

Or positionally:

node /Users/dean.ward/projects/crosstalk/dist/index.js export <session-id> ./transcript.md

Agent IDs and keys

Pass agent_id on every tool call. Use stable, unique identifiers like "frontend" and "backend" so messages are attributed correctly. The CROSSTALK_AGENT_ID env var is a reminder in config — agents should still pass it explicitly in tool calls.

Agent keys prevent id collisions

The first time an agent_id is claimed in a session (via create_session or join_session), the server issues an agent_key — a secret token bound to that id in that session. To act as that id afterwards you must pass the matching key:

  • Requires the key: join_session, send_message, mark_read, get_wait_command / get_watch_command, and the wait / watch CLIs (via --agent-key). The wait command generated by get_wait_command already embeds it.
  • No key needed: read-only calls (read_messages, get_session, list_sessions, search_sessions, export).

If another agent tries to use an id that's already claimed without its key, the call is rejected with a message telling it to pick a unique id. This makes id collisions impossible rather than silently merging two agents into one identity.

Keys are only ever returned to the claiming agent — they never appear in summaries, read_messages, or exported transcripts. They're a collision guard for cooperating local agents, not a security boundary (the wait command embeds the key in plain text).

Migration: participants in sessions created before keys existed have no key; the next agent to act as that id adopts it and is issued a key going forward.