@selfhost.dev/mcp-server 0.7.0 → 0.9.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 (53) hide show
  1. package/README.md +118 -17
  2. package/dist/index.js +19 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/tools/alerts.js +83 -12
  5. package/dist/tools/alerts.js.map +1 -1
  6. package/dist/tools/auth.js +13 -1
  7. package/dist/tools/auth.js.map +1 -1
  8. package/dist/tools/backups.js +197 -18
  9. package/dist/tools/backups.js.map +1 -1
  10. package/dist/tools/billing.js +5 -1
  11. package/dist/tools/billing.js.map +1 -1
  12. package/dist/tools/capacity.d.ts +2 -0
  13. package/dist/tools/capacity.js +253 -0
  14. package/dist/tools/capacity.js.map +1 -0
  15. package/dist/tools/database-users.js +33 -6
  16. package/dist/tools/database-users.js.map +1 -1
  17. package/dist/tools/deployments.js +14 -16
  18. package/dist/tools/deployments.js.map +1 -1
  19. package/dist/tools/env-vars.js +23 -0
  20. package/dist/tools/env-vars.js.map +1 -1
  21. package/dist/tools/github.js +65 -4
  22. package/dist/tools/github.js.map +1 -1
  23. package/dist/tools/instance-logs.d.ts +2 -0
  24. package/dist/tools/instance-logs.js +151 -0
  25. package/dist/tools/instance-logs.js.map +1 -0
  26. package/dist/tools/instances.js +251 -25
  27. package/dist/tools/instances.js.map +1 -1
  28. package/dist/tools/organizations.js +107 -13
  29. package/dist/tools/organizations.js.map +1 -1
  30. package/dist/tools/postgres-tls.d.ts +16 -0
  31. package/dist/tools/postgres-tls.js +64 -0
  32. package/dist/tools/postgres-tls.js.map +1 -0
  33. package/dist/tools/project-activities.d.ts +2 -0
  34. package/dist/tools/project-activities.js +52 -0
  35. package/dist/tools/project-activities.js.map +1 -0
  36. package/dist/tools/project-backups.d.ts +2 -0
  37. package/dist/tools/project-backups.js +159 -0
  38. package/dist/tools/project-backups.js.map +1 -0
  39. package/dist/tools/project-databases.js +26 -2
  40. package/dist/tools/project-databases.js.map +1 -1
  41. package/dist/tools/project-services.js +136 -25
  42. package/dist/tools/project-services.js.map +1 -1
  43. package/dist/tools/projects.js +151 -5
  44. package/dist/tools/projects.js.map +1 -1
  45. package/dist/tools/scaling.js +401 -36
  46. package/dist/tools/scaling.js.map +1 -1
  47. package/dist/tools/webhooks.d.ts +2 -0
  48. package/dist/tools/webhooks.js +195 -0
  49. package/dist/tools/webhooks.js.map +1 -0
  50. package/dist/types/models.d.ts +8 -1
  51. package/dist/types/tiers.js +5 -0
  52. package/dist/types/tiers.js.map +1 -1
  53. package/package.json +3 -2
@@ -0,0 +1,195 @@
1
+ import { z } from "zod";
2
+ import { apiRequest } from "../client.js";
3
+ import { session } from "../session.js";
4
+ /**
5
+ * Outgoing webhook endpoints - HTTPS callbacks the platform POSTs to when
6
+ * something happens in the organization.
7
+ *
8
+ * Endpoints (org-scoped, full CRUD):
9
+ * - GET /organizations/:pid/webhook_endpoints
10
+ * - GET /organizations/:pid/webhook_endpoints/:id
11
+ * - POST /organizations/:pid/webhook_endpoints
12
+ * - PATCH /organizations/:pid/webhook_endpoints/:id
13
+ * - DELETE /organizations/:pid/webhook_endpoints/:id (204)
14
+ *
15
+ * These are ORG-WIDE and event-typed, which makes them different from the
16
+ * deployment notification bindings in `deployment-notifications.ts` - those wire
17
+ * one deployment's outcomes to a notification channel (Slack, email). If a user
18
+ * wants "tell my Slack when this app deploys", that is the other module. If they
19
+ * want "POST to my service when anything of this type happens anywhere in the
20
+ * org", it is this one.
21
+ *
22
+ * A `secret` is generated server-side at create so the receiver can verify the
23
+ * signature. It is not settable.
24
+ */
25
+ const VALID_EVENTS = [
26
+ "instance.scaling_failed",
27
+ "deploy_run.succeeded",
28
+ "deploy_run.failed",
29
+ "deploy_run.aborted",
30
+ "preview_deployment.created",
31
+ "preview_deployment.destroyed",
32
+ "database_user.password_rotated",
33
+ ];
34
+ /** What each event is actually for - so a subscription is a choice, not a guess. */
35
+ const EVENT_NOTES = [
36
+ "`instance.scaling_failed` - an auto-scale or resize did not complete. The one worth subscribing to for anything production: a failed scale is silent otherwise, and a failed cluster scale plan also blocks all further scaling for that group.",
37
+ "`deploy_run.succeeded` / `deploy_run.failed` / `deploy_run.aborted` - a build run reached a terminal state. Fires for every deployment in the org, not one app.",
38
+ "`preview_deployment.created` / `preview_deployment.destroyed` - a per-branch preview environment appeared or went away.",
39
+ "`database_user.password_rotated` - a database user's password was rotated. THIS IS THE SIGNAL a credential consumer needs when `password_rotation_enabled` is true on that user: the payload never carries the password, so the receiver must refetch with `rotate_database_user_password`. Subscribing to it is what makes automatic rotation safe rather than breaking.",
40
+ ].join("\n- ");
41
+ const noOrgMessage = "No organization selected. Call `select_organization` first or pass an organization_id.";
42
+ function resolveOrgId(organization_id) {
43
+ return organization_id || session.getActiveOrgId();
44
+ }
45
+ export function registerWebhookTools(server) {
46
+ server.tool("list_webhook_endpoints", `List the organization's outgoing webhook endpoints - the HTTPS URLs the platform calls when subscribed events happen.
47
+
48
+ Each row carries its \`pid\`, \`url\` and \`subscribed_events\`.
49
+
50
+ Not to be confused with deployment notification channels (\`list_deployment_notification_bindings\`), which push a specific deployment's outcome to Slack or email. These are org-wide, event-typed HTTP callbacks for a receiver you run.`, {
51
+ organization_id: z.string().optional().describe("Organization PID (defaults to active org)"),
52
+ }, async ({ organization_id }) => {
53
+ const orgId = resolveOrgId(organization_id);
54
+ if (!orgId)
55
+ return { content: [{ type: "text", text: noOrgMessage }] };
56
+ const result = await apiRequest(`/organizations/${orgId}/webhook_endpoints`, {
57
+ toolName: "list_webhook_endpoints",
58
+ skipOrgInjection: true,
59
+ });
60
+ if (!result.success) {
61
+ return { content: [{ type: "text", text: `Failed to list webhook endpoints (${result.statusCode}): ${result.message}` }] };
62
+ }
63
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
64
+ });
65
+ server.tool("get_webhook_endpoint", "Get one webhook endpoint - its URL and the events it is subscribed to. Use it to check what a receiver will actually be told before changing anything.", {
66
+ webhook_endpoint_id: z.string().min(1).describe("Endpoint pid, from `list_webhook_endpoints`"),
67
+ organization_id: z.string().optional().describe("Organization PID (defaults to active org)"),
68
+ }, async ({ webhook_endpoint_id, organization_id }) => {
69
+ const orgId = resolveOrgId(organization_id);
70
+ if (!orgId)
71
+ return { content: [{ type: "text", text: noOrgMessage }] };
72
+ const result = await apiRequest(`/organizations/${orgId}/webhook_endpoints/${webhook_endpoint_id}`, {
73
+ toolName: "get_webhook_endpoint",
74
+ skipOrgInjection: true,
75
+ });
76
+ if (!result.success) {
77
+ return { content: [{ type: "text", text: `Failed to get webhook endpoint (${result.statusCode}): ${result.message}` }] };
78
+ }
79
+ return { content: [{ type: "text", text: JSON.stringify(result.data, null, 2) }] };
80
+ });
81
+ server.tool("create_webhook_endpoint", `Register an HTTPS URL to receive platform events for this organization.
82
+
83
+ The URL MUST be HTTPS - plain HTTP is rejected. At least one event is required.
84
+
85
+ Available events:
86
+ - ${EVENT_NOTES}
87
+
88
+ Subscribe deliberately: every event fires for the WHOLE organization, so \`deploy_run.*\` on a busy org is a lot of traffic to a receiver that only cares about one app.
89
+
90
+ A signing \`secret\` is generated server-side at create and is NOT settable. The receiver should verify it - tell the user to grab it from the create response, because that is how they authenticate the calls.
91
+
92
+ Requires \`webhook_endpoints:create\` (owner or admin).`, {
93
+ organization_id: z.string().optional().describe("Organization PID (defaults to active org)"),
94
+ url: z.string().url().describe("HTTPS URL to POST events to. Plain HTTP is rejected."),
95
+ subscribed_events: z.array(z.enum(VALID_EVENTS)).min(1).describe("Events to send. An unknown event name is rejected with the offending value named."),
96
+ }, async ({ organization_id, url, subscribed_events }) => {
97
+ const orgId = resolveOrgId(organization_id);
98
+ if (!orgId)
99
+ return { content: [{ type: "text", text: noOrgMessage }] };
100
+ if (!url.toLowerCase().startsWith("https://")) {
101
+ return { content: [{ type: "text", text: "Webhook URLs must be HTTPS - the backend rejects plain HTTP. Events carry account activity, so the transport has to be encrypted." }] };
102
+ }
103
+ const result = await apiRequest(`/organizations/${orgId}/webhook_endpoints`, {
104
+ method: "POST",
105
+ body: { webhook_endpoint: { url, subscribed_events } },
106
+ toolName: "create_webhook_endpoint",
107
+ skipOrgInjection: true,
108
+ });
109
+ if (!result.success) {
110
+ return { content: [{ type: "text", text: `Failed to create webhook endpoint (${result.statusCode}): ${result.message}` }] };
111
+ }
112
+ return {
113
+ content: [
114
+ { type: "text", text: `Webhook endpoint created for ${url}, subscribed to ${subscribed_events.length} event type(s). The response includes a signing secret - the receiver needs it to verify calls, so pass it to the user now; it is generated once.` },
115
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
116
+ ],
117
+ };
118
+ });
119
+ server.tool("update_webhook_endpoint", `Change a webhook endpoint's URL or which events it receives.
120
+
121
+ \`subscribed_events\` REPLACES the list, it does not add to it. Read the current set with \`get_webhook_endpoint\` and send the full intended list, or you will silently unsubscribe the receiver from everything you left out.
122
+
123
+ Available events:
124
+ - ${EVENT_NOTES}
125
+
126
+ Requires \`webhook_endpoints:update\` (owner or admin).`, {
127
+ webhook_endpoint_id: z.string().min(1).describe("Endpoint pid, from `list_webhook_endpoints`"),
128
+ organization_id: z.string().optional().describe("Organization PID (defaults to active org)"),
129
+ url: z.string().url().optional().describe("New HTTPS URL"),
130
+ subscribed_events: z.array(z.enum(VALID_EVENTS)).min(1).optional().describe("REPLACES the whole subscription list - include everything the endpoint should keep receiving"),
131
+ }, async ({ webhook_endpoint_id, organization_id, url, subscribed_events }) => {
132
+ const orgId = resolveOrgId(organization_id);
133
+ if (!orgId)
134
+ return { content: [{ type: "text", text: noOrgMessage }] };
135
+ if (url && !url.toLowerCase().startsWith("https://")) {
136
+ return { content: [{ type: "text", text: "Webhook URLs must be HTTPS - the backend rejects plain HTTP." }] };
137
+ }
138
+ const endpoint = {};
139
+ if (url !== undefined)
140
+ endpoint.url = url;
141
+ if (subscribed_events !== undefined)
142
+ endpoint.subscribed_events = subscribed_events;
143
+ if (Object.keys(endpoint).length === 0) {
144
+ return { content: [{ type: "text", text: "No changes to apply - pass `url`, `subscribed_events`, or both." }] };
145
+ }
146
+ const result = await apiRequest(`/organizations/${orgId}/webhook_endpoints/${webhook_endpoint_id}`, {
147
+ method: "PATCH",
148
+ body: { webhook_endpoint: endpoint },
149
+ toolName: "update_webhook_endpoint",
150
+ skipOrgInjection: true,
151
+ });
152
+ if (!result.success) {
153
+ return { content: [{ type: "text", text: `Failed to update webhook endpoint (${result.statusCode}): ${result.message}` }] };
154
+ }
155
+ return {
156
+ content: [
157
+ { type: "text", text: subscribed_events ? "Webhook endpoint updated. Its subscription is now exactly the list you sent." : "Webhook endpoint updated." },
158
+ { type: "text", text: JSON.stringify(result.data, null, 2) },
159
+ ],
160
+ };
161
+ });
162
+ server.tool("delete_webhook_endpoint", `DESTRUCTIVE: delete a webhook endpoint. Its delivery history goes with it and the receiver stops being told anything.
163
+
164
+ Check what it was subscribed to first (\`get_webhook_endpoint\`). If it carried \`database_user.password_rotated\` for a user with automatic rotation on, deleting this silently removes the only signal that credential's consumer gets - that consumer will break at the next rotation with nothing to explain why. Say so before proceeding.
165
+
166
+ If the goal is to receive less rather than nothing, \`update_webhook_endpoint\` can narrow the event list instead.
167
+
168
+ Requires \`webhook_endpoints:destroy\` (owner or admin). You MUST set confirm=true.`, {
169
+ webhook_endpoint_id: z.string().min(1).describe("Endpoint pid, from `list_webhook_endpoints`"),
170
+ organization_id: z.string().optional().describe("Organization PID (defaults to active org)"),
171
+ confirm: z.boolean().describe("Must be true. The receiver stops getting events and the delivery history is removed."),
172
+ }, async ({ webhook_endpoint_id, organization_id, confirm }) => {
173
+ const orgId = resolveOrgId(organization_id);
174
+ if (!orgId)
175
+ return { content: [{ type: "text", text: noOrgMessage }] };
176
+ if (!confirm) {
177
+ return {
178
+ content: [{
179
+ type: "text",
180
+ text: `About to delete webhook endpoint ${webhook_endpoint_id}. Its receiver stops getting events and the delivery history is removed.\n\nCall \`get_webhook_endpoint\` first and check what it is subscribed to - if it carries \`database_user.password_rotated\`, something downstream may depend on it to refetch credentials. To narrow the events instead of removing the endpoint, use \`update_webhook_endpoint\`.\n\nTo proceed, call again with confirm=true.`,
181
+ }],
182
+ };
183
+ }
184
+ const result = await apiRequest(`/organizations/${orgId}/webhook_endpoints/${webhook_endpoint_id}`, {
185
+ method: "DELETE",
186
+ toolName: "delete_webhook_endpoint",
187
+ skipOrgInjection: true,
188
+ });
189
+ if (!result.success) {
190
+ return { content: [{ type: "text", text: `Failed to delete webhook endpoint (${result.statusCode}): ${result.message}` }] };
191
+ }
192
+ return { content: [{ type: "text", text: `Webhook endpoint ${webhook_endpoint_id} deleted.` }] };
193
+ });
194
+ }
195
+ //# sourceMappingURL=webhooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/tools/webhooks.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,14 @@ export interface User {
9
9
  updated_at: string;
10
10
  }
11
11
  export interface Organization {
12
- id: string;
12
+ /**
13
+ * The organization's public id. `Organization` uses `pid` as its primary key on the
14
+ * backend and the table has no `id` column, so the API never serializes one - reading
15
+ * `id` here silently yields undefined. `id` stays declared, optional, only so an older
16
+ * or differently-shaped payload still type-checks.
17
+ */
18
+ pid: string;
19
+ id?: string;
13
20
  name: string;
14
21
  description: string | null;
15
22
  is_creator: boolean;
@@ -9,6 +9,8 @@ export const EXEMPT_TOOLS = new Set([
9
9
  "list_regions",
10
10
  "get_instance_types_for_region",
11
11
  "list_storage_types",
12
+ // Project server sizes - public reference data, same stance as the AWS lookups above.
13
+ "list_server_types",
12
14
  ]);
13
15
  export const WRITE_TOOLS = new Set([
14
16
  "create_or_login_user",
@@ -127,6 +129,9 @@ export const WRITE_TOOLS = new Set([
127
129
  // Postgres extensions (Module 26)
128
130
  "enable_timescaledb",
129
131
  "enable_pgvector",
132
+ // GitHub installation teardown (Module 19)
133
+ "disconnect_github_installation",
134
+ "disconnect_all_github_installations",
130
135
  ]);
131
136
  export function categorizeOperation(toolName) {
132
137
  if (EXEMPT_TOOLS.has(toolName))
@@ -1 +1 @@
1
- {"version":3,"file":"tiers.js","sourceRoot":"","sources":["../../src/types/tiers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tiers.js","sourceRoot":"","sources":["../../src/types/tiers.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selfhost.dev/mcp-server",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "MCP server for SelfHost.dev - provision managed databases and deploy apps (PaaS) via natural language",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -48,7 +48,8 @@
48
48
  "start": "node dist/index.js",
49
49
  "dev": "tsx src/index.ts",
50
50
  "typecheck": "tsc --noEmit",
51
- "prepublishOnly": "npm run build"
51
+ "prepublishOnly": "npm run check && npm run build",
52
+ "check": "node scripts/check-tools.mjs"
52
53
  },
53
54
  "dependencies": {
54
55
  "@modelcontextprotocol/sdk": "^1.12.1",