Skip to content

Conversation

@waldekmastykarz
Copy link
Collaborator

this is a breaking change so will need to be released in a new major version

Stdio Proxy Feature

Overview

This PR introduces a new stdio command to Dev Proxy that enables proxying stdin/stdout/stderr of local executables. This is particularly useful for testing and debugging Model Context Protocol (MCP) servers and other stdio-based applications.

Why?

Modern AI development increasingly relies on local tools that communicate via stdio, particularly MCP servers. These servers:

  • Receive JSON-RPC requests via stdin
  • Send JSON-RPC responses via stdout
  • Report errors via stderr

Developers need to:

  1. Inspect the messages flowing between clients (like VS Code) and MCP servers
  2. Mock responses to test client behavior without running actual server logic
  3. Debug communication issues using familiar DevTools interface
  4. Test error handling by injecting mock responses or blocking requests

Previously, there was no way to intercept and manipulate stdio traffic like Dev Proxy does for HTTP. This feature fills that gap.

Capabilities

1. Stdio Proxy Command

New command that wraps any executable and proxies its stdio streams:

devproxy stdio <command> [args...]

Example:

devproxy stdio npx -y @modelcontextprotocol/server-filesystem

2. Plugin Architecture for Stdio

New IStdioPlugin interface that allows plugins to:

  • Intercept stdin before it reaches the child process (BeforeStdinAsync)
  • Intercept stdout after receiving from child process (AfterStdoutAsync)
  • Intercept stderr after receiving from child process (AfterStderrAsync)
  • Log and record stdio request/response pairs (AfterStdioRequestLogAsync)
  • Process recordings when session ends (AfterStdioRecordingStopAsync)

Plugins can modify, consume, or mock messages by setting ResponseState.HasBeenSet = true.

3. DevTools Plugin Support

The DevToolsPlugin now works with stdio, allowing you to:

  • View stdin/stdout/stderr in Chrome DevTools Network tab
  • Inspect message bodies with proper JSON formatting
  • See timing information for each request/response
  • View log entries from plugins

Messages appear as:

  • stdio://command-name URLs
  • STDIN method for requests
  • STDOUT (200) and STDERR (500) for responses

4. Mock Stdio Response Plugin

New MockStdioResponsePlugin for mocking stdio responses:

  • Match by body fragment - intercept stdin containing specific text
  • Support placeholders - use @stdin.body.propertyPath to reference values from stdin
  • Block unmocked requests - optionally prevent unmatched stdin from reaching child
  • File-based responses - use @filename to load mock content from files
  • Nth occurrence matching - target specific occurrences of matching requests

5. LatencyPlugin Support

The LatencyPlugin now works with stdio to simulate network delays.

Sample Configurations

Basic Stdio Configuration

devproxyrc.json:

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v2.0.0/rc.schema.json",
  "plugins": [
    {
      "name": "MockStdioResponsePlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "mockStdioResponsePlugin"
    },
    {
      "name": "DevToolsPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "devTools"
    }
  ],
  "urlsToWatch": [
    "https://api.example.com/*"
  ],
  "devTools": {
    "preferredBrowser": "Edge"
  },
  "mockStdioResponsePlugin": {
    "mocksFile": "stdio-mocks.json"
  }
}

Mock File for JSON-RPC/MCP

stdio-mocks.json:

{
  "mocks": [
    {
      "request": {
        "bodyFragment": "tools/list"
      },
      "response": {
        "stdout": "{\"jsonrpc\":\"2.0\",\"id\":@stdin.body.id,\"result\":{\"tools\":[]}}\n"
      }
    },
    {
      "request": {
        "bodyFragment": "GetBestPractices"
      },
      "response": {
        "stdout": "{\"jsonrpc\":\"2.0\",\"id\":@stdin.body.id,\"result\":{\"content\":[{\"type\":\"text\",\"text\":\"Mock best practices response\"}]}}\n"
      }
    }
  ]
}

Using File-Based Mock Responses

stdio-mocks.json:

{
  "mocks": [
    {
      "request": {
        "bodyFragment": "initialize"
      },
      "response": {
        "stdout": "@initialize-response.json"
      }
    }
  ]
}

initialize-response.json:

{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"Mock MCP Server","version":"1.0.0"}}}

Blocking Unmocked Requests

{
  "mockStdioResponsePlugin": {
    "mocksFile": "stdio-mocks.json",
    "blockUnmockedRequests": true
  }
}

With Latency Simulation

{
  "plugins": [
    {
      "name": "LatencyPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "latencyPlugin"
    },
    {
      "name": "MockStdioResponsePlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "mockStdioResponsePlugin"
    }
  ],
  "latencyPlugin": {
    "minMs": 100,
    "maxMs": 500
  }
}

Usage Examples

Testing an MCP Server

# Start Dev Proxy with MCP server
devproxy stdio npx -y @modelcontextprotocol/server-filesystem

# Dev Proxy will:
# 1. Start the MCP server as a child process
# 2. Intercept all stdin/stdout/stderr
# 3. Apply configured plugins (mocks, latency, etc.)
# 4. Open DevTools for inspection (if DevToolsPlugin enabled)

Command-Line Options

# Specify a custom mocks file
devproxy stdio --stdio-mocks-file ./my-mocks.json npx my-server

# Disable mocks
devproxy stdio --no-stdio-mocks npx my-server

Placeholder Support

Placeholders allow dynamic values in mock responses by referencing stdin properties:

Placeholder Description
@stdin.body.id JSON-RPC request ID
@stdin.body.method JSON-RPC method name
@stdin.body.params.name Nested property access

Example:

{
  "response": {
    "stdout": "{\"jsonrpc\":\"2.0\",\"id\":@stdin.body.id,\"result\":\"Hello @stdin.body.params.name\"}\n"
  }
}

Files Changed Summary

New Files

  • DevProxy/Commands/StdioCommand.cs - CLI command implementation
  • DevProxy/Stdio/ProxySession.cs - Core proxy session management
  • DevProxy/Logging/StdioFileLoggerProvider.cs - File logging for stdio sessions
  • DevProxy.Abstractions/Plugins/IStdioPlugin.cs - Plugin interface
  • DevProxy.Abstractions/Proxy/StdioEvents.cs - Event args and models
  • DevProxy.Abstractions/Models/MockStdioResponse.cs - Mock response models
  • DevProxy.Plugins/Mocking/MockStdioResponsePlugin.cs - Mocking plugin
  • DevProxy.Plugins/Mocking/MockStdioResponsesLoader.cs - Mock file loader

Modified Files

  • DevProxy.Plugins/Inspection/DevToolsPlugin.cs - Added stdio support
  • DevProxy.Plugins/Behavior/LatencyPlugin.cs - Added stdio support
  • DevProxy.Abstractions/Plugins/BasePlugin.cs - Added default IStdioPlugin implementations
  • DevProxy/Commands/DevProxyCommand.cs - Integrated stdio command
  • Various plugins - Updated logging context usage

Architecture

┌─────────────────┐     stdin      ┌──────────────────┐     stdin      ┌─────────────────┐
│  Parent Process │ ─────────────▶ │   ProxySession   │ ─────────────▶ │  Child Process  │
│   (VS Code)     │                │                  │                │   (MCP Server)  │
│                 │ ◀───────────── │  ┌────────────┐  │ ◀───────────── │                 │
└─────────────────┘    stdout      │  │  Plugins   │  │    stdout      └─────────────────┘
                                   │  └────────────┘  │
                                   └──────────────────┘

Plugins are invoked in order:

  1. BeforeStdinAsync - Before forwarding stdin to child
  2. AfterStdoutAsync - After receiving stdout from child
  3. AfterStderrAsync - After receiving stderr from child

@garrytrinder
Copy link
Contributor

Great idea! We might also want to include an optional path to a config file, devproxy stdio --config-file ./config.json npx my-server for example.

@waldekmastykarz
Copy link
Collaborator Author

waldekmastykarz commented Jan 6, 2026

Great idea! We might also want to include an optional path to a config file, devproxy stdio --config-file ./config.json npx my-server for example.

To do:

  • add support for config
  • add schemas for the new plugins
  • double check if it's a breaking change or not

@waldekmastykarz waldekmastykarz marked this pull request as ready for review January 8, 2026 10:18
@waldekmastykarz waldekmastykarz requested a review from a team as a code owner January 8, 2026 10:18
Copilot AI review requested due to automatic review settings January 8, 2026 10:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a comprehensive stdio proxy feature that enables Dev Proxy to intercept and manipulate stdin/stdout/stderr communication with local executables, particularly targeting Model Context Protocol (MCP) servers. The implementation adds a new stdio command, extends the plugin architecture with IStdioPlugin, and provides stdio support for existing plugins (DevTools, Latency, and a new MockStdioResponse plugin).

Key changes:

  • New stdio proxy session management with message interception capabilities
  • Plugin architecture extension allowing plugins to participate in both HTTP and stdio scenarios
  • DevTools integration for inspecting stdio traffic via Chrome DevTools
  • Mock response system for stdio with JSON-RPC placeholder support

Reviewed changes

Copilot reviewed 43 out of 43 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
schemas/v2.1.0/mockstdioresponseplugin.schema.json JSON schema for MockStdioResponsePlugin configuration
schemas/v2.1.0/mockstdioresponseplugin.mocksfile.schema.json JSON schema for stdio mock response files
DevProxy/Stdio/ProxySession.cs Core stdio proxy session with stream forwarding and plugin invocation
DevProxy/Logging/StdioFileLoggerProvider.cs File-based logger to avoid interfering with proxied streams
DevProxy/Commands/StdioCommand.cs CLI command for stdio proxy functionality
DevProxy/Commands/DevProxyCommand.cs Integration of stdio command and configuration
DevProxy/Commands/DevProxyConfigOptions.cs Command-line options for stdio subcommand
DevProxy/Extensions/ILoggingBuilderExtensions.cs Logging configuration for stdio mode
DevProxy/Plugins/PluginServiceExtensions.cs Registration of IStdioPlugin instances in DI
DevProxy.Abstractions/Plugins/IStdioPlugin.cs Interface for stdio-aware plugins
DevProxy.Abstractions/Plugins/BasePlugin.cs Default IStdioPlugin implementation for all plugins
DevProxy.Abstractions/Proxy/StdioEvents.cs Event argument classes for stdio operations
DevProxy.Abstractions/Models/MockStdioResponse.cs Model classes for stdio mock responses
DevProxy.Abstractions/Proxy/IProxyLogger.cs Logging context for stdio operations
DevProxy.Abstractions/Extensions/ILoggerExtensions.cs Extension methods for stdio logging
DevProxy.Plugins/Mocking/MockStdioResponsePlugin.cs Plugin for mocking stdio responses with placeholder support
DevProxy.Plugins/Mocking/MockStdioResponsesLoader.cs Loader for stdio mock configuration files
DevProxy.Plugins/Inspection/DevToolsPlugin.cs Extended to support stdio traffic inspection in DevTools
DevProxy.Plugins/Behavior/LatencyPlugin.cs Extended to simulate latency for stdio operations
DevProxy.Plugins/Inspection/WebSocketServer.cs Added client connection event for message buffering
DevProxy/Proxy/ProxyEngine.cs Updated logging context instantiation
Multiple plugin files Updated to use explicit new LoggingContext(e) instead of new(e)

waldekmastykarz and others added 2 commits January 8, 2026 11:31
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.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.

2 participants