Skip to main content
Glama

ws-mcp

WebSocket session management as MCP tools — with per-frame Shannon entropy tracking.

ws has 241,560,546 weekly downloads. There are zero MCP servers that expose WebSocket primitives. This is that server.


The problem

AI agents cannot interact with live WebSocket streams without custom glue code per integration. There is no standardized way for an LLM to open a persistent connection, send a typed frame, and receive structured events as tool call responses. Debugging message flows requires external tooling (Wireshark, browser devtools) that is incompatible with agentic workflows.

ws-mcp exposes five atomic operations over HTTP: open a session, send a frame, drain buffered frames, inspect telemetry, and close. Each frame — inbound or outbound — is scored with a Shannon entropy delta relative to the session's cumulative message distribution, so an agent can detect when a stream is diverging from its expected schema without any external monitoring.


Related MCP server: MCP Server Demo

Why the entropy delta is not optional

The server maintains a live token-frequency Counter per session_id inside the same process that holds the socket open. For each frame, it computes:

delta = H(t) - H(t-1)

where H(t) is the Shannon entropy of the cumulative byte distribution after frame t. A delta near zero means the frame is statistically consistent with the session's established message pattern. A large positive delta means the frame introduced new information — schema divergence, anomalous payload, or a new message type.

This requires coupling MCP tool state with a live socket registry and a probabilistic frame classifier in the same process. It cannot be replicated by wrapping ws.send() in a stateless proxy.


Base URL

https://npm-package-ws-has-241560546-w-production.up.railway.app

Authentication

None. There is no API key, header, or auth mechanism of any kind on this asset today — every request is served regardless of headers sent.

Pricing

  • POST /ws-sessions/open requires an x402 payment: $0.01 USDC on Base Sepolia (testnet), scheme exact. A request without payment gets 402 Payment Required with the payment details in the payment-required response header.

  • All other operations (send-frame, drain-frames, telemetry, close) act on a session that was already paid for at open time and are not separately charged.


Endpoints

POST /ws-sessions/open

Establishes a persistent WebSocket connection to a target URL and registers it in the stateful session registry. Returns a session_id for all subsequent operations. Requires an x402 payment (see Pricing above).

Use when an agent needs to initiate a long-lived connection before sending or receiving frames. Do NOT use to reconnect an already-open session — call close first, or use the session_id of an existing session.

Request

{
  "target_url": "wss://echo.websocket.org",
  "connect_timeout_seconds": 10.0,
  "extra_headers": {
    "X-Custom-Header": "value"
  }
}

target_url must start with ws:// or wss:// (validated, not just a regex in the schema). connect_timeout_seconds defaults to 10.0, range 0.560.0. extra_headers is optional (HTTP headers sent on the upgrade request).

Response (201)

{
  "session_id": "8662044208f54095b327ab4c29e1554c",
  "target_url": "wss://echo.websocket.org",
  "state": "OPEN",
  "opened_at_unix": 1785012145.03,
  "message": "Session '8662044208f54095b327ab4c29e1554c' is OPEN. Use session_id for all subsequent operations."
}

session_id is a 32-character hex string (uuid.uuid4().hex, no dashes) — use it as-is in the paths below.

Errors: 422 if target_url doesn't start with ws:///wss://; 504 if the handshake doesn't complete within connect_timeout_seconds; 502 if the host is unreachable or refuses the connection; 503 if the server is already at NEXUS_WS_MAX_SESSIONS concurrent sessions.


POST /ws-sessions/{session_id}/send-frame

Sends a single text or binary frame over an open session and returns the frame's Shannon entropy delta relative to the session baseline.

Use for request-response patterns or publishing commands to a WebSocket server. Do NOT use to send a sequence of frames in bulk — call this endpoint once per frame. Fails if session_id does not exist or the connection is not in OPEN state.

Request

{
  "payload": "{\"action\": \"subscribe\", \"channel\": \"trades\"}",
  "frame_type": "text"
}

frame_type is "text" (default) or "binary" — if binary, payload must be a hex-encoded string.

Response (200)

{
  "session_id": "8662044208f54095b327ab4c29e1554c",
  "frame_type": "text",
  "payload_bytes": 44,
  "entropy_after": 4.237441,
  "entropy_delta": 0.245712,
  "schema_valid": true,
  "sent_at_unix": 1785012151.68
}

Errors: 404 if session_id is not in the registry (session_id '<id>' not found in registry); 409 if the session isn't in OPEN state; 422 if frame_type is "binary" and payload isn't valid hex; 502 if the underlying socket send fails.


POST /ws-sessions/{session_id}/drain-frames

Returns up to max_frames buffered inbound frames received since the last drain (or session open), each annotated with its Shannon entropy delta and schema validation result.

Use to poll for incoming messages without holding a blocking connection. Do NOT use as a real-time streaming mechanism — it returns only frames already buffered (buffer capped at 512 frames per session; oldest frames are dropped once full).

Request

{
  "max_frames": 10
}

max_frames defaults to 32, range 1256.

Response (200)

{
  "session_id": "8662044208f54095b327ab4c29e1554c",
  "frames_returned": 2,
  "frames": [
    {
      "payload": "{\"event\": \"trade\", \"price\": 42381.50}",
      "frame_type": "text",
      "entropy_after": 3.91,
      "entropy_delta": 0.12,
      "schema_valid": true,
      "received_at": 1785012155.203
    },
    {
      "payload": "{\"event\": \"liquidation\", \"size\": 180000, \"side\": \"long\"}",
      "frame_type": "text",
      "entropy_after": 5.38,
      "entropy_delta": 1.47,
      "schema_valid": false,
      "received_at": 1785012155.891
    }
  ],
  "buffer_remaining": 0
}

The entropy_delta of 1.47 on the second frame combined with schema_valid: false indicates the stream introduced content statistically inconsistent with the session's established byte distribution — a signal an agent can use to decide whether to escalate or re-negotiate the subscription.

Errors: 404 if session_id is not in the registry.


POST /ws-sessions/{session_id}/telemetry

Returns real-time telemetry for a session: connection state, frame counts, cumulative and rolling Shannon entropy statistics, schema violation rate, and uptime. Takes no request body.

Use to diagnose whether a stream is diverging from its expected schema or to verify a session is still alive before sending. Do NOT use as the primary liveness check in a tight loop — it recomputes rolling statistics on every call.

Response (200)

{
  "session_id": "8662044208f54095b327ab4c29e1554c",
  "target_url": "wss://echo.websocket.org",
  "state": "OPEN",
  "uptime_seconds": 108.77,
  "frames_sent": 1,
  "frames_received": 2,
  "current_entropy": 4.16695,
  "rolling_entropy_mean": 0.18,
  "rolling_entropy_std": 0.31,
  "rolling_entropy_max": 1.47,
  "cumulative_entropy_bits": 4.16695,
  "schema_violations": 1,
  "schema_violation_rate": 0.333333,
  "inbound_buffer_depth": 2
}

Errors: 404 if session_id is not in the registry.


POST /ws-sessions/{session_id}/close

Sends a WebSocket close frame with the specified status code, waits for the server close handshake, and removes the session from the registry. Returns a final telemetry snapshot. session_id is permanently deallocated after this call and cannot be reused.

Request (both fields optional)

{
  "status_code": 1000,
  "reason": "agent task complete"
}

status_code defaults to 1000, range 10004999 (RFC 6455). reason defaults to "", max 123 bytes.

Response (200)

{
  "session_id": "8662044208f54095b327ab4c29e1554c",
  "target_url": "wss://echo.websocket.org",
  "final_state": "CLOSED",
  "frames_sent": 1,
  "frames_received": 2,
  "terminal_entropy": 4.16695,
  "schema_violations": 1,
  "uptime_seconds": 310.2,
  "message": "Session '8662044208f54095b327ab4c29e1554c' closed with status 1000. Total frames exchanged: 3. session_id is permanently deallocated."
}

Errors: 404 if session_id is not in the registry.


End-to-end example: open a session, send a frame, drain, close

import httpx

BASE = "https://npm-package-ws-has-241560546-w-production.up.railway.app"

# 1. Open a session (requires x402 payment on this call — omitted here for brevity,
#    see https://docs.x402.org for how to attach a payment header)
r = httpx.post(f"{BASE}/ws-sessions/open", json={
    "target_url": "wss://echo.websocket.org",
    "connect_timeout_seconds": 10.0,
})
r.raise_for_status()
session_id = r.json()["session_id"]

# 2. Send a frame (not billed)
httpx.post(f"{BASE}/ws-sessions/{session_id}/send-frame", json={
    "payload": '{"action": "ping"}',
    "frame_type": "text",
}).raise_for_status()

# 3. Drain buffered inbound frames, alert on entropy spikes
ENTROPY_ALERT_THRESHOLD = 1.0

drain = httpx.post(f"{BASE}/ws-sessions/{session_id}/drain-frames", json={
    "max_frames": 20,
}).json()

for frame in drain["frames"]:
    if frame["entropy_delta"] > ENTROPY_ALERT_THRESHOLD or not frame["schema_valid"]:
        print(f"[ALERT] delta={frame['entropy_delta']:.2f} schema_valid={frame['schema_valid']}")
        print(f"        payload={frame['payload'][:120]}")

# 4. Inspect session health before deciding to continue
telemetry = httpx.post(f"{BASE}/ws-sessions/{session_id}/telemetry").json()
print(f"schema_violation_rate={telemetry['schema_violation_rate']:.3f} "
      f"current_entropy={telemetry['current_entropy']:.2f}")

# 5. Close cleanly
httpx.post(f"{BASE}/ws-sessions/{session_id}/close", json={
    "status_code": 1000,
    "reason": "task complete",
}).raise_for_status()

curl example

# Open a session (requires x402 payment — this call alone will return 402
# without a valid payment header; see https://docs.x402.org)
curl -X POST https://npm-package-ws-has-241560546-w-production.up.railway.app/ws-sessions/open \
  -H "Content-Type: application/json" \
  -d '{"target_url": "wss://echo.websocket.org"}'

# Send a frame (replace SESSION_ID with the value returned above — not billed)
curl -X POST https://npm-package-ws-has-241560546-w-production.up.railway.app/ws-sessions/SESSION_ID/send-frame \
  -H "Content-Type: application/json" \
  -d '{"payload": "hello world", "frame_type": "text"}'

# Drain buffered inbound frames
curl -X POST https://npm-package-ws-has-241560546-w-production.up.railway.app/ws-sessions/SESSION_ID/drain-frames \
  -H "Content-Type: application/json" \
  -d '{"max_frames": 5}'

MCP

Connect an MCP-compatible client to the streamable HTTP endpoint at:

https://npm-package-ws-has-241560546-w-production.up.railway.app/mcp

Exposes the same 5 operations as MCP tools (open_websocket_session, send_typed_ws_frame, drain_ws_frame_buffer, inspect_ws_session_telemetry, close_websocket_session), with the same parameters and payment requirement (open_websocket_session requires x402 payment; the other 4 do not).


SDKs

  • sdk_wrappers/sdk.js — real HTTP client (axios) against the base URL above. Published to npm as @nexus-mcp-infra/npm-package-ws-has-241560546-weekly-downloads-but-sdk. This is the one to use.

  • mcp_wrapper/npm_package_ws_has_241560546_weekly_downloads_but_sdk.pydo not use. It defaults to a placeholder domain (https://api.ws-mcp.nexus.ai/v1) that isn't this service, calls routes (/sessions/open, /frames/send, etc.) that don't exist on this API, and requires an api_key that this service doesn't check. It was never fixed after the initial build; kept in the repo for history, not for use.

Direct HTTP calls to the endpoint above (via httpx, requests, or curl as shown) require no SDK.


Error reference

HTTP status

Condition

402

Payment required (only on POST /ws-sessions/open, x402)

404

session_id does not exist in the registry

409

Session is not in OPEN state (e.g., not yet connected or already closing)

422

target_url is not a valid ws:///wss:// URL, or a binary payload is not valid hex

502

Network error connecting to target_url, or error sending a frame

503

Session registry at capacity

504

WebSocket handshake did not complete within connect_timeout_seconds


Stack

Python 3.13 — FastAPI — websockets (asyncio-native). The session registry and per-session entropy state live in-process; the entropy tracker maintains a token-frequency Counter per session_id that is updated on every frame without blocking the socket loop. Sessions do not survive a process restart/redeploy.

F
license - not found
-
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
    -
    quality
    D
    maintenance
    A WebSocket-based Model Control Protocol (MCP) server that processes model requests and provides responses. Supports chat and text completion actions with a standardized JSON protocol for AI model communication.
    Last updated
    1

View all related MCP servers

Related MCP Connectors

  • MCP server exposing the Backtest360 engine API as tools for AI agents.

  • MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.

  • AI Reasoning Cache & Consensus Layer with 11 MCP tools via Streamable HTTP.

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/nexus-mcp-infra/npm-package-ws-has-241560546-weekly-downloads-but-sdk'

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