Skip to content

fix(devframe): return complete connection metadata from dev server handles - #484

Draft
SaKaNa-Y wants to merge 4 commits into
devframes:mainfrom
SaKaNa-Y:fix/dev-server-sse-handle
Draft

SaKaNa-Y wants to merge 4 commits into
devframes:mainfrom
SaKaNa-Y:fix/dev-server-sse-handle

Conversation

@SaKaNa-Y

@SaKaNa-Y SaKaNa-Y commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

With WebSocket disabled, createDevServer() opens an HTTP listener and then throws before returning the handle needed to close it. With WebSocket and SSE enabled, the handle advertises only WebSocket. HTTP discovery also omits the names of strict JSON methods, so an RPC client can send a Map to a handler that expects JSON.

Return a closable handle for each transport configuration. Both discovery paths read the strict JSON method names from the current RPC registry, including methods registered after startup. Each call to handle.connectionMeta() returns a fresh JSON snapshot, so static configuration values such as URL have the same representation as in HTTP discovery.

For a devframe mounted at /nested/tool/, callers can reach the same MCP route from either descriptor. Local WebSocket and SSE paths follow the same rule.

Source Advertised MCP path Resolve against
handle.connectionMeta() /nested/tool/tools/mcp handle.origin
__connection.json tools/mcp Discovery document URL

Evidence

  • Real WebSocket and SSE calls use both discovery paths. A strict JSON method accepts a plain object and rejects a Map before the handler runs. An unflagged method round-trips the same Map.
  • Before the snapshot fix, initializing MCP through the nested handle returned 404, and reading a handle with a URL in its static configuration threw DataCloneError. The regression tests now initialize MCP through both descriptors and read the same URL string from both. Mutating a handle snapshot leaves subsequent snapshots and HTTP discovery intact.
  • The lifecycle tests close the SSE-only and HTTP-only handles and verify port reuse. The SSE-only test also verifies registry cleanup.

Local lint, documentation lint, Knip, typecheck and build passed. The full Vitest run passed 1,843 tests with 14 skipped on Node 26.13.0.

Merge Danger

Door: Two-way. The patch can be reverted without a data migration.

Blast Radius: Live connection discovery. RPC clients using a strict JSON method now reject unsupported values before dispatch. An incorrect path or method list can prevent a connection or select the wrong encoding.

Keep the shared HTTP handle and RPC group when WebSocket is disabled. Return instance connection metadata while retaining transport JSON encoding hints.

Cover SSE-only startup and disposal, returned-descriptor RPC over both transports, nested and renamed routes, and transport-disabled controls.
@vercel

vercel Bot commented Nov 5, 2026

Copy link
Copy Markdown

@SaKaNa-Y is attempting to deploy a commit to the NuxtLabs Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

👁️‍🗨️ Review this pull request with grouped, summarized diffs at:
👉 https://pulls.review/gh/devframes/devframe/484?from=github-actions[bot]

raw result

💭 analyzed by vercel-ai-gateway/anthropic/claude-sonnet-5
🕰️ 2026-11-08 06:31 UTC
🔗 head fd48a7c
🤖 automated by pulls.review

{
  "headSha": "fd48a7c228b9b6d2ad062e5d42c83c9e284bc1ad",
  "result": {
    "overallSummary": "Fixes `createDevServer()` so it always returns a usable, closable handle regardless of transport configuration, and aligns `handle.connectionMeta()` with HTTP discovery (`__connection.json`) in both path resolution and strict JSON method advertisement, preventing 404s on nested mounts and unsafe RPC calls to JSON-only methods.",
    "groups": [
      {
        "key": "core-fix",
        "label": "Dev server handle & discovery fix",
        "summary": "Fixes `createDevServer()` to always return a closable handle (even with WebSocket disabled), make `handle.connectionMeta()` produce the same JSON snapshot as HTTP discovery (resolving paths against the base path), and have both discovery paths read strict JSON-serializable method names live from the RPC registry.",
        "category": "api",
        "filePaths": [
          "packages/devframe/src/adapters/dev.ts",
          "packages/devframe/src/node/instance-shell.ts"
        ],
        "critical": true,
        "fileNotes": [
          {
            "path": "packages/devframe/src/node/instance-shell.ts",
            "text": "`startServerTier()` is now also used when WS is disabled but a shared HTTP server was supplied, so the handle/RPC group are always available for `close()`. `addLocalMethodHints` lazily reads the registry via a getter so later-registered methods are reflected without re-snapshotting."
          },
          {
            "path": "packages/devframe/src/adapters/dev.ts",
            "text": "`connectionMeta()` now deep-clones via JSON round-trip (matching HTTP discovery's serialization, including custom `toJSON`) and rewrites local websocket/sse/mcp paths with `joinURL(basePath, ...)` so nested mounts resolve correctly against `handle.origin`."
          }
        ]
      },
      {
        "key": "tests",
        "label": "Transport & discovery tests",
        "summary": "Adds a new regression suite covering SSE-only/HTTP-only handles, nested-mount MCP discovery, static-config JSON parity, and port reuse on close; updates existing discovery assertions to expect `jsonSerializableMethods` and late-registered methods.",
        "category": "tests",
        "filePaths": [
          "packages/devframe/src/adapters/__tests__/dev-transports.test.ts",
          "packages/devframe/src/adapters/__tests__/dev.test.ts",
          "packages/devframe/src/adapters/__tests__/initiate.test.ts",
          "packages/hub/src/node/__tests__/initiate.test.ts",
          "examples/custom-hub-next/tests/next-devframe-hub.test.ts"
        ],
        "fileNotes": [
          {
            "path": "packages/devframe/src/adapters/__tests__/dev-transports.test.ts",
            "text": "New end-to-end suite exercising real WS/SSE RPC calls through both `handle.connectionMeta()` and `__connection.json`, including strict-JSON rejection of `Map` values and handle close/port-release behavior."
          }
        ]
      },
      {
        "key": "docs",
        "label": "Dev adapter docs",
        "summary": "Documents the fuller `StartedServer` shape, clarifies that `close()` releases the HTTP listener even with WebSocket disabled, and explains that `connectionMeta()` returns fresh JSON snapshots resolved against `handle.origin`.",
        "category": "docs",
        "filePaths": [
          "docs/content/2.adapters/3.dev.md"
        ]
      }
    ],
    "schemaVersion": 1,
    "source": "llm",
    "generatedAt": "2026-11-08T06:31:27.532Z",
    "model": "vercel-ai-gateway/anthropic/claude-sonnet-5",
    "locale": "en"
  }
}

Read method flags from the current RPC registry when a client requests the instance descriptor. This gives SSE-only handles and HTTP discovery the same encoding hints as WebSocket handles, including methods registered after startup.
@SaKaNa-Y
SaKaNa-Y force-pushed the fix/dev-server-sse-handle branch from 85e31b1 to 70282fa Compare November 7, 2026 07:53
Return a fresh connection descriptor for each handle call so a viewer can edit its copy without changing HTTP discovery. Resolve local RPC paths from the mount so viewers can connect from the server origin.
Serialize connection metadata using the discovery JSON format so static configuration can supply values with toJSON methods. Resolve same-origin MCP paths against the mount base for callers that receive the handle.

Exercise both discovery paths with a real MCP initialize request and compare the published representation of a URL in static configuration.
@SaKaNa-Y SaKaNa-Y changed the title fix(devframe): return complete dev server handles for SSE fix(devframe): return complete connection metadata from dev server handles Nov 8, 2026

This branch has not been deployed

No deployments
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