Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@ coverage/
# Manual test results (generated)
test-results/

# Local agent/review artifacts (generated)
.context/

# Override global gitignore for CI/CD
!.github/
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@
All notable changes to mainwp-mcp are documented here.
Format follows [Keep a Changelog](https://keepachangelog.com/).

## [1.0.0-beta.3] - 2026-06-10

### Added

Ability namespaces are now configurable. The server used to surface only `mainwp/` abilities; the new `abilityNamespaces` setting (or the `MAINWP_ABILITY_NAMESPACES` environment variable) lets you expose abilities that third-party MainWP extensions register through the WordPress Abilities API. The first namespace in the list is the primary one and its tools keep their plain names, so `mainwp/list-sites-v1` still appears as `list_sites_v1`. Abilities from other namespaces carry a prefix: `acme/do-thing-v1` becomes `acme__do_thing_v1`. Hyphenated namespaces such as `acme-corp` work end to end, including execution. Built-in resources and prompt completions depend on `mainwp/get-site-v1` and `mainwp/list-sites-v1`, so keep `mainwp` in the list when adding others.

The server now warns at startup when `mainwp` is missing from `abilityNamespaces`, and after fetching abilities when the namespace filter matches none of them. A misconfigured allowlist used to boot a server that advertised zero tools with nothing in the logs explaining why. An empty upstream gets its own message so a dead API is distinguishable from a filter mismatch.

### Removed

The `toolNameToAbilityName` export is gone. Tool names stopped being uniquely decodable once multiple namespaces came into play, so reverse lookup now goes through an index built when abilities are fetched. Anything importing that function from this package needs to switch to `getAbilityByToolName`.

### Fixed

A failed ability refresh can no longer leave the tool index half built. The cache swaps in atomically, and if the fetch hits duplicate tool names the previous index keeps serving while the error is reported. Abilities with malformed names, an extra slash for example, are dropped at fetch time with a logged warning rather than surfacing as invalid MCP tool names.

Failed tool calls now set `isError: true` on the MCP result. The error JSON was already in the response content but the flag was missing, so clients that branch on it treated failures as successes. Unknown tools, input validation failures, ability execution failures, cancellations, safe mode blocks, and confirmation rejections all carry the flag; confirmation previews and idempotent no-change responses stay ordinary results. The JSON error bodies are unchanged, so anything parsing them keeps working.

## [1.0.0-beta.2] - 2026-03-26

### Changed
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ For Windsurf and other hosts, use the same JSON configuration pattern shown abov
| `MAINWP_MAX_RETRIES` | No | `2` | Total retry attempts including initial request |
| `MAINWP_RETRY_BASE_DELAY` | No | `1000` | Base delay between retries in milliseconds |
| `MAINWP_RETRY_MAX_DELAY` | No | `2000` | Maximum delay between retries in milliseconds |
| `MAINWP_ABILITY_NAMESPACES` | No | `mainwp` | Comma-separated ability namespace allowlist |

> **⚠️ Security Warning: SSL Verification**
>
Expand Down Expand Up @@ -454,7 +455,7 @@ Configuration loads from `./settings.json` or `~/.config/mainwp-mcp/settings.jso

## Optimizing Token Usage

You have access to 64 tools, which consume approximately 28,000 tokens in your AI's context window. Two settings help reduce this footprint.
You have access to around 60 tools (the exact count varies by Dashboard version), which consume approximately 28,000 tokens in your AI's context window. Two settings help reduce this footprint.

### Compact Schema Mode

Expand All @@ -476,7 +477,7 @@ Compact mode truncates descriptions to 60 characters and removes examples while

### Limiting Exposed Tools

You can expose only the tools you need. These configurations cover common scenarios:
You can expose only the tools you need. These configurations cover common scenarios. Tool names from non-primary namespaces (added via `abilityNamespaces`) use the `{namespace}__{tool}` form — e.g. `acme__do_thing_v1` — so reference them that way in `allowedTools` / `blockedTools`.

**Read-only monitoring** (17 tools, ~73% reduction):

Expand Down Expand Up @@ -667,9 +668,9 @@ See the [Security Guide](docs/security.md#confirmation-guardrails) for more deta

## Tools

Over 60 tools organized by category. Each tool shows parameters with type, requirement, and description.
Around 60 tools organized by category (the exact count varies by Dashboard version). Each tool shows parameters with type, requirement, and description.

> **Note:** Tool names omit the `mainwp` namespace. The ability `mainwp/list-sites-v1` becomes `list_sites_v1`.
> **Note:** Tool names omit the primary namespace (default `mainwp`), so `mainwp/list-sites-v1` becomes `list_sites_v1`. If you add other namespaces via `abilityNamespaces`, abilities in those namespaces are exposed as `{namespace}__{tool}` (e.g. `acme/do-thing-v1` → `acme__do_thing_v1`). Keep `mainwp` in `abilityNamespaces` — the `mainwp://site/{id}` resource and the site ID prompt completions call `mainwp/get-site-v1` and `mainwp/list-sites-v1` directly and start returning errors or empty results if those abilities are filtered out.

<details>
<summary>Sites</summary>
Expand Down
30 changes: 29 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ The server accepts configuration through environment variables and a configurati
| `MAINWP_MAX_RETRIES` | No | `2` | Total retry attempts including initial request |
| `MAINWP_RETRY_BASE_DELAY` | No | `1000` | Base delay between retries in milliseconds |
| `MAINWP_RETRY_MAX_DELAY` | No | `2000` | Maximum delay between retries in milliseconds |
| `MAINWP_ABILITY_NAMESPACES` | No | `mainwp` | Comma-separated namespace allowlist (see below) |

## Configuration File

Expand Down Expand Up @@ -69,16 +70,43 @@ Create a `settings.json` file in one of these locations (checked in order):
| `maxRetries` | `MAINWP_MAX_RETRIES` | number |
| `retryBaseDelay` | `MAINWP_RETRY_BASE_DELAY` | number |
| `retryMaxDelay` | `MAINWP_RETRY_MAX_DELAY` | number |
| `abilityNamespaces` | `MAINWP_ABILITY_NAMESPACES` | string[] |

A JSON schema is available at `settings.schema.json` for IDE autocompletion.

### Ability Namespaces

By default the server only surfaces abilities in the `mainwp/` namespace. Set `abilityNamespaces` to expose abilities from third-party MainWP extensions that register their own abilities (via the WordPress Abilities API). The first entry in the list is the **primary** namespace — abilities in it get unprefixed tool names. Other namespaces get a `{namespace}__{tool}` prefix so MCP tool names stay collision-free.

```json
{
"abilityNamespaces": ["mainwp", "acme"]
}
```

```bash
MAINWP_ABILITY_NAMESPACES="mainwp,acme"
```

Examples with `abilityNamespaces: ["mainwp", "acme"]`:

| Ability name | MCP tool name |
| ----------------------- | ------------------------ |
| `mainwp/list-sites-v1` | `list_sites_v1` |
| `acme/do-thing-v1` | `acme__do_thing_v1` |
| `acme-corp/do-thing-v1` | `acme_corp__do_thing_v1` |

When combining with `allowedTools` / `blockedTools`, use the prefixed form for non-primary namespaces (e.g. `"allowedTools": ["list_sites_v1", "acme__do_thing_v1"]`).

**Keep `mainwp` in the list.** The `mainwp://site/{id}` resource calls the `mainwp/get-site-v1` ability directly, and prompt argument completions for site IDs call `mainwp/list-sites-v1`. Removing `mainwp` from `abilityNamespaces` filters those abilities out: `mainwp://site/{id}` returns an error payload, site ID completions come back empty, and `mainwp://help` lists no MainWP tools. If you want to expose third-party namespaces, add them alongside `mainwp` rather than replacing it.

---

## Tool Filtering

Control which tools are exposed to AI assistants. Useful for limiting access to read-only operations, hiding destructive tools in production, or reducing context size for the AI.

The server exposes 64 tools by default, consuming approximately 28,000 tokens. Tool filtering can reduce this significantly while limiting the AI to specific capabilities.
The server exposes around 60 tools by default (the exact count varies by Dashboard version), consuming approximately 28,000 tokens. Tool filtering can reduce this significantly while limiting the AI to specific capabilities.

### Whitelist Mode

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mainwp/mcp",
"version": "1.0.0-beta.2",
"version": "1.0.0-beta.3",
"description": "MCP Server for MainWP Dashboard - Exposes MainWP Abilities API as MCP tools",
"type": "module",
"main": "dist/index.js",
Expand Down
1 change: 1 addition & 0 deletions settings.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"blockedTools": ["delete_site_v1"],
"schemaVerbosity": "standard",
"responseFormat": "compact",
"abilityNamespaces": ["mainwp"],

"retryEnabled": true,
"maxRetries": 2,
Expand Down
11 changes: 11 additions & 0 deletions settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@
"default": "compact",
"description": "Response JSON format. 'compact' omits whitespace to reduce token usage. 'pretty' uses 2-space indentation for human-readable output. Default is 'compact'."
},
"abilityNamespaces": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
},
"minItems": 1,
"uniqueItems": true,
"default": ["mainwp"],
"description": "Allowed ability namespaces. Abilities whose name's namespace prefix is in this list are surfaced as MCP tools. The first entry is the primary namespace — its abilities get unprefixed tool names (e.g. mainwp/list-sites-v1 -> list_sites_v1). Abilities in other namespaces get a {ns}__ prefix (e.g. acme/do-thing-v1 -> acme__do_thing_v1; hyphens in the namespace are converted to underscores so the tool name stays within [a-z0-9_]). Category slugs are matched by `{namespace}-` prefix, so an extension's categories are included when its namespace is listed here. Defaults to [\"mainwp\"]; add third-party MainWP extension namespaces here to expose their abilities."
},
"retryEnabled": {
"type": "boolean",
"default": true,
Expand Down
Loading
Loading