From e91f60b71896b6503b259d89b228d4791b4664c5 Mon Sep 17 00:00:00 2001 From: Jack Arturo Date: Sat, 6 Jun 2026 16:21:44 +0200 Subject: [PATCH 1/3] =?UTF-8?q?docs:=20adopt=20house=20standard=20?= =?UTF-8?q?=E2=80=94=20CLAUDE.md=20imports=20AGENTS.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Promote CLAUDE.md's content to AGENTS.md (canonical, read by all agents) and reduce CLAUDE.md to the bare `@AGENTS.md` import. Mechanical only; the freshness pass follows in the next commit. Co-Authored-By: Claude Opus 4.8 --- AGENTS.md | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 111 +----------------------------------------------------- 2 files changed, 111 insertions(+), 110 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..58c51da --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,110 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Commands + +### Build & Development +- **Build TypeScript**: `npm run build` - Compiles TypeScript to JavaScript in `dist/` +- **Development mode**: `npm run dev` - Runs with tsx watch for hot reload +- **Start compiled**: `npm start` or `node dist/index.js` - Run the compiled server + +### Code Quality +- **Lint**: `npm run lint` - Check ESLint issues +- **Fix lint**: `npm run lint:fix` - Auto-fix ESLint issues +- **Format**: `npm run format` - Format with Prettier +- **Check format**: `npm run format:check` - Verify Prettier formatting + +### Testing & Debugging +- **Enable debug logs**: Set `DEBUG=mcp-local-wp` environment variable +- **Test connection**: After building, run `node dist/index.js` to verify MySQL connection + +## Architecture + +### Core Components + +1. **`src/index.ts`** - MCP server entry point + - Sets up StdioServerTransport for Claude/Cursor communication + - Implements two tools: `mysql_query` and `mysql_schema` + - Auto-detects Local by Flywheel or falls back to environment variables + +2. **`src/local-detector.ts`** - Local by Flywheel auto-detection + - Scans running processes for mysqld with Local paths + - Extracts socket path from process arguments + - Parses my.cnf for port configuration + - Returns LocalSiteInfo with connection details + +3. **`src/mysql-client.ts`** - MySQL connection management + - Wraps mysql2/promise for async operations + - Validates queries are read-only (SELECT/SHOW/DESCRIBE/EXPLAIN) + - Blocks multi-statement queries for security + - Supports parameter binding with `?` placeholders + +4. **`src/types.ts`** - TypeScript interfaces + - LocalSiteInfo: socket path, port, site ID, config path + - MySQLConnectionConfig: connection parameters + +### MCP Tools + +**mysql_query**: +- Executes read-only SQL queries +- Parameters: `sql` (string), optional `params` (array) +- Enforces single statement, read-only operations + +**mysql_schema**: +- Inspects database structure via INFORMATION_SCHEMA +- No params: lists all tables with row counts +- With `table` param: shows columns and indexes + +### Security Model + +- **Read-only enforcement**: Regex validates queries start with SELECT/SHOW/DESCRIBE/EXPLAIN +- **Single statement only**: Semicolons blocked except at end +- **Parameter binding**: Uses mysql2's parameterized queries to prevent injection +- **Local-only design**: Prioritizes Unix socket connections + +## Local by Flywheel Integration + +The server solves Local's dynamic path problem where site IDs (like `lx97vbzE7`) change on restart: + +1. Process detection finds running mysqld with `--defaults-file` argument +2. Extracts site directory from config path (e.g., `/Users/.../Local/run/lx97vbzE7/`) +3. Constructs socket path: `{siteDir}/mysql/mysqld.sock` +4. Falls back to environment variables if Local isn't detected + +Typical Local paths: +- Config: `~/Library/Application Support/Local/run/{siteId}/conf/mysql/my.cnf` +- Socket: `~/Library/Application Support/Local/run/{siteId}/mysql/mysqld.sock` +- Database name: `local` +- Credentials: `root`/`root` + +## Environment Variables (Fallback) + +When Local isn't detected or for custom setups: +- `MYSQL_HOST` - Default: localhost +- `MYSQL_PORT` - Default: 3306 +- `MYSQL_USER` - Default: root +- `MYSQL_PASS` - Default: root +- `MYSQL_DB` - Default: local +- `MYSQL_SOCKET_PATH` - Unix socket path (optional) + +## Development Workflow + +1. Ensure Local by Flywheel is running with an active site +2. Make changes to TypeScript source files +3. Run `npm run dev` for development with hot reload +4. For production: `npm run build` then test with `npm start` +5. Use `npm run lint:fix` and `npm run format` before committing + +## WordPress Database Patterns + +Common WordPress tables and their purposes: +- `wp_posts` - Posts, pages, attachments, custom post types +- `wp_postmeta` - Custom fields and post metadata +- `wp_options` - Site settings and plugin options +- `wp_users` - User accounts +- `wp_usermeta` - User metadata and capabilities +- `wp_terms`, `wp_term_taxonomy`, `wp_term_relationships` - Taxonomies (categories, tags) +- `wp_comments`, `wp_commentmeta` - Comments and metadata + +Note: Table prefix may vary (default is `wp_` but could be custom). \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 58c51da..43c994c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,110 +1 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Commands - -### Build & Development -- **Build TypeScript**: `npm run build` - Compiles TypeScript to JavaScript in `dist/` -- **Development mode**: `npm run dev` - Runs with tsx watch for hot reload -- **Start compiled**: `npm start` or `node dist/index.js` - Run the compiled server - -### Code Quality -- **Lint**: `npm run lint` - Check ESLint issues -- **Fix lint**: `npm run lint:fix` - Auto-fix ESLint issues -- **Format**: `npm run format` - Format with Prettier -- **Check format**: `npm run format:check` - Verify Prettier formatting - -### Testing & Debugging -- **Enable debug logs**: Set `DEBUG=mcp-local-wp` environment variable -- **Test connection**: After building, run `node dist/index.js` to verify MySQL connection - -## Architecture - -### Core Components - -1. **`src/index.ts`** - MCP server entry point - - Sets up StdioServerTransport for Claude/Cursor communication - - Implements two tools: `mysql_query` and `mysql_schema` - - Auto-detects Local by Flywheel or falls back to environment variables - -2. **`src/local-detector.ts`** - Local by Flywheel auto-detection - - Scans running processes for mysqld with Local paths - - Extracts socket path from process arguments - - Parses my.cnf for port configuration - - Returns LocalSiteInfo with connection details - -3. **`src/mysql-client.ts`** - MySQL connection management - - Wraps mysql2/promise for async operations - - Validates queries are read-only (SELECT/SHOW/DESCRIBE/EXPLAIN) - - Blocks multi-statement queries for security - - Supports parameter binding with `?` placeholders - -4. **`src/types.ts`** - TypeScript interfaces - - LocalSiteInfo: socket path, port, site ID, config path - - MySQLConnectionConfig: connection parameters - -### MCP Tools - -**mysql_query**: -- Executes read-only SQL queries -- Parameters: `sql` (string), optional `params` (array) -- Enforces single statement, read-only operations - -**mysql_schema**: -- Inspects database structure via INFORMATION_SCHEMA -- No params: lists all tables with row counts -- With `table` param: shows columns and indexes - -### Security Model - -- **Read-only enforcement**: Regex validates queries start with SELECT/SHOW/DESCRIBE/EXPLAIN -- **Single statement only**: Semicolons blocked except at end -- **Parameter binding**: Uses mysql2's parameterized queries to prevent injection -- **Local-only design**: Prioritizes Unix socket connections - -## Local by Flywheel Integration - -The server solves Local's dynamic path problem where site IDs (like `lx97vbzE7`) change on restart: - -1. Process detection finds running mysqld with `--defaults-file` argument -2. Extracts site directory from config path (e.g., `/Users/.../Local/run/lx97vbzE7/`) -3. Constructs socket path: `{siteDir}/mysql/mysqld.sock` -4. Falls back to environment variables if Local isn't detected - -Typical Local paths: -- Config: `~/Library/Application Support/Local/run/{siteId}/conf/mysql/my.cnf` -- Socket: `~/Library/Application Support/Local/run/{siteId}/mysql/mysqld.sock` -- Database name: `local` -- Credentials: `root`/`root` - -## Environment Variables (Fallback) - -When Local isn't detected or for custom setups: -- `MYSQL_HOST` - Default: localhost -- `MYSQL_PORT` - Default: 3306 -- `MYSQL_USER` - Default: root -- `MYSQL_PASS` - Default: root -- `MYSQL_DB` - Default: local -- `MYSQL_SOCKET_PATH` - Unix socket path (optional) - -## Development Workflow - -1. Ensure Local by Flywheel is running with an active site -2. Make changes to TypeScript source files -3. Run `npm run dev` for development with hot reload -4. For production: `npm run build` then test with `npm start` -5. Use `npm run lint:fix` and `npm run format` before committing - -## WordPress Database Patterns - -Common WordPress tables and their purposes: -- `wp_posts` - Posts, pages, attachments, custom post types -- `wp_postmeta` - Custom fields and post metadata -- `wp_options` - Site settings and plugin options -- `wp_users` - User accounts -- `wp_usermeta` - User metadata and capabilities -- `wp_terms`, `wp_term_taxonomy`, `wp_term_relationships` - Taxonomies (categories, tags) -- `wp_comments`, `wp_commentmeta` - Comments and metadata - -Note: Table prefix may vary (default is `wp_` but could be custom). \ No newline at end of file +@AGENTS.md From f294ee164f02259b79033454ab37cf2b68a5f962 Mon Sep 17 00:00:00 2001 From: Jack Arturo Date: Sat, 6 Jun 2026 16:28:24 +0200 Subject: [PATCH 2/3] docs: freshen AGENTS.md against current code - Correct tool inventory: 2 documented tools -> 4 default tools (mysql_query, mysql_schema, mysql_current_site, mysql_list_sites) plus conditional mysql_write gated by MYSQL_ALLOW_WRITES - Document 5-level site-selection priority (SITE_ID/SITE_NAME/CWD/process/filesystem) and add missing env vars (MYSQL_ALLOW_WRITES, SITE_ID, SITE_NAME, LOCAL_RUN_DIR, LOCAL_SITES_JSON) - Update security model for the gated write path; generalize intro to coding agents and fix stale CLAUDE.md H1 Co-Authored-By: Claude Opus 4.8 --- AGENTS.md | 92 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 27 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 58c51da..5afcf3f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,13 +1,14 @@ -# CLAUDE.md +# AGENTS.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +This file provides guidance to coding agents (Claude Code, Cursor, Codex, and others) when working with code in this repository. ## Commands ### Build & Development -- **Build TypeScript**: `npm run build` - Compiles TypeScript to JavaScript in `dist/` +- **Build TypeScript**: `npm run build` - Compiles TypeScript to JavaScript in `dist/` (also runs `postbuild` to `chmod +x dist/index.js`) - **Development mode**: `npm run dev` - Runs with tsx watch for hot reload - **Start compiled**: `npm start` or `node dist/index.js` - Run the compiled server +- **Smoke test**: `npm test` - Prints a build-ok message (no real test suite yet) ### Code Quality - **Lint**: `npm run lint` - Check ESLint issues @@ -24,63 +25,88 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ### Core Components 1. **`src/index.ts`** - MCP server entry point - - Sets up StdioServerTransport for Claude/Cursor communication - - Implements two tools: `mysql_query` and `mysql_schema` + - Sets up StdioServerTransport for agent communication + - Registers the MCP tools and routes `CallToolRequest`s - Auto-detects Local by Flywheel or falls back to environment variables + - Reads `MYSQL_ALLOW_WRITES` at startup to decide whether to expose the write tool (`src/index.ts:18`) -2. **`src/local-detector.ts`** - Local by Flywheel auto-detection - - Scans running processes for mysqld with Local paths - - Extracts socket path from process arguments - - Parses my.cnf for port configuration - - Returns LocalSiteInfo with connection details +2. **`src/local-detector.ts`** - Local by Flywheel auto-detection and site selection + - `selectSite()` implements the 5-level priority order (`src/local-detector.ts:315`) + - Loads/parses Local's `sites.json` for site metadata (name, path, domain, port) + - Scans running processes for mysqld with Local paths; filesystem fallback by socket mtime + - `listAvailableSites()` enumerates configured sites and running status (`src/local-detector.ts:413`) 3. **`src/mysql-client.ts`** - MySQL connection management - Wraps mysql2/promise for async operations - - Validates queries are read-only (SELECT/SHOW/DESCRIBE/EXPLAIN) + - `executeReadOnlyQuery()` validates read-only statements (`src/mysql-client.ts:46`) + - `executeWriteQuery()` validates gated write statements (`src/mysql-client.ts:76`) - Blocks multi-statement queries for security - Supports parameter binding with `?` placeholders 4. **`src/types.ts`** - TypeScript interfaces - - LocalSiteInfo: socket path, port, site ID, config path - - MySQLConnectionConfig: connection parameters + - `LocalSiteInfo`: socket path, port, site ID, config path + - `MySQLConnectionConfig`: connection parameters + - `WriteResult`: affectedRows, optional insertId/changedRows + - `LocalSiteEntry` / `LocalSitesConfig` / `SiteSelectionResult`: site metadata + selection context ### MCP Tools +The server registers **4 tools by default**. A **5th tool, `mysql_write`, is registered only when `MYSQL_ALLOW_WRITES=true`** (`src/index.ts:134`). + **mysql_query**: - Executes read-only SQL queries - Parameters: `sql` (string), optional `params` (array) -- Enforces single statement, read-only operations +- Enforces single statement, read-only operations (SELECT/SHOW/DESCRIBE/EXPLAIN) **mysql_schema**: - Inspects database structure via INFORMATION_SCHEMA -- No params: lists all tables with row counts +- No params: lists all tables with engine/row counts/sizes - With `table` param: shows columns and indexes +**mysql_current_site**: +- Returns the currently connected Local site (name, ID, path, domain, socket, port) and the `selectionMethod` used + +**mysql_list_sites**: +- Lists all configured Local sites and whether each is running + +**mysql_write** (only when `MYSQL_ALLOW_WRITES=true`): +- Executes INSERT/UPDATE/DELETE; schema operations (CREATE/DROP/ALTER/TRUNCATE) are blocked +- UPDATE and DELETE require parameterized WHERE clauses (non-empty `params`) +- Subqueries (SELECT) are rejected inside write statements + ### Security Model -- **Read-only enforcement**: Regex validates queries start with SELECT/SHOW/DESCRIBE/EXPLAIN -- **Single statement only**: Semicolons blocked except at end -- **Parameter binding**: Uses mysql2's parameterized queries to prevent injection -- **Local-only design**: Prioritizes Unix socket connections +- **Read path**: regex validates the first token is one of SELECT/SHOW/DESCRIBE/EXPLAIN (`src/mysql-client.ts:46`) +- **Gated write path**: writes are off unless `MYSQL_ALLOW_WRITES=true`. When enabled, only INSERT/UPDATE/DELETE are allowed; UPDATE/DELETE require parameterized WHERE clauses; subqueries are blocked (`src/mysql-client.ts:76`) +- **Single statement only**: more than one non-empty statement (split on `;`) is rejected +- **Parameter binding**: uses mysql2's parameterized queries to prevent injection +- **Local-only design**: prioritizes Unix socket connections +- **Identifier safety**: table names used in schema introspection are restricted to `[A-Za-z0-9_]` ## Local by Flywheel Integration -The server solves Local's dynamic path problem where site IDs (like `lx97vbzE7`) change on restart: +The server solves Local's dynamic path problem where site IDs (like `lx97vbzE7`) change on restart. + +### Site selection priority (`selectSite()`) -1. Process detection finds running mysqld with `--defaults-file` argument -2. Extracts site directory from config path (e.g., `/Users/.../Local/run/lx97vbzE7/`) -3. Constructs socket path: `{siteDir}/mysql/mysqld.sock` -4. Falls back to environment variables if Local isn't detected +1. `SITE_ID` env var — direct site ID lookup in `sites.json` +2. `SITE_NAME` env var — case-insensitive site-name lookup +3. CWD detection — current working directory matched against a site's path +4. Process detection — running mysqld with a Local `--defaults-file` argument +5. Filesystem fallback — newest running socket under Local's run directory + +If none of the above resolve, the server falls back to plain environment variables. Typical Local paths: - Config: `~/Library/Application Support/Local/run/{siteId}/conf/mysql/my.cnf` - Socket: `~/Library/Application Support/Local/run/{siteId}/mysql/mysqld.sock` +- Sites metadata: `~/Library/Application Support/Local/sites.json` - Database name: `local` - Credentials: `root`/`root` -## Environment Variables (Fallback) +## Environment Variables -When Local isn't detected or for custom setups: +### Connection fallback (used when Local isn't detected or for custom setups) - `MYSQL_HOST` - Default: localhost - `MYSQL_PORT` - Default: 3306 - `MYSQL_USER` - Default: root @@ -88,6 +114,18 @@ When Local isn't detected or for custom setups: - `MYSQL_DB` - Default: local - `MYSQL_SOCKET_PATH` - Unix socket path (optional) +### Behavior +- `MYSQL_ALLOW_WRITES` - Set to `true` to expose the `mysql_write` tool (default: writes disabled) +- `DEBUG` - Include `mcp-local-wp` to enable debug logging + +### Site selection / Local paths +- `SITE_ID` - Explicit Local site ID (highest priority) +- `SITE_NAME` - Local site name for lookup +- `LOCAL_RUN_DIR` - Override Local's run directory +- `LOCAL_SITES_JSON` - Override path to Local's `sites.json` + +On Windows, default Local paths resolve via `%LOCALAPPDATA%` / `%APPDATA%`. + ## Development Workflow 1. Ensure Local by Flywheel is running with an active site @@ -107,4 +145,4 @@ Common WordPress tables and their purposes: - `wp_terms`, `wp_term_taxonomy`, `wp_term_relationships` - Taxonomies (categories, tags) - `wp_comments`, `wp_commentmeta` - Comments and metadata -Note: Table prefix may vary (default is `wp_` but could be custom). \ No newline at end of file +Note: Table prefix may vary (default is `wp_` but could be custom). From a58bda6b2320a1ce5f254c77e9c4b5285bf41264 Mon Sep 17 00:00:00 2001 From: Jack Arturo Date: Sat, 6 Jun 2026 17:22:17 +0200 Subject: [PATCH 3/3] =?UTF-8?q?docs:=20address=20Copilot=20review=20?= =?UTF-8?q?=E2=80=94=20correct=20write-guard=20guarantee=20and=20allowed?= =?UTF-8?q?=20read=20tokens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- AGENTS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 5afcf3f..cab9be2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,7 +56,7 @@ The server registers **4 tools by default**. A **5th tool, `mysql_write`, is reg **mysql_query**: - Executes read-only SQL queries - Parameters: `sql` (string), optional `params` (array) -- Enforces single statement, read-only operations (SELECT/SHOW/DESCRIBE/EXPLAIN) +- Enforces single statement, read-only operations (SELECT/SHOW/DESCRIBE/DESC/EXPLAIN) **mysql_schema**: - Inspects database structure via INFORMATION_SCHEMA @@ -71,13 +71,13 @@ The server registers **4 tools by default**. A **5th tool, `mysql_write`, is reg **mysql_write** (only when `MYSQL_ALLOW_WRITES=true`): - Executes INSERT/UPDATE/DELETE; schema operations (CREATE/DROP/ALTER/TRUNCATE) are blocked -- UPDATE and DELETE require parameterized WHERE clauses (non-empty `params`) +- UPDATE and DELETE require a non-empty `params` array (at least one bound parameter); the guard does not verify that a WHERE clause is present - Subqueries (SELECT) are rejected inside write statements ### Security Model -- **Read path**: regex validates the first token is one of SELECT/SHOW/DESCRIBE/EXPLAIN (`src/mysql-client.ts:46`) -- **Gated write path**: writes are off unless `MYSQL_ALLOW_WRITES=true`. When enabled, only INSERT/UPDATE/DELETE are allowed; UPDATE/DELETE require parameterized WHERE clauses; subqueries are blocked (`src/mysql-client.ts:76`) +- **Read path**: regex validates the first token is one of SELECT/SHOW/DESCRIBE/DESC/EXPLAIN (`src/mysql-client.ts:46`) +- **Gated write path**: writes are off unless `MYSQL_ALLOW_WRITES=true`. When enabled, only INSERT/UPDATE/DELETE are allowed; UPDATE/DELETE require a non-empty `params` array (at least one bound parameter; a WHERE clause is not verified); subqueries are blocked (`src/mysql-client.ts:76`) - **Single statement only**: more than one non-empty statement (split on `;`) is rejected - **Parameter binding**: uses mysql2's parameterized queries to prevent injection - **Local-only design**: prioritizes Unix socket connections