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

mcp-webrtc-transport

v0.1.5

Published

Reusable browser-side WebRTC transport for peer-to-peer MCP sessions.

Readme

mcp-webrtc-transport

Reusable browser-side TypeScript module for peer-to-peer MCP sessions over WebRTC.

Screenshots

These screenshots are stored in the repository so they can also be reused from README files and package pages.

Peer MCP overview

Peer MCP session setup

What it provides

  • peer discovery through a signaling websocket
  • SDP offer / answer exchange
  • ICE candidate relay
  • direct MCP tool calls over WebRTC data channels
  • event callbacks for peer snapshots, tool catalogs, tool results, and connection state

Use case

This module is useful when an AI client needs to call tools on a nearby browser or edge device without sending the tool payloads through a central server.

For example, you can use it in a support or operations dashboard where:

  • a browser-based AI assistant connects to a field gateway or kiosk
  • the server is only used to help the two peers discover each other
  • diagnostics, log access, and device actions are exchanged directly over WebRTC

Why use it:

  • sensitive tool inputs and results stay on the peer-to-peer channel
  • it reduces server bandwidth because MCP traffic does not need to be proxied
  • it works well for local-device, edge-device, or privacy-first assistant workflows

Sequence diagrams

Backend signaling

sequenceDiagram
  autonumber
  participant Provider as Provider Peer
  participant Server as Signaling Server
  participant Client as AI Client Peer

  Provider->>Server: peer_announce(name, role=provider, tools)
  Client->>Server: peer_announce(name, role=client)
  Server-->>Provider: peer_snapshot
  Server-->>Client: peer_snapshot

  Client->>Server: signal offer (SDP)
  Server-->>Provider: relay offer
  Provider->>Server: signal answer (SDP)
  Server-->>Client: relay answer

  Note over Provider,Client: WebRTC data channel opens
  Provider-->>Client: tool_catalog
  Client->>Provider: tool_call
  Provider-->>Client: tool_result

Backend-free manual signaling

sequenceDiagram
  autonumber
  participant Provider as Provider Peer
  participant OOB as Manual Exchange
  participant Client as AI Client Peer

  Provider->>Provider: createManualOffer()
  Provider->>OOB: Share offer payload
  OOB-->>Client: Offer payload

  Client->>Client: createManualAnswer(offer)
  Client->>OOB: Share answer payload
  OOB-->>Provider: Answer payload

  Provider->>Provider: applyManualAnswer(answer)
  Note over Provider,Client: WebRTC data channel opens
  Provider-->>Client: tool_catalog
  Client->>Provider: tool_call
  Provider-->>Client: tool_result

Build

From the repository frontend workspace:

cd app
npm run build:mcp-module

The compiled package is emitted to:

modules/mcp-webrtc-transport/dist

TypeScript example

import { P2PMcpClient, TMcpTool } from "mcp-webrtc-transport";

const tools: TMcpTool[] = [
  {
    name: "get_device_status",
    description: "Return the current device health summary.",
    parameters: { device_id: "edge-gateway-01" },
  },
];

const client = new P2PMcpClient({
  signalingBaseUrl: "ws://localhost:8001",
  identity: {
    peerName: "Edge Gateway",
    role: "provider",
    tools,
  },
  toolCallHandler: async (message) => {
    if (message.tool === "get_device_status") {
      return {
        device_id: "edge-gateway-01",
        status: "healthy",
        transport: "webrtc-datachannel",
      };
    }

    return { error: `Unknown tool: ${message.tool}` };
  },
});

await client.connect("24f5e189");

Backend-free manual signaling

You can also run MCP peer connection without a signaling backend by exchanging SDP payloads manually (copy and paste, QR, or any out-of-band channel).

This mode is useful for local demos, LAN tests, and environments where running a signaling server is not desired.

Provider flow (manual offer)

import { P2PMcpClient, TMcpTool } from "mcp-webrtc-transport";

const tools: TMcpTool[] = [
  {
    name: "get_device_status",
    description: "Return current status",
    parameters: { device_id: "edge-gateway-01" },
  },
];

const provider = new P2PMcpClient({
  identity: {
    peerName: "Edge Gateway",
    role: "provider",
    tools,
  },
  toolCallHandler: async (message) => {
    if (message.tool === "get_device_status") {
      return {
        device_id: message.parameters.device_id,
        status: "healthy",
        transport: "webrtc-datachannel",
      };
    }
    return { error: `Unknown tool: ${message.tool}` };
  },
});

const offer = await provider.createManualOffer();
// Send `offer.sdp` to the client peer by copy/paste or QR.

Client flow (apply offer, return answer)

import { P2PMcpClient } from "mcp-webrtc-transport";

const client = new P2PMcpClient({
  identity: {
    peerName: "AI Client",
    role: "client",
  },
  onToolCatalog: (message) => {
    console.log("catalog", message.tools);
  },
  onToolResult: (message) => {
    console.log("result", message.result);
  },
});

const answer = await client.createManualAnswer({
  sdp: offerSdpFromProvider,
  type: "offer",
});
// Send `answer.sdp` back to provider.

Provider final step

await provider.applyManualAnswer({
  sdp: answerSdpFromClient,
  type: "answer",
});

// After data channel opens, the client can call tools directly.
client.sendToolCall("get_device_status", { device_id: "edge-gateway-01" });

Notes:

  • In manual mode, do not call connect(sessionId).
  • In websocket mode, keep using signalingBaseUrl + connect(sessionId).
  • A direct connection can still fail on restrictive networks; use TURN infrastructure for high-connectivity production environments.

Python interoperability example

This package is browser-side TypeScript, so Python usage is typically an interoperability story rather than a direct package import. A Python peer can implement the same signaling contract and exchange the same MCP messages over a WebRTC data channel.

import json

SIGNALING_HTTP_BASE = "http://localhost:8001"
SIGNALING_WS_BASE = "ws://localhost:8001"

async def get_device_status(parameters: dict) -> dict:
    return {
        "device_id": parameters.get("device_id", "edge-gateway-01"),
        "status": "healthy",
        "transport": "webrtc-datachannel",
    }

async def handle_tool_call(raw_message: str) -> str:
    message = json.loads(raw_message)
    if message["type"] != "tool_call":
        return json.dumps({"ok": False, "error": "unsupported_message"})

    if message["tool"] == "get_device_status":
        result = await get_device_status(message.get("parameters", {}))
    else:
        result = {"error": f"Unknown tool: {message['tool']}"}

    return json.dumps(
        {
            "type": "tool_result",
            "requestId": message["requestId"],
            "tool": message["tool"],
            "result": result,
            "ok": "error" not in result,
        }
    )

See the repository guide for a longer walkthrough:

Signaling contract

This package expects a signaling backend that exposes:

  • GET /v1/mcp/session/
  • GET /v1/mcp/session/<session_id>/
  • ws://<host>/ws/mcp/<session_id>/<peer_id>

In this repository, Django Channels provides that signaling layer while MCP payloads remain peer-to-peer over WebRTC.