Skip to main content
Glama
README.md
# dev-tools-mcp

[![CI](https://github.com/briancox730/dev-tools-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/briancox730/dev-tools-mcp/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)

An MCP ([Model Context Protocol](https://modelcontextprotocol.io)) stdio server that gives Claude Code - or any MCP client - five structured development tools:

| # | Tool | What it does |
|---|------|-------------|
| 1 | `run_e2e_tests` | Runs Playwright tests, starts the dev server, returns pass/fail per test |
| 2 | `execute_sql_as_role` | Executes SQL as a Postgres role with JWT claims - verifies Supabase RLS |
| 3 | `typecheck` | Runs `tsc --noEmit`, returns structured errors (file, line, code, message) |
| 4 | `npm_run` | Runs any npm script, parses Vitest/Jest output into structured results |
| 5 | `nextjs_build` | Runs `next build`, returns structured errors + page list with sizes |

## Prerequisites

- **Node.js** ≥ 18
- **Claude Code** installed
- For the RLS tool: **psql** on your PATH and a running Supabase/Postgres instance

## Quick start

```bash
# 1. Clone the repo
git clone https://github.com/briancox730/dev-tools-mcp.git
cd dev-tools-mcp

# 2. Install dependencies
npm install

# 3. Build
npm run build

# 4. Register with Claude Code (local scope - this project only)
claude mcp add --transport stdio dev-tools -- node /absolute/path/to/dev-tools-mcp/build/index.js

# OR register globally (available in all projects)
claude mcp add --transport stdio --scope user dev-tools -- node /absolute/path/to/dev-tools-mcp/build/index.js
```

## Alternative: edit `.claude.json` directly

Add this to the `mcpServers` key in `~/.claude.json` (global) or `.claude.json` in your project root:

```json
{
  "mcpServers": {
    "dev-tools": {
      "type": "stdio",
      "command": "node",
      "args": ["/absolute/path/to/dev-tools-mcp/build/index.js"]
    }
  }
}
```

Then restart Claude Code.

## Verify it's working

Inside Claude Code, run `/mcp` - you should see `dev-tools: connected` with 5 tools listed.

## Tool details

### 1. `run_e2e_tests` - Playwright E2E runner

Runs Playwright with the JSON reporter and parses results into structured output.

**Inputs:**
- `project_dir` (required) - absolute path to your project
- `test_pattern` - glob or file path to filter, e.g. `"tests/auth.spec.ts"`
- `headed` - run in headed mode (default: false)
- `start_server` - use Playwright's webServer config (default: true)
- `timeout_seconds` - kill after this many seconds (default: 120)

**What you get back:**
```json
{
  "success": true,
  "summary": { "passed": 8, "failed": 1, "skipped": 0, "total": 9 },
  "tests": [
    { "name": "Auth > should redirect unauthenticated users", "status": "passed", "duration_ms": 1200 },
    { "name": "Auth > should show dashboard after login", "status": "failed", "duration_ms": 3400, "error": "Expected element to be visible..." }
  ]
}
```

**Tip:** Make sure your `playwright.config.ts` has a `webServer` section so Playwright auto-starts your dev server:
```ts
export default defineConfig({
  webServer: {
    command: 'npm run dev',
    port: 3000,
    reuseExistingServer: !process.env.CI,
  },
});
```

### 2. `execute_sql_as_role` - Supabase RLS tester

Executes SQL as a specific Postgres role with JWT claims, then rolls back. Never mutates data.

**Inputs:**
- `connection_string` (required) - e.g. `"postgresql://postgres:postgres@localhost:54322/postgres"`
- `sql` (required) - the query to run
- `role` - Postgres role (default: `"authenticated"`)
- `user_id` - UUID set as `auth.uid()`
- `claims` - additional JWT claims, e.g. `{ "app_role": "parent" }`

**Example prompt for Claude Code:**
> "Run `SELECT * FROM children` as an authenticated parent user with id `abc-123` and confirm they can only see their own children."

**What you get back:**
```json
{
  "success": true,
  "role": "authenticated",
  "user_id": "abc-123",
  "query": "SELECT * FROM children",
  "rows": [["id-1", "abc-123", "Alice"], ["id-2", "abc-123", "Bob"]],
  "row_count": 2
}
```

### 3. `typecheck` - TypeScript checker

Runs `tsc --noEmit` and parses the output into structured errors.

**Inputs:**
- `project_dir` (required)
- `tsconfig` - relative path to tsconfig (default: `"tsconfig.json"`)
- `files` - check only specific files

**What you get back:**
```json
{
  "success": false,
  "error_count": 2,
  "errors": [
    { "file": "src/utils.ts", "line": 42, "column": 5, "code": "TS2345", "message": "Argument of type 'string' is not assignable..." },
    { "file": "src/api.ts", "line": 18, "column": 12, "code": "TS2339", "message": "Property 'foo' does not exist on type..." }
  ]
}
```

### 4. `npm_run` - Structured npm script runner

Runs any npm script with `CI=true` and `FORCE_COLOR=0`, then parses Vitest/Jest output.

**Inputs:**
- `project_dir` (required)
- `script` (required) - e.g. `"test"`, `"test:unit"`, `"lint"`
- `args` - extra args passed after `--`
- `env` - extra environment variables

**What you get back:**
```json
{
  "success": false,
  "exit_code": 1,
  "script": "test",
  "summary": { "total_tests": 14, "passed_tests": 12, "failed_tests": 2 },
  "file_results": [
    { "file": "src/auth.test.ts", "status": "failed", "tests_failed": 2 },
    { "file": "src/utils.test.ts", "status": "passed", "tests_passed": 5 }
  ],
  "raw_stdout": "..."
}
```

### 5. `nextjs_build` - Next.js build validator

Runs `next build` in production mode and parses errors and page output.

**Inputs:**
- `project_dir` (required)
- `env` - extra env vars for the build
- `timeout_seconds` - (default: 180)

**What you get back:**
```json
{
  "success": true,
  "error_count": 0,
  "errors": [],
  "pages": [
    { "path": "/", "size_kb": 5.42, "type": "static" },
    { "path": "/dashboard", "size_kb": 12.1, "type": "dynamic" },
    { "path": "/api/auth", "size_kb": 0, "type": "ssr" }
  ],
  "build_duration_ms": 24500
}
```

## Development

```bash
# Watch mode
npm run watch

# Run the unit tests (Vitest)
npm test

# Test interactively with the MCP Inspector
npm run inspect
```

### Tests

The output parsers are the trickiest, most regression-prone part of the codebase.
They turn free-form Vitest/Jest/`tsc`/`next build` console output into structured
JSON. Those pure functions are unit-tested with [Vitest](https://vitest.dev) under
[`test/`](./test); run them with `npm test`. CI (see
[`.github/workflows/ci.yml`](.github/workflows/ci.yml)) runs `npm ci`, the
TypeScript build, and the test suite on Node 20 and 22 for every push and pull
request.

## Customization ideas

- **Add a `lint` tool** that parses ESLint JSON output
- **Add a `prisma_migrate` tool** for structured migration status
- **Add a `docker_compose` tool** to manage test containers
- **Wire the RLS tool to Supabase CLI** (`supabase db test`) instead of raw psql

## License

[MIT](./LICENSE) © 2026 Brian Cox

TDQS

A3.7/5.0

Scored across 5 tools

Disambiguation4/5

每个工具针对开发流程的不同方面: 端到端测试, SQL角色执行, 类型检查, 运行脚本, 构建。npm_run可以运行任意脚本, 包括测试, 但run_e2e_tests专门用于Playwright, 因此边界相对清晰。少数可能重叠, 但描述有帮助。

Naming Consistency3/5

命名模式部分一致: 有些工具使用动词开头(run_e2e_tests, execute_sql_as_role), 而其他使用名词开头(npm_run, nextjs_build)或单词(typecheck)。虽然每个名称都具描述性, 但顺序和风格并不统一。

Tool Count5/5

5个工具的数量非常适合开发工具服务器。每个工具都服务于核心开发任务, 没有冗余或过度精简, 范围恰当。

Completeness4/5

工具集覆盖了主要开发步骤: 测试, 类型检查, 构建, 运行脚本以及SQL安全验证。npm_run可以运行lint或单元测试等其他任务, 因此没有明显缺口, 但可能缺少如专门的格式或代码质量检查工具, 不过可通配。

Maintenance

ActivityMaintained
ResponsivenessNo issues