@wickedevolutions/abilities-mcp 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,81 @@
1
+ # Changelog
2
+
3
+ All notable changes to Abilities MCP are documented here.
4
+
5
+ ## [1.3.1] - 2026-03-19
6
+
7
+ ### Fixed
8
+ - Convert execute-ability error payloads to `isError` format — fold `input_schema` into error text for AI self-correction (#2)
9
+ - Convert JSON-RPC error objects to `isError` format for client visibility (#4)
10
+
11
+ ### Changed
12
+ - Remove protocol version rewriting from both transports — Adapter now handles MCP version negotiation natively
13
+
14
+ ## [1.3.0] - 2026-03-11
15
+
16
+ ### Added
17
+ - Schema validation warnings in sanitizer — logs when WordPress responses contain non-standard fields
18
+ - Architecture documentation (`docs/architecture.md`)
19
+
20
+ ### Changed
21
+ - Renamed from WP Abilities MCP to **Abilities MCP**
22
+ - Package name: `@wicked-evolutions/abilities-mcp`
23
+ - Entry point: `abilities-mcp.js`
24
+ - GPL-2.0 compliance headers on all source files
25
+ - Repo cleanup: removed ROADMAP (GitHub project board is the authority), fixed stale references
26
+
27
+ ## [1.2.0] - 2026-03-09
28
+
29
+ ### Added
30
+ - JSON-RPC batch coalescing in `HttpTransport._drainQueue()` — 10ms window accumulates concurrent messages and dispatches as a single JSON-RPC batch POST. Reduces N round-trips to 1 for concurrent multi-agent workloads. Sequential single-agent sessions are unaffected.
31
+
32
+ ### Fixed
33
+ - Fix integer overflow in synthetic handshake IDs — replace `Date.now()` with incrementing counter; 13-digit ms timestamps exceed 32-bit int max, causing `TypeError` in PHP `strict_types=1` environments
34
+ - Fix missing cookie support in HTTP transport — add per-host cookie jar; parse `Set-Cookie` response headers and send `Cookie` on subsequent requests so PHP native sessions aren't dropped between requests
35
+ - Fix incomplete session recovery — extend re-handshake trigger to include HTTP 401/403 when an active session exists; some WordPress configs return these for stale session tokens rather than 404/410
36
+
37
+ ## [1.1.0] - 2026-03-08
38
+
39
+ ### Added
40
+ - Permission metadata passthrough — sanitizer preserves `permission` and `enabled` annotation fields from MCP Adapter
41
+ - `[DISABLED]` label injection — tools with `enabled: false` get description suffix showing required permission level
42
+ - Annotation whitelisting — keeps MCP-compliant fields (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`, `title`, `permission`, `enabled`), strips non-standard fields
43
+ - Automated test suite using `node:test` (18 sanitizer tests)
44
+
45
+ ### Fixed
46
+ - Fix multisite blog_id not switching for subsite queries — build per-subsite endpoint URLs so WordPress boots into the correct blog context natively (#3)
47
+ - Fix tool registration failure in Claude Code — strip non-standard annotation fields while preserving MCP-compliant ones (#5)
48
+ - Fix HTTP multisite session loss — reuse existing transport for same-endpoint subsites instead of creating competing connections (#1, PR #2)
49
+
50
+ ## [1.0.0] - 2026-02-26
51
+
52
+ ### Added
53
+ - Unified multi-site MCP bridge
54
+ - HTTP transport with session management (Mcp-Session-Id/Token headers)
55
+ - SSH transport with auto-reconnect, exponential backoff, and healthcheck pings
56
+ - Multi-site routing with `site` parameter injection on all tools
57
+ - Lazy connections — non-default sites connect on first tool call
58
+ - MCP handshake replay for mid-session connections
59
+ - WordPress multisite support via dot notation
60
+ - `wp-sites.json` configuration with config file search order
61
+ - `passwordCommand` and `passwordEnv` for secure credential storage
62
+ - Claude Desktop registration (`--register` flag)
63
+ - Debug logging (`--debug` flag)
64
+ - Zero dependencies — Node.js built-in modules only
65
+ - `wp_bridge_health` — check connectivity status of all configured sites
66
+ - `wp_browse_tools` / `wp_load_tools` — category-based lazy tool loading
67
+ - GitHub infrastructure: issue templates, PR template, Actions workflow
68
+
69
+ ### Security
70
+ - Security audit — 9 findings, all fixed
71
+ - Session token forwarding (Mcp-Session-Token header)
72
+ - Schema sanitization (strips non-standard fields from tool definitions)
73
+
74
+ ### Known Issues
75
+ - Session lock contention with concurrent bridge instances (#4) — server-side MySQL GET_LOCK fix deployed, bridge-side auto-recovery adds ~200ms latency
76
+
77
+ ---
78
+
79
+ ## License
80
+
81
+ GPL-2.0-or-later
package/LICENSE ADDED
@@ -0,0 +1,12 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ For the full license text, see: https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html
10
+
11
+ This software is distributed under the terms of the GNU General Public License
12
+ version 2 or later (GPL-2.0-or-later).
package/README.md ADDED
@@ -0,0 +1,321 @@
1
+ # Abilities MCP
2
+
3
+ > One MCP to Rule Your WordPress World.
4
+
5
+ Open-source MCP bridge that connects any AI client to your WordPress sites through the [WordPress Abilities API](https://developer.wordpress.org/reference/functions/wp_register_ability/). Single STDIO server, multi-site routing, zero dependencies.
6
+
7
+ ## Features
8
+
9
+ - **Multi-site routing** — Single MCP server serves all your WordPress sites
10
+ - **Site parameter injection** — LLM sees a `site` enum on every tool, defaults to your primary site
11
+ - **Lazy connections** — Sites connect on first use, not at startup
12
+ - **HTTP transport** — Application Passwords with MCP session management
13
+ - **WordPress multisite** — Subdomain/subdirectory multisites via dot notation (`site.blog`)
14
+ - **Auto-reconnect** — Exponential backoff, healthcheck pings, session recovery
15
+ - **Zero dependencies** — Node.js built-in modules only
16
+
17
+ ## What You Can Do
18
+
19
+ The abilities available to your AI agent depend on which ability plugins you install. With [Abilities for AI](https://wickedevolutions.com/abilities-for-ai) installed, your agent gets access to:
20
+
21
+ **Content & Publishing** — content, blocks, patterns, media, menus, taxonomies, comments, revisions
22
+ **Site Management** — plugins, themes, settings, users, site health, cache, cron, rewrite rules
23
+ **Infrastructure** — filesystem, meta, REST discovery, knowledge layer
24
+ **Third-party integrations** — auto-detected modules for supported plugins (Astra, Spectra, SureCart, Presto Player, and more)
25
+
26
+ Additional ability plugins extend coverage further. For example, [Abilities for Fluent Plugins](https://github.com/Wicked-Evolutions/abilities-for-fluent-plugins) adds modules for FluentCRM, Fluent Community, Fluent Forms, FluentBooking, Fluent Support, Fluent Boards, FluentSMTP, FluentAuth, Fluent Snippets, Fluent Messaging, FluentCart, and FluentAffiliate.
27
+
28
+ Every ability enforces `current_user_can()` at execution time — your WordPress role is the security boundary.
29
+
30
+ > **Sign up for the Abilities for AI alpha release:** https://wickedevolutions.com/abilities-for-ai
31
+
32
+ ## Quick Start
33
+
34
+ ### 1. Set up WordPress
35
+
36
+ Create a dedicated WordPress user for AI access and generate an Application Password.
37
+
38
+ **In WordPress Admin → Users → Add New:**
39
+
40
+ | Field | Value |
41
+ |-------|-------|
42
+ | Username | `mcp-agent` (or any name you prefer) |
43
+ | Role | **Administrator** for full access, or **Editor** for content-only access |
44
+
45
+ **Then generate an Application Password:**
46
+
47
+ Go to **Users → Edit (your mcp-agent user) → Application Passwords**, enter a name (e.g. "MCP Bridge"), and click **Add New Application Password**. Copy the generated password — it's shown only once.
48
+
49
+ #### Choosing a role
50
+
51
+ | Role | Access | Use case |
52
+ |------|--------|----------|
53
+ | **Administrator** | All modules — content, plugins, themes, settings, users, cache, cron, filesystem, and more | Full site management |
54
+ | **Editor** | Content, Blocks, Taxonomies, Patterns, Meta, Media | Content publishing workflows — safe for teams where AI should write but not configure |
55
+
56
+ > **Tip:** Start with Editor. Upgrade to Administrator when you need infrastructure abilities like plugin management, theme switching, or settings changes.
57
+
58
+ #### Required plugins
59
+
60
+ Install both on your WordPress site:
61
+
62
+ 1. **[Abilities for AI](https://wickedevolutions.com/abilities-for-ai)** — registers WordPress abilities across content, site management, infrastructure, and third-party integration modules
63
+ 2. **[Abilities MCP Adapter](https://github.com/Wicked-Evolutions/abilities-mcp-adapter)** — exposes abilities as MCP tools via REST API
64
+
65
+ ### 2. Configure your sites
66
+
67
+ Copy the example config and edit:
68
+
69
+ ```bash
70
+ cp wp-sites.example.json wp-sites.json
71
+ ```
72
+
73
+ Edit `wp-sites.json` with your site details.
74
+
75
+ ### 3. Add to your MCP client
76
+
77
+ Works with any MCP-compatible client — Claude Code, Claude Desktop, Gemini CLI, Cursor, Windsurf, VS Code, and any other IDE or AI tool that supports the Model Context Protocol.
78
+
79
+ Add the server to your client's MCP config (usually `.mcp.json`, `settings.json`, or equivalent):
80
+
81
+ ```json
82
+ {
83
+ "mcpServers": {
84
+ "wordpress": {
85
+ "command": "node",
86
+ "args": ["/path/to/abilities-mcp/abilities-mcp.js"]
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ | Client | Config location |
93
+ |--------|----------------|
94
+ | Claude Code | `.mcp.json` in project root or `~/.claude/.mcp.json` |
95
+ | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (or use `--register`) |
96
+ | Gemini CLI | `~/.gemini/settings.json` |
97
+ | Cursor | `.cursor/mcp.json` in project root |
98
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` |
99
+ | VS Code (Copilot) | `.vscode/mcp.json` in project root |
100
+
101
+ For Claude Desktop, you can also auto-register:
102
+
103
+ ```bash
104
+ node abilities-mcp.js --register
105
+ ```
106
+
107
+ ## Configuration
108
+
109
+ ### `wp-sites.json`
110
+
111
+ ```json
112
+ {
113
+ "defaultSite": "mysite",
114
+ "sites": {
115
+ "mysite": {
116
+ "label": "My WordPress Site",
117
+ "url": "https://example.com",
118
+ "transport": "http",
119
+ "http": {
120
+ "endpoint": "https://example.com/wp-json/mcp/mcp-adapter-default-server",
121
+ "username": "mcp-agent",
122
+ "passwordCommand": "security find-generic-password -a mcp-agent -s example.com -w"
123
+ }
124
+ }
125
+ }
126
+ }
127
+ ```
128
+
129
+ ### Config file search order
130
+
131
+ 1. `--config=/path/to/wp-sites.json` (explicit)
132
+ 2. Same directory as `abilities-mcp.js`
133
+ 3. `~/.abilities-mcp/wp-sites.json`
134
+
135
+ ### WordPress Multisite
136
+
137
+ For WordPress multisites, add a `multisite` object mapping subsite keys to their URLs:
138
+
139
+ ```json
140
+ {
141
+ "network": {
142
+ "label": "My Network",
143
+ "transport": "http",
144
+ "http": {
145
+ "endpoint": "https://example.com/wp-json/mcp/mcp-adapter-default-server",
146
+ "username": "mcp-agent",
147
+ "passwordCommand": "security find-generic-password -a mcp-agent -s example.com -w"
148
+ },
149
+ "multisite": {
150
+ "main": "https://example.com/",
151
+ "blog": "https://blog.example.com/",
152
+ "shop": "https://shop.example.com/"
153
+ }
154
+ }
155
+ }
156
+ ```
157
+
158
+ Use dot notation to target subsites: `"site": "network.blog"`
159
+
160
+ ### Secure password storage
161
+
162
+ Three options for providing Application Passwords, from most to least secure:
163
+
164
+ #### `passwordCommand` (recommended)
165
+
166
+ Runs a shell command at startup and uses stdout as the password. Works with any OS keychain or secrets manager:
167
+
168
+ ```json
169
+ {
170
+ "http": {
171
+ "endpoint": "https://example.com/wp-json/mcp/mcp-adapter-default-server",
172
+ "username": "mcp-agent",
173
+ "passwordCommand": "security find-generic-password -a mcp-agent -s example.com -w"
174
+ }
175
+ }
176
+ ```
177
+
178
+ **macOS Keychain** — store the password first, then reference it:
179
+
180
+ ```bash
181
+ # Store (one-time)
182
+ security add-generic-password -a mcp-agent -s example.com -w 'YOUR_APP_PASSWORD'
183
+
184
+ # The passwordCommand retrieves it at runtime
185
+ "passwordCommand": "security find-generic-password -a mcp-agent -s example.com -w"
186
+ ```
187
+
188
+ **Linux (secret-tool / GNOME Keyring):**
189
+
190
+ ```bash
191
+ # Store
192
+ secret-tool store --label="WP MCP" service example.com user mcp-agent <<< 'YOUR_APP_PASSWORD'
193
+
194
+ # Config
195
+ "passwordCommand": "secret-tool lookup service example.com user mcp-agent"
196
+ ```
197
+
198
+ **1Password CLI:**
199
+
200
+ ```bash
201
+ "passwordCommand": "op read 'op://Vault/WordPress MCP/password'"
202
+ ```
203
+
204
+ #### `passwordEnv`
205
+
206
+ Reads the password from an environment variable. Useful in CI/CD, Docker, or when you set secrets via `.env` files:
207
+
208
+ ```json
209
+ {
210
+ "http": {
211
+ "endpoint": "https://example.com/wp-json/mcp/mcp-adapter-default-server",
212
+ "username": "mcp-agent",
213
+ "passwordEnv": "WP_MCP_PASSWORD"
214
+ }
215
+ }
216
+ ```
217
+
218
+ Set the variable before starting the bridge:
219
+
220
+ ```bash
221
+ # Shell export
222
+ export WP_MCP_PASSWORD="xxxx xxxx xxxx xxxx xxxx xxxx"
223
+
224
+ # Or in a .env file loaded by your shell/Docker
225
+ WP_MCP_PASSWORD=xxxx xxxx xxxx xxxx xxxx xxxx
226
+ ```
227
+
228
+ The bridge reads `process.env.WP_MCP_PASSWORD` at connection time. If the variable is not set, it throws an error immediately.
229
+
230
+ #### `password` (not recommended)
231
+
232
+ Plaintext password directly in the config. Avoid this — config files end up in repos, backups, and logs:
233
+
234
+ ```json
235
+ {
236
+ "http": {
237
+ "password": "xxxx xxxx xxxx xxxx xxxx xxxx"
238
+ }
239
+ }
240
+ ```
241
+
242
+ #### Priority order
243
+
244
+ If multiple are set: `passwordEnv` → `passwordCommand` → `password`.
245
+
246
+ ## Bridge Tools
247
+
248
+ The bridge provides three built-in tools (not forwarded to WordPress):
249
+
250
+ | Tool | Description |
251
+ |------|-------------|
252
+ | `wp_bridge_health` | Check connectivity status of all configured WordPress sites |
253
+ | `wp_browse_tools` | List WordPress tool categories with counts (requires `toolFilter.enabled: true`) |
254
+ | `wp_load_tools` | Activate/deactivate tool categories for lazy loading |
255
+
256
+ ## Usage
257
+
258
+ ### Multi-site mode
259
+
260
+ When multiple sites are configured, every tool gets an optional `site` parameter:
261
+
262
+ ```json
263
+ {
264
+ "name": "content-list",
265
+ "arguments": {
266
+ "site": "staging",
267
+ "post_type": "post"
268
+ }
269
+ }
270
+ ```
271
+
272
+ Omit `site` to use the default site.
273
+
274
+ ## CLI Options
275
+
276
+ | Flag | Description |
277
+ |------|-------------|
278
+ | `--config=<path>` | Path to wp-sites.json |
279
+ | `--server=<name>` | MCP adapter server name |
280
+ | `--debug` | Enable debug logging to `/tmp/abilities-mcp.log` |
281
+ | `--register` | Register in Claude Desktop config |
282
+ | `--name=<name>` | Server name for `--register` (default: `wordpress`) |
283
+
284
+ ## Architecture
285
+
286
+ ```mermaid
287
+ graph TD
288
+ Client[AI Client<br/>Claude Code · Gemini CLI · Cursor · any MCP client] -->|STDIO| Bridge[Abilities MCP]
289
+ Bridge -->|HTTP POST| SiteA[Site A]
290
+ Bridge -->|HTTP POST| SiteB[Site B]
291
+ Bridge -->|SSH + WP-CLI| SiteC[Site C]
292
+
293
+ subgraph "Each WordPress Site"
294
+ Adapter[Abilities MCP Adapter] --> AbilitiesAPI[WordPress Abilities API]
295
+ AbilitiesAPI --> Plugins[Ability Plugins]
296
+ end
297
+ ```
298
+
299
+ - One STDIO process handles all sites through a unified connection pool
300
+ - **HTTP transport** — Application Passwords with MCP session management, batch coalescing, auto-reconnect
301
+ - **SSH transport** — WP-CLI over SSH tunnel, healthcheck pings, handshake replay
302
+ - Lazy connections — non-default sites connect on first tool call
303
+ - Tool list comes from the default site with `site` enum injected
304
+ - Permission metadata (`permission`, `enabled`) flows through annotations to the LLM
305
+ - Error responses include `input_schema` for AI self-correction
306
+
307
+ See [docs/architecture.md](docs/architecture.md) for the full technical deep dive — transport comparison tables, session management, multi-site routing internals, and security model.
308
+
309
+ ## Known Limitations
310
+
311
+ - **Session lock contention** ([#4](https://github.com/Wicked-Evolutions/abilities-mcp/issues/4)) — Concurrent bridge instances targeting the same site can cause session loss. Use a single bridge process per site.
312
+
313
+ ## Requirements
314
+
315
+ - Node.js >= 18
316
+ - WordPress 6.9+ with [Abilities for AI](https://wickedevolutions.com/abilities-for-ai) and [Abilities MCP Adapter](https://github.com/Wicked-Evolutions/abilities-mcp-adapter) installed
317
+ - Application Passwords enabled (default in WordPress 5.6+)
318
+
319
+ ## License
320
+
321
+ GPL-2.0-or-later
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * abilities-mcp v1.0.0
4
+ *
5
+ * One MCP to Rule Your WordPress World.
6
+ *
7
+ * Unified multi-site MCP bridge for WordPress Abilities API. Replaces
8
+ * mcp-ssh-bridge and mcp-http-bridge with a single STDIO server that
9
+ * routes tool calls to any configured WordPress site via SSH or HTTP.
10
+ *
11
+ * Usage:
12
+ * node abilities-mcp.js (uses wp-sites.json)
13
+ * node abilities-mcp.js --config=/path/to/wp-sites.json (explicit config)
14
+ * node abilities-mcp.js --host=<ssh-host> --path=<wp-path> (legacy single-site)
15
+ * node abilities-mcp.js --register [--name=<name>] (Claude Desktop setup)
16
+ *
17
+ * Copyright (C) 2026 Influencentricity | Wicked Evolutions
18
+ * @package Wicked-Evolutions/abilities-mcp
19
+ * @version 1.0.0
20
+ * @license GPL-2.0-or-later
21
+ */
22
+
23
+ 'use strict';
24
+
25
+ const { createLogger } = require('./lib/logger');
26
+ const { loadConfig, buildSiteKeyEnum } = require('./lib/config');
27
+ const { ConnectionPool } = require('./lib/connection-pool');
28
+ const { ToolCatalog } = require('./lib/tool-catalog');
29
+ const { McpRouter } = require('./lib/router');
30
+ const { SshTransport } = require('./lib/transports/ssh-transport');
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // CLI argument parsing
34
+ // ---------------------------------------------------------------------------
35
+
36
+ const args = {};
37
+ process.argv.slice(2).forEach(arg => {
38
+ if (arg.startsWith('--')) {
39
+ const [key, ...rest] = arg.slice(2).split('=');
40
+ args[key] = rest.length ? rest.join('=') : true;
41
+ }
42
+ });
43
+
44
+ const debug = !!args.debug;
45
+ const register = !!args.register;
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Registration mode (--register)
49
+ // ---------------------------------------------------------------------------
50
+
51
+ if (register) {
52
+ const { registerClaudeDesktop } = require('./lib/register');
53
+ registerClaudeDesktop({ name: args.name || 'wordpress', configPath: args.config });
54
+ process.exit(0);
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Initialize
59
+ // ---------------------------------------------------------------------------
60
+
61
+ const log = createLogger(debug);
62
+ log('abilities-mcp v1.0.0 starting');
63
+
64
+ // Ensure SSH agent is available (macOS launchd discovery)
65
+ SshTransport.ensureSshAuthSock();
66
+
67
+ let config;
68
+ try {
69
+ config = loadConfig(args);
70
+ } catch (err) {
71
+ process.stderr.write(`abilities-mcp: ${err.message}\n`);
72
+ process.exit(1);
73
+ }
74
+
75
+ const isMultiSite = config._isMultiSite;
76
+ const siteKeys = buildSiteKeyEnum(config);
77
+ log(`Config loaded: ${siteKeys.length} site(s): ${siteKeys.join(', ')} (default: ${config.defaultSite})`);
78
+ log(`Multi-site mode: ${isMultiSite}`);
79
+
80
+ const pool = new ConnectionPool(config, log);
81
+ const catalog = new ToolCatalog(config, log);
82
+
83
+ if (catalog.isEnabled()) {
84
+ log('Tool filtering enabled');
85
+ } else {
86
+ log('Tool filtering disabled (no toolFilter in config or enabled: false)');
87
+ }
88
+
89
+ function sendToClient(data) {
90
+ process.stdout.write(data + '\n');
91
+ }
92
+
93
+ const router = new McpRouter({
94
+ config,
95
+ siteKeys,
96
+ isMultiSite,
97
+ pool,
98
+ catalog,
99
+ sendToClient,
100
+ log,
101
+ });
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // Client STDIO processing
105
+ // ---------------------------------------------------------------------------
106
+
107
+ let inputBuffer = '';
108
+
109
+ process.stdin.on('data', (chunk) => {
110
+ inputBuffer += chunk.toString();
111
+
112
+ let newlineIdx;
113
+ while ((newlineIdx = inputBuffer.indexOf('\n')) !== -1) {
114
+ const line = inputBuffer.slice(0, newlineIdx);
115
+ inputBuffer = inputBuffer.slice(newlineIdx + 1);
116
+ if (line.trim()) {
117
+ let msg;
118
+ try {
119
+ msg = JSON.parse(line.trim());
120
+ } catch (e) {
121
+ log(`Non-JSON from client (dropped): ${line.substring(0, 200)}`);
122
+ continue;
123
+ }
124
+ router.handleClientMessage(msg, line.trim());
125
+ }
126
+ }
127
+ });
128
+
129
+ process.stdin.on('end', () => {
130
+ log('Client stdin closed — shutting down');
131
+ shutdown();
132
+ });
133
+
134
+ // ---------------------------------------------------------------------------
135
+ // Startup — connect to default site
136
+ // ---------------------------------------------------------------------------
137
+
138
+ (async function main() {
139
+ try {
140
+ const transport = await pool.connectDefault((parsedMsg, rawLine) => {
141
+ router.handleTransportMessage(parsedMsg, rawLine);
142
+ });
143
+ router.setDefaultTransport(transport);
144
+ log(`Default transport connected: ${config.defaultSite}`);
145
+ router.drainEarlyQueue();
146
+ } catch (err) {
147
+ process.stderr.write(`abilities-mcp: Failed to connect to default site: ${err.message}\n`);
148
+ process.exit(1);
149
+ }
150
+ })();
151
+
152
+ // ---------------------------------------------------------------------------
153
+ // Signal handling
154
+ // ---------------------------------------------------------------------------
155
+
156
+ function shutdown() {
157
+ log('Shutting down');
158
+ pool.shutdownAll().then(() => {
159
+ process.exit(0);
160
+ }).catch(() => {
161
+ process.exit(1);
162
+ });
163
+ }
164
+
165
+ process.on('SIGTERM', shutdown);
166
+ process.on('SIGINT', shutdown);
167
+ process.on('unhandledRejection', (reason) => {
168
+ log(`Unhandled rejection: ${reason}`);
169
+ });
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Bridge tools — synthetic tools handled locally, never forwarded to WordPress.
5
+ *
6
+ * Copyright (C) 2026 Influencentricity | Wicked Evolutions
7
+ * @license GPL-2.0-or-later
8
+ */
9
+
10
+ const BRIDGE_TOOLS = [
11
+ {
12
+ name: 'wp_bridge_health',
13
+ description: 'Check connectivity status of all configured WordPress sites. Returns status (connected/reachable/unreachable) and latency for each site.',
14
+ inputSchema: {
15
+ type: 'object',
16
+ properties: {
17
+ site: {
18
+ type: 'string',
19
+ description: 'Optional: check only this site. Omit to check all configured sites.',
20
+ },
21
+ },
22
+ },
23
+ },
24
+ {
25
+ name: 'wp_browse_tools',
26
+ description: 'List available WordPress tool categories with tool counts. Shows which categories are currently loaded. Use wp_load_tools to activate categories.',
27
+ inputSchema: {
28
+ type: 'object',
29
+ properties: {},
30
+ },
31
+ },
32
+ {
33
+ name: 'wp_load_tools',
34
+ description: 'Activate WordPress tool categories to make their tools available. After loading, the tools list is automatically refreshed.',
35
+ inputSchema: {
36
+ type: 'object',
37
+ properties: {
38
+ categories: {
39
+ type: 'array',
40
+ items: { type: 'string' },
41
+ description: 'Category names to activate (e.g. ["fluent-crm", "content", "media"]). Use wp_browse_tools to see available categories.',
42
+ },
43
+ deactivate: {
44
+ type: 'array',
45
+ items: { type: 'string' },
46
+ description: 'Optional: category names to deactivate (unload from the tools list).',
47
+ },
48
+ },
49
+ required: ['categories'],
50
+ },
51
+ },
52
+ ];
53
+
54
+ const BRIDGE_TOOL_NAMES = new Set(BRIDGE_TOOLS.map(t => t.name));
55
+
56
+ function isBridgeTool(name) {
57
+ return BRIDGE_TOOL_NAMES.has(name);
58
+ }
59
+
60
+ function injectBridgeTools(msg) {
61
+ if (!msg.result || !Array.isArray(msg.result.tools)) return;
62
+ for (const tool of BRIDGE_TOOLS) {
63
+ msg.result.tools.push(tool);
64
+ }
65
+ }
66
+
67
+ module.exports = { BRIDGE_TOOLS, isBridgeTool, injectBridgeTools };