@selfhost.dev/mcp-server 0.9.0 → 0.10.0

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.
Files changed (45) hide show
  1. package/README.md +205 -43
  2. package/dist/client.d.ts +7 -0
  3. package/dist/client.js +2 -1
  4. package/dist/client.js.map +1 -1
  5. package/dist/index.js +21 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/tools/alerts.js +12 -2
  8. package/dist/tools/alerts.js.map +1 -1
  9. package/dist/tools/backups.js +29 -5
  10. package/dist/tools/backups.js.map +1 -1
  11. package/dist/tools/billing.js +233 -39
  12. package/dist/tools/billing.js.map +1 -1
  13. package/dist/tools/database-users.js +53 -6
  14. package/dist/tools/database-users.js.map +1 -1
  15. package/dist/tools/deployments.js +47 -3
  16. package/dist/tools/deployments.js.map +1 -1
  17. package/dist/tools/github.js +21 -4
  18. package/dist/tools/github.js.map +1 -1
  19. package/dist/tools/instance-logs.js +2 -2
  20. package/dist/tools/instance-logs.js.map +1 -1
  21. package/dist/tools/instances.js +423 -88
  22. package/dist/tools/instances.js.map +1 -1
  23. package/dist/tools/organizations.js +5 -1
  24. package/dist/tools/organizations.js.map +1 -1
  25. package/dist/tools/pitr.d.ts +22 -0
  26. package/dist/tools/pitr.js +75 -10
  27. package/dist/tools/pitr.js.map +1 -1
  28. package/dist/tools/postgres-extensions.js +176 -57
  29. package/dist/tools/postgres-extensions.js.map +1 -1
  30. package/dist/tools/project-backups.js +130 -0
  31. package/dist/tools/project-backups.js.map +1 -1
  32. package/dist/tools/project-databases.js +16 -23
  33. package/dist/tools/project-databases.js.map +1 -1
  34. package/dist/tools/project-services.js +544 -53
  35. package/dist/tools/project-services.js.map +1 -1
  36. package/dist/tools/project-ssh.d.ts +2 -0
  37. package/dist/tools/project-ssh.js +338 -0
  38. package/dist/tools/project-ssh.js.map +1 -0
  39. package/dist/tools/projects.js +21 -3
  40. package/dist/tools/projects.js.map +1 -1
  41. package/dist/tools/scaling.js +1 -1
  42. package/dist/tools/scaling.js.map +1 -1
  43. package/dist/types/tiers.js +27 -4
  44. package/dist/types/tiers.js.map +1 -1
  45. package/package.json +2 -1
package/README.md CHANGED
@@ -6,8 +6,8 @@ The power of your own server. The simplicity of a PaaS. Driven by AI.
6
6
 
7
7
  **One platform, two capabilities:**
8
8
 
9
- - **Projects** - your apps, shipped. Connect a GitHub repo, click deploy, and it's live: auto-detected builds (Nixpacks, Dockerfile, Docker Compose, or static), auto-deploy on every push, live streaming build logs, environment variables, custom domains, pause and resume to scale to zero, and databases right inside the project. The ease of Railway, Render, and Vercel, with a server you actually control.
10
- - **Managed Databases** - production-grade database hosting, your way. Managed PostgreSQL, MySQL, Redis, and ClickHouse on AWS, in our cloud or your own AWS account (BYOC), with TimescaleDB and pgvector a flag away on PostgreSQL. Automated backups, snapshots, replicas, and metrics across engines; PITR and parameter tuning on PostgreSQL and MySQL; connection pooling on PostgreSQL, MySQL, and ClickHouse.
9
+ - **Projects** - your apps, shipped. Connect a GitHub repo, click deploy, and it's live: auto-detected builds (Nixpacks, Dockerfile, Docker Compose, or static), auto-deploy on every push, live streaming build logs, environment variables, custom domains, pause and resume to scale to zero, and databases right inside the project. Pick which of six locations across three continents the server runs in, add one-click apps (Supabase, n8n, n8n with Postgres, Twenty CRM) next to your own code, and turn on free daily server backups. The ease of Railway, Render, and Vercel, with a server you actually control.
10
+ - **Managed Databases** - production-grade database hosting, your way. Managed PostgreSQL, MySQL, Redis, ClickHouse, and OpenSearch on AWS, in our cloud or your own AWS account (BYOC), with an eleven-entry PostgreSQL extension catalog (pgvector, TimescaleDB, PostGIS, pg_cron and more). Automated backups, snapshots, replicas, and metrics across engines; PITR, client TLS and parameter tuning on PostgreSQL; parameter tuning on MySQL; connection pooling on PostgreSQL, MySQL, and ClickHouse; offsite data backups and zero-downtime cluster tier scaling on ClickHouse; built-in k-NN vector search and an opt-in Dashboards UI on OpenSearch. Read engine logs and sample the live query workload without SSH.
11
11
 
12
12
  One account, one bill, one AI control plane. Pay only for what you run: prepaid credits from $5, no plans or tiers.
13
13
 
@@ -90,14 +90,30 @@ Connect a repo, deploy, and it's live - SelfHost takes your code from GitHub to
90
90
  | *"Create a project and deploy my GitHub repo into it"* | Provisions the project, waits until it's ready, auto-detects the build, and deploys the repo |
91
91
  | *"Deploy the main branch of my-org/api"* | Creates the deployment, triggers the build, and streams live logs until it's running |
92
92
  | *"Turn on auto-deploy so every push ships"* | Enables auto-deploy; future pushes to the branch redeploy automatically |
93
+ | *"Put my project's server in Singapore"* | Shows what each family costs there, with live stock, and provisions it where you picked. A server cannot be moved later, so this is asked up front |
93
94
  | *"Add a private Postgres database to my project"* | Provisions a Postgres database inside the project, with the credentials and visibility you choose |
94
- | *"Spin up Supabase in this project"* | Deploys the Supabase template alongside your app |
95
+ | *"Spin up Supabase in this project"* | Deploys the Supabase template alongside your app and hands you the Studio credentials once it is up |
96
+ | *"I want n8n for my automations"* | Deploys n8n (or n8n backed by Postgres, if it is more than light use) and warns you the first person to open the URL claims the owner account |
97
+ | *"Give me a CRM"* | Deploys Twenty, and tells you it takes up to 15 minutes rather than leaving you wondering if it hung |
98
+ | *"Turn on backups for this project"* | Enables daily server backups, seven kept. Free - there is no backup charge for projects |
99
+ | *"Why did my project go down last night?"* | Reads the merged feed of Hetzner server actions and platform events, so "we stopped it for billing" is distinguishable from "the machine was rebuilt" |
100
+ | *"Roll this project's server back to yesterday's backup"* | Lists the backups, tells you exactly what you lose, and rebuilds the disk once you confirm |
95
101
  | *"Set DATABASE_URL on my app and redeploy"* | Updates the env var (without wiping the others) and rolls out the change |
96
102
  | *"Point app.example.com at my deployment"* | Attaches the custom domain and returns the exact DNS records to add, then verifies them |
97
103
  | *"Show me the build logs for the last deploy"* | Streams the build and container logs for that run |
98
104
  | *"Pause my staging app so I stop paying for it"* | Pauses the deployment (scales to zero); resume instantly when you need it |
99
105
  | *"Roll back to the last working deploy"* | Re-deploys the commit from the previous successful run |
100
106
  | *"Notify my team channel when a deploy fails"* | Binds a notification channel to the app's deploy outcomes |
107
+ | *"Stop this build, I pushed the wrong thing"* | Aborts the run in flight, and confirms nothing was rolled back - what is deployed keeps serving |
108
+ | *"Is my app actually up?"* | Reads `last_deploy_status`, which catches the case a run says `active` but the container has since crashed |
109
+ | *"Give me a GitLab / Nextcloud / Metabase / Odoo instance"* | Any of 26 one-click apps, with the right credential shape for each and a real boot-time estimate |
110
+ | *"I want a CRM"* | Offers Twenty, EspoCRM or Krayin, asks for the credentials that app actually accepts, and warns when the first visitor to the URL claims the owner account |
111
+ | *"Why is my n8n container crash-looping?"* | Captures the container's own stdout/stderr from the host - where a failed migration or bad env var actually shows |
112
+ | *"Why is my project server full?"* | Reads live per-container CPU and memory, so you learn which service is eating the box rather than guessing |
113
+ | *"Give me a shell on my project server"* | Installs your public key and hands you `ssh shell@<ip>` - a non-root user, and it says so rather than promising root |
114
+ | *"Only let me SSH in from the office"* | Restricts port 22 to the CIDRs you name, and warns that the platform sees the MCP's address, not your laptop's |
115
+ | *"Snapshot this project before I upgrade it"* | Takes an on-demand whole-VM image you keep until you delete it - the real undo a project has |
116
+ | *"Point crm.example.com at my EspoCRM"* | Checks DNS resolves to the project first (so the certificate request cannot fail), then attaches the domain |
101
117
 
102
118
  ### Manage database infrastructure with Managed Databases
103
119
 
@@ -117,18 +133,31 @@ Production-grade, standalone database hosting.
117
133
  | *"Enable ProxySQL pooling on my MySQL database"* | Configures MySQL connection pooling; clients connect on port 6033 |
118
134
  | *"Spin up a ClickHouse cluster for analytics with 2 replicas"* | Provisions a 3-node Keeper-quorum cluster (min 4 GB RAM per node), priced up front |
119
135
  | *"Enable CHProxy on my ClickHouse instance"* | Configures ClickHouse query pooling; clients connect over HTTP on port 9090 |
120
- | *"Upgrade my ClickHouse instance to 25.1"* | Steps the version up in place, one release at a time, after a snapshot |
121
- | *"Give me a Postgres 17 instance with TimescaleDB for sensor data"* | Provisions Postgres with the extension installed during setup |
122
- | *"Turn on TimescaleDB on my existing prod Postgres"* | Confirms the one-time PG restart, then installs and enables it |
136
+ | *"Spin up an OpenSearch cluster for log search"* | Prices the REAL machine count first - HA adds 3 cluster-manager nodes, so 1 replica is 5 billed nodes, not 2 - then provisions it |
137
+ | *"Give me a Postgres 17 instance with TimescaleDB for sensor data"* | Reads the extension catalog for that version and installs the extensions during provisioning |
138
+ | *"Turn on PostGIS and pg_cron on my existing prod Postgres"* | Tells you which of them restart PostgreSQL, which this instance cannot run and why, then enables them together |
139
+ | *"Can my database survive losing a node without losing a write?"* | Explains the three durability modes honestly - and that `required` buys RPO 0 by refusing writes when it cannot be held |
123
140
  | *"Alert me if my hypertable compression ratio drops below 5"* | Creates a rule on the `timescaledb_compression_ratio` metric |
124
141
  | *"Tune innodb_buffer_pool_size on my MySQL instance"* | Reads the current MySQL config and applies the override |
125
142
  | *"Add a read-only database user for the reporting app"* | Creates a DBMS-level credential with the right grants |
126
143
  | *"Restore my prod database to 14:30 yesterday into a new instance"* | Sets up a point-in-time recovery from WAL + base backups |
127
144
  | *"How has my prod database been doing this week?"* | Summarizes CPU / memory / disk / engine metrics from the agent heartbeats |
145
+ | *"Why is my database slow right now?"* | Samples the live query workload and reads the engine log, no SSH involved |
146
+ | *"Show me the Postgres error log"* | Fetches the tail of whichever log you name, from a list of what that engine actually has |
147
+ | *"Encrypt connections to my Postgres"* | Turns on client TLS across the primary and every replica, and reminds you to move clients to sslmode=require |
148
+ | *"Back up my ClickHouse data offsite every night"* | Creates a BACKUP ALL schedule streaming to its own S3 bucket, with the retention you pick |
149
+ | *"Restore that ClickHouse backup"* | Restores it, after telling you plainly that it MERGES rather than rolling back |
150
+ | *"Let my ClickHouse cluster grow and shrink with load"* | Sets the tier floor and ceiling, quotes what the ceiling costs, and adds the up and down triggers |
151
+ | *"My cluster stopped autoscaling"* | Finds the failed scale plan blocking it - which nothing else surfaces - and retries or discards it |
152
+ | *"Make Priya an admin"* | Changes the member's role, and explains it if the hierarchy refuses |
153
+ | *"POST to my service whenever a deploy fails"* | Registers a webhook endpoint and hands you the signing secret |
154
+ | *"How long will my credit last?"* | Reports the balance, the burn rate, the runway, and any credit about to expire |
128
155
  | *"Scale my instance to r6i.2xlarge and 1TB"* | Issues a manual scale, polls the resize event, and warns if the storage cascades to cluster peers |
129
156
  | *"Grow storage automatically before the disk fills up"* | Creates a doubling storage rule up to a ceiling you approve, priced per node |
130
157
  | *"Auto-stop my staging DB at 7pm weekdays"* | Creates a scheduled scaling policy with start/stop times |
131
- | *"Top up my wallet with $25"* | Checks the balance, returns a Razorpay payment link to add credit |
158
+ | *"Top up my wallet with $25"* | Checks the balance and opens Razorpay checkout; can save the card for one-tap top-ups afterwards |
159
+ | *"Stop my resources being stopped when credit runs out"* | Sets up auto-recharge against a saved card, with a monthly cap you approve, and shows the burn rate that makes the threshold meaningful |
160
+ | *"Prove my ClickHouse backups actually restore"* | Schedules restore drills - a scratch instance is provisioned, restored into, row-count checked and torn down - and tells you they bill while they run |
132
161
 
133
162
  ### Safety Built In
134
163
 
@@ -138,7 +167,7 @@ Destructive operations (delete, stop, reboot) require explicit confirmation. The
138
167
 
139
168
  ## Features
140
169
 
141
- ### 189 tools across 31 modules
170
+ ### 216 tools across 32 modules
142
171
 
143
172
  Every tool maps to a real API endpoint. No mock data, no shortcuts.
144
173
 
@@ -167,13 +196,18 @@ Multi-tenant - everything is scoped to your org.
167
196
  | `update_organization` | Change name, description, or IAM role |
168
197
  | `delete_organization` | Soft-delete org and all related resources |
169
198
  | `list_members` | See team members and their roles |
199
+ | `update_member_role` | Promote or demote an existing member (the only way to change a role after they've joined) |
170
200
  | `remove_member` | Remove a member (owner/admin only) |
171
- | `invite_to_org` | Send org invitation with role assignment |
201
+ | `invite_to_org` | Send org invitation at a role you pick, by name |
172
202
  | `list_org_invitations` | Pending invitations |
173
203
  | `cancel_invitation` | Revoke a pending invitation |
174
204
  | `get_my_invitations` | Invitations sent to you |
175
205
  | `list_activity_logs` | Audit trail of all actions in the org |
176
206
 
207
+ Five roles, highest first: **Owner** (100), **Admin** (80), **Billing** (60), **Manager** (40), **Member** (20). Both invite and role-change take the role by name, so you never have to hunt for an id.
208
+
209
+ The hierarchy is enforced and the refusals are worth knowing in advance: an owner manages everyone, but an **admin can only manage strictly below their own level** - never a peer admin, and never granting a role at or above their own. Nobody can change **their own** role (self-demotion locks you out, self-promotion is an escalation), so an owner stepping down has to be demoted by another owner, and the last owner cannot be demoted or removed at all.
210
+
177
211
  #### Cloud Credentials
178
212
  Bring your own AWS account (BYOC) or use SelfHost's managed infrastructure.
179
213
 
@@ -201,28 +235,36 @@ VPCs, subnets, and security groups. Optional - auto-created during provisioning
201
235
  | `update_security_group` | Replace inbound rules on a security group |
202
236
 
203
237
  #### Database Instances
204
- The core feature. Full lifecycle management for PostgreSQL, MySQL, Redis, and ClickHouse.
238
+ The core feature. Full lifecycle management for PostgreSQL, MySQL, Redis, ClickHouse, and OpenSearch.
205
239
 
206
240
  What each engine supports:
207
241
 
208
- | | PostgreSQL | MySQL | Redis | ClickHouse |
209
- |---|---|---|---|---|
210
- | Versions (**default**) | **18**, 17, 16 | 8.4, **8.0** | **7.2**, 7.0 | 26.5, 25.1, **24.8**, 24.3 |
211
- | Default port | 5432 | 3306 | 6379 | 8123 HTTP / 9000 native |
212
- | Backups, snapshots, replicas, metrics, resize, fork | ✅ | ✅ | ✅ | ✅ |
213
- | Connection pooling | PgBouncer (6432) | ProxySQL (6033) | - | CHProxy (9090) |
214
- | Parameter tuning | ✅ | ✅ | - | - |
215
- | Point-in-time recovery | ✅ | ✅ | - | - |
216
- | Database users | ✅ | ✅ | - | ✅ |
217
- | Extensions | TimescaleDB, pgvector | - | - | - |
218
- | In-place version upgrade | - | - | - | ✅ |
219
- | Minimum RAM | - | - | - | 4 GB per node |
242
+ | | PostgreSQL | MySQL | Redis | ClickHouse | OpenSearch |
243
+ |---|---|---|---|---|---|
244
+ | Versions (**default**) | **18**, 17, 16 | 8.4, **8.0** | **7.2**, 7.0 | **26.8** | **2.19**, 2.16, 2.13 |
245
+ | Default port | 5432 | 3306 | 6379 | 8123 HTTP / 9000 native | 9200 (HTTPS only) |
246
+ | Backups, snapshots, replicas, metrics, resize, fork | ✅ | ✅ | ✅ | ✅ | ✅ |
247
+ | Connection pooling | PgBouncer (6432) | ProxySQL (6033) | - | CHProxy (9090) | - |
248
+ | Parameter tuning | ✅ | ✅ | - | - | - |
249
+ | Point-in-time recovery | ✅ | ✅ | - | - | - |
250
+ | HA durability modes (RPO) | ✅ | - | - | - | - |
251
+ | Database users | ✅ | ✅ | - | ✅ | ✅ |
252
+ | Extensions | 11-entry catalog | - | - | - | - |
253
+ | Named databases | ✅ | ✅ | - | ✅ | - |
254
+ | Multi-AZ toggle after create | ✅ | ✅ | ✅ | ✅ | - |
255
+ | Minimum RAM | - | - | - | 4 GB per node | 4 GB per node |
256
+
257
+ Leaving `db_version` off `create_instance` provisions the **bold** default above. The backend's own fallback is the *oldest* major each adapter accepts, so the tools always send the default explicitly - these match the console's create wizard, so both surfaces hand out the same database.
220
258
 
221
- Leaving `db_version` off `create_instance` provisions the **bold** default above - the newest major for PostgreSQL and Redis, and the stable release rather than the newest for MySQL (8.0) and ClickHouse (the 24.8 LTS). These match the console's create wizard, so both surfaces hand out the same database.
259
+ **There is no in-place version upgrade on any engine.** ClickHouse had one; selfhost#2580 withdrew it and pinned the platform to the 26.8 LTS, so a version change now means a new instance plus a restore or migration into it.
222
260
 
223
- **TimescaleDB and pgvector are not separate engines** - both are PostgreSQL plus a flag. Set `timescaledb_enabled` / `vector_enabled` on `create_instance` to have them installed during provisioning, or add them to a running instance later with `enable_timescaledb` / `enable_pgvector`. Enabling TimescaleDB after the fact restarts PostgreSQL once; pgvector doesn't. Neither can be disabled once on, so both tools require confirmation. On AWS the two can coexist on one instance.
261
+ **PostgreSQL HA durability is a three-way RPO dial** (`selfhost#2582`), set at create with `data_durability` and changed later with `set_postgres_durability`. `async` acks locally (RPO > 0). `preferred` waits while standbys are healthy and *degrades* to no-wait when none are, so it never blocks writes and never guarantees zero loss. `required` is RPO 0 honestly implemented: when the synchronous set cannot be held, **the primary stops accepting writes** rather than acking data no survivor holds. Only `required` while `sla_held` is true may be described to a user as zero-data-loss. A 2-node `required` cluster has *no automated failover*, so it needs explicit `no_auto_failover_ack`.
224
262
 
225
- ClickHouse is the one engine whose topology changes the bill, and the tools handle it for you: replication runs on a Keeper/Raft quorum rather than streaming replication, so multi-AZ requires at least one replica (2 is the recommended default). At exactly **1 replica** the platform adds a third, keeper-only node (`t4g.small` + 20 GB) to hold the quorum, and that node is billed - pass `type_of_dbms` to `estimate_instance_cost` and it prices and itemizes it. At 2+ replicas the quorum runs embedded on the data nodes with no extra node.
263
+ **PostgreSQL extensions are a catalog, not two flags.** `timescaledb_enabled` / `vector_enabled` were removed backend-side - pass `extensions: ["pgvector", ...]` to `create_instance` instead, or add them later with `enable_instance_extensions`. See the extensions section below.
264
+
265
+ **Two engines provision nodes the pricing endpoint cannot see**, so pass `type_of_dbms` to `estimate_instance_cost` or the quote is wrong:
266
+ - **ClickHouse** replicates through a Keeper/Raft quorum, so multi-AZ needs at least one replica (2 is the recommended default). At exactly **1 replica** the platform adds a third, keeper-only node (`t4g.small` + 20 GB) to hold the quorum, and it bills. At 2+ replicas the quorum runs embedded on the data nodes with no extra node.
267
+ - **OpenSearch** HA is a cluster topology: `replica_count` buys *data* nodes, and the moment redundancy is asked for, **3 dedicated cluster-manager nodes** are added on top. So `replica_count: 1` is **5 billed machines, not 2**. That topology is also permanent - OpenSearch is the one engine whose multi-AZ cannot be toggled after create.
226
268
 
227
269
  | Tool | Description |
228
270
  |------|-------------|
@@ -240,8 +282,10 @@ ClickHouse is the one engine whose topology changes the bill, and the tools hand
240
282
  | `reboot_instance` | Reboot instances (with confirmation) |
241
283
  | `delete_instance` | Delete instances with optional final snapshot (with confirmation) |
242
284
  | `fork_instance` | Clone a database into a new independent instance |
243
- | `upgrade_clickhouse_version` | Step a ClickHouse instance up one version in place (ClickHouse only, with confirmation) |
244
285
  | `refresh_instance_group` | Refresh cached state for a master + replica group |
286
+ | `set_postgres_durability` | Change the PostgreSQL HA durability mode - the RPO dial. Must be sent alone, master only (with confirmation) |
287
+ | `get_opensearch_dashboards` | Dashboards state, URL, and the RAM verdicts that decide whether it can be enabled (OpenSearch only) |
288
+ | `set_opensearch_dashboards` | Turn the OpenSearch Dashboards UI on or off on the cluster's endpoint node (with confirmation) |
245
289
 
246
290
  #### PostgreSQL Configuration
247
291
  Tune database parameters with calculated defaults per instance type.
@@ -252,18 +296,32 @@ Tune database parameters with calculated defaults per instance type.
252
296
  | `get_pg_config` | Current config with defaults, overrides, and effective values |
253
297
  | `update_pg_config` | Apply overrides (auto-syncs to replicas, set null to revert) |
254
298
 
255
- #### PostgreSQL Extensions (TimescaleDB + pgvector)
256
- Neither is a separate engine - both are PostgreSQL plus a flag. Pass `timescaledb_enabled` or `vector_enabled` to `create_instance` to have them installed during provisioning, or add them to a running instance with the tools below. Both can be on at once.
299
+ #### PostgreSQL Extensions (an 11-entry catalog)
300
+ A server-side catalog, not a pair of booleans. `timescaledb_enabled` and `vector_enabled` were **removed** from the API (selfhost#2516) - and a request still sending them is *ignored* rather than rejected, which is why v0.9.1's `enable_timescaledb` / `enable_pgvector` tools reported success and did nothing. They are gone.
301
+
302
+ Today's catalog: `pgvector`, `timescaledb`, `pgcrypto`, `uuid-ossp`, `postgres_fdw`, `pg_partman`, `pg_stat_statements`, `pg_trgm`, `pg_cron`, `postgis`, `pg_textsearch`. Read it from the API rather than this list - compatibility is per entry (`pg_textsearch` needs PostgreSQL 17 or 18) and the set has grown twice.
257
303
 
258
- **Both are one-way.** The platform refuses to disable either once it's on (dropping an extension can silently break queries that depend on it), so both tools ask for confirmation first.
304
+ **Forward-only.** There is no disable path on any endpoint, because dropping an extension can break objects that depend on it.
259
305
 
260
306
  | Tool | Description |
261
307
  |------|-------------|
262
- | `enable_timescaledb` | Install TimescaleDB on a running Postgres instance - **restarts PostgreSQL once**, irreversible (with confirmation) |
263
- | `enable_pgvector` | Install pgvector on a running Postgres instance - no restart, irreversible (with confirmation) |
308
+ | `list_postgres_extension_catalog` | The catalog with no instance in play - for `create_instance` and project databases. Carries `coolify_supported`, which you must filter on before offering anything for a project database |
309
+ | `list_instance_extensions` | Per-instance state, split into `enabled` / `available` / `unavailable` (each with a reason) |
310
+ | `enable_instance_extensions` | Enable one or more on a running instance; pre-flights which of them restart PostgreSQL, are already on, or this instance cannot run (with confirmation) |
311
+
312
+ Pass `extensions: [...]` to `create_instance` to have them installed during provisioning - restart-requiring entries cost nothing extra on that path. Project (Coolify) databases take `extensions` **at creation only**, and only entries whose `coolify_supported` is true.
264
313
 
265
314
  Once TimescaleDB is installed, five extra metrics join the instance's `postgresql.database` alert set: `timescaledb_hypertable_count`, `timescaledb_chunk_count`, `timescaledb_compressed_chunk_count`, `timescaledb_compression_ratio`, and `timescaledb_total_size_bytes`. They're absent from heartbeats until then, so don't write rules against them on a plain Postgres instance - they'd never fire.
266
315
 
316
+ #### PostgreSQL Client TLS
317
+ Encryption in transit for client connections. **Group-wide**: one call covers the primary and every replica, and certificates are issued per group.
318
+
319
+ | Tool | Description |
320
+ |------|-------------|
321
+ | `set_postgres_tls` | Turn client TLS on or off across a Postgres instance group (turning it off asks for confirmation - it downgrades every client connection) |
322
+
323
+ Once it's on, move clients to `sslmode=require` or stricter; a client left on the default may keep connecting in the clear. And note that **a restore or a fork starts with TLS off** even when the source had it on, because the new group has no certificate yet - re-enable it explicitly rather than assuming it carried over.
324
+
267
325
  #### MySQL Configuration
268
326
  Tune MySQL server parameters on a managed instance. No preview step - read the current config, then apply overrides.
269
327
 
@@ -371,24 +429,32 @@ PostgreSQL and MySQL (Redis and ClickHouse excluded - snapshots are their recove
371
429
  |------|-------------|
372
430
  | `get_pitr` | Current config + restore window (oldest / latest restorable moments) |
373
431
  | `enable_pitr` | Set up transaction-log archiving to an S3 bucket with retention days |
374
- | `configure_pitr` | Toggle between `enabled` (resume) and `disabled` (pause) |
432
+ | `configure_pitr` | Change the retention window (retention only - it never started or stopped anything; v0.9.1 sent a `status` the endpoint ignored) |
433
+ | `pause_pitr` | Suspend scheduled base backups and retention pruning (with confirmation). WAL archiving and the S3 bill continue |
434
+ | `resume_pitr` | Resume a paused config, optionally with a new window; triggers a catch-up base backup if the last one went stale |
375
435
  | `retry_pitr` | Retry after an `error` state (e.g. fix IAM, then retry) |
376
436
  | `trigger_pitr_base_backup` | Run a one-off base backup now |
377
437
  | `list_pitr_base_backups` | List historical base backups with status + timestamps |
378
438
  | `restore_pitr` | Restore to a target time into a new instance (with confirmation) |
379
439
 
380
440
  #### Billing & Wallet (PAYG)
381
- Prepaid **credit wallet** + payment-method linking. No plans or tiers. Credit is
441
+ Prepaid **credit wallet** + auto-recharge. No plans or tiers. Credit is
382
442
  consumed per usage and gated by wallet balance. All endpoints scoped to the active org.
383
443
 
444
+ **There is no separate "link a card" call any more.** `POST billing/link` was removed backend-side, so v0.9.1's `link_billing_payment_method` had been 404ing. A card is now saved by passing `save_card: true` on a real `create_topup`; `DELETE billing/link` survives as the un-link.
445
+
384
446
  | Tool | Description |
385
447
  |------|-------------|
386
448
  | `get_wallet_balance` | Wallet balance, currency, payment state, hourly burn rate + runway |
387
449
  | `list_skus` | Priced resource catalog (hourly rates); estimate how long a top-up lasts |
388
450
  | `list_credit_ledger` | Credit lots: `topup` / `free_credit` (welcome grant, has expiry) / `usage_charge` |
389
451
  | `list_wallet_transactions` | Per-resource hourly charges (`applied` / `skipped`) |
390
- | `create_topup` | Buy credit; returns a hosted Razorpay `payment_url` to open in a browser |
391
- | `link_billing_payment_method` | Initiate Razorpay card link; returns short URL to open in browser |
452
+ | `create_topup` | Buy credit. `save_card` stores the card for later (the only way to save one now), `use_saved_card` charges it off-session with no browser, and `idempotency_key` stops a blind retry double-charging |
453
+ | `unlink_billing_payment_method` | Remove the saved card (with confirmation) - this also stops auto-recharge being able to run |
454
+ | `get_auto_recharge` | Auto-recharge config, the saved cards available, the per-charge cap, and the burn/runway that makes the numbers meaningful |
455
+ | `configure_auto_recharge` | Set threshold / amount / monthly cap and the card to charge |
456
+ | `enable_auto_recharge` | Switch it on - a standing authorisation to charge the card unprompted (with confirmation) |
457
+ | `disable_auto_recharge` | Switch it off, keeping the settings (with confirmation) |
392
458
  | `update_billing_contact` | Update billing phone / tax ID / country / state for invoices |
393
459
 
394
460
  ---
@@ -419,12 +485,18 @@ Native Hetzner backups of a project's whole VM - the only backup a project has,
419
485
 
420
486
  Daily, seven kept, both fixed. Hetzner assigns the window shortly *after* backups are switched on, so a just-enabled project reporting a null window is "pending", not broken.
421
487
 
488
+ **Two different things live here.** The `backups` tools toggle Hetzner's automatic daily schedule, which Hetzner rotates and deletes with the server - you cannot choose when one is taken. The `snapshots` tools are manual point-in-time images the user asks for and keeps until they delete them, capped at 10, which is what you want before a risky change. Both restore by rebuilding the whole VM, and neither is billed to the customer.
489
+
422
490
  | Tool | Description |
423
491
  |------|-------------|
424
492
  | `list_project_backups` | Backups + schedule, read live from Hetzner (works on a stopped project) |
425
493
  | `enable_project_backups` | Turn on automatic daily backups (free) |
426
494
  | `disable_project_backups` | Turn them off **and delete every existing backup** (with confirmation) |
427
495
  | `restore_project_backup` | Rebuild the server's disk from a backup - a true rollback; everything written since is lost (image id + exact project name + confirmation) |
496
+ | `list_project_snapshots` | On-demand snapshots + the cap (10). Unlike the dailies these are never rotated - they persist until deleted |
497
+ | `create_project_snapshot` | Snapshot the whole server now. The tool to reach for **before** a risky change; also free |
498
+ | `restore_project_snapshot` | Rebuild the VM from a snapshot - the most destructive project operation (image id + exact project name + confirmation) |
499
+ | `delete_project_snapshot` | Delete one snapshot to free a slot (with confirmation) |
428
500
 
429
501
  ### PaaS - Project Activity
430
502
  | Tool | Description |
@@ -436,6 +508,8 @@ Reading both sources together is the point: a project that stopped answering sho
436
508
  ### PaaS - Deployments (GitHub apps)
437
509
  A **deployment** is an app built from a GitHub repo, living inside an `active` project. Build/run history is returned inline on the deployment.
438
510
 
511
+ **`last_deploy_status` is not a run status.** It is the deployment's own rolled-up state and it carries one value no run has: `crashed`, meaning the build succeeded and the container has since died. So a deployment whose newest run is `active` can be `crashed` right now - read it before telling anyone their app is up. Org owners are emailed once if a crash-loop episode lasts past ~10 minutes.
512
+
439
513
  | Tool | Description |
440
514
  |------|-------------|
441
515
  | `list_deployments` | List apps in the org, each with inline build runs (newest 50) |
@@ -445,9 +519,10 @@ A **deployment** is an app built from a GitHub repo, living inside an `active` p
445
519
  | `update_deployment_build_config` | Change build_pack / base_directory / port / dockerfile / env_vars |
446
520
  | `delete_deployment` | Remove the app and its Coolify resources (with confirmation) |
447
521
  | `trigger_deploy` | Manually deploy the current branch (new run) |
522
+ | `abort_deploy` | Stop the in-flight build run (with confirmation). Nothing is rolled back - what is deployed keeps serving |
448
523
  | `get_deployment_health` | Reachability of the deploy URL + attached domains |
449
524
  | `get_run_logs` | The log surface: build **and** running-container output for one run (poll until terminal) |
450
- | `redeploy_run` | Re-run a build from the same commit |
525
+ | `redeploy_run` | Re-run a build. Same commit when the run recorded one; branch HEAD when it did not (manual runs never record a SHA, and those used to be refused) |
451
526
  | `rollback_run` | Re-deploy the commit from a prior successful run |
452
527
 
453
528
  ### PaaS - Custom Domains
@@ -489,18 +564,54 @@ Postgres/Redis/MySQL/MongoDB provisioned inside a project (distinct from the man
489
564
  | `delete_project_database` | Delete a project database (exact name match + confirmation) |
490
565
 
491
566
  ### PaaS - Project Services (one-click apps)
492
- Four templates, not one: `supabase`, `n8n`, `n8n-with-postgresql` (the same n8n backed by a Postgres sidecar instead of SQLite) and `twenty` (CRM).
567
+ **Twenty-six templates**, not four: `supabase`, `n8n`, `n8n-with-postgresql`, `twenty`, `wordpress`, `gitlab`, `elasticsearch`, `elasticsearch-with-kibana`, `immich`, `nextcloud`, `jenkins`, `grafana`, `grafana-with-postgresql`, `strapi`, `metabase`, `anythingllm`, `stalwart`, `chatwoot`, `mautic`, `minio`, `open-webui-with-ollama`, `litellm`, `odoo`, `paperless`, `espocrm`, `krayin`. The backend auto-discovers them from its strategy files, so it ships templates ahead of its clients - an unrecognised `template_type` still lists, deletes, inspects and takes a domain.
493
568
 
494
- **Only Supabase takes admin credentials.** n8n and Twenty have none - the first person to open the public URL claims the owner account through the app's own first-run screen. `create_project_service` refuses `dashboard_*` fields on those rather than sending values the backend drops, and says so.
569
+ **Three credential shapes, and the wrong one is refused before the request goes out** (the backend silently drops fields a template does not accept, which would leave a user believing they set a password):
570
+ - *username + password* - becomes the app's admin login. The accepted username form varies: a slug for Jenkins/Grafana/EspoCRM, an **email** for Krayin (a bare `admin` creates an account nobody can sign in as), anything for the rest.
571
+ - *password only* - the app owns the username (GitLab is always `root`, Elasticsearch always `elastic`).
572
+ - *nothing* - **the first visitor to the public URL claims the owner account.** Every create of one of these says so unprompted.
495
573
 
496
- Boot windows differ a lot: Supabase ~5 min, n8n up to 10, Twenty up to 15 (images plus migrations). Don't call Twenty stuck early.
574
+ **Capacity is a refusal at declare time, not slow degradation.** A project is one VM: `heavy` templates must leave more headroom (65% memory / 75% disk vs 80% / 85%), some need a minimum *total* host RAM, at most 5 children exist per project, Stalwart is one-per-project (its mail ports are fixed on the host), and Krayin cannot run on an ARM (`cax*`) server at all. A project server cannot be resized, so exhausting it means a new project.
575
+
576
+ Boot windows run from ~3 min (MinIO) to ~25 (GitLab). The create response names the expected window so nothing gets called stuck early.
497
577
 
498
578
  | Tool | Description |
499
579
  |------|-------------|
500
580
  | `list_project_services` | List deployed templates (status, `provisioning` stages, `error_message`) - **no credentials** |
501
581
  | `get_project_service` | One service **including its credentials** - the only endpoint that returns them |
502
- | `create_project_service` | Deploy `supabase` / `n8n` / `n8n-with-postgresql` / `twenty` into a project |
582
+ | `create_project_service` | Deploy any of the 26 templates into a project, with per-template credential validation |
503
583
  | `delete_project_service` | Delete a project service (exact name match + confirmation) |
584
+ | `set_project_service_custom_domain` | Point a domain at a service's web UI. **DNS must already resolve to the project IP** - the backend refuses otherwise, to avoid burning Let's Encrypt quota on certain-to-fail issuance |
585
+ | `remove_project_service_custom_domain` | Re-pin the platform hostname (with confirmation) - breaks anything registered against the custom domain |
586
+
587
+ #### Container inspection (services **and** project databases)
588
+ Read-only inspection of the Docker containers on a project's box, shared by services and project databases. **Request-then-read**, like the managed-database log surface. Read-only by construction: the backend emits only allow-listed `docker ps / logs / stats / inspect`, scoped to that resource's containers - there is deliberately **no** start / stop / restart / remove / exec surface anywhere.
589
+
590
+ | Tool | Description |
591
+ |------|-------------|
592
+ | `request_project_container_logs` | Dispatch a log capture (default 200 lines, max 1000) |
593
+ | `get_project_container_logs` | Read the captured logs - where a crash loop or failed migration actually shows up |
594
+ | `request_project_container_stats` | Dispatch a `docker stats` snapshot (CPU, memory, network, block IO) |
595
+ | `get_project_container_stats` | Read it - this is the answer to "why is my project server full?" |
596
+
597
+ ### PaaS - SSH Access to Project Servers
598
+ Customer shell access to a project's own server: an org-wide public-key library, per-project installation, and the firewall rule deciding who may reach port 22. The backend shipped this on 2026-08-29 and nothing consumed it for a fortnight - no console surface and no MCP tool - so the only way in was us adding a key by hand.
599
+
600
+ **It is not root.** Keys land on a dedicated non-root `shell` user with no sudo and no docker group, so the command is `ssh shell@<ip>`. The box also runs platform components (the Coolify control plane and its keys, Traefik, our agent), which a root or docker grant would expose. Consequently the `shell` user **cannot see containers** - they live under root-owned `/var/lib/docker` - so container logs and stats come from the inspection tools above, not from SSH.
601
+
602
+ **Public keys only** on this path; a pasted private key is refused (and the tools refuse it before it crosses the wire). A platform-generated keypair is a different route: `create_project` with `generate_ssh_key`, which returns the private half exactly once and never stores it.
603
+
604
+ **Scope is per server.** `authorized_keys` is a per-box file, so a key on project A grants nothing on project B - and deleting a *library* key does **not** revoke access on servers it is already installed on.
605
+
606
+ | Tool | Description |
607
+ |------|-------------|
608
+ | `list_org_ssh_keys` | The org's public-key library (a key here grants nothing until installed) |
609
+ | `add_org_ssh_key` | Save a public key to the library |
610
+ | `delete_org_ssh_key` | Remove a library entry (with confirmation). **Does not revoke** - the key stays on servers it was installed on |
611
+ | `list_project_ssh_keys` | Installed and pending keys, the ready-to-copy `ssh_command`, and the source firewall |
612
+ | `add_project_ssh_key` | Install a key on one project. `access_level: write` is **not installed** - it queues for a SelfHost platform admin's approval |
613
+ | `remove_project_ssh_key` | Revoke one key on one project (with confirmation) - this is what actually removes it from `authorized_keys` |
614
+ | `set_project_ssh_access` | Restrict port 22 to `anywhere` / `my_ip` / `custom` CIDRs (with confirmation). **`my_ip` captures the address *this MCP server* connects from**, not the user's laptop |
504
615
 
505
616
  ### PaaS - Deployment Notifications
506
617
  | Tool | Description |
@@ -561,14 +672,51 @@ Events: `instance.scaling_failed`, `deploy_run.succeeded` / `.failed` / `.aborte
561
672
  ```
562
673
  You: "Deploy my-org/web to a new project"
563
674
 
564
- Claude: Creates a project → polls until it's provisioned and active
675
+ Claude: Shows sizes and locations with live prices → you pick
676
+ → creates the project → polls until provisioned and active
565
677
  → detects the build config from the repo (build pack, port)
566
- → creates the deployment and triggers the first build
678
+ → creates the deployment; it comes back `provisioning` with no
679
+ URL yet, which is normal, so it keeps polling
567
680
  → streams the build logs until the app is live
568
681
  → hands you the deploy URL
569
682
  ```
570
683
 
571
- Then: *"add a private Postgres to it"*, *"set my env vars"*, *"point app.example.com at it"*, or *"turn on auto-deploy"* to round out the app.
684
+ The deployment's URL is null on creation. Registration happens in the background
685
+ (it used to run inline and time out on private repos), so the deploy URL appears
686
+ when the status reaches `active`, not in the create response.
687
+
688
+ Then: *"add a private Postgres to it"*, *"drop n8n next to it"*, *"set my env vars"*, *"point app.example.com at it"*, *"turn on backups"*, or *"turn on auto-deploy"* to round out the app.
689
+
690
+ ### Add a one-click app to a project
691
+
692
+ ```
693
+ You: "I want n8n in my project"
694
+
695
+ Claude: Asks whether you want it public, and whether the Postgres-backed
696
+ variant (durability and concurrency; it cannot be switched later)
697
+ → deploys it → polls until active (up to ~10 min)
698
+ → fetches the URL, and tells you the FIRST person to open it
699
+ becomes the owner, so finish setup before sharing it
700
+ ```
701
+
702
+ Supabase is the exception: it takes a dashboard username and password up front
703
+ and those become the Studio login. n8n and Twenty have no admin credentials at
704
+ all - the first visitor claims the account.
705
+
706
+ ### Diagnose a slow database
707
+
708
+ ```
709
+ You: "My prod Postgres feels slow"
710
+
711
+ Claude: Samples the live query workload (5-60s window)
712
+ → reads the engine log tail
713
+ → checks CPU / memory / disk and the replica count
714
+ → tells you what the numbers actually say
715
+ ```
716
+
717
+ Both the log fetch and the workload sample are request-then-read: the platform
718
+ agent collects them and the result is read back a moment later. Sample while the
719
+ problem is happening - a quiet window will make a struggling database look fine.
572
720
 
573
721
  ### Switch AWS Account (BYOC vs Platform-Managed)
574
722
 
@@ -606,6 +754,12 @@ Claude: Calls reauthenticate → clears stored credentials
606
754
 
607
755
  ### Restore from Backup
608
756
 
757
+ Three different things get called "restore" here and they do not do the same
758
+ thing. Whichever one you ask for, the tools state which you are getting before
759
+ touching anything.
760
+
761
+ **A disk snapshot** becomes a NEW instance - the original is untouched:
762
+
609
763
  ```
610
764
  You: "Restore my prod database from yesterday's snapshot"
611
765
 
@@ -614,6 +768,14 @@ Claude: Lists snapshots → you pick one
614
768
  → shows you the new password (save it!)
615
769
  ```
616
770
 
771
+ **A ClickHouse data backup MERGES into a running instance.** It is not a
772
+ rollback: rows written after the backup was taken survive it. You can also
773
+ restore into a different instance, which is how you clone a dataset.
774
+
775
+ **A project backup IS a rollback.** It overwrites the server's whole disk, so
776
+ Coolify, every service and every project database revert, and anything written
777
+ since is gone. It asks for the project's exact name before doing it.
778
+
617
779
  ---
618
780
 
619
781
  ## Rate Limiting
package/dist/client.d.ts CHANGED
@@ -5,6 +5,13 @@ interface RequestOptions {
5
5
  query?: Record<string, string | undefined>;
6
6
  toolName?: string;
7
7
  skipOrgInjection?: boolean;
8
+ /**
9
+ * Extra request headers, merged over the defaults. Only for contracts the
10
+ * backend reads from a header rather than the body - `Idempotency-Key` on
11
+ * `POST billing/topup`, which is real money and must not double-charge on a
12
+ * blind retry.
13
+ */
14
+ headers?: Record<string, string>;
8
15
  }
9
16
  export declare function apiRequest<T>(path: string, options?: RequestOptions): Promise<ClientResult<T>>;
10
17
  export {};
package/dist/client.js CHANGED
@@ -4,7 +4,7 @@ import { rateLimiter } from "./rate-limiter.js";
4
4
  import { session } from "./session.js";
5
5
  import { stripSensitiveFields } from "./utils/formatters.js";
6
6
  export async function apiRequest(path, options = {}) {
7
- const { method = "GET", toolName, skipOrgInjection } = options;
7
+ const { method = "GET", toolName, skipOrgInjection, headers: extraHeaders } = options;
8
8
  let { body, query } = options;
9
9
  // Auto-inject organization_id from session if active org is set
10
10
  const orgId = session.getActiveOrgId();
@@ -42,6 +42,7 @@ export async function apiRequest(path, options = {}) {
42
42
  Authorization: `Bearer ${token}`,
43
43
  "Content-Type": "application/json",
44
44
  Accept: "application/json",
45
+ ...extraHeaders,
45
46
  };
46
47
  const fetchOptions = { method, headers };
47
48
  if (body && method !== "GET") {
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
2
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
5
  import { config } from "./config.js";
@@ -35,9 +36,25 @@ import { registerPostgresTlsTools } from "./tools/postgres-tls.js";
35
36
  import { registerInstanceLogTools } from "./tools/instance-logs.js";
36
37
  import { registerCapacityTools } from "./tools/capacity.js";
37
38
  import { registerWebhookTools } from "./tools/webhooks.js";
39
+ import { registerProjectSshTools } from "./tools/project-ssh.js";
40
+ /**
41
+ * Version reported in the MCP handshake, read from package.json rather than written twice.
42
+ * It had been pinned at "0.1.0" through eight releases, so every client saw a version the
43
+ * server had not been for a year. `package.json` is one level above both `src/` (tsx) and
44
+ * `dist/` (published), so the same relative path resolves on either. Falls back rather than
45
+ * failing to boot: an unreadable manifest is not a reason to refuse to serve tools.
46
+ */
47
+ function resolveVersion() {
48
+ try {
49
+ return createRequire(import.meta.url)("../package.json").version ?? "0.0.0";
50
+ }
51
+ catch {
52
+ return "0.0.0";
53
+ }
54
+ }
38
55
  const server = new McpServer({
39
56
  name: "selfhost",
40
- version: "0.1.0",
57
+ version: resolveVersion(),
41
58
  });
42
59
  // Module 1: Auth & Users
43
60
  registerAuthTools(server);
@@ -104,6 +121,9 @@ registerInstanceLogTools(server);
104
121
  registerCapacityTools(server);
105
122
  // Module 32: Webhook endpoints - org-wide outgoing HTTPS event callbacks
106
123
  registerWebhookTools(server);
124
+ // Module 33: Customer SSH access to project servers - org key library, per-project
125
+ // installation, and the source firewall on port 22. Non-root `shell` user.
126
+ registerProjectSshTools(server);
107
127
  // Server utility tools
108
128
  server.tool("reauthenticate", "Clear stored credentials and sign in again (use to switch accounts or fix auth issues)", {}, async () => {
109
129
  try {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -19,8 +19,12 @@ const noOrgMessage = "No organization selected. Call `select_organization` first
19
19
  *
20
20
  * NOT LISTED, deliberately: `mongodb.database` is a valid metric_type backend-side
21
21
  * but its adapter reports no metrics, so no rule on it can ever fire.
22
- * `opensearch.database` has metrics but OpenSearch is not a customer-reachable
23
- * engine on this surface.
22
+ *
23
+ * `opensearch.database` IS listed as of the 2026-09 sync - OpenSearch became
24
+ * customer-creatable on 2026-09-14, so its rules can now be built here. Its list is
25
+ * shard- and JVM-shaped rather than query-shaped, and note that `cpu_percent` and
26
+ * `disk_used_percent` appear in BOTH `system`/`storage` and `opensearch.database`:
27
+ * the engine reports its own view of them, so pick the type deliberately.
24
28
  */
25
29
  const METRIC_CATALOGUE = {
26
30
  system: [
@@ -68,6 +72,12 @@ const METRIC_CATALOGUE = {
68
72
  "future_parts", "is_session_expired", "lost_replicas",
69
73
  "noquorum_detached_parts",
70
74
  ],
75
+ "opensearch.database": [
76
+ "cluster_status", "active_shards", "relocating_shards", "initializing_shards",
77
+ "unassigned_shards", "active_primary_shards", "number_of_nodes",
78
+ "jvm_heap_used_percent", "cpu_percent", "disk_used_percent",
79
+ "indexing_rate", "search_rate", "replication_lag_seconds",
80
+ ],
71
81
  };
72
82
  const METRIC_TYPES = Object.keys(METRIC_CATALOGUE);
73
83
  /** Rendered into create/list descriptions so the allow-list is visible, not guessed at. */