Skip to content

feat: add mock-network tool for intercepting and stubbing browser requests#32

Merged
cryptotavares merged 1 commit into
mainfrom
cryptotavares/add-command-to-mock-network-requests
May 29, 2026
Merged

feat: add mock-network tool for intercepting and stubbing browser requests#32
cryptotavares merged 1 commit into
mainfrom
cryptotavares/add-command-to-mock-network-requests

Conversation

@cryptotavares

@cryptotavares cryptotavares commented May 28, 2026

Copy link
Copy Markdown
Collaborator

Description

Current State

The tool system provides 29 tools covering browser interaction, navigation, state management, knowledge, and advanced capabilities (CDP, batching). However, there is no built-in mechanism for intercepting and stubbing HTTP network requests during an active browser session. Agents and developers who need to mock API responses — for example, stubbing accounts.api.cx.metamask.io endpoints to return controlled balances or network lists — must either rely on external mock servers (via MockServerCapability) or use raw CDP commands, both of which require significant setup and lack structured observability.

Solution

This PR adds a new mock_network tool (tool #30) that provides targeted Playwright-based network interception directly on the active BrowserContext. It exposes four actions — add, clear, list, and requests — through both the tool system (POST /tool/mock_network) and the CLI (mm mock-network <action>).


Architecture

How It Works

The system is built around a NetworkMockRouteManager class that manages Playwright route handlers for a given BrowserContext. Manager instances are stored in a module-level WeakMap<BrowserContext, NetworkMockRouteManager>, ensuring one manager per browser context with automatic garbage collection when the context is destroyed.

Agent / CLI
    │
    ▼
mm mock-network add <json>
    │
    ▼  HTTP POST /tool/mock_network
┌─────────────────────────────────────────────┐
│  mockNetworkTool(input, context)            │
│                                             │
│  1. Validate active session                 │
│  2. Get/create NetworkMockRouteManager      │
│     (WeakMap keyed by BrowserContext)        │
│  3. Dispatch by action: add/clear/list/req  │
└──────────────────┬──────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────┐
│  NetworkMockRouteManager                    │
│                                             │
│  #rules: NetworkMockRouteRule[]             │
│  #requestRecords: NetworkMockRequestRecord[]│
│  #routePatterns: Set<string>               │
│                                             │
│  Origin-scoped interception:                │
│  ┌─────────────────────────────────────┐    │
│  │ browserContext.route(               │    │
│  │   "https://api.example.com/**",     │    │
│  │   routeHandler                      │    │
│  │ )                                   │    │
│  └─────────────────────────────────────┘    │
│                                             │
│  Route handler dispatch:                    │
│  request → findMatchingRule(rules, req)     │
│    ├─ match found → route.fulfill(response) │
│    └─ no match   → route.continue()         │
└─────────────────────────────────────────────┘

Origin-Scoped Interception

Rather than installing a separate Playwright route handler per rule, the manager installs one handler per unique origin (e.g., https://accounts.api.cx.metamask.io/**). When a request arrives, the shared handler iterates rules in reverse order (newest first) to find a match by HTTP method and URL pattern. This avoids handler proliferation and ensures that replacing a rule with the same id but a different origin correctly unroutes the orphaned origin pattern.

URL Matching

Rules support exact URLs and simple glob patterns:

  • Exact match: https://api.example.com/v2/supportedNetworks
  • Single-star glob (*): matches any segment except / — e.g., https://api.example.com/*.json
  • Double-star glob (**): matches everything including / — e.g., https://api.example.com/v5/balances**

Glob patterns are converted to RegExp at match time via globToRegExp().

Rule Lifecycle

  • Add: Rules are keyed by id. Adding a rule with an existing id replaces it. If the replaced rule's origin is no longer used by any remaining rule, the origin's Playwright route handler is removed (unroute).
  • Clear: Removes all rules, clears request records, and unroutes all installed origin patterns.
  • List: Returns a snapshot of currently registered rules.
  • Requests: Returns recorded hit/miss request records with an optional --limit for the most recent N entries. The manager retains up to 500 records (configurable), evicting oldest entries on overflow.

Response Construction

Each rule specifies a response with:

  • status (default: 200)
  • json or body (mutually exclusive — validated by Zod schema)
  • Optional headers (keys normalized to lowercase, merged over defaults)

For JSON responses, default headers include content-type: application/json and access-control-allow-origin: *. For text responses, content-type: text/plain is used instead.

CLI Integration

The mm mock-network subcommand routes through routeMockNetworkCommand() in src/cli/mm.ts. The CLI accepts three input shapes for add and normalizes them before sending to the daemon:

Input Shape Normalized To
Single rule object {id, method, url, response} { rule: <object> }
Array of rules [{...}, {...}] { routes: <array> }
Config object { routes: [...] } { routes: <array> }

This normalization happens in normalizeMockNetworkAddPayload().

Validation

A Zod schema (mockNetworkInputSchema) validates all inputs:

  • HTTP method is uppercased via .transform()
  • URL must be an absolute http:// or https:// URL (validated by attempting new URL())
  • Response must have exactly one of json or body (enforced via .refine())
  • Status must be an integer between 100–599
  • For add, exactly one of rule or routes must be provided

Tool Category

mock_network is categorized as mutating, meaning tool responses include post-execution observations (state, a11y, testIds).


Changes

New Files

  • src/tools/mock-network.ts — Tool implementation and NetworkMockRouteManager class
  • src/tools/mock-network.test.ts — 476 lines of tests covering all actions, edge cases, URL matching, and request recording

Modified Files

  • src/cli/mm.tsrouteMockNetworkCommand(), parseJsonArgument(), normalizeMockNetworkAddPayload(), help text
  • src/cli/mm.test.ts — CLI routing tests for all mock-network subcommands
  • src/tools/registry.ts — Registered mock_network tool, updated category counts
  • src/tools/types/tool-inputs.ts — Type definitions for mock network inputs
  • src/tools/types/tool-outputs.ts — Type definitions for mock network results
  • src/validation/schemas.ts — Zod schemas for route rules and mock-network input
  • src/validation/schemas.test.ts — Schema validation tests
  • src/tools/index.ts — Re-export
  • README.md — Documentation for the new tool and CLI commands
  • SKILL.md — Agent-facing reference and error code updates
  • vitest.config.mts — Updated coverage thresholds

@cryptotavares cryptotavares requested a review from a team as a code owner May 28, 2026 22:21
@cryptotavares cryptotavares merged commit adfca4a into main May 29, 2026
25 checks passed
@cryptotavares cryptotavares deleted the cryptotavares/add-command-to-mock-network-requests branch May 29, 2026 10:27
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