ts-mcp-durable-browser-automation
by irgiaryanda
README.md
# 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 Safety** — `strict: 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
```bash
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:
```bash
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:
```bash
npm run prove
```
Output file: `automation_proof.md`
### Type-check (zero errors)
```bash
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. |
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues