Skip to main content
Glama
irgiaryanda

ts-mcp-durable-browser-automation

by irgiaryanda

ts-mcp-durable-browser-automation

A production-grade Model Context Protocol (MCP) Server built in TypeScript that wraps a resilient Playwright browser engine with an SQLite-backed Exactly-Once (idempotency) lock, purpose-built to automate interactions with legacy AEC (Architecture, Engineering, Construction) portals that expose unpredictable, brittle APIs.


Architecture

Three discrete, independently testable layers:

LLM / MCP Client
      │
      ▼
┌─────────────────────────────┐
│  Layer 1 · MCP Interface    │  @modelcontextprotocol/sdk · Zod v4 input validation
└────────────┬────────────────┘
             │
             ▼
┌─────────────────────────────┐
│  Layer 2 · Idempotency Lock │  SQLite state machine: PENDING → SUCCESS | FAILED
└────────────┬────────────────┘  Rejects concurrent duplicate requests
             │
             ▼
┌─────────────────────────────┐
│  Layer 3 · Playwright Engine│  Resilient locators · auto-retry · screenshot capture
└─────────────────────────────┘  Guaranteed browser.close() via try/finally

Core Features

  • Exactly-Once Execution — Every tool call requires an idempotencyKey. The SQLite lock engine prevents duplicate executions and returns cached results on replay.

  • Race Condition Protection — Concurrent requests with the same key are rejected while a PENDING operation is in flight.

  • Resilient Browser Automation — Playwright uses getByRole / getByText locators instead of fragile CSS selectors, with explicit auto-waiting for unpredictable DOM mutations.

  • Zero Browser Leaks — All browser and context instances are destroyed in try/finally blocks, even on fatal crashes.

  • Strict Type Safetystrict: true TypeScript, Zod v4 runtime schema validation, no any types.

  • Physical Proof Generation — A CLI script runs a full end-to-end simulation and outputs a markdown artifact containing structured execution logs and Base64-encoded screenshots.


Project Structure

ts-mcp-durable-browser-automation/
├── src/
│   ├── index.ts                 # MCP Server entry point
│   ├── mcp/                     # Zod schemas, tool handlers
│   ├── automation/              # Playwright lifecycle, submitPermit task
│   ├── core/                    # SQLite idempotency lock, structured logger
│   └── types/                   # Shared TypeScript interfaces
├── scripts/
│   └── generate_proof.ts        # CLI — generates automation_proof.md
├── tests/
│   ├── idempotency.test.ts      # Proves exactly-once lock behaviour
│   └── browser.test.ts          # Proves browser lifecycle + form automation
├── docs/
│   └── mock_portal.html         # Offline legacy AEC portal target
└── automation_proof.md          # Generated artifact (Base64 screenshots + logs)

Prerequisites

  • Node.js ≥ 20

  • npm ≥ 10


Setup

git clone https://github.com/irgiaryanda/ts-mcp-durable-browser-automation.git
cd ts-mcp-durable-browser-automation
npm install
npx playwright install chromium

Commands

Run automated tests

Mathematically proves the idempotency lock (SUCCESS cache hit, PENDING race rejection, FAILED retry) and Playwright browser lifecycle:

npm test

Expected output:

Tests:       8 passed, 8 total

Generate physical proof artifact

Runs a full end-to-end simulation against the offline mock AEC portal and writes automation_proof.md with execution logs and a Base64 screenshot:

npm run prove

Output file: automation_proof.md

Type-check (zero errors)

npx tsc --noEmit

MCP Tool: submit_planning_permit

Parameter

Type

Description

idempotencyKey

string

Unique key guaranteeing exactly-once execution

projectName

string

Name of the construction project

permitType

enum

new_construction | renovation | demolition | change_of_use | heritage

applicantId

string

Registered applicant identifier

submitDate

string

Date in YYYY-MM-DD format

The tool is safe to retry — replaying a call with an existing idempotencyKey that previously succeeded returns the cached result instantly without re-executing the browser automation.


Key Technical Decisions

Decision

Rationale

SQLite for idempotency store

Zero-dependency, file-based, ACID-compliant. No external service required.

Zod v4 for runtime validation

Full compatibility with @modelcontextprotocol/sdk type system (ZodRawShapeCompat).

getByRole / getByText locators

Immune to CSS class changes in legacy portal markup.

try/finally around all browser instances

Guarantees browser.close() even on unhandled exceptions.

Offline mock_portal.html as test target

Eliminates network flakiness — deterministic, reproducible automation proof.