8000 fix(transport): make reference WebSocket tests dual-stack by Mnehmos · Pull Request #79 · Mnehmos/mnehmos.rpg.mcp · GitHub
[go: up one dir, main page]

Skip to content

fix(transport): make reference WebSocket tests dual-stack - #79

Merged
Mnehmos merged 7 commits into
mainfrom
fix/character-proficiency-persistence
Aug 12, 2026
Merged

Mnehmos merged 7 commits into
mainfrom
fix/character-proficiency-persistence

Conversation

@Mnehmos
@Mnehmos Mnehmos commented Aug 12, 2026 •
Copy link
Copy Markdown
Owner

Scope

Bind the reference engine WebSocket transport to the dual-stack host by default so Node 18 localhost resolution to ::1 works alongside IPv4.

Verification

  • npm test -- tests/server/websocket.test.ts
  • npm run build

This is required for the reference service CI matrix before Railway deployment.

Summary by CodeRabbit

  • New Features
    • Added HTTP access with health checks, authentication, request limits, and configurable hosting.
    • Character creation and updates now support skill, saving throw, expertise, armor, weapon, tool, and language proficiencies.
    • Added automatic class-based proficiency defaults and support for configurable data-directory storage.
  • Bug Fixes
    • Corrected scene lookup behavior.
    • Improved WebSocket network accessibility with dual-stack host support.
  • Deployment
    • Added Railway deployment configuration with health monitoring and automatic restarts.

Mnehmos and others added 7 commits August 12, 2026 09:11
Adds a Node http-based Streamable HTTP transport (POST /mcp, GET /health)
alongside the existing stdio/tcp/websocket/unix transports, auto-selected
when PORT is set or --http is passed. Auth via RPG_MCP_TRANSPORT_TOKEN,
same convention as the tcp/websocket transports.
Stateless StreamableHTTPServerTransport throws 'Stateless transport
cannot be reused across requests' after the first POST /mcp, since
sharing one transport instance across unrelated HTTP requests violates
the SDK's stateless-mode contract (server/webStandardStreamableHttp.js
guards this explicitly via _hasHandledRequest). Discovered live against
the deployed Railway service: the first initialize succeeded, every
call after it returned a bare 500.

Extract tool registration into buildServer(pubsub, auditLogger) and
call it once per request for the HTTP transport (cheap, no I/O),
while stdio/tcp/unix/websocket keep a single long-lived instance.
findLatestForParticipant(characterId, worldId?) was called as
(worldId, characterId), so the DM-facing 'current' preview always
returned scene: null. Both params are string, so TS could not catch it.
Agent auto-injection was unaffected — the prompt composer calls
listLatestForParticipant with the correct order.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…works

Railway's private network is IPv6-only. A server bound to '0.0.0.0'
listens on IPv4 only, so it is reachable on its public domain but NOT
on <service>.railway.internal -- peer services get a connection
failure with no obvious cause.

Default the HTTP host to '::', which Node binds dual-stack (IPv6 +
IPv4), so both the private hostname and the public domain work. Still
overridable via --host for local/dev binding.

Found while wiring rpg-mcp-live to this service: the integration had
to fall back to the public domain because the private hostname was
unreachable.
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@Mnehmos
Mnehmos merged commit 61f72ea into main Aug 12, 2026
3 of 4 checks passed
@coderabbitai
coderabbitai Bot commented Aug 12, 2026 •
Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bd62399e-3e4b-41d9-95dc-b703d970503f

📥 Commits

Reviewing files that changed from the base of the PR and between d65fe0a and 45b607e.

📒 Files selected for processing (12)
  • railway.json
  • src/schema/character.ts
  • src/server/consolidated/character-manage.ts
  • src/server/consolidated/scene-manage.ts
  • src/server/consolidated/spawn-manage.ts
  • src/server/index.ts
  • src/server/transport/http.ts
  • src/server/transport/websocket.ts
  • src/storage/index.ts
  • src/storage/migrations.ts
  • src/storage/repos/character.repo.ts
  • tests/server/consolidated/character-manage.test.ts

📝 Walkthrough

Walkthrough

The PR adds character proficiency schemas, management inputs, defaults, database persistence, and migrations. It adds reusable server construction and stateless HTTP MCP transport support with authentication and health checks. It also adds Railway deployment settings and updates runtime defaults.

Changes

Character proficiency flow

Layer / File(s) Summary
Character proficiency contracts and management
src/schema/character.ts, src/server/consolidated/character-manage.ts, src/server/consolidated/spawn-manage.ts, tests/server/consolidated/character-manage.test.ts
Character schemas and management tools accept proficiency fields. Creation applies class-based defaults. Tests cover create, update, and retrieval.
Character proficiency persistence
src/storage/repos/character.repo.ts, src/storage/migrations.ts
The repository serializes and restores seven proficiency fields. Migrations add missing JSON columns to existing databases.

Server transport and deployment

Layer / File(s) Summary
Reusable server and HTTP transport
src/server/index.ts, src/server/transport/http.ts, src/server/transport/websocket.ts
Server construction is shared across transports. HTTP supports /mcp< 8000 /code>, /health, authorization, bounded request bodies, structured errors, and per-request MCP servers. WebSocket defaults to ::.
Runtime defaults and deployment configuration
railway.json, src/storage/index.ts, src/server/consolidated/scene-manage.ts
Railway deployment settings are added. RPG_DATA_DIR provides a database path fallback. Participant lookup argument order is corrected.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant HTTPServer
  participant McpServer
  participant MCPTransport
  Client->>HTTPServer: POST /mcp with authorization
  HTTPServer->>McpServer: create server for request
  HTTPServer->>MCPTransport: create request transport
  MCPTransport->>McpServer: handle MCP request
  McpServer-->>Client: return MCP response
  HTTPServer->>MCPTransport: close transport when connection closes
Loading

Possibly related PRs

  • Mnehmos/mnehmos.rpg.mcp#77: Contains the same HTTP transport, server construction, Railway configuration, and scene-management changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/character-proficiency-persistence

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

zxrfire pushed a commit to zxrfire/mnehmos.rpg.mcp that referenced this pull request Sep 8, 2026
* feat(transport): add HTTP MCP transport for Railway hosting

Adds a Node http-based Streamable HTTP transport (POST /mcp, GET /health)
alongside the existing stdio/tcp/websocket/unix transports, auto-selected
when PORT is set or --http is passed. Auth via RPG_MCP_TRANSPORT_TOKEN,
same convention as the tcp/websocket transports.

* fix(transport): build a fresh McpServer+transport per HTTP request

Stateless StreamableHTTPServerTransport throws 'Stateless transport
cannot be reused across requests' after the first POST /mcp, since
sharing one transport instance across unrelated HTTP requests violates
the SDK's stateless-mode contract (server/webStandardStreamableHttp.js
guards this explicitly via _hasHandledRequest). Discovered live against
the deployed Railway service: the first initialize succeeded, every
call after it returned a bare 500.

Extract tool registration into buildServer(pubsub, auditLogger) and
call it once per request for the HTTP transport (cheap, no I/O),
while stdio/tcp/unix/websocket keep a single long-lived instance.

* fix(scene): swap transposed args in scene_manage action:'current'

findLatestForParticipant(characterId, worldId?) was called as
(worldId, characterId), so the DM-facing 'current' preview always
returned scene: null. Both params are string, so TS could not catch it.
Agent auto-injection was unaffected — the prompt composer calls
listLatestForParticipant with the correct order.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(transport): bind HTTP transport dual-stack so private networking works

Railway's private network is IPv6-only. A server bound to '0.0.0.0'
listens on IPv4 only, so it is reachable on its public domain but NOT
on <service>.railway.internal -- peer services get a connection
failure with no obvious cause.

Default the HTTP host to '::', which Node binds dual-stack (IPv6 +
IPv4), so both the private hostname and the public domain work. Still
overridable via --host for local/dev binding.

Found while wiring rpg-mcp-live to this service: the integration had
to fall back to the public domain because the private hostname was
unreachable.

* fix(character): persist proficiencies in reference state

* fix(railway): expose reference deployment identity and volume path

* fix(transport): bind WebSocket dual-stack by default

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

0