@wickedevolutions/abilities-mcp 1.3.1 → 1.5.3
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 +111 -0
- package/README.md +88 -17
- package/abilities-mcp.js +191 -114
- package/lib/auth/bridge-identity-provider.js +34 -0
- package/lib/auth/browser-launcher.js +67 -0
- package/lib/auth/config-migration.js +322 -0
- package/lib/auth/dcr-client.js +123 -0
- package/lib/auth/discovery-client.js +273 -0
- package/lib/auth/errors.js +114 -0
- package/lib/auth/events.js +55 -0
- package/lib/auth/fresh-each-time-identity.js +101 -0
- package/lib/auth/http-json.js +151 -0
- package/lib/auth/index.js +88 -0
- package/lib/auth/keychain-secret-store.js +265 -0
- package/lib/auth/loopback-server.js +249 -0
- package/lib/auth/memory-secret-store.js +0 -0
- package/lib/auth/oauth-client.js +357 -0
- package/lib/auth/pkce.js +93 -0
- package/lib/auth/schema-v2.js +110 -0
- package/lib/auth/secret-store.js +78 -0
- package/lib/auth/token-manager.js +378 -0
- package/lib/cli/commands/add-site.js +226 -0
- package/lib/cli/commands/force-downgrade.js +93 -0
- package/lib/cli/commands/list-sites.js +93 -0
- package/lib/cli/commands/reauth.js +108 -0
- package/lib/cli/commands/revoke.js +127 -0
- package/lib/cli/commands/self-check.js +158 -0
- package/lib/cli/commands/test.js +174 -0
- package/lib/cli/commands/upgrade-auth.js +259 -0
- package/lib/cli/config-store.js +328 -0
- package/lib/cli/context.js +102 -0
- package/lib/cli/errors.js +227 -0
- package/lib/cli/index.js +173 -0
- package/lib/cli/output.js +175 -0
- package/lib/cli/parse-args.js +80 -0
- package/lib/config-source-line.js +85 -0
- package/lib/config.js +282 -22
- package/lib/connection-pool.js +214 -11
- package/lib/router.js +29 -11
- package/lib/transports/oauth-http-transport.js +601 -0
- package/package.json +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,117 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Abilities MCP are documented here.
|
|
4
4
|
|
|
5
|
+
## [1.5.3] - 2026-05-04
|
|
6
|
+
|
|
7
|
+
**macOS hotfix: OAuth in Claude Desktop's `.mcpb` runtime now works.** Hotfix to v1.5.2's `.mcpb` operator UX on macOS. v1.5.2 shipped with keytar prebuilds bundled, but Claude Desktop's hardened-runtime host process on macOS rejects native modules with mismatched code-signing Team IDs — a system-level macOS protection that applies to every hardened app, not a Claude Desktop quirk. This blocked OAuth inside Claude Desktop's `.mcpb` runtime even though the bundle itself is structurally correct (loads cleanly via system Node from the extracted `.mcpb`). The hotfix adds a darwin-only shell-out to the macOS `security` CLI when keytar fails to load. Validated end-to-end on darwin-arm64 by an operator running the documented progression (install `.mcpb` → `upgrade-auth` → `add-site` → multi-site OAuth in the same Claude Desktop entry, read and write confirmed live on two production WordPress sites via OAuth bearer) before release.
|
|
8
|
+
|
|
9
|
+
Bridge-only release — no companion adapter or ai release this hotfix.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **`KeychainSecretStore` falls back to the macOS `security` CLI when keytar fails to load on darwin** (PR [#40](https://github.com/Wicked-Evolutions/abilities-mcp/pull/40), closes [#39](https://github.com/Wicked-Evolutions/abilities-mcp/issues/39)). When `require('keytar')` throws on darwin (the hardened-runtime Team ID mismatch), `_load()` sets `_fallbackMode = 'security-cli'` instead of throwing. `get` / `set` / `delete` then dispatch to `security find-generic-password -w` / `add-generic-password -U` / `delete-generic-password` via `child_process.execFile` (no shell — argv passes verbatim, no shell-injection surface). `findAll` returns `[]` in fallback mode (security CLI has no clean enumerate-by-service; the bridge runtime path doesn't depend on it — only the CLI subcommand `list-sites` does, which runs in system Node where keytar loads normally). Stderr matching `/could not be found/i` maps to keytar's null (get) / false (delete) return semantics; other stderr propagates as `SecretStoreError` code `security_cli_failed`. `isAvailable()` returns true in both keytar and fallback modes. **Linux/Windows behavior unchanged** — keytar load failures on those platforms still throw `keytar_unavailable` (no fallback engaged; the security CLI is darwin-only). Test seams (`requireKeytar`, `platform`, `exec` injection on the constructor) added for fallback-path unit testing without breaking the test runtime's real `require('keytar')`. New `test/auth/keychain-secret-store-darwin-fallback.test.js` covers keytar-success preservation, fallback engagement on darwin, "could not be found" mapping, error propagation, `isAvailable` in both modes, linux/win32 throw-not-fallback, and `findAll` in fallback mode. The first time the `.mcpb`-installed bridge accesses a keychain entry on darwin, macOS will prompt for keychain access — operator clicks "Always Allow" once per entry and the prompt persists thereafter. This is a macOS keychain ACL property; the same prompt would have appeared with keytar-native if it had loaded.
|
|
14
|
+
|
|
15
|
+
### Internal
|
|
16
|
+
|
|
17
|
+
- **Test count:** `265 → 275` (+10 in `keychain-secret-store-darwin-fallback.test.js`).
|
|
18
|
+
- **Bundle size unchanged** (~420 kB packed, ~1.3 MB unpacked — the four platform-specific keytar binaries dominate; the secret-store code change is small relative to that).
|
|
19
|
+
|
|
20
|
+
### Known unverified — research outstanding for a follow-up release
|
|
21
|
+
|
|
22
|
+
- **Linux:** whether keytar's libsecret native binding loads cleanly inside Linux Claude Desktop's `.mcpb` runtime is unverified. Likely works (Linux's runtime model differs from macOS — no Team ID matching), but no Linux Claude Desktop access during this hotfix to test empirically. Operators on Linux Claude Desktop should test and report.
|
|
23
|
+
- **Windows:** whether keytar's win32 native binding loads cleanly inside Windows Claude Desktop's hardened process is unverified. If it doesn't, a separate Windows-specific fix shape is needed (PowerShell credential cmdlets, or formally limiting Windows operators to the CLI install path). Tracked for a follow-up release.
|
|
24
|
+
|
|
25
|
+
## [1.5.2] - 2026-05-03
|
|
26
|
+
|
|
27
|
+
**OAuth flow now works inside `.mcpb`.** This release makes the documented `.mcpb` operator UX work end-to-end: install the extension from Claude Desktop with an Application Password, then run `abilities-mcp upgrade-auth <site>` from a terminal to migrate that single connection to OAuth in place, then `abilities-mcp add-site https://other.com` to add more sites — all surfacing through the same Claude Desktop "Abilities MCP" entry. Before this release, the keytar binary wasn't bundled with the `.mcpb` and the `.mcpb` install never persisted to `~/.abilities-mcp/wp-sites.json`, so the documented progression failed at the moment OAuth touched the keychain.
|
|
28
|
+
|
|
29
|
+
Bridge-only release — no companion adapter or ai release this sprint.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **`.mcpb` bundle now ships keytar prebuilds for darwin x64, darwin arm64, win32 x64, and linux x64** (PR [#35](https://github.com/Wicked-Evolutions/abilities-mcp/pull/35), closes [#33](https://github.com/Wicked-Evolutions/abilities-mcp/issues/33)). Without these, `KeychainSecretStore` failed at first request with `Cannot find module 'keytar'` even on the host platform — verified empirically against the v1.5.1 bundle. The pack pipeline moves from a single-line `mcpb pack` invocation to a staging-directory build (`scripts/pack-mcpb.js`) that fetches each platform's prebuild via `prebuild-install` and patches keytar's hardcoded single-slot loader (`var keytar = require('../build/Release/keytar.node')` in keytar 7.9.0) with a multi-platform-aware loader keyed on `process.platform`-`process.arch`. The patch is staging-only — `node_modules/keytar/` in the project tree is byte-identical pre-pack and post-pack, pinned by the new `scripts/verify-pack-isolation.js` (run via `npm run verify:pack-isolation`). Pre-patch substring assertion on the upstream loader fails loud if a future keytar bump changes the loader shape.
|
|
34
|
+
|
|
35
|
+
- **Bridge emits one operator-visible `Config source:` line on startup to stderr** (PR [#36](https://github.com/Wicked-Evolutions/abilities-mcp/pull/36), closes [#32](https://github.com/Wicked-Evolutions/abilities-mcp/issues/32)). Captured in Claude Desktop's per-server MCP log so the operator can tell at a glance which `loadConfig` source won. Names the source (`env-var` / `[explicit-config]` / `[script-adjacent]` / `[home-dir]` / `legacy-cli`), the file path (tildified) or hostname, the site count, and the per-site auth method. Sample output:
|
|
36
|
+
```
|
|
37
|
+
Config source: ABILITIES_MCP_URL env var (single-site basic auth: example.com as wp_user)
|
|
38
|
+
Config source: [home-dir] ~/.abilities-mcp/wp-sites.json (3 sites: helena oauth, wicked oauth, tnn apppassword)
|
|
39
|
+
```
|
|
40
|
+
No secrets — only IDs, methods, hostnames, paths, counts. Always-on, not gated by `--debug`.
|
|
41
|
+
|
|
42
|
+
- **`.mcpb` install seeds `~/.abilities-mcp/wp-sites.json` on first launch** (PR [#37](https://github.com/Wicked-Evolutions/abilities-mcp/pull/37), closes [#34](https://github.com/Wicked-Evolutions/abilities-mcp/issues/34)). When the env-var-mode bridge boots and the home-dir config doesn't exist, `seedFromEnvIfMissing` writes a v2 apppassword entry derived from the `ABILITIES_MCP_*` env vars before serving the first MCP request. `list-sites`, `upgrade-auth`, and `add-site` now operate on a single source of truth that already includes the `.mcpb`-installed site. The site-id is derived from the URL hostname, matching `add-site`'s `deriveSiteId`. Guards: pre-existing `wp-sites.json` is **never overwritten**; missing env vars / malformed URL / keytar unavailable → graceful no-op (bridge falls back to env-var-only mode); file-write failure → keychain entry rolled back so operators don't accumulate orphan secrets.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **keytar pinned `^7.9.0` → `~7.9.0`** (patch versions only) so the staging script's pre-patch loader-shape substring assertion has a stable target. CLI install behavior is unchanged — keytar stays in `optionalDependencies` (skips gracefully on platforms without prebuilds).
|
|
47
|
+
- **`_configSource` discriminant renamed `'env'` → `'env-var'`** to align with the documented set (`explicit-config`, `script-adjacent`, `home-dir`, `env-var`, `legacy-cli`). Internal field, prefixed with underscore; only one test was reading the prior value (updated).
|
|
48
|
+
|
|
49
|
+
### Internal
|
|
50
|
+
|
|
51
|
+
- **Bundle size:** `~115 kB → ~413 kB` packed / `~1.2 MB` unpacked. The four platform-specific keytar prebuilds (darwin-x64 ~83 kB, darwin-arm64 ~99 kB, win32-x64 ~707 kB, linux-x64 ~76 kB) are embedded for the `.mcpb` install path. CLI install paths are unaffected — keytar stays in `optionalDependencies` and is host-only via the operator's `npm install`.
|
|
52
|
+
- **Test count:** `237 → 265` (+28 across the sprint: 0 new in PR #35, +15 in PR #36, +13 in PR #37). Node CI matrix unchanged: 18, 20, 22.
|
|
53
|
+
- New maintenance scripts: `npm run pack:mcpb` (staging build + multi-platform prebuild fetch), `npm run verify:pack-isolation` (asserts `node_modules/keytar/` byte-identity across pack runs).
|
|
54
|
+
|
|
55
|
+
## [1.5.1] - 2026-05-02
|
|
56
|
+
|
|
57
|
+
Stretch-to-stable release. Closes the OAuth 2.1 alpha audit pass and the two integration-seam regressions surfaced during Helena's Phase B operator verification, plus the async-config tech-debt sweep that shares the bridge's startup path. No new features, no surface changes — the v1.5.x line is now stable for broader operator adoption.
|
|
58
|
+
|
|
59
|
+
Companion releases: [abilities-mcp-adapter v1.4.4](https://github.com/Wicked-Evolutions/abilities-mcp-adapter/releases/tag/v1.4.4) and [abilities-for-ai v1.9.1](https://github.com/Wicked-Evolutions/abilities-for-ai/releases/tag/v1.9.1) — coordinated multi-repo release per the Stretch to Stable sprint plan.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- **Schema v1→v2 auto-migration is now actually wired into boot** (PR #25, closes #23). `migrateFile()` shipped in v1.5.0 but the Phase 4/5 OAuth handoff missed the call sites — the migration code existed but never ran. Now invoked at two points before any consumer touches the config: (a) in `abilities-mcp.js` startup before the MCP server reads `wp-sites.json`, and (b) in `lib/cli/index.js`'s `runCommand()` before any subcommand parses the file. Both call sites share a single `KeychainSecretStore` instance. CLI subcommands no longer error with `v<unknown> but CLI expects v2` against legacy v1 configs; the OAuth `add-site` / `upgrade-auth` flows can reach the auth code path on a fresh install. Idempotent — second-run on an already-v2 file is a no-op.
|
|
64
|
+
- **Post-migration v2 apppassword sites validate and connect** (PR #27, closes #26). Helena's Phase B run surfaced a regression: when PR #25's wired migration converted a multi-site v1 config to v2, every non-OAuth site moved to `auth.method: 'apppassword'` with `auth.password_ref`, and the legacy `http.password` / `http.passwordEnv` / `http.passwordCommand` fields were stripped per Appendix F.5 — keychain becomes the sole source of truth. The runtime side still spoke v1 only: `validateSiteConfig()` had no apppassword branch, so v2 sites fell through to the legacy http validator which rejected them with `requires one of http.password, http.passwordEnv, or http.passwordCommand`, and `ConnectionPool._createTransport` had no resolver for `auth.password_ref`. Two parallel branches added (validator + async `resolveSitePassword(site, secretStore)` helper that reads keychain via the SecretStore), pool dispatches on `auth.method === 'apppassword'`, and `KeychainSecretStore` is constructed lazily so SSH-only / v1-only setups still avoid loading keytar. Multi-site acceptance test (1 oauth + 2 apppassword + 1 ssh-carrier) pins the routing.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- **Async config loading** (PR #28, closes #5). The boot chain — `resolveConfigFilePath`, `loadConfig`, `loadConfigFile`, `validateSiteConfig`, `resolvePassword` — is async. File reads use `fs.promises`; the `passwordCommand` shell-out goes through `util.promisify(exec)` rather than `execFile` so existing operator configs that rely on shell features (pipes, redirects, `$()` substitution — e.g. `op read 'op://Vault/foo' | tr -d '\n'`) keep working unchanged. `loadConfig` now returns a `Promise`; `abilities-mcp.js`'s bootstrap awaits it (the IIFE was already async per the migration wiring above). Pure-dispatch helpers (`resolveSiteKey`, `buildSiteKeyEnum`, `buildEnvConfig`, `buildLegacyConfig`) stayed synchronous — they have no I/O and converting them would touch every call site for no runtime benefit.
|
|
69
|
+
|
|
70
|
+
### Internal
|
|
71
|
+
|
|
72
|
+
- Test count: 237 (+27 since 1.5.0). Node CI matrix: 18, 20, 22.
|
|
73
|
+
- Validator coverage extended: 8 acceptance/reject cases for v2 apppassword (http and ssh carriers, hand-edited reject paths), 9 cases for the async surface (`loadConfig` Promise return, `resolveConfigFilePath` async, `resolvePassword` env / command / shell-feature regression).
|
|
74
|
+
|
|
75
|
+
## [1.5.0] - 2026-04-28
|
|
76
|
+
|
|
77
|
+
OAuth 2.1 release. The bridge is now a full OAuth client: it discovers the authorization server via RFC 9728, performs Dynamic Client Registration (RFC 7591), drives the authorization-code grant with PKCE S256 (RFC 7636) through a loopback browser flow (RFC 8252), persists tokens in the OS keychain, refreshes them automatically, and sends Bearer tokens through the runtime MCP transport.
|
|
78
|
+
|
|
79
|
+
Companion release: [abilities-mcp-adapter v1.4.3](https://github.com/Wicked-Evolutions/abilities-mcp-adapter/releases/tag/v1.4.3) — the OAuth resource server + authorization server.
|
|
80
|
+
|
|
81
|
+
### Added — OAuth 2.1 client core (#15)
|
|
82
|
+
|
|
83
|
+
- **`lib/auth/` module** — full OAuth 2.1 client: `oauth-client.js` (state machine), `discovery-client.js` (RFC 9728 + RFC 8414, refuses HTTP, refuses redirects on `.well-known/*`, throws `CapabilityPinningError` on pinned 404), `dcr-client.js` (RFC 7591), `pkce.js` (32-byte verifier, S256, 16-byte hex state, `crypto.timingSafeEqual` with length-mismatch CPU-burn defense), `loopback-server.js` (RFC 8252 loopback callback), `browser-launcher.js` (cross-platform `open`), `token-manager.js` (refresh window, retry semantics, expired/revoked state machine).
|
|
84
|
+
- **`SecretStore` interface** — three implementations: `keychain-secret-store.js` (libsecret on Linux, Keychain on macOS, Credential Manager on Windows), `memory-secret-store.js` (testing), `secret-store.js` (interface). All token persistence flows through this interface.
|
|
85
|
+
- **`BridgeIdentityProvider` interface** + **`FreshEachTimeIdentityProvider`** (v1.0 implementation per Appendix H.3.2 binding amendment): `getClientId()` always returns null → fresh DCR on every flow. `persistClientId()` is a documented no-op pending v1.1's persistent-identity upgrade contract.
|
|
86
|
+
- **`schema-v2.js`** — keychain references replace inline secrets in `wp-sites.json`. `schema_version: 2` with `auth.method`, `access_token_ref`, `refresh_token_ref`, `oauth_capability_pinned`, `apppassword_fallback`. `config-migration.js` upgrades v1 configs in place.
|
|
87
|
+
- **`OAuthHttpTransport`** (#18) — runtime transport that uses `TokenManager.getAccessToken()` before each request, builds `Authorization: Bearer ...`, handles 401 → `forceRefresh` → retry-once, surfaces terminal auth failure via `onAuthStatusChange('expired')`. `ConnectionPool._create()` dispatches to this when `siteConfig.auth.method === 'oauth'`.
|
|
88
|
+
|
|
89
|
+
### Added — OAuth CLI subcommands (#16)
|
|
90
|
+
|
|
91
|
+
- **`abilities-mcp <subcommand>`** dispatches to eight new subcommands wrapping `lib/auth/`: the six in the sprint plan — `add-site`, `reauth`, `revoke`, `list-sites`, `test`, `upgrade-auth` — plus two design-doc extensions: `force-downgrade` (J.1, escape hatch for the H.2.3 capability-pin failure) and `self-check` (J.2, the H.2.6 Authorization-header probe). Each subcommand subscribes to the OAuth state machine and prints operator-facing progress lines. Error messages name the exact next action. Bare `node abilities-mcp.js` (no subcommand) still starts the MCP STDIO server unchanged.
|
|
92
|
+
- Exit-code table (`0`/`1`/`2`/`3`/`4`/`5`) mapping success / generic / usage / config / auth / capability-pinning failures, documented in `abilities-mcp --help`.
|
|
93
|
+
- `--debug` flag includes the `cause` stack on errors for troubleshooting.
|
|
94
|
+
- `force-downgrade` audit lives on the site config (`force_downgrade.{at, expires_at, reason}`) and is surfaced in `list-sites` for 30 days.
|
|
95
|
+
|
|
96
|
+
### Security
|
|
97
|
+
|
|
98
|
+
- **H-7: removed dead refresh-intent keychain code** (#21). `TokenManager.refresh()` previously wrote a `${siteId}/refresh-intent` keychain entry before sending the refresh request and deleted it on every exit path. The marker had no reader — the original H.2.1 mid-flight crash-recovery semantics were never implemented. With the adapter's C-2 fix shipping encrypt-at-rest grace-window retry on the server (adapter PR #61), the bridge no longer needs an in-flight intent marker. Pure deletion of dead code that paid a keychain write per refresh.
|
|
99
|
+
- **H-8: client_id port guard in `_runRegister`** (#22). `OAuthClient._runRegister` previously returned a persisted `client_id` from `identityProvider.getClientId()` without verifying that the registered loopback redirect_uri's port matched the live loopback port. v1.0 was safe by accident because `FreshEachTimeIdentityProvider.getClientId()` always returns null. v1.1's persistent-identity upgrade (per Appendix H.3.2) would have surfaced the bug: a stale persisted client_id whose registered port no longer matched would fail server-side `redirect_uri_valid()`. Defensive fix: `_runRegister` now always calls `identityProvider.clearClientId()` before DCR. v1.1+ designs that want to short-circuit DCR on persisted client_id must do so at the `OAuthClient.run()` level after verifying the loopback port matches the registration. See follow-up [abilities-mcp-adapter#73](https://github.com/Wicked-Evolutions/abilities-mcp-adapter/issues/73) for the spec amendment.
|
|
100
|
+
|
|
101
|
+
### Internal
|
|
102
|
+
|
|
103
|
+
- Test count: 210 (+34 since 1.4.0). Node CI matrix: 18, 20, 22.
|
|
104
|
+
- `infra: retarget project automation` (#14) for the OAuth sprint workflow.
|
|
105
|
+
|
|
106
|
+
## [1.4.0] - 2026-04-26
|
|
107
|
+
|
|
108
|
+
### Added
|
|
109
|
+
- `.mcpb` distribution bundle for one-click install in Claude Desktop (#8). `manifest.json` against MCPB spec v0.3, `.mcpbignore`, and `npm run pack:mcpb` script. Application Password is stored encrypted in the OS keychain via `sensitive: true`. Published as a GitHub Release asset.
|
|
110
|
+
- `ABILITIES_MCP_URL` / `ABILITIES_MCP_USERNAME` / `ABILITIES_MCP_PASSWORD` environment-variable config fallback in `lib/config.js`. When no `wp-sites.json` exists, the bridge builds a single-site config from the env vars and auto-derives the MCP adapter endpoint as `<URL>/wp-json/mcp/mcp-adapter-default-server`. Covers the `.mcpb` install path and any env-var-based MCP client (`claude mcp add`, Docker, etc.).
|
|
111
|
+
- `npm run validate:mcpb` — validates `manifest.json` against the MCPB schema.
|
|
112
|
+
|
|
113
|
+
### Changed
|
|
114
|
+
- README restructured around three install paths: `.mcpb` bundle (recommended for Claude Desktop), env-vars + npm install (Claude Code / Cursor / Docker), and `wp-sites.json` (multi-site / power users). Existing `wp-sites.json` users are unaffected.
|
|
115
|
+
|
|
5
116
|
## [1.3.1] - 2026-03-19
|
|
6
117
|
|
|
7
118
|
### Fixed
|
package/README.md
CHANGED
|
@@ -16,22 +16,26 @@ Open-source MCP bridge that connects any AI client to your WordPress sites throu
|
|
|
16
16
|
|
|
17
17
|
## What You Can Do
|
|
18
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:
|
|
19
|
+
The abilities available to your AI agent depend on which ability plugins you install. With [Abilities for AI](https://community.wickedevolutions.com/item/abilities-for-ai/) installed, your agent gets access to:
|
|
20
20
|
|
|
21
21
|
**Content & Publishing** — content, blocks, patterns, media, menus, taxonomies, comments, revisions
|
|
22
22
|
**Site Management** — plugins, themes, settings, users, site health, cache, cron, rewrite rules
|
|
23
23
|
**Infrastructure** — filesystem, meta, REST discovery, knowledge layer
|
|
24
24
|
**Third-party integrations** — auto-detected modules for supported plugins (Astra, Spectra, SureCart, Presto Player, and more)
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
**[Abilities for Fluent Plugins](https://github.com/Wicked-Evolutions/abilities-for-fluent-plugins)** is our continuously-enhanced third-party translator — bringing AI control to FluentCRM, FluentCommunity, FluentForms, FluentBooking, FluentSupport, FluentBoards, FluentSMTP, FluentAuth, FluentSnippets, FluentMessaging, FluentCart, and FluentAffiliate. We build and maintain it because we use Fluent's plugins ourselves and wanted them AI-native.
|
|
27
|
+
|
|
28
|
+
Beyond Fluent, the bridge is plugin-agnostic by design. Any plugin that registers abilities through the WordPress Abilities API becomes available automatically — no configuration in this bridge required. We urge every WordPress plugin developer to prioritize native Abilities API support over anything else.
|
|
27
29
|
|
|
28
30
|
Every ability enforces `current_user_can()` at execution time — your WordPress role is the security boundary.
|
|
29
31
|
|
|
30
|
-
> **Sign up for the Abilities for AI alpha release:** https://wickedevolutions.com/abilities-for-ai
|
|
32
|
+
> **Sign up for the Abilities for AI alpha release:** https://community.wickedevolutions.com/item/abilities-for-ai/
|
|
33
|
+
|
|
34
|
+
## Install
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
There are three install paths. Pick the one that matches how you use AI clients.
|
|
33
37
|
|
|
34
|
-
###
|
|
38
|
+
### Set up WordPress (required for all paths)
|
|
35
39
|
|
|
36
40
|
Create a dedicated WordPress user for AI access and generate an Application Password.
|
|
37
41
|
|
|
@@ -59,24 +63,77 @@ Go to **Users → Edit (your mcp-agent user) → Application Passwords**, enter
|
|
|
59
63
|
|
|
60
64
|
Install both on your WordPress site:
|
|
61
65
|
|
|
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://
|
|
66
|
+
1. **[Abilities for AI](https://community.wickedevolutions.com/item/abilities-for-ai/)** — registers WordPress abilities across content, site management, infrastructure, and third-party integration modules
|
|
67
|
+
2. **[Abilities MCP Adapter](https://community.wickedevolutions.com/item/abilities-mcp-adapter/)** — exposes abilities as MCP tools via REST API
|
|
68
|
+
|
|
69
|
+
Both are available as free downloads from our store, or install from GitHub: [abilities-for-ai](https://github.com/Wicked-Evolutions/abilities-for-ai) and [abilities-mcp-adapter](https://github.com/Wicked-Evolutions/abilities-mcp-adapter).
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### Path 1 — `.mcpb` bundle for Claude Desktop (recommended)
|
|
74
|
+
|
|
75
|
+
Single-click install for Claude Desktop on macOS and Windows. The Application Password is stored encrypted in your OS keychain (macOS Keychain / Windows Credential Manager).
|
|
76
|
+
|
|
77
|
+
1. Download `abilities-mcp.mcpb` from the [latest GitHub Release](https://github.com/Wicked-Evolutions/abilities-mcp/releases/latest).
|
|
78
|
+
2. Double-click the file. Claude Desktop opens an "Install Extension" dialog.
|
|
79
|
+
3. Type three things:
|
|
80
|
+
- **WordPress Site URL** — `https://example.com`
|
|
81
|
+
- **WordPress Username** — `mcp-agent`
|
|
82
|
+
- **Application Password** — paste the password from the previous step
|
|
83
|
+
4. Click **Install**. The connection is live.
|
|
84
|
+
|
|
85
|
+
The bundle covers the single-site case. For multi-site (one bridge connected to several WordPress sites at once), use Path 3.
|
|
86
|
+
|
|
87
|
+
---
|
|
64
88
|
|
|
65
|
-
### 2
|
|
89
|
+
### Path 2 — Env vars (Claude Code, Cursor, Docker, any MCP client)
|
|
66
90
|
|
|
67
|
-
|
|
91
|
+
Install the bridge from npm, then point your client at it with three environment variables:
|
|
68
92
|
|
|
69
93
|
```bash
|
|
70
|
-
|
|
94
|
+
npm install -g @wickedevolutions/abilities-mcp
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
In your client's MCP config:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"mcpServers": {
|
|
102
|
+
"wordpress": {
|
|
103
|
+
"command": "abilities-mcp",
|
|
104
|
+
"env": {
|
|
105
|
+
"ABILITIES_MCP_URL": "https://example.com",
|
|
106
|
+
"ABILITIES_MCP_USERNAME": "mcp-agent",
|
|
107
|
+
"ABILITIES_MCP_PASSWORD": "xxxx xxxx xxxx xxxx xxxx xxxx"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
71
112
|
```
|
|
72
113
|
|
|
73
|
-
|
|
114
|
+
The endpoint is auto-derived as `<URL>/wp-json/mcp/mcp-adapter-default-server`. Single-site only — for multi-site, use Path 3.
|
|
115
|
+
|
|
116
|
+
For `claude mcp add` users:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
claude mcp add wordpress \
|
|
120
|
+
--env ABILITIES_MCP_URL=https://example.com \
|
|
121
|
+
--env ABILITIES_MCP_USERNAME=mcp-agent \
|
|
122
|
+
--env ABILITIES_MCP_PASSWORD='xxxx xxxx xxxx xxxx xxxx xxxx' \
|
|
123
|
+
-- abilities-mcp
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### Path 3 — `wp-sites.json` (multi-site, power users)
|
|
74
129
|
|
|
75
|
-
|
|
130
|
+
Use this when you connect one bridge to multiple WordPress sites, when you want passwords sourced from a keychain or shell command, or when you're targeting WordPress multisite networks via dot-notation routing.
|
|
76
131
|
|
|
77
|
-
|
|
132
|
+
```bash
|
|
133
|
+
cp wp-sites.example.json wp-sites.json
|
|
134
|
+
```
|
|
78
135
|
|
|
79
|
-
|
|
136
|
+
Edit `wp-sites.json` with your sites, then add the server to your client's MCP config:
|
|
80
137
|
|
|
81
138
|
```json
|
|
82
139
|
{
|
|
@@ -126,11 +183,15 @@ node abilities-mcp.js --register
|
|
|
126
183
|
}
|
|
127
184
|
```
|
|
128
185
|
|
|
129
|
-
### Config
|
|
186
|
+
### Config search order
|
|
130
187
|
|
|
131
188
|
1. `--config=/path/to/wp-sites.json` (explicit)
|
|
132
|
-
2.
|
|
189
|
+
2. `wp-sites.json` in the same directory as `abilities-mcp.js`
|
|
133
190
|
3. `~/.abilities-mcp/wp-sites.json`
|
|
191
|
+
4. `ABILITIES_MCP_URL` / `ABILITIES_MCP_USERNAME` / `ABILITIES_MCP_PASSWORD` env vars (single-site, used by the `.mcpb` bundle)
|
|
192
|
+
5. `--host=<ssh-host> --path=<wp-path>` (legacy SSH single-site)
|
|
193
|
+
|
|
194
|
+
The first one found wins. If a `wp-sites.json` exists, env vars are ignored.
|
|
134
195
|
|
|
135
196
|
### WordPress Multisite
|
|
136
197
|
|
|
@@ -313,9 +374,19 @@ See [docs/architecture.md](docs/architecture.md) for the full technical deep div
|
|
|
313
374
|
## Requirements
|
|
314
375
|
|
|
315
376
|
- 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
|
|
377
|
+
- WordPress 6.9+ with [Abilities for AI](https://community.wickedevolutions.com/item/abilities-for-ai/) and [Abilities MCP Adapter](https://github.com/Wicked-Evolutions/abilities-mcp-adapter) installed
|
|
317
378
|
- Application Passwords enabled (default in WordPress 5.6+)
|
|
318
379
|
|
|
380
|
+
## Evolving Knowledge
|
|
381
|
+
|
|
382
|
+
We continuously add knowledge docs, skills, and agent patterns to [knowledge.wickedevolutions.com](https://knowledge.wickedevolutions.com).
|
|
383
|
+
|
|
384
|
+
## Disclaimer
|
|
385
|
+
|
|
386
|
+
Humans make mistakes — as we know from the present day and history. Humans trained AI. AI acts accordingly. AI predicts probability based on the context window it holds. It is trained to sound certain, as if everything is truth, and to "fix" everything so the human becomes satisfied.
|
|
387
|
+
|
|
388
|
+
Learn how to communicate with AI. You are fully responsible for using AI in your life, business, and projects. Using these products is your personal responsibility to learn and own.
|
|
389
|
+
|
|
319
390
|
## License
|
|
320
391
|
|
|
321
392
|
GPL-2.0-or-later
|
package/abilities-mcp.js
CHANGED
|
@@ -23,147 +23,224 @@
|
|
|
23
23
|
'use strict';
|
|
24
24
|
|
|
25
25
|
const { createLogger } = require('./lib/logger');
|
|
26
|
-
const { loadConfig, buildSiteKeyEnum } = require('./lib/config');
|
|
26
|
+
const { loadConfig, buildSiteKeyEnum, resolveConfigFilePath } = require('./lib/config');
|
|
27
|
+
const { formatConfigSourceLine } = require('./lib/config-source-line');
|
|
27
28
|
const { ConnectionPool } = require('./lib/connection-pool');
|
|
28
29
|
const { ToolCatalog } = require('./lib/tool-catalog');
|
|
29
30
|
const { McpRouter } = require('./lib/router');
|
|
30
31
|
const { SshTransport } = require('./lib/transports/ssh-transport');
|
|
32
|
+
const { migrateFile } = require('./lib/auth/config-migration');
|
|
33
|
+
const { KeychainSecretStore } = require('./lib/auth/keychain-secret-store');
|
|
31
34
|
|
|
32
35
|
// ---------------------------------------------------------------------------
|
|
33
|
-
//
|
|
36
|
+
// Subcommand routing (Phase 5 OAuth CLI)
|
|
34
37
|
// ---------------------------------------------------------------------------
|
|
38
|
+
// `abilities-mcp <subcommand>` (e.g. add-site, list-sites) dispatches to the
|
|
39
|
+
// OAuth CLI in lib/cli/. With no subcommand the bridge starts the MCP server
|
|
40
|
+
// as before — this preserves backwards compatibility for every existing
|
|
41
|
+
// invocation path (Claude Desktop, .mcpb bundle, bare `node abilities-mcp.js`).
|
|
42
|
+
const { isKnownSubcommand, runCommand, HELP_TEXT, isHelpToken } = require('./lib/cli');
|
|
35
43
|
|
|
36
|
-
const
|
|
37
|
-
|
|
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
|
-
// ---------------------------------------------------------------------------
|
|
44
|
+
const rawArgs = process.argv.slice(2);
|
|
45
|
+
const firstToken = rawArgs[0];
|
|
50
46
|
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
registerClaudeDesktop({ name: args.name || 'wordpress', configPath: args.config });
|
|
47
|
+
if (firstToken && isHelpToken(firstToken)) {
|
|
48
|
+
process.stdout.write(HELP_TEXT.join('\n') + '\n');
|
|
54
49
|
process.exit(0);
|
|
55
50
|
}
|
|
56
51
|
|
|
52
|
+
const isSubcommandInvocation = firstToken && isKnownSubcommand(firstToken);
|
|
53
|
+
|
|
54
|
+
if (isSubcommandInvocation) {
|
|
55
|
+
(async () => {
|
|
56
|
+
try {
|
|
57
|
+
const { exitCode, lines, errLines } = await runCommand({
|
|
58
|
+
subcommand: firstToken,
|
|
59
|
+
argv: rawArgs.slice(1),
|
|
60
|
+
});
|
|
61
|
+
if (lines.length) process.stdout.write(lines.join('\n') + '\n');
|
|
62
|
+
if (errLines.length) process.stderr.write(errLines.join('\n') + '\n');
|
|
63
|
+
process.exit(exitCode);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
// Last-resort safety net — runCommand normally catches everything.
|
|
66
|
+
process.stderr.write(`abilities-mcp: ${err.message}\n`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
})();
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
// ---------------------------------------------------------------------------
|
|
58
|
-
//
|
|
73
|
+
// MCP server mode — the original CLI argument parsing (no subcommand).
|
|
74
|
+
// Skipped when a subcommand was dispatched above; otherwise we'd race the
|
|
75
|
+
// IIFE's process.exit() against the bootstrap that awaits loadConfig() and
|
|
76
|
+
// connectDefault().
|
|
59
77
|
// ---------------------------------------------------------------------------
|
|
60
78
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
79
|
+
if (!isSubcommandInvocation) {
|
|
80
|
+
const args = {};
|
|
81
|
+
rawArgs.forEach(arg => {
|
|
82
|
+
if (arg.startsWith('--')) {
|
|
83
|
+
const [key, ...rest] = arg.slice(2).split('=');
|
|
84
|
+
args[key] = rest.length ? rest.join('=') : true;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
66
87
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
config = loadConfig(args);
|
|
70
|
-
} catch (err) {
|
|
71
|
-
process.stderr.write(`abilities-mcp: ${err.message}\n`);
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
88
|
+
const debug = !!args.debug;
|
|
89
|
+
const register = !!args.register;
|
|
74
90
|
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
if (register) {
|
|
92
|
+
const { registerClaudeDesktop } = require('./lib/register');
|
|
93
|
+
registerClaudeDesktop({ name: args.name || 'wordpress', configPath: args.config });
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
79
96
|
|
|
80
|
-
const
|
|
81
|
-
|
|
97
|
+
const log = createLogger(debug);
|
|
98
|
+
log('abilities-mcp v1.0.0 starting');
|
|
99
|
+
|
|
100
|
+
// Ensure SSH agent is available (macOS launchd discovery)
|
|
101
|
+
SshTransport.ensureSshAuthSock();
|
|
102
|
+
|
|
103
|
+
// -------------------------------------------------------------------------
|
|
104
|
+
// Async startup — schema v1→v2 migration must complete BEFORE loadConfig.
|
|
105
|
+
// -------------------------------------------------------------------------
|
|
106
|
+
// Per Appendix F.5 (binding): the migration is "Triggered on first bridge
|
|
107
|
+
// launch after upgrade. One-shot, non-destructive." `migrateFile` is
|
|
108
|
+
// idempotent — second-run on a v2 file is a no-op. Called before
|
|
109
|
+
// `loadConfig` so that when v1 is on disk, `loadConfig` reads the freshly
|
|
110
|
+
// rewritten v2 file (with secrets lifted into the keychain).
|
|
111
|
+
//
|
|
112
|
+
// The MCP server uses a fresh `KeychainSecretStore` instance here. The
|
|
113
|
+
// store is a stateless wrapper over keytar — entry identity is determined
|
|
114
|
+
// entirely by (service, account), so a freshly constructed instance writes
|
|
115
|
+
// to the same keychain entries the runtime/CLI later read.
|
|
116
|
+
//
|
|
117
|
+
// Env-var single-site mode (.mcpb path) and legacy --host/--path mode have
|
|
118
|
+
// no on-disk wp-sites.json; `resolveConfigFilePath` returns null and we
|
|
119
|
+
// skip migration entirely.
|
|
120
|
+
(async function bootstrap() {
|
|
121
|
+
const filePath = await resolveConfigFilePath(args);
|
|
122
|
+
if (filePath) {
|
|
123
|
+
try {
|
|
124
|
+
const result = await migrateFile({
|
|
125
|
+
filePath,
|
|
126
|
+
secretStore: new KeychainSecretStore(),
|
|
127
|
+
});
|
|
128
|
+
if (result.migrated) {
|
|
129
|
+
log(`Migrated wp-sites.json v1 → v2 (${result.liftedCount} secret(s) lifted; backup: ${result.backupPath})`);
|
|
130
|
+
}
|
|
131
|
+
} catch (err) {
|
|
132
|
+
process.stderr.write(`abilities-mcp: schema migration failed: ${err.message}\n`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
82
136
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
137
|
+
let config;
|
|
138
|
+
try {
|
|
139
|
+
config = await loadConfig(args);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
process.stderr.write(`abilities-mcp: ${err.message}\n`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
88
144
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
145
|
+
// Emit a single config-source line to stderr so operators can diagnose
|
|
146
|
+
// which mode the bridge is in at a glance (Claude Desktop's MCP log
|
|
147
|
+
// captures the server's stderr stream). Always-on, not gated by --debug:
|
|
148
|
+
// operator-visibility is the entire point of #32 and createLogger is a
|
|
149
|
+
// debug-only file logger that wouldn't reach Claude Desktop's log.
|
|
150
|
+
process.stderr.write(formatConfigSourceLine(config) + '\n');
|
|
151
|
+
|
|
152
|
+
const isMultiSite = config._isMultiSite;
|
|
153
|
+
const siteKeys = buildSiteKeyEnum(config);
|
|
154
|
+
log(`Config loaded: ${siteKeys.length} site(s): ${siteKeys.join(', ')} (default: ${config.defaultSite})`);
|
|
155
|
+
log(`Multi-site mode: ${isMultiSite}`);
|
|
156
|
+
|
|
157
|
+
const pool = new ConnectionPool(config, log);
|
|
158
|
+
const catalog = new ToolCatalog(config, log);
|
|
159
|
+
|
|
160
|
+
if (catalog.isEnabled()) {
|
|
161
|
+
log('Tool filtering enabled');
|
|
162
|
+
} else {
|
|
163
|
+
log('Tool filtering disabled (no toolFilter in config or enabled: false)');
|
|
164
|
+
}
|
|
92
165
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
isMultiSite,
|
|
97
|
-
pool,
|
|
98
|
-
catalog,
|
|
99
|
-
sendToClient,
|
|
100
|
-
log,
|
|
101
|
-
});
|
|
166
|
+
function sendToClient(data) {
|
|
167
|
+
process.stdout.write(data + '\n');
|
|
168
|
+
}
|
|
102
169
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
170
|
+
const router = new McpRouter({
|
|
171
|
+
config,
|
|
172
|
+
siteKeys,
|
|
173
|
+
isMultiSite,
|
|
174
|
+
pool,
|
|
175
|
+
catalog,
|
|
176
|
+
sendToClient,
|
|
177
|
+
log,
|
|
178
|
+
});
|
|
106
179
|
|
|
107
|
-
|
|
180
|
+
// -----------------------------------------------------------------------
|
|
181
|
+
// Client STDIO processing
|
|
182
|
+
// -----------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
let inputBuffer = '';
|
|
185
|
+
|
|
186
|
+
process.stdin.on('data', (chunk) => {
|
|
187
|
+
inputBuffer += chunk.toString();
|
|
188
|
+
|
|
189
|
+
let newlineIdx;
|
|
190
|
+
while ((newlineIdx = inputBuffer.indexOf('\n')) !== -1) {
|
|
191
|
+
const line = inputBuffer.slice(0, newlineIdx);
|
|
192
|
+
inputBuffer = inputBuffer.slice(newlineIdx + 1);
|
|
193
|
+
if (line.trim()) {
|
|
194
|
+
let msg;
|
|
195
|
+
try {
|
|
196
|
+
msg = JSON.parse(line.trim());
|
|
197
|
+
} catch (e) {
|
|
198
|
+
log(`Non-JSON from client (dropped): ${line.substring(0, 200)}`);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
router.handleClientMessage(msg, line.trim());
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
});
|
|
108
205
|
|
|
109
|
-
process.stdin.on('
|
|
110
|
-
|
|
206
|
+
process.stdin.on('end', () => {
|
|
207
|
+
log('Client stdin closed — shutting down');
|
|
208
|
+
shutdown();
|
|
209
|
+
});
|
|
111
210
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
211
|
+
// -----------------------------------------------------------------------
|
|
212
|
+
// Startup — connect to default site
|
|
213
|
+
// -----------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
const transport = await pool.connectDefault((parsedMsg, rawLine) => {
|
|
217
|
+
router.handleTransportMessage(parsedMsg, rawLine);
|
|
218
|
+
});
|
|
219
|
+
router.setDefaultTransport(transport);
|
|
220
|
+
log(`Default transport connected: ${config.defaultSite}`);
|
|
221
|
+
router.drainEarlyQueue();
|
|
222
|
+
} catch (err) {
|
|
223
|
+
process.stderr.write(`abilities-mcp: Failed to connect to default site: ${err.message}\n`);
|
|
224
|
+
process.exit(1);
|
|
125
225
|
}
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
226
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
227
|
+
// -----------------------------------------------------------------------
|
|
228
|
+
// Signal handling
|
|
229
|
+
// -----------------------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
function shutdown() {
|
|
232
|
+
log('Shutting down');
|
|
233
|
+
pool.shutdownAll().then(() => {
|
|
234
|
+
process.exit(0);
|
|
235
|
+
}).catch(() => {
|
|
236
|
+
process.exit(1);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
137
239
|
|
|
138
|
-
(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
240
|
+
process.on('SIGTERM', shutdown);
|
|
241
|
+
process.on('SIGINT', shutdown);
|
|
242
|
+
process.on('unhandledRejection', (reason) => {
|
|
243
|
+
log(`Unhandled rejection: ${reason}`);
|
|
142
244
|
});
|
|
143
|
-
|
|
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
|
-
});
|
|
245
|
+
})();
|
|
163
246
|
}
|
|
164
|
-
|
|
165
|
-
process.on('SIGTERM', shutdown);
|
|
166
|
-
process.on('SIGINT', shutdown);
|
|
167
|
-
process.on('unhandledRejection', (reason) => {
|
|
168
|
-
log(`Unhandled rejection: ${reason}`);
|
|
169
|
-
});
|