microsoft / microsoft/vscode-languageserver-node

Message middleware / interceptor API for `vscode-jsonrpc`

Open
#1,757 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

feature-request help wanted
Dominant language
TypeScript
Stars
1.8k
Forks
404
Avg merge
2d 8h
Merged PRs (30d)
14

Description

Feature Request: Message middleware / interceptor API for vscode-jsonrpc

Summary

Add a way to intercept or decorate outgoing and incoming JSON-RPC messages at the transport layer, so that callers can inject or extract arbitrary metadata (such as W3C Trace Context traceparent/tracestate headers) without forking the library.

Motivation

vscode-jsonrpc is used as the JS/TS transport in many language server and extension host scenarios. In our scenario, we are communicating between a VS Code extension and a .NET process. That process uses StreamJsonRpc, which offers an ActivityTracingStrategy that automatically injects W3C Trace Context into outgoing MessagePack envelopes and extracts it from incoming ones, enabling distributed tracing across the process boundary.

We would like the same on the JS side. Concretely: when a TS client sends a JSON-RPC request, we want to inject a traceparent field alongside jsonrpc, id, method, and params. StreamJsonRpc will then pick it up and parent the server-side span under the client's span, giving a cohesive cross-process trace in tools like Jaeger or the Aspire Dashboard.

Currently this is not possible because MessageWriter is constructed inside createMessageConnection() and is not accessible to callers after the fact. There is no hook to wrap or intercept messages before they are written to the stream.

Proposed API

A message middleware interface that can be passed to createMessageConnection():

export interface MessageMiddleware {
  /** Called before a message is written. Return the (optionally mutated) message, or a new one. */
  onSend?(message: Message): Message
  /** Called after a message is read, before it is dispatched. Return the (optionally mutated) message. */
  onReceive?(message: Message): Message
}

Usage:

import { createMessageConnection, MessageMiddleware } from 'vscode-jsonrpc'

const tracing: MessageMiddleware = {
  onSend(msg) {
    if (isRequestMessage(msg)) {
      // inject W3C traceparent alongside standard JSON-RPC fields
      ;(msg as any).traceparent = getCurrentTraceparent()
    }
    return msg
  },
  onReceive(msg) {
    if (isRequestMessage(msg)) {
      const tp = (msg as any).traceparent
      if (tp) setIncomingTraceparent(tp)
    }
    return msg
  },
}

const conn = createMessageConnection(reader, writer, logger, { middleware: tracing })
Alternatives considered
  • Subclassing MessageWriter — the writer instance is not exposed after createMessageConnection() returns, so this is not possible without forking.
  • Using a custom StreamMessageWriter wrapper — requires re-implementing the full framing protocol; fragile and error-prone.
  • Encoding metadata in method params — invasive; requires changes to every call site; breaks protocol contracts.
Notes
  • The traceparent field is a top-level JSON-RPC extension field (not inside params), which is valid per the JSON-RPC 2.0 spec (unknown fields are ignored). StreamJsonRpc already uses this convention.
  • This request is not limited to tracing — the same API would be useful for propagating auth tokens, request deadlines, or any other cross-cutting concern.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at createMessageConnection() and the MessageWriter construction described in the issue, then trace how messages are written and read before dispatch. Compare the proposed MessageMiddleware hooks with those paths; done means callers can pass middleware through createMessageConnection() to transform outgoing and incoming messages without reimplementing framing.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, backend-api-design
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.