@otwa/mcp-server 0.1.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/dist/index.js ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
5
+ const server_1 = require("./server");
6
+ const env_1 = require("./util/env");
7
+ const version_1 = require("./util/version");
8
+ async function main() {
9
+ // Surface CLI version/help without trying to read the API key — useful for
10
+ // doctor-style checks before the customer has set OTWA_API_KEY.
11
+ const argv = process.argv.slice(2);
12
+ if (argv.includes('--version') || argv.includes('-v')) {
13
+ const pkg = (0, version_1.packageInfo)();
14
+ process.stdout.write(`${pkg.name} ${pkg.version}\n`);
15
+ return;
16
+ }
17
+ if (argv.includes('--help') || argv.includes('-h')) {
18
+ printHelp();
19
+ return;
20
+ }
21
+ let env;
22
+ try {
23
+ env = (0, env_1.readEnv)();
24
+ }
25
+ catch (err) {
26
+ // Write to stderr so it shows up in MCP-client logs; exit non-zero so
27
+ // the client surfaces it as an install error.
28
+ const msg = err instanceof Error ? err.message : String(err);
29
+ process.stderr.write(`[otwa-mcp] ${msg}\n`);
30
+ process.exit(1);
31
+ }
32
+ const server = (0, server_1.createServer)(env);
33
+ const transport = new stdio_js_1.StdioServerTransport();
34
+ await server.connect(transport);
35
+ // Graceful shutdown so the LLM client doesn't see a hang on Ctrl-C / SIGTERM.
36
+ const shutdown = async (signal) => {
37
+ try {
38
+ await server.close();
39
+ }
40
+ catch {
41
+ // best-effort
42
+ }
43
+ process.stderr.write(`[otwa-mcp] shutdown on ${signal}\n`);
44
+ process.exit(0);
45
+ };
46
+ process.on('SIGINT', () => void shutdown('SIGINT'));
47
+ process.on('SIGTERM', () => void shutdown('SIGTERM'));
48
+ }
49
+ function printHelp() {
50
+ const pkg = (0, version_1.packageInfo)();
51
+ process.stdout.write(`${pkg.name} ${pkg.version}\n` +
52
+ '\n' +
53
+ 'Official Model Context Protocol server for otwa.cloud.\n' +
54
+ '\n' +
55
+ 'Usage:\n' +
56
+ ' otwa-mcp Start the stdio MCP server (the normal case — spawned\n' +
57
+ ' by Claude Code, Cursor, Windsurf, Zed, etc.)\n' +
58
+ ' otwa-mcp --version Print version and exit\n' +
59
+ ' otwa-mcp --help Show this help\n' +
60
+ '\n' +
61
+ 'Environment:\n' +
62
+ ' OTWA_API_KEY Required. Generate at https://otwa.cloud/dashboard/settings/api-keys\n' +
63
+ ' OTWA_API_BASE Optional. Default https://api.otwa.cloud\n' +
64
+ ' OTWA_TELEMETRY Optional. Set to 1 to send anonymous usage pings.\n' +
65
+ '\n' +
66
+ 'Docs: https://otwa.cloud/docs/mcp\n');
67
+ }
68
+ main().catch((err) => {
69
+ const msg = err instanceof Error ? err.stack || err.message : String(err);
70
+ process.stderr.write(`[otwa-mcp] fatal: ${msg}\n`);
71
+ process.exit(1);
72
+ });
73
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/server.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createServer = createServer;
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const http_1 = require("./client/http");
6
+ const _register_1 = require("./tools/_register");
7
+ const version_1 = require("./util/version");
8
+ function createServer(env) {
9
+ const pkg = (0, version_1.packageInfo)();
10
+ const server = new mcp_js_1.McpServer({
11
+ name: pkg.name,
12
+ version: pkg.version,
13
+ });
14
+ const client = new http_1.OtwaClient({
15
+ apiKey: env.apiKey,
16
+ apiBase: env.apiBase,
17
+ });
18
+ (0, _register_1.registerAllTools)(server, client);
19
+ return server;
20
+ }
21
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";;AAMA,oCAcC;AApBD,oEAAoE;AACpE,wCAA2C;AAC3C,iDAAqD;AACrD,4CAA6C;AAG7C,SAAgB,YAAY,CAAC,GAAY;IACvC,MAAM,GAAG,GAAG,IAAA,qBAAW,GAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,IAAI,kBAAS,CAAC;QAC3B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO;KACrB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,IAAI,iBAAU,CAAC;QAC5B,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,OAAO,EAAE,GAAG,CAAC,OAAO;KACrB,CAAC,CAAC;IAEH,IAAA,4BAAgB,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.jsonResult = jsonResult;
4
+ exports.textResult = textResult;
5
+ exports.safeHandler = safeHandler;
6
+ const errors_1 = require("../client/errors");
7
+ /** Format a JSON value as a single text-block tool result. The LLM gets clean
8
+ * pretty-printed JSON it can reason about and quote back to the user. */
9
+ function jsonResult(value) {
10
+ return {
11
+ content: [
12
+ {
13
+ type: 'text',
14
+ text: JSON.stringify(value, null, 2),
15
+ },
16
+ ],
17
+ };
18
+ }
19
+ /** Format a plain prose response (no JSON). Used for narrative tools like
20
+ * SSO link issuance where dumping JSON would just confuse the model. */
21
+ function textResult(text) {
22
+ return { content: [{ type: 'text', text }] };
23
+ }
24
+ /** Wrap a tool handler so any throw becomes a structured error result instead
25
+ * of crashing the transport. MCP clients display these to the user and let
26
+ * the model see the error message and recover. */
27
+ function safeHandler(fn) {
28
+ return async (args) => {
29
+ try {
30
+ return await fn(args);
31
+ }
32
+ catch (err) {
33
+ const message = err instanceof errors_1.OtwaApiError
34
+ ? err.message
35
+ : err instanceof Error
36
+ ? err.message
37
+ : String(err);
38
+ return {
39
+ isError: true,
40
+ content: [{ type: 'text', text: message }],
41
+ };
42
+ }
43
+ };
44
+ }
45
+ //# sourceMappingURL=_helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_helpers.js","sourceRoot":"","sources":["../../src/tools/_helpers.ts"],"names":[],"mappings":";;AAKA,gCASC;AAID,gCAEC;AAKD,kCAmBC;AA3CD,6CAAgD;AAEhD;0EAC0E;AAC1E,SAAgB,UAAU,CAAC,KAAc;IACvC,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;aACrC;SACF;KACF,CAAC;AACJ,CAAC;AAED;yEACyE;AACzE,SAAgB,UAAU,CAAC,IAAY;IACrC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAC/C,CAAC;AAED;;mDAEmD;AACnD,SAAgB,WAAW,CACzB,EAA4C;IAE5C,OAAO,KAAK,EAAE,IAAW,EAAE,EAAE;QAC3B,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,OAAO,GACX,GAAG,YAAY,qBAAY;gBACzB,CAAC,CAAC,GAAG,CAAC,OAAO;gBACb,CAAC,CAAC,GAAG,YAAY,KAAK;oBACpB,CAAC,CAAC,GAAG,CAAC,OAAO;oBACb,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACpB,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;aAC3C,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerAllTools = registerAllTools;
4
+ const account_1 = require("./account");
5
+ const billing_1 = require("./billing");
6
+ const catalog_1 = require("./catalog");
7
+ const networking_1 = require("./networking");
8
+ const servers_1 = require("./servers");
9
+ const webhooks_1 = require("./webhooks");
10
+ function registerAllTools(server, client) {
11
+ (0, account_1.registerAccountTools)(server, client);
12
+ (0, catalog_1.registerCatalogTools)(server, client);
13
+ (0, servers_1.registerServerTools)(server, client);
14
+ (0, networking_1.registerNetworkingTools)(server, client);
15
+ (0, billing_1.registerBillingTools)(server, client);
16
+ (0, webhooks_1.registerWebhookTools)(server, client);
17
+ }
18
+ //# sourceMappingURL=_register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_register.js","sourceRoot":"","sources":["../../src/tools/_register.ts"],"names":[],"mappings":";;AASA,4CAOC;AAdD,uCAAiD;AACjD,uCAAiD;AACjD,uCAAiD;AACjD,6CAAuD;AACvD,uCAAgD;AAChD,yCAAkD;AAElD,SAAgB,gBAAgB,CAAC,MAAiB,EAAE,MAAkB;IACpE,IAAA,8BAAoB,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,IAAA,8BAAoB,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,IAAA,6BAAmB,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,IAAA,oCAAuB,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,IAAA,8BAAoB,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,IAAA,+BAAoB,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerAccountTools = registerAccountTools;
4
+ const _helpers_1 = require("./_helpers");
5
+ function registerAccountTools(server, client) {
6
+ server.registerTool('otwa_account', {
7
+ title: 'Get otwa.cloud account info',
8
+ description: 'Returns the authenticated user account: id, email, name, balance, tier, signup date. ' +
9
+ 'Call this first to confirm which account the current API key belongs to before any other operation.',
10
+ inputSchema: {},
11
+ }, (0, _helpers_1.safeHandler)(async () => {
12
+ const account = await client.request('/v1/account');
13
+ return (0, _helpers_1.jsonResult)(account);
14
+ }));
15
+ }
16
+ //# sourceMappingURL=account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/tools/account.ts"],"names":[],"mappings":";;AAKA,oDAeC;AAjBD,yCAAqD;AAErD,SAAgB,oBAAoB,CAAC,MAAiB,EAAE,MAAkB;IACxE,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,6BAA6B;QACpC,WAAW,EACT,uFAAuF;YACvF,qGAAqG;QACvG,WAAW,EAAE,EAAE;KAChB,EACD,IAAA,sBAAW,EAAC,KAAK,IAAI,EAAE;QACrB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAkB,aAAa,CAAC,CAAC;QACrE,OAAO,IAAA,qBAAU,EAAC,OAAO,CAAC,CAAC;IAC7B,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerBillingTools = registerBillingTools;
4
+ const zod_1 = require("zod");
5
+ const _helpers_1 = require("./_helpers");
6
+ function registerBillingTools(server, client) {
7
+ server.registerTool('otwa_list_invoices', {
8
+ title: 'List billing invoices',
9
+ description: 'Returns paged invoice history (most recent first). Each entry has id, invoiceNumber, total, ' +
10
+ 'status (paid/open/void), and creation date. Use otwa_get_invoice for line-item detail.',
11
+ inputSchema: {
12
+ page: zod_1.z.number().int().min(1).optional().describe('Page number, default 1.'),
13
+ limit: zod_1.z.number().int().min(1).max(100).optional().describe('Page size, default 20, max 100.'),
14
+ },
15
+ }, (0, _helpers_1.safeHandler)(async ({ page, limit }) => {
16
+ const res = await client.request('/v1/billing/invoices', {
17
+ query: { page, limit },
18
+ });
19
+ return (0, _helpers_1.jsonResult)(res);
20
+ }));
21
+ server.registerTool('otwa_get_invoice', {
22
+ title: 'Get one invoice in detail',
23
+ description: 'Returns the full invoice including line items, taxes, and payment status.',
24
+ inputSchema: {
25
+ id: zod_1.z.string().describe('Invoice UUID. Source: otwa_list_invoices.'),
26
+ },
27
+ }, (0, _helpers_1.safeHandler)(async ({ id }) => {
28
+ const res = await client.request(`/v1/billing/invoices/${encodeURIComponent(id)}`);
29
+ return (0, _helpers_1.jsonResult)(res);
30
+ }));
31
+ server.registerTool('otwa_list_transactions', {
32
+ title: 'List billing transactions (top-ups, charges, refunds)',
33
+ description: 'Returns paged transaction history. Distinct from invoices — invoices are the document, ' +
34
+ 'transactions are the money movements (Stripe charges, crypto deposits, balance adjustments).',
35
+ inputSchema: {
36
+ page: zod_1.z.number().int().min(1).optional().describe('Page number, default 1.'),
37
+ limit: zod_1.z.number().int().min(1).max(100).optional().describe('Page size, default 20, max 100.'),
38
+ },
39
+ }, (0, _helpers_1.safeHandler)(async ({ page, limit }) => {
40
+ const res = await client.request('/v1/billing/transactions', { query: { page, limit } });
41
+ return (0, _helpers_1.jsonResult)(res);
42
+ }));
43
+ server.registerTool('otwa_get_wallet_balance', {
44
+ title: 'Get crypto wallet balances + deposit addresses',
45
+ description: 'Returns the on-chain wallets the account holds, with chain, address, and balance. The address ' +
46
+ 'can be shared with the user to top up. Read-only — does not create new addresses.',
47
+ inputSchema: {},
48
+ }, (0, _helpers_1.safeHandler)(async () => {
49
+ const res = await client.request('/v1/wallet');
50
+ return (0, _helpers_1.jsonResult)(res);
51
+ }));
52
+ }
53
+ //# sourceMappingURL=billing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing.js","sourceRoot":"","sources":["../../src/tools/billing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerCatalogTools = registerCatalogTools;
4
+ const _helpers_1 = require("./_helpers");
5
+ function registerCatalogTools(server, client) {
6
+ server.registerTool('otwa_list_products', {
7
+ title: 'List server products (plans / sizes)',
8
+ description: 'Returns every server plan available to the current account, including CPU, RAM, disk, bandwidth, ' +
9
+ 'monthly price, and the productId UUID required to call otwa_create_server. Resellers see wholesale prices.',
10
+ inputSchema: {},
11
+ }, (0, _helpers_1.safeHandler)(async () => {
12
+ const products = await client.request('/v1/products');
13
+ return (0, _helpers_1.jsonResult)(products);
14
+ }));
15
+ server.registerTool('otwa_list_regions', {
16
+ title: 'List available regions',
17
+ description: 'Returns the list of regions where new servers can be deployed. Use the `slug` value as the ' +
18
+ '`region` argument to otwa_create_server. Most accounts can omit region — the platform picks one ' +
19
+ "based on product availability — but specifying a region pins the deploy location.",
20
+ inputSchema: {},
21
+ }, (0, _helpers_1.safeHandler)(async () => {
22
+ const regions = await client.request('/v1/regions');
23
+ return (0, _helpers_1.jsonResult)(regions);
24
+ }));
25
+ server.registerTool('otwa_list_os_templates', {
26
+ title: 'List operating-system templates',
27
+ description: 'Returns the catalogue of OS images available for new servers and reinstalls. Each entry has ' +
28
+ '`id` (use as `osTemplate`), `family` (use as `os` — Ubuntu, Debian, Rocky, etc.) and `label`. ' +
29
+ 'Always call this before otwa_create_server so you pick a real template id, never a guessed string.',
30
+ inputSchema: {},
31
+ }, (0, _helpers_1.safeHandler)(async () => {
32
+ const templates = await client.request('/v1/os-templates');
33
+ return (0, _helpers_1.jsonResult)(templates);
34
+ }));
35
+ }
36
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/tools/catalog.ts"],"names":[],"mappings":";;AAKA,oDA+CC;AAjDD,yCAAqD;AAErD,SAAgB,oBAAoB,CAAC,MAAiB,EAAE,MAAkB;IACxE,MAAM,CAAC,YAAY,CACjB,oBAAoB,EACpB;QACE,KAAK,EAAE,sCAAsC;QAC7C,WAAW,EACT,mGAAmG;YACnG,4GAA4G;QAC9G,WAAW,EAAE,EAAE;KAChB,EACD,IAAA,sBAAW,EAAC,KAAK,IAAI,EAAE;QACrB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAoB,cAAc,CAAC,CAAC;QACzE,OAAO,IAAA,qBAAU,EAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,mBAAmB,EACnB;QACE,KAAK,EAAE,wBAAwB;QAC/B,WAAW,EACT,6FAA6F;YAC7F,kGAAkG;YAClG,mFAAmF;QACrF,WAAW,EAAE,EAAE;KAChB,EACD,IAAA,sBAAW,EAAC,KAAK,IAAI,EAAE;QACrB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAmB,aAAa,CAAC,CAAC;QACtE,OAAO,IAAA,qBAAU,EAAC,OAAO,CAAC,CAAC;IAC7B,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,wBAAwB,EACxB;QACE,KAAK,EAAE,iCAAiC;QACxC,WAAW,EACT,8FAA8F;YAC9F,gGAAgG;YAChG,oGAAoG;QACtG,WAAW,EAAE,EAAE;KAChB,EACD,IAAA,sBAAW,EAAC,KAAK,IAAI,EAAE;QACrB,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,OAAO,CAAuB,kBAAkB,CAAC,CAAC;QACjF,OAAO,IAAA,qBAAU,EAAC,SAAS,CAAC,CAAC;IAC/B,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerNetworkingTools = registerNetworkingTools;
4
+ const zod_1 = require("zod");
5
+ const _helpers_1 = require("./_helpers");
6
+ const HOSTNAME_REGEX = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$/;
7
+ function registerNetworkingTools(server, client) {
8
+ server.registerTool('otwa_list_server_ips', {
9
+ title: 'List all IPs assigned to a server',
10
+ description: 'Returns every IP attached to a server (primary + additional), with gateway, netmask, CIDR, ' +
11
+ 'and current PTR (reverse DNS).',
12
+ inputSchema: {
13
+ id: zod_1.z.string().describe('Server UUID.'),
14
+ },
15
+ }, (0, _helpers_1.safeHandler)(async ({ id }) => {
16
+ const ips = await client.request(`/v1/servers/${encodeURIComponent(id)}/ips`);
17
+ return (0, _helpers_1.jsonResult)(ips);
18
+ }));
19
+ server.registerTool('otwa_set_ptr', {
20
+ title: 'Set the PTR record (reverse DNS) for a server IP',
21
+ description: 'Sets the reverse DNS hostname for an IP attached to a server. Changes propagate to PowerDNS ' +
22
+ 'within seconds. Useful for mail servers and anything that needs forward-confirmed reverse DNS.',
23
+ inputSchema: {
24
+ id: zod_1.z.string().describe('Server UUID.'),
25
+ ip: zod_1.z.string().describe('IPv4 address. Must already be attached to this server.'),
26
+ hostname: zod_1.z
27
+ .string()
28
+ .max(253)
29
+ .regex(HOSTNAME_REGEX, 'Hostname must be a valid FQDN (letters/digits/hyphens/dots).')
30
+ .describe('FQDN to publish — e.g. "mail.example.com". Should resolve forward to the same IP.'),
31
+ },
32
+ }, (0, _helpers_1.safeHandler)(async ({ id, ip, hostname }) => {
33
+ const res = await client.request(`/v1/servers/${encodeURIComponent(id)}/ips/${encodeURIComponent(ip)}/ptr`, { method: 'PUT', body: { hostname } });
34
+ return (0, _helpers_1.jsonResult)(res);
35
+ }));
36
+ server.registerTool('otwa_delete_ptr', {
37
+ title: 'Remove the PTR record for a server IP',
38
+ description: 'Removes the reverse DNS hostname for an IP, returning it to the otwa.cloud default.',
39
+ inputSchema: {
40
+ id: zod_1.z.string().describe('Server UUID.'),
41
+ ip: zod_1.z.string().describe('IPv4 address.'),
42
+ },
43
+ }, (0, _helpers_1.safeHandler)(async ({ id, ip }) => {
44
+ const res = await client.request(`/v1/servers/${encodeURIComponent(id)}/ips/${encodeURIComponent(ip)}/ptr`, { method: 'DELETE' });
45
+ return (0, _helpers_1.jsonResult)(res);
46
+ }));
47
+ }
48
+ //# sourceMappingURL=networking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"networking.js","sourceRoot":"","sources":["../../src/tools/networking.ts"],"names":[],"mappings":";;AAOA,0DA8DC;AApED,6BAAwB;AAExB,yCAAqD;AAErD,MAAM,cAAc,GAAG,qFAAqF,CAAC;AAE7G,SAAgB,uBAAuB,CAAC,MAAiB,EAAE,MAAkB;IAC3E,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,KAAK,EAAE,mCAAmC;QAC1C,WAAW,EACT,6FAA6F;YAC7F,gCAAgC;QAClC,WAAW,EAAE;YACX,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;SACxC;KACF,EACD,IAAA,sBAAW,EAAC,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QAC3B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,eAAe,kBAAkB,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QAC9E,OAAO,IAAA,qBAAU,EAAC,GAAG,CAAC,CAAC;IACzB,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,kDAAkD;QACzD,WAAW,EACT,8FAA8F;YAC9F,gGAAgG;QAClG,WAAW,EAAE;YACX,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;YACvC,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wDAAwD,CAAC;YACjF,QAAQ,EAAE,OAAC;iBACR,MAAM,EAAE;iBACR,GAAG,CAAC,GAAG,CAAC;iBACR,KAAK,CAAC,cAAc,EAAE,8DAA8D,CAAC;iBACrF,QAAQ,CAAC,mFAAmF,CAAC;SACjG;KACF,EACD,IAAA,sBAAW,EAAC,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QACzC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAC9B,eAAe,kBAAkB,CAAC,EAAE,CAAC,QAAQ,kBAAkB,CAAC,EAAE,CAAC,MAAM,EACzE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,CACtC,CAAC;QACF,OAAO,IAAA,qBAAU,EAAC,GAAG,CAAC,CAAC;IACzB,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,KAAK,EAAE,uCAAuC;QAC9C,WAAW,EAAE,qFAAqF;QAClG,WAAW,EAAE;YACX,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;YACvC,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC;SACzC;KACF,EACD,IAAA,sBAAW,EAAC,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QAC/B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAC9B,eAAe,kBAAkB,CAAC,EAAE,CAAC,QAAQ,kBAAkB,CAAC,EAAE,CAAC,MAAM,EACzE,EAAE,MAAM,EAAE,QAAQ,EAAE,CACrB,CAAC;QACF,OAAO,IAAA,qBAAU,EAAC,GAAG,CAAC,CAAC;IACzB,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerServerTools = registerServerTools;
4
+ const zod_1 = require("zod");
5
+ const errors_1 = require("../client/errors");
6
+ const confirm_1 = require("../guards/confirm");
7
+ const _helpers_1 = require("./_helpers");
8
+ const HOSTNAME_REGEX = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/;
9
+ const LABEL_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9 _-]*$/;
10
+ function registerServerTools(server, client) {
11
+ // ── Reads ───────────────────────────────────────────────────────────────
12
+ server.registerTool('otwa_list_servers', {
13
+ title: 'List all servers on this account',
14
+ description: 'Returns every server the account owns, with id, label, status, region, primary IP, and any ' +
15
+ 'additional IPs. Use the `id` field as input to per-server tools.',
16
+ inputSchema: {},
17
+ }, (0, _helpers_1.safeHandler)(async () => {
18
+ const servers = await client.request('/v1/servers');
19
+ return (0, _helpers_1.jsonResult)(servers);
20
+ }));
21
+ server.registerTool('otwa_get_server', {
22
+ title: 'Get a server by id',
23
+ description: 'Returns full detail for a server: status, specs (vCPU/RAM/disk/bandwidth), region, OS, primary ' +
24
+ 'IP, gateway, and any additional IPs. Always call this before any destructive operation so the ' +
25
+ 'user can confirm against the current label.',
26
+ inputSchema: {
27
+ id: zod_1.z.string().describe('Server UUID. Retrieve via otwa_list_servers.'),
28
+ },
29
+ }, (0, _helpers_1.safeHandler)(async ({ id }) => {
30
+ const server = await client.request(`/v1/servers/${encodeURIComponent(id)}`);
31
+ return (0, _helpers_1.jsonResult)(server);
32
+ }));
33
+ server.registerTool('otwa_get_server_credentials', {
34
+ title: 'Get current root credentials for a server',
35
+ description: 'Returns the SSH username, root password, IP and port for a server. SENSITIVE — never paste the ' +
36
+ 'password into a chat transcript; instead summarise that credentials are available and offer to ' +
37
+ 'open the dashboard via otwa_get_dashboard_sso.',
38
+ inputSchema: {
39
+ id: zod_1.z.string().describe('Server UUID.'),
40
+ },
41
+ }, (0, _helpers_1.safeHandler)(async ({ id }) => {
42
+ const creds = await client.request(`/v1/servers/${encodeURIComponent(id)}/credentials`);
43
+ return (0, _helpers_1.jsonResult)(creds);
44
+ }));
45
+ server.registerTool('otwa_get_server_stats', {
46
+ title: 'Get live VM stats for a server',
47
+ description: 'Returns the latest CPU / RAM / disk / network metrics for a server. Numbers come from vSphere ' +
48
+ 'and are seconds-fresh.',
49
+ inputSchema: {
50
+ id: zod_1.z.string().describe('Server UUID.'),
51
+ },
52
+ }, (0, _helpers_1.safeHandler)(async ({ id }) => {
53
+ const stats = await client.request(`/v1/servers/${encodeURIComponent(id)}/stats`);
54
+ return (0, _helpers_1.jsonResult)(stats);
55
+ }));
56
+ // ── Create ──────────────────────────────────────────────────────────────
57
+ server.registerTool('otwa_create_server', {
58
+ title: 'Provision a new server',
59
+ description: 'Creates and provisions a new server on otwa.cloud. The customer is billed immediately for the ' +
60
+ 'first month — quote the monthly price from otwa_list_products and get explicit confirmation ' +
61
+ 'before calling. Always call otwa_list_products + otwa_list_os_templates first so productId, ' +
62
+ '`os` and `osTemplate` come from the real catalogue, never guessed.',
63
+ inputSchema: {
64
+ productId: zod_1.z
65
+ .string()
66
+ .uuid()
67
+ .describe('Product UUID. Required. Source: otwa_list_products → field `id`.'),
68
+ os: zod_1.z
69
+ .string()
70
+ .min(1)
71
+ .max(64)
72
+ .describe('OS family slug — e.g. "ubuntu", "debian", "rocky". Source: otwa_list_os_templates → `family`.'),
73
+ osTemplate: zod_1.z
74
+ .string()
75
+ .min(1)
76
+ .max(128)
77
+ .describe('Specific OS template id — e.g. "ubuntu-22.04-x64". Source: otwa_list_os_templates → `id`.'),
78
+ region: zod_1.z
79
+ .string()
80
+ .max(64)
81
+ .optional()
82
+ .describe('Optional region slug. Source: otwa_list_regions → `slug`. Omit to let the platform pick.'),
83
+ label: zod_1.z
84
+ .string()
85
+ .min(1)
86
+ .max(64)
87
+ .regex(LABEL_REGEX, 'Label must start with alphanumeric and contain only letters/numbers/spaces/-/_')
88
+ .optional()
89
+ .describe('Human-readable label shown in the dashboard, e.g. "prod-api-01".'),
90
+ hostname: zod_1.z
91
+ .string()
92
+ .min(1)
93
+ .max(63)
94
+ .regex(HOSTNAME_REGEX, 'Hostname must be DNS-compatible (alphanumeric and hyphens, no leading/trailing hyphen).')
95
+ .optional()
96
+ .describe('Optional DNS-compatible hostname (≤63 chars, no underscores). Defaults to a generated value.'),
97
+ addons: zod_1.z
98
+ .array(zod_1.z.string())
99
+ .optional()
100
+ .describe('Optional list of addon ids — e.g. extra IPs, backups. Look these up via otwa_list_addons-equivalent later.'),
101
+ },
102
+ }, (0, _helpers_1.safeHandler)(async (args) => {
103
+ const created = await client.request('/v1/servers', {
104
+ method: 'POST',
105
+ body: args,
106
+ idempotent: true,
107
+ timeoutMs: 90_000,
108
+ });
109
+ return (0, _helpers_1.jsonResult)(created);
110
+ }));
111
+ // ── Updates ─────────────────────────────────────────────────────────────
112
+ server.registerTool('otwa_rename_server', {
113
+ title: 'Rename a server',
114
+ description: 'Changes the human-readable label of a server. Cosmetic — does not affect hostname or DNS.',
115
+ inputSchema: {
116
+ id: zod_1.z.string().describe('Server UUID.'),
117
+ label: zod_1.z
118
+ .string()
119
+ .min(1)
120
+ .max(64)
121
+ .regex(LABEL_REGEX, 'Label must start with alphanumeric and contain only letters/numbers/spaces/-/_'),
122
+ },
123
+ }, (0, _helpers_1.safeHandler)(async ({ id, label }) => {
124
+ const res = await client.request(`/v1/servers/${encodeURIComponent(id)}/label`, {
125
+ method: 'PATCH',
126
+ body: { label },
127
+ });
128
+ return (0, _helpers_1.jsonResult)(res);
129
+ }));
130
+ server.registerTool('otwa_power_server', {
131
+ title: 'Power on / off / reboot a server',
132
+ description: 'Changes the power state of a server. `start` is safe; `stop` is a hard power-off (no clean ' +
133
+ "shutdown — the guest OS isn't signalled, the VM is killed); `reboot` is a hard reset. " +
134
+ 'For `stop` and `reboot`, confirm with the user first because in-flight writes can be lost.',
135
+ inputSchema: {
136
+ id: zod_1.z.string().describe('Server UUID.'),
137
+ action: zod_1.z
138
+ .enum(['start', 'stop', 'reboot'])
139
+ .describe('Power action: start | stop | reboot.'),
140
+ confirm: zod_1.z
141
+ .boolean()
142
+ .optional()
143
+ .describe('Required `true` for stop/reboot. Confirms the user has been warned about possible data loss.'),
144
+ },
145
+ }, (0, _helpers_1.safeHandler)(async ({ id, action, confirm }) => {
146
+ if ((action === 'stop' || action === 'reboot') && confirm !== true) {
147
+ throw new errors_1.OtwaApiError(`The "${action}" action is a hard ${action === 'stop' ? 'power-off' : 'reset'} and may lose in-flight writes. ` +
148
+ 'Re-call this tool with `confirm: true` once the user has explicitly approved it.', 400, 'confirmation_required');
149
+ }
150
+ const res = await client.request(`/v1/servers/${encodeURIComponent(id)}/power/${encodeURIComponent(action)}`, { method: 'POST' });
151
+ return (0, _helpers_1.jsonResult)(res);
152
+ }));
153
+ server.registerTool('otwa_reset_server_password', {
154
+ title: 'Reset a server root password',
155
+ description: 'Generates a new root password and pushes it into the VM via vSphere guest ops. The new password ' +
156
+ 'is returned in the response — SENSITIVE, summarise rather than echoing into the chat. ' +
157
+ 'Existing SSH sessions are not killed, but anyone using the old password is locked out.',
158
+ inputSchema: {
159
+ id: zod_1.z.string().describe('Server UUID.'),
160
+ ...confirm_1.mediumConfirm,
161
+ },
162
+ }, (0, _helpers_1.safeHandler)(async ({ id }) => {
163
+ const res = await client.request(`/v1/servers/${encodeURIComponent(id)}/password-reset`, {
164
+ method: 'POST',
165
+ timeoutMs: 60_000,
166
+ });
167
+ return (0, _helpers_1.jsonResult)(res);
168
+ }));
169
+ // ── Destructive ─────────────────────────────────────────────────────────
170
+ server.registerTool('otwa_reinstall_server', {
171
+ title: 'Reinstall a server with a new OS',
172
+ description: 'Wipes the disk and rebuilds the server from a different OS template. PRESERVES id, label, ' +
173
+ 'region, IP and addons; DESTROYS everything on disk. Costs nothing. Requires the API key to ' +
174
+ 'carry the `servers:destroy` scope.\n\n' +
175
+ 'SAFETY: Before calling, (1) call otwa_get_server and show the user the current label and IP, ' +
176
+ '(2) explicitly tell them the disk will be wiped, (3) wait for confirmation in chat.',
177
+ inputSchema: {
178
+ id: zod_1.z.string().describe('Server UUID.'),
179
+ os: zod_1.z.string().min(1).describe('Target OS family slug from otwa_list_os_templates.'),
180
+ osTemplate: zod_1.z.string().min(1).describe('Target OS template id from otwa_list_os_templates.'),
181
+ ...confirm_1.destructiveConfirm,
182
+ },
183
+ }, (0, _helpers_1.safeHandler)(async ({ id, os, osTemplate }) => {
184
+ const res = await client.request(`/v1/servers/${encodeURIComponent(id)}/reinstall`, {
185
+ method: 'POST',
186
+ body: { os, osTemplate },
187
+ idempotent: true,
188
+ timeoutMs: 90_000,
189
+ });
190
+ return (0, _helpers_1.jsonResult)(res);
191
+ }));
192
+ server.registerTool('otwa_destroy_server', {
193
+ title: 'Permanently destroy a server',
194
+ description: 'Terminates a server permanently. Disk is wiped, IPs are released, billing stops. CANNOT BE ' +
195
+ 'UNDONE. Requires the API key to carry the `servers:destroy` scope.\n\n' +
196
+ 'SAFETY: Before calling, (1) call otwa_get_server and show the user the current label, IP and ' +
197
+ 'specs, (2) wait for explicit confirmation in chat, (3) pass `expectedLabel` matching the ' +
198
+ 'current label as a typo-guard. If labels differ, the call will be rejected.',
199
+ inputSchema: {
200
+ id: zod_1.z.string().describe('Server UUID.'),
201
+ expectedLabel: zod_1.z
202
+ .string()
203
+ .describe("The server's CURRENT label (from otwa_get_server). The call fails if this does not match — " +
204
+ 'a typo-guard against destroying the wrong server.'),
205
+ ...confirm_1.destructiveConfirm,
206
+ },
207
+ }, (0, _helpers_1.safeHandler)(async ({ id, expectedLabel }) => {
208
+ const current = await client.request(`/v1/servers/${encodeURIComponent(id)}`);
209
+ const actualLabel = current.label || '';
210
+ if (actualLabel.trim() !== expectedLabel.trim()) {
211
+ throw new errors_1.OtwaApiError(`Refusing to destroy server ${id}: expectedLabel "${expectedLabel}" does not match the ` +
212
+ `current label "${actualLabel}". Re-fetch via otwa_get_server, confirm with the user, ` +
213
+ 'and call again with the correct expectedLabel.', 400, 'label_mismatch');
214
+ }
215
+ const res = await client.request(`/v1/servers/${encodeURIComponent(id)}`, {
216
+ method: 'DELETE',
217
+ });
218
+ return (0, _helpers_1.jsonResult)(res);
219
+ }));
220
+ // ── SSO (handoff to dashboard) ──────────────────────────────────────────
221
+ server.registerTool('otwa_get_dashboard_sso', {
222
+ title: 'Issue a 5-minute SSO link into the otwa.cloud dashboard',
223
+ description: 'Returns a short-lived URL that signs the user into the otwa.cloud dashboard, optionally to a ' +
224
+ "specific path. Use this to hand off to the web UI for anything the MCP doesn't expose — noVNC " +
225
+ 'console, billing top-up, advanced settings.',
226
+ inputSchema: {
227
+ id: zod_1.z
228
+ .string()
229
+ .optional()
230
+ .describe('Optional server UUID. If provided, lands on /dashboard/servers/<id>.'),
231
+ next: zod_1.z
232
+ .string()
233
+ .optional()
234
+ .describe('Optional dashboard path to land on, e.g. "/dashboard/billing". Must start with "/".'),
235
+ },
236
+ }, (0, _helpers_1.safeHandler)(async ({ id, next }) => {
237
+ const path = id ? `/v1/servers/${encodeURIComponent(id)}/sso` : '/v1/sso';
238
+ const res = await client.request(path, {
239
+ method: 'POST',
240
+ body: next ? { next } : {},
241
+ });
242
+ return (0, _helpers_1.textResult)(`Dashboard SSO link (valid ${res.expiresIn} seconds): ${res.url}\n\n` +
243
+ 'Share this URL with the user; it auto-logs them in. Do not store or reuse it — it expires.');
244
+ }));
245
+ }
246
+ //# sourceMappingURL=servers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"servers.js","sourceRoot":"","sources":["../../src/tools/servers.ts"],"names":[],"mappings":"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"}