@selfhost.dev/mcp-server 0.6.0 → 0.8.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.
- package/README.md +43 -11
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/tools/alerts.js +2 -1
- package/dist/tools/alerts.js.map +1 -1
- package/dist/tools/auth.js +13 -1
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/backups.js +15 -4
- package/dist/tools/backups.js.map +1 -1
- package/dist/tools/deployments.js +8 -14
- package/dist/tools/deployments.js.map +1 -1
- package/dist/tools/env-vars.js +23 -0
- package/dist/tools/env-vars.js.map +1 -1
- package/dist/tools/github.js +65 -4
- package/dist/tools/github.js.map +1 -1
- package/dist/tools/instances.js +249 -22
- package/dist/tools/instances.js.map +1 -1
- package/dist/tools/organizations.js +9 -5
- package/dist/tools/organizations.js.map +1 -1
- package/dist/tools/postgres-extensions.d.ts +2 -0
- package/dist/tools/postgres-extensions.js +105 -0
- package/dist/tools/postgres-extensions.js.map +1 -0
- package/dist/tools/project-databases.js +36 -2
- package/dist/tools/project-databases.js.map +1 -1
- package/dist/tools/projects.js +46 -3
- package/dist/tools/projects.js.map +1 -1
- package/dist/tools/scaling.js +268 -36
- package/dist/tools/scaling.js.map +1 -1
- package/dist/types/models.d.ts +8 -1
- package/dist/types/tiers.js +8 -0
- package/dist/types/tiers.js.map +1 -1
- package/package.json +3 -1
package/dist/tools/env-vars.js
CHANGED
|
@@ -7,6 +7,7 @@ import { session } from "../session.js";
|
|
|
7
7
|
*
|
|
8
8
|
* Endpoints (base /api/v1/platform/github_repo_deployments/:pid/env_vars):
|
|
9
9
|
* - GET /env_vars list { key, value }
|
|
10
|
+
* - GET /env_vars/detect scan the repo for the vars it expects
|
|
10
11
|
* - PUT /env_vars FULL REPLACE - deletes keys not in the payload
|
|
11
12
|
* - PATCH /env_vars merge/upsert - never deletes
|
|
12
13
|
* - DELETE /env_vars/:key remove one key
|
|
@@ -49,6 +50,28 @@ export function registerEnvVarTools(server) {
|
|
|
49
50
|
}
|
|
50
51
|
return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
|
|
51
52
|
});
|
|
53
|
+
server.tool("detect_env_vars", `Scan a deployment's repo/branch for the environment variables it expects, and report which are already configured. Read-only - it changes nothing.
|
|
54
|
+
|
|
55
|
+
Returns \`detected_vars\` [{ key, hint, source, configured, configured_value }] and a \`sources\` summary (.env.example, GitHub workflow secrets, CI files, Dockerfile/compose, framework config). \`hint\` is the placeholder found in the repo - a hint, NOT a real secret, so never set it as a value; ask the user for each missing one.
|
|
56
|
+
|
|
57
|
+
Use it right after creating a deployment (or when a build fails on a missing variable) to find out what still needs setting, then apply the answers with \`merge_env_vars\`. Vars already stored come back with configured=true so you can skip them.`, {
|
|
58
|
+
deployment_pid: z.string().min(1),
|
|
59
|
+
}, async ({ deployment_pid }) => {
|
|
60
|
+
if (!hasOrg())
|
|
61
|
+
return { content: [{ type: "text", text: NO_ORG }] };
|
|
62
|
+
const result = await apiRequest(`${DEPLOYMENTS_BASE}/${deployment_pid}/env_vars/detect`, {
|
|
63
|
+
toolName: "detect_env_vars",
|
|
64
|
+
});
|
|
65
|
+
if (!result.success) {
|
|
66
|
+
return { content: [{ type: "text", text: `Failed to detect env vars (${result.statusCode}): ${result.message}` }] };
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
content: [
|
|
70
|
+
{ type: "text", text: "Detected from the repo. `hint` values are placeholders from the source files - ask the user for the real values, don't set the hints." },
|
|
71
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
});
|
|
52
75
|
server.tool("set_env_vars", "REPLACE a deployment's ENTIRE set of environment variables with the provided map. ⚠️ Any existing key NOT included here is DELETED. To add or edit a few variables without removing the rest, use `merge_env_vars` instead - or call `list_env_vars` first and pass the full merged set here. Keys must match ^[A-Za-z_][A-Za-z0-9_]*$; values must be non-empty. Saving may trigger a rate-limited redeploy.", {
|
|
53
76
|
deployment_pid: z.string().min(1),
|
|
54
77
|
env_vars: z.record(z.string()).describe("The COMPLETE desired set { KEY: value }; replaces all existing vars"),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-vars.js","sourceRoot":"","sources":["../../src/tools/env-vars.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC
|
|
1
|
+
{"version":3,"file":"env-vars.js","sourceRoot":"","sources":["../../src/tools/env-vars.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/github.js
CHANGED
|
@@ -6,10 +6,12 @@ import { session } from "../session.js";
|
|
|
6
6
|
* list branches, auto-detect a repo's build config, and get the install URL.
|
|
7
7
|
*
|
|
8
8
|
* Endpoints:
|
|
9
|
-
* - GET
|
|
10
|
-
* - GET
|
|
11
|
-
* - POST
|
|
12
|
-
* -
|
|
9
|
+
* - GET /api/v1/platform/github_installations
|
|
10
|
+
* - GET /api/v1/platform/github_installations/:installation_pid/branches?repo_full_name=
|
|
11
|
+
* - POST /api/v1/platform/github_installations/detect_repo_config
|
|
12
|
+
* - DELETE /api/v1/platform/github_installations/:installation_pid (one)
|
|
13
|
+
* - DELETE /api/v1/platform/github_installations/disconnect (all, this org)
|
|
14
|
+
* - GET /github/connect?organization_pid= (returns a browser install URL)
|
|
13
15
|
*
|
|
14
16
|
* NOTE: connecting a GitHub account is an interactive, browser-based GitHub App
|
|
15
17
|
* installation - it CANNOT be completed headlessly. `get_github_connect_url`
|
|
@@ -102,5 +104,64 @@ export function registerGithubTools(server) {
|
|
|
102
104
|
}],
|
|
103
105
|
};
|
|
104
106
|
});
|
|
107
|
+
// No `connected_accounts` tool. `GET /github_installations/connected_accounts` 500s:
|
|
108
|
+
// the action calls `current_user.organization_users`, an association that does not exist
|
|
109
|
+
// (it is `organization_memberships`), so it has never worked and the console never calls
|
|
110
|
+
// it. `list_github_installations` covers the useful case for the active org.
|
|
111
|
+
server.tool("disconnect_github_installation", "DESTRUCTIVE: disconnect one GitHub App installation from the active organization. Pushes for its repos stop triggering deploys immediately. The backend refuses while deployments are still using the installation - delete or repoint those first. This only detaches it on the SelfHost side; the response includes a GitHub URL the user must open to revoke the app on GitHub itself. Reconnecting later is a browser flow (`get_github_connect_url`).", {
|
|
112
|
+
installation_pid: z.string().min(1).describe("Installation pid (ghi_...), from `list_github_installations`"),
|
|
113
|
+
confirm: z.boolean().describe("Must be true to proceed."),
|
|
114
|
+
}, async ({ installation_pid, confirm }) => {
|
|
115
|
+
if (!orgId())
|
|
116
|
+
return { content: [{ type: "text", text: NO_ORG }] };
|
|
117
|
+
if (!confirm) {
|
|
118
|
+
return {
|
|
119
|
+
content: [{
|
|
120
|
+
type: "text",
|
|
121
|
+
text: `⚠️ About to DISCONNECT GitHub installation ${installation_pid}. Auto-deploys from its repositories stop immediately.\n\nCall again with confirm=true to proceed.`,
|
|
122
|
+
}],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const result = await apiRequest(`${INSTALLATIONS_BASE}/${installation_pid}`, {
|
|
126
|
+
method: "DELETE",
|
|
127
|
+
toolName: "disconnect_github_installation",
|
|
128
|
+
});
|
|
129
|
+
if (!result.success) {
|
|
130
|
+
return { content: [{ type: "text", text: `Failed to disconnect installation (${result.statusCode}): ${result.message}` }] };
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
content: [
|
|
134
|
+
{ type: "text", text: "Installation disconnected. Open the GitHub revoke URL below to remove the app on GitHub's side as well." },
|
|
135
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
server.tool("disconnect_all_github_installations", "DESTRUCTIVE: disconnect EVERY GitHub App installation from the active organization at once. All auto-deploys stop. Blocked by the backend while any deployment still uses one of them. Returns `disconnected_count` and a `github_revoke_urls` list the user must open to revoke the app on GitHub. Prefer `disconnect_github_installation` when only one account should go.", {
|
|
140
|
+
confirm: z.boolean().describe("Must be true to proceed."),
|
|
141
|
+
}, async ({ confirm }) => {
|
|
142
|
+
if (!orgId())
|
|
143
|
+
return { content: [{ type: "text", text: NO_ORG }] };
|
|
144
|
+
if (!confirm) {
|
|
145
|
+
return {
|
|
146
|
+
content: [{
|
|
147
|
+
type: "text",
|
|
148
|
+
text: "⚠️ About to disconnect ALL GitHub installations for this organization. Every repository's auto-deploys stop immediately.\n\nCall again with confirm=true to proceed.",
|
|
149
|
+
}],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const result = await apiRequest(`${INSTALLATIONS_BASE}/disconnect`, {
|
|
153
|
+
method: "DELETE",
|
|
154
|
+
toolName: "disconnect_all_github_installations",
|
|
155
|
+
});
|
|
156
|
+
if (!result.success) {
|
|
157
|
+
return { content: [{ type: "text", text: `Failed to disconnect installations (${result.statusCode}): ${result.message}` }] };
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{ type: "text", text: "GitHub installations disconnected. Open each URL in `github_revoke_urls` to revoke the app on GitHub's side as well." },
|
|
162
|
+
{ type: "text", text: JSON.stringify(result.data, null, 2) },
|
|
163
|
+
],
|
|
164
|
+
};
|
|
165
|
+
});
|
|
105
166
|
}
|
|
106
167
|
//# sourceMappingURL=github.js.map
|
package/dist/tools/github.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/tools/github.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/tools/github.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/instances.js
CHANGED
|
@@ -51,11 +51,23 @@ const ENGINE_VERSIONS = {
|
|
|
51
51
|
clickhouse: CLICKHOUSE_VERSIONS,
|
|
52
52
|
};
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
54
|
+
* Version sent when the caller passes none.
|
|
55
|
+
*
|
|
56
|
+
* This has to be explicit on every engine: the backend's fallback is
|
|
57
|
+
* `supported_versions.first`, which is the OLDEST major each adapter still accepts
|
|
58
|
+
* (postgres 14, redis 7.0, clickhouse 24.3), so omitting `db_version` silently
|
|
59
|
+
* provisions a years-old release. These mirror the console's create-wizard defaults
|
|
60
|
+
* (`CreateDatabaseOptions`) so both surfaces hand out the same database - newest for
|
|
61
|
+
* postgres/redis, and the stable release rather than the newest for mysql (8.0 over 8.4)
|
|
62
|
+
* and clickhouse (the 24.8 LTS over 25.1/26.5).
|
|
57
63
|
*/
|
|
58
|
-
const
|
|
64
|
+
const ENGINE_DEFAULT_VERSION = {
|
|
65
|
+
postgres: "18",
|
|
66
|
+
mysql: "8.0",
|
|
67
|
+
redis: "7.2",
|
|
68
|
+
clickhouse: "24.8",
|
|
69
|
+
};
|
|
70
|
+
const CLICKHOUSE_DEFAULT_VERSION = ENGINE_DEFAULT_VERSION.clickhouse;
|
|
59
71
|
/**
|
|
60
72
|
* Hard RAM floor per engine - backend `InstancesController::MIN_RAM_GB`. ClickHouse below
|
|
61
73
|
* 4 GiB fails its systemd Type=notify startup, so create is rejected outright.
|
|
@@ -121,6 +133,95 @@ const tagSchema = z.object({
|
|
|
121
133
|
key: z.string().min(1),
|
|
122
134
|
value: z.string(),
|
|
123
135
|
});
|
|
136
|
+
/**
|
|
137
|
+
* Collapse heartbeat frames into per-instance, per-metric stats. A day of heartbeats
|
|
138
|
+
* from one instance is hundreds of frames; the agent consuming this needs the shape of
|
|
139
|
+
* the series (is it climbing? how high did it get?), not every sample.
|
|
140
|
+
*
|
|
141
|
+
* Non-numeric metric values are skipped rather than coerced - a metric that reports a
|
|
142
|
+
* string would otherwise land in the summary as NaN.
|
|
143
|
+
*/
|
|
144
|
+
function summarizeMetrics(frames) {
|
|
145
|
+
const byInstance = new Map();
|
|
146
|
+
const ordered = [...frames].sort((a, b) => a.timestamp_ms - b.timestamp_ms);
|
|
147
|
+
for (const frame of ordered) {
|
|
148
|
+
const key = frame.resource?.pid ?? frame.resource?.agent_id ?? "unknown";
|
|
149
|
+
let entry = byInstance.get(key);
|
|
150
|
+
if (!entry) {
|
|
151
|
+
entry = {
|
|
152
|
+
instance_pid: frame.resource?.pid ?? null,
|
|
153
|
+
agent_id: frame.resource?.agent_id ?? null,
|
|
154
|
+
host_name: frame.resource?.host_name ?? null,
|
|
155
|
+
samples: 0,
|
|
156
|
+
first_sample: null,
|
|
157
|
+
last_sample: null,
|
|
158
|
+
metric_sets: {},
|
|
159
|
+
};
|
|
160
|
+
byInstance.set(key, entry);
|
|
161
|
+
}
|
|
162
|
+
const at = new Date(frame.timestamp_ms).toISOString();
|
|
163
|
+
entry.first_sample ??= at;
|
|
164
|
+
entry.last_sample = at;
|
|
165
|
+
entry.samples += 1;
|
|
166
|
+
for (const set of frame.metric_sets ?? []) {
|
|
167
|
+
// Storage arrives as one set PER MOUNT POINT (/, /boot, and the engine's data
|
|
168
|
+
// volume), all typed "storage". Merging them would blend a 97%-full /boot into the
|
|
169
|
+
// data volume's numbers, so each mount keeps its own bucket - the same axis alert
|
|
170
|
+
// rules use, where `mount_point` is required.
|
|
171
|
+
const mount = set.attributes?.mount_point;
|
|
172
|
+
const bucketKey = typeof mount === "string" && mount.length > 0 ? `${set.type}[${mount}]` : set.type;
|
|
173
|
+
const bucket = (entry.metric_sets[bucketKey] ??= {});
|
|
174
|
+
for (const [name, value] of Object.entries(set.metrics ?? {})) {
|
|
175
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
176
|
+
continue;
|
|
177
|
+
const stat = bucket[name];
|
|
178
|
+
if (!stat) {
|
|
179
|
+
bucket[name] = { latest: value, min: value, max: value, avg: value, samples: 1 };
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// avg is kept as a running mean so the whole series never has to be held.
|
|
183
|
+
stat.avg = (stat.avg * stat.samples + value) / (stat.samples + 1);
|
|
184
|
+
stat.samples += 1;
|
|
185
|
+
stat.latest = value;
|
|
186
|
+
if (value < stat.min)
|
|
187
|
+
stat.min = value;
|
|
188
|
+
if (value > stat.max)
|
|
189
|
+
stat.max = value;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const round = (n) => Math.round(n * 1000) / 1000;
|
|
195
|
+
return [...byInstance.values()].map((entry) => ({
|
|
196
|
+
...entry,
|
|
197
|
+
metric_sets: Object.fromEntries(Object.entries(entry.metric_sets).map(([type, metrics]) => [
|
|
198
|
+
type,
|
|
199
|
+
Object.fromEntries(Object.entries(metrics).map(([name, stat]) => [
|
|
200
|
+
name,
|
|
201
|
+
{ latest: round(stat.latest), min: round(stat.min), max: round(stat.max), avg: round(stat.avg), samples: stat.samples },
|
|
202
|
+
])),
|
|
203
|
+
])),
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Present prices the way the console and the website do: all-in, with the platform
|
|
208
|
+
* margin folded into each infrastructure line instead of shown as its own row.
|
|
209
|
+
*
|
|
210
|
+
* The pricing API returns pre-margin bases plus one lump `markup_monthly`, so the lump
|
|
211
|
+
* is redistributed across the lines it was charged on (compute + storage; public IPv4
|
|
212
|
+
* is never marked up). Line sums stay exactly equal to `total_monthly`.
|
|
213
|
+
*/
|
|
214
|
+
function foldMarkup(e) {
|
|
215
|
+
const base = e.breakdown.compute_base_monthly + e.breakdown.storage_base_monthly;
|
|
216
|
+
const multiplier = base > 0 ? 1 + e.breakdown.markup_monthly / base : 1;
|
|
217
|
+
return {
|
|
218
|
+
compute: e.breakdown.compute_base_monthly * multiplier,
|
|
219
|
+
storage: e.breakdown.storage_base_monthly * multiplier,
|
|
220
|
+
instanceHourly: e.unit_prices.instance_hourly * multiplier,
|
|
221
|
+
instanceMonthly: e.unit_prices.instance_monthly * multiplier,
|
|
222
|
+
storageMonthly: e.unit_prices.storage_monthly * multiplier,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
124
225
|
export function registerInstanceTools(server) {
|
|
125
226
|
// --- Reference data tools (exempt from rate limits) ---
|
|
126
227
|
server.tool("list_regions", "List available AWS regions for instance deployment", {}, async () => {
|
|
@@ -179,7 +280,10 @@ export function registerInstanceTools(server) {
|
|
|
179
280
|
server.tool("estimate_instance_cost", `Estimate the monthly cost of a database instance BEFORE creating it using the SelfHost pricing API.
|
|
180
281
|
Call this to present accurate cost to the user for approval before calling create_instance.
|
|
181
282
|
|
|
182
|
-
Returns an itemized
|
|
283
|
+
Returns an itemized breakdown of compute, storage and add-ons. Prices are ALL-IN: the
|
|
284
|
+
platform margin is folded into each line rather than billed separately, matching the
|
|
285
|
+
console and the website - so quote these numbers to the user as-is and never add a
|
|
286
|
+
platform fee on top.
|
|
183
287
|
|
|
184
288
|
Pass type_of_dbms whenever you know the engine. ClickHouse prices differently: with
|
|
185
289
|
replica_count=1 the platform provisions a third, keeper-only node (${CLICKHOUSE_KEEPER_INSTANCE_TYPE} + ${CLICKHOUSE_KEEPER_STORAGE_GB} GB) to hold
|
|
@@ -244,24 +348,25 @@ out for a ClickHouse cluster and the estimate understates the real bill.`, {
|
|
|
244
348
|
keeperError = `Could not price the Keeper node (${keeperResult.statusCode}): ${keeperResult.message}`;
|
|
245
349
|
}
|
|
246
350
|
}
|
|
351
|
+
const allIn = foldMarkup(data);
|
|
247
352
|
const lines = [
|
|
248
|
-
"Estimated Monthly Cost
|
|
353
|
+
"Estimated Monthly Cost (all-in)",
|
|
249
354
|
"=================================",
|
|
250
355
|
`Region: ${data.region}`,
|
|
251
356
|
`Instance: ${data.instance_type}`,
|
|
252
|
-
` $${
|
|
253
|
-
`Storage: ${data.storage_size_gb} GiB ${data.storage_type} → $${
|
|
357
|
+
` $${allIn.instanceHourly.toFixed(4)}/hr → $${allIn.instanceMonthly.toFixed(2)}/mo`,
|
|
358
|
+
`Storage: ${data.storage_size_gb} GiB ${data.storage_type} → $${allIn.storageMonthly.toFixed(2)}/mo`,
|
|
254
359
|
`Databases: ${data.database_count}`,
|
|
255
360
|
`Replicas: ${data.replica_count}`,
|
|
256
361
|
"",
|
|
257
362
|
"Breakdown:",
|
|
258
|
-
` Compute: $${
|
|
259
|
-
` Storage: $${
|
|
260
|
-
` Markup: $${data.breakdown.markup_monthly.toFixed(2)}`,
|
|
363
|
+
` Compute: $${allIn.compute.toFixed(2)}`,
|
|
364
|
+
` Storage: $${allIn.storage.toFixed(2)}`,
|
|
261
365
|
` Public IPv4: $${data.breakdown.public_ipv4_monthly.toFixed(2)}`,
|
|
262
366
|
];
|
|
263
367
|
if (keeper) {
|
|
264
|
-
|
|
368
|
+
const keeperAllIn = foldMarkup(keeper);
|
|
369
|
+
lines.push("", `ClickHouse Keeper node (${CLICKHOUSE_KEEPER_INSTANCE_TYPE} + ${CLICKHOUSE_KEEPER_STORAGE_GB} GiB ${keeper.storage_type}):`, ` Compute: $${keeperAllIn.compute.toFixed(2)}`, ` Storage: $${keeperAllIn.storage.toFixed(2)}`, ` Subtotal: $${keeper.total_monthly.toFixed(2)}`, " (Required third quorum member because replica_count=1. Two replicas instead run the", " quorum embedded on the data nodes - no Keeper node and no third line item.)");
|
|
265
370
|
}
|
|
266
371
|
const total = data.total_monthly + (keeper?.total_monthly ?? 0);
|
|
267
372
|
lines.push("", `TOTAL: $${total.toFixed(2)}/month`);
|
|
@@ -271,7 +376,7 @@ out for a ClickHouse cluster and the estimate understates the real bill.`, {
|
|
|
271
376
|
if (keeperError) {
|
|
272
377
|
lines.push("", `⚠️ ${keeperError}`, "This total EXCLUDES the Keeper node that a 1-replica ClickHouse cluster requires - the real bill will be higher.");
|
|
273
378
|
}
|
|
274
|
-
lines.push("", `Pricing model: ${data.pricing_model}`, `Currency: ${data.currency}
|
|
379
|
+
lines.push("", `Pricing model: ${data.pricing_model}`, `Currency: ${data.currency}`, "All-in: every line already includes the platform margin. Charged hourly against wallet credit.");
|
|
275
380
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
276
381
|
});
|
|
277
382
|
// --- Instance lifecycle tools ---
|
|
@@ -315,15 +420,23 @@ ENGINE-SPECIFIC FOLLOW-UPS (route to the right tools per engine after creating):
|
|
|
315
420
|
- Performance tuning: the pg-config tools for postgres, the mysql-config tools for mysql; none for redis or clickhouse.
|
|
316
421
|
- PITR applies to postgres and mysql only - not redis, not clickhouse.
|
|
317
422
|
- database-users applies to postgres, mysql and clickhouse - not redis.
|
|
318
|
-
-
|
|
319
|
-
- db_version can be upgraded in place on clickhouse only (upgrade_clickhouse_version)
|
|
423
|
+
- postgresql_config and the two extension flags are postgres-only.
|
|
424
|
+
- db_version can be upgraded in place on clickhouse only (upgrade_clickhouse_version).
|
|
425
|
+
|
|
426
|
+
POSTGRES EXTENSIONS (neither is a separate engine - both are postgres + a flag):
|
|
427
|
+
- timescaledb_enabled → TimescaleDB (hypertables, continuous aggregates, compression) for
|
|
428
|
+
time-series workloads. Installing it at create costs nothing extra; enabling it later
|
|
429
|
+
restarts PostgreSQL once (enable_timescaledb).
|
|
430
|
+
- vector_enabled → pgvector (the \`vector\` type + similarity search) for embeddings.
|
|
431
|
+
- Both can be on at once here, and both are ENABLE-ONLY - the backend refuses to turn
|
|
432
|
+
either off afterwards, so ask the user before setting them.`, {
|
|
320
433
|
organization_id: z.string().optional().describe("Organization PID (defaults to active org)"),
|
|
321
434
|
region: z.string().min(1, "AWS region is required (e.g. us-east-1)"),
|
|
322
435
|
identifier: z.string().min(1, "Database instance name"),
|
|
323
436
|
instance_type: z.string().min(1, "EC2 instance type (e.g. t3.micro, r6i.xlarge)"),
|
|
324
437
|
type_of_dbms: z.enum(ENGINES).describe("Database engine"),
|
|
325
438
|
storage: storageSchema,
|
|
326
|
-
db_version: z.string().optional().describe(`Database version. postgres: ${ENGINE_VERSIONS.postgres.join(", ")}. mysql: ${ENGINE_VERSIONS.mysql.join(", ")}. redis: ${ENGINE_VERSIONS.redis.join(", ")}. clickhouse: ${ENGINE_VERSIONS.clickhouse.join(", ")} (
|
|
439
|
+
db_version: z.string().optional().describe(`Database version. postgres: ${ENGINE_VERSIONS.postgres.join(", ")} (default ${ENGINE_DEFAULT_VERSION.postgres}). mysql: ${ENGINE_VERSIONS.mysql.join(", ")} (default ${ENGINE_DEFAULT_VERSION.mysql}, the stable release). redis: ${ENGINE_VERSIONS.redis.join(", ")} (default ${ENGINE_DEFAULT_VERSION.redis}). clickhouse: ${ENGINE_VERSIONS.clickhouse.join(", ")} (default ${ENGINE_DEFAULT_VERSION.clickhouse}, the LTS). Omitting this sends the default explicitly - the backend's own fallback is the oldest major it still supports.`),
|
|
327
440
|
username: z.string().regex(/^[a-z_][a-z0-9_-]{0,30}$/i).optional().describe("Database username (default: admin). Redis ignores this (auth is a single requirepass/password); it applies to postgres, mysql and clickhouse. On clickhouse the customer user is `admin` - the built-in `default` user is platform-internal and not usable."),
|
|
328
441
|
custom_password: z.string().min(8).max(128).optional().describe("Database password (8-128 printable ASCII chars). Auto-generated if omitted."),
|
|
329
442
|
description: z.string().optional(),
|
|
@@ -337,6 +450,8 @@ ENGINE-SPECIFIC FOLLOW-UPS (route to the right tools per engine after creating):
|
|
|
337
450
|
allowed_cidr_ranges: z.array(cidrRangeSchema).max(24).optional().describe("IP whitelist (max 24 CIDR ranges)"),
|
|
338
451
|
tags: z.array(tagSchema).optional().describe("AWS resource tags"),
|
|
339
452
|
snapshot_id: z.string().optional().describe("Restore from a backup snapshot ID"),
|
|
453
|
+
timescaledb_enabled: z.boolean().optional().describe("Install TimescaleDB for time-series data (postgres only). Enable-only - it cannot be turned off later, so confirm with the user first."),
|
|
454
|
+
vector_enabled: z.boolean().optional().describe("Install pgvector for embeddings / similarity search (postgres only). Enable-only - it cannot be turned off later, so confirm with the user first."),
|
|
340
455
|
postgresql_config: z.record(z.unknown()).optional().describe("PostgreSQL parameter overrides (use preview_pg_config to see available params)"),
|
|
341
456
|
iam_role_arn: z.string().optional().describe("IAM role ARN for the EC2 instance"),
|
|
342
457
|
db_port: z.number().int().min(0).max(65535).optional().describe(`Database port (default ${ENGINE_DEFAULT_PORT.postgres} postgres, ${ENGINE_DEFAULT_PORT.mysql} mysql, ${ENGINE_DEFAULT_PORT.redis} redis, ${ENGINE_DEFAULT_PORT.clickhouse} clickhouse HTTP)`),
|
|
@@ -359,6 +474,23 @@ ENGINE-SPECIFIC FOLLOW-UPS (route to the right tools per engine after creating):
|
|
|
359
474
|
if (params.db_version && !ENGINE_VERSIONS[engine].includes(params.db_version)) {
|
|
360
475
|
return { content: [{ type: "text", text: `'${params.db_version}' is not a supported ${engine} version. Supported: ${ENGINE_VERSIONS[engine].join(", ")}.` }] };
|
|
361
476
|
}
|
|
477
|
+
// Extension flags are postgres-only. The backend silently forces them false on other
|
|
478
|
+
// engines, which would look like a successful "TimescaleDB instance" that has none -
|
|
479
|
+
// so refuse instead of quietly dropping the request.
|
|
480
|
+
if (engine !== "postgres") {
|
|
481
|
+
const requested = [
|
|
482
|
+
params.timescaledb_enabled ? "timescaledb_enabled" : null,
|
|
483
|
+
params.vector_enabled ? "vector_enabled" : null,
|
|
484
|
+
].filter(Boolean);
|
|
485
|
+
if (requested.length > 0) {
|
|
486
|
+
return {
|
|
487
|
+
content: [{
|
|
488
|
+
type: "text",
|
|
489
|
+
text: `${requested.join(" and ")} ${requested.length > 1 ? "are" : "is"} PostgreSQL-only and would be ignored on ${engine}. Create this as postgres to get ${requested.length > 1 ? "them" : "it"}, or drop the flag${requested.length > 1 ? "s" : ""}.${engine === "clickhouse" ? " (For analytics workloads ClickHouse is already columnar - it needs no extension.)" : ""}`,
|
|
490
|
+
}],
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
}
|
|
362
494
|
// Multi-AZ topology. ClickHouse replicates through a Keeper/Raft quorum rather than
|
|
363
495
|
// streaming replication, so it needs at least one replica and allows a wider fan-out.
|
|
364
496
|
const multiAz = params.multi_az === true;
|
|
@@ -406,11 +538,11 @@ ENGINE-SPECIFIC FOLLOW-UPS (route to the right tools per engine after creating):
|
|
|
406
538
|
},
|
|
407
539
|
public_access: true, // Always true - private access not yet supported
|
|
408
540
|
};
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
541
|
+
// Always pin the version. Left off the wire the backend falls back to the oldest
|
|
542
|
+
// major the adapter supports (postgres 14, redis 7.0, clickhouse 24.3), which is
|
|
543
|
+
// not what the console gives the same user.
|
|
544
|
+
const dbVersion = params.db_version ?? ENGINE_DEFAULT_VERSION[engine];
|
|
545
|
+
body.db_version = dbVersion;
|
|
414
546
|
if (params.username)
|
|
415
547
|
body.username = params.username;
|
|
416
548
|
if (params.custom_password)
|
|
@@ -438,6 +570,10 @@ ENGINE-SPECIFIC FOLLOW-UPS (route to the right tools per engine after creating):
|
|
|
438
570
|
body.tags = params.tags;
|
|
439
571
|
if (params.snapshot_id)
|
|
440
572
|
body.snapshot_id = params.snapshot_id;
|
|
573
|
+
if (params.timescaledb_enabled)
|
|
574
|
+
body.timescaledb_enabled = true;
|
|
575
|
+
if (params.vector_enabled)
|
|
576
|
+
body.vector_enabled = true;
|
|
441
577
|
if (params.postgresql_config)
|
|
442
578
|
body.postgresql_config = params.postgresql_config;
|
|
443
579
|
if (params.iam_role_arn)
|
|
@@ -459,6 +595,7 @@ ENGINE-SPECIFIC FOLLOW-UPS (route to the right tools per engine after creating):
|
|
|
459
595
|
"Instance provisioning started!",
|
|
460
596
|
"",
|
|
461
597
|
`PID: ${data.pid}`,
|
|
598
|
+
`Engine: ${engine} ${dbVersion}${params.db_version ? "" : " (default)"}`,
|
|
462
599
|
`Host: ${data.host}`,
|
|
463
600
|
`Username: ${data.username}`,
|
|
464
601
|
`Password: ${data.password}`,
|
|
@@ -468,6 +605,20 @@ ENGINE-SPECIFIC FOLLOW-UPS (route to the right tools per engine after creating):
|
|
|
468
605
|
`Group ID: ${data.group_id}`,
|
|
469
606
|
`Organization: ${data.organization_id}`,
|
|
470
607
|
];
|
|
608
|
+
if (engine === "postgres" && (params.timescaledb_enabled || params.vector_enabled)) {
|
|
609
|
+
const extensions = [
|
|
610
|
+
params.timescaledb_enabled ? "TimescaleDB" : null,
|
|
611
|
+
params.vector_enabled ? "pgvector" : null,
|
|
612
|
+
].filter(Boolean);
|
|
613
|
+
lines.push("", `${extensions.join(" + ")} ${extensions.length > 1 ? "are" : "is"} installed during provisioning (no restart needed at create).`);
|
|
614
|
+
if (params.timescaledb_enabled) {
|
|
615
|
+
lines.push("- Start with: CREATE TABLE ... then SELECT create_hypertable('your_table', 'time');", "- The timescaledb_* metrics (hypertable/chunk counts, compression ratio, total size) join this instance's `postgresql.database` alert set.");
|
|
616
|
+
}
|
|
617
|
+
if (params.vector_enabled) {
|
|
618
|
+
lines.push("- The `vector` column type and similarity operators are ready to use.");
|
|
619
|
+
}
|
|
620
|
+
lines.push(`- Neither extension can be disabled later.`);
|
|
621
|
+
}
|
|
471
622
|
if (engine === "clickhouse") {
|
|
472
623
|
lines.push("", `ClickHouse ${dbVersion}`, `- Connect over HTTP on port ${ENGINE_DEFAULT_PORT.clickhouse} (native TCP on ${CLICKHOUSE_NATIVE_PORT}).`);
|
|
473
624
|
if (multiAz) {
|
|
@@ -523,7 +674,83 @@ ENGINE-SPECIFIC FOLLOW-UPS (route to the right tools per engine after creating):
|
|
|
523
674
|
}
|
|
524
675
|
return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
|
|
525
676
|
});
|
|
526
|
-
server.tool("
|
|
677
|
+
server.tool("get_instance_metrics", `Read historical metrics for managed database instances - the same agent-heartbeat series the console graphs.
|
|
678
|
+
|
|
679
|
+
Use this to answer "how is my database doing?", to size a change before making it, or to sanity-check an alert rule (a rule on a metric that never appears here will never fire).
|
|
680
|
+
|
|
681
|
+
Returns a per-instance, per-metric summary (latest / min / max / avg + sample count) by default. Set raw=true only when you genuinely need every sample - a busy instance produces hundreds of frames per day and they will flood the context.
|
|
682
|
+
|
|
683
|
+
Metric sets (same names alert rules use):
|
|
684
|
+
- system: cpu_percent, memory_percent, disk_usage_percent, load_avg_1/5/15, swap_usage_percent, cpu_io_wait_percent
|
|
685
|
+
- network: recv_bytes_per_sec, sent_bytes_per_sec
|
|
686
|
+
- storage: disk_used_percent, disk_free_bytes, inodes_used_percent, read/write_io_per_second, ... reported PER MOUNT POINT and keyed \`storage[/mount]\` in the summary. Read the engine's data volume (e.g. \`storage[/selfhostdev/postgresql]\`), not \`storage[/]\` or \`storage[/boot]\` - a full /boot says nothing about database capacity.
|
|
687
|
+
- <engine>.database: postgresql.database | mysql.database | redis.database | clickhouse.database - the engine's own counters
|
|
688
|
+
- pgbouncer.stats appears once pooling is enabled. It is READABLE here but NOT alertable - the alert-rule catalog has no such metric_type, so a rule on it is rejected.
|
|
689
|
+
|
|
690
|
+
An empty result usually means the agent has not reported in that window (a stopped instance, or one still provisioning), not that the instance is idle.`, {
|
|
691
|
+
organization_id: z.string().optional().describe("Organization PID (defaults to active org)"),
|
|
692
|
+
instance_pid: z.string().optional().describe("Limit to one instance (awsinst_...). Omit for every instance in the org."),
|
|
693
|
+
start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD").optional()
|
|
694
|
+
.describe("Inclusive start date, YYYY-MM-DD (default: yesterday). The window covers whole days."),
|
|
695
|
+
end_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD").optional()
|
|
696
|
+
.describe("Inclusive end date, YYYY-MM-DD (default: today)"),
|
|
697
|
+
metric_type: z.enum(["system", "network", "storage", "postgresql.database", "mysql.database", "redis.database", "clickhouse.database"]).optional()
|
|
698
|
+
.describe("Only return this metric set. Cheaper than filtering client-side."),
|
|
699
|
+
raw: z.boolean().optional().describe("Return every heartbeat frame instead of the summary. Verbose - use a narrow window and a single instance."),
|
|
700
|
+
}, async ({ organization_id, instance_pid, start_date, end_date, metric_type, raw }) => {
|
|
701
|
+
const orgId = resolveOrgId(organization_id);
|
|
702
|
+
if (!orgId) {
|
|
703
|
+
return { content: [{ type: "text", text: noOrgMessage }] };
|
|
704
|
+
}
|
|
705
|
+
const today = new Date();
|
|
706
|
+
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
|
|
707
|
+
const asDate = (d) => d.toISOString().slice(0, 10);
|
|
708
|
+
const from = start_date ?? asDate(yesterday);
|
|
709
|
+
const to = end_date ?? asDate(today);
|
|
710
|
+
if (from > to) {
|
|
711
|
+
return { content: [{ type: "text", text: `start_date ${from} is after end_date ${to}.` }] };
|
|
712
|
+
}
|
|
713
|
+
const query = { start_date: from, end_date: to };
|
|
714
|
+
if (instance_pid)
|
|
715
|
+
query.pid = instance_pid;
|
|
716
|
+
if (metric_type)
|
|
717
|
+
query.metric_type = metric_type;
|
|
718
|
+
const result = await apiRequest(`/organizations/${orgId}/instance_metrics`, {
|
|
719
|
+
toolName: "get_instance_metrics",
|
|
720
|
+
query,
|
|
721
|
+
skipOrgInjection: true,
|
|
722
|
+
});
|
|
723
|
+
if (!result.success) {
|
|
724
|
+
return { content: [{ type: "text", text: `Failed to get instance metrics (${result.statusCode}): ${result.message}` }] };
|
|
725
|
+
}
|
|
726
|
+
const frames = Array.isArray(result.data) ? result.data : [];
|
|
727
|
+
if (frames.length === 0) {
|
|
728
|
+
return {
|
|
729
|
+
content: [{
|
|
730
|
+
type: "text",
|
|
731
|
+
text: `No metrics reported between ${from} and ${to}${instance_pid ? ` for ${instance_pid}` : ""}. The agent may not have checked in - confirm the instance is running with \`get_instance\`.`,
|
|
732
|
+
}],
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
if (raw) {
|
|
736
|
+
return {
|
|
737
|
+
content: [{ type: "text", text: JSON.stringify({ window: { start_date: from, end_date: to }, frames }, null, 2) }],
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
return {
|
|
741
|
+
content: [{
|
|
742
|
+
type: "text",
|
|
743
|
+
text: JSON.stringify({ window: { start_date: from, end_date: to }, instances: summarizeMetrics(frames) }, null, 2),
|
|
744
|
+
}],
|
|
745
|
+
};
|
|
746
|
+
});
|
|
747
|
+
// No manual failover tool on purpose. `POST /instances/:pid/trigger_failover` exists, but
|
|
748
|
+
// failover is an AUTOMATIC platform behaviour: InstanceHealthCheckJob promotes a replica on
|
|
749
|
+
// its own when the master fails (30s cooldown), and the console exposes failovers only as
|
|
750
|
+
// history - activity counts, a `failover_completed` state, achievement badges - with no
|
|
751
|
+
// button to trigger one. The endpoint is an operator escape hatch, so it stays out of an
|
|
752
|
+
// LLM's reach: it drops every connection and the old master never returns as master.
|
|
753
|
+
server.tool("update_instance", "Update an instance's configuration. Some changes are synchronous (db_port, backup, CIDR ranges), others are async (instance_type, multi_az, storage). Cannot update replicas - update the master instead. Not handled here: enabling TimescaleDB or pgvector on postgres (enable_timescaledb / enable_pgvector - both irreversible) and ClickHouse version upgrades (upgrade_clickhouse_version).", {
|
|
527
754
|
instance_id: z.string().min(1, "Instance PID is required"),
|
|
528
755
|
identifier: z.string().optional().describe("New instance name"),
|
|
529
756
|
delete_protection: z.boolean().optional(),
|