@rarecloudio/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/README.md ADDED
@@ -0,0 +1,155 @@
1
+ # @rarecloudio/mcp-server
2
+
3
+ Model Context Protocol server for the [RareCloud](https://rarecloud.io) API.
4
+
5
+ Drop into [Claude Code](https://claude.com/claude-code), Claude Desktop, [Cursor](https://cursor.com), or your own MCP client to let AI agents inspect and reason about your RareCloud account — list servers, browse the catalog, check billing, plan deployments.
6
+
7
+ ## What it does
8
+
9
+ Exposes **20 read-only tools** wrapping the RareCloud REST API:
10
+
11
+ | Category | Tool | Purpose |
12
+ |---|---|---|
13
+ | Catalog | `list_catalog_products` | Browse orderable plans, filter by kind/backend |
14
+ | | `get_catalog_plan` | Full plan detail: specs + pricing + billing tracks |
15
+ | | `list_regions` | Available datacenter regions |
16
+ | | `list_images` | OS images (Ubuntu / Debian / Windows / ...) |
17
+ | Services | `list_services` | All your running services (filter by category) |
18
+ | | `get_service` | Detail on one service |
19
+ | | `get_service_metrics` | CPU / RAM / disk / bandwidth time series |
20
+ | | `list_backups` | Backups for one legacy VPS |
21
+ | | `get_provisioning_state` | Setup state of a pending service (paid / unprovisioned / stuck) |
22
+ | | `list_os_templates` | OS templates a legacy VPS can be reinstalled with |
23
+ | | `list_upgrade_options` | Plans+cycles a service could upgrade/downgrade to |
24
+ | Billing | `list_invoices` | Invoice history |
25
+ | | `get_invoice` | Detail on one invoice |
26
+ | | `get_credit_balance` | Current prepaid credit |
27
+ | | `get_credit_ledger` | Credit movements (top-ups, vouchers, metering) |
28
+ | Support | `list_tickets` | Support tickets (filter by status) |
29
+ | | `get_ticket` | One ticket with its full thread |
30
+ | Account | `get_account` | Profile (email, name, country) |
31
+ | | `list_ssh_keys` | SSH keys on a server (per-service) |
32
+ | | `get_account_limits` | Resource caps + current usage |
33
+
34
+ ## Read-only by design
35
+
36
+ This release is **read-only**. No `create_server`, no `destroy`, no `snapshot create`. The reason is architectural: mutating actions need a derived, scoped token plus a mandatory plan-and-approve step before they're safe to expose to an LLM. That ships in a future release.
37
+
38
+ In the meantime: the agent can read, recommend, and generate Terraform/CLI commands. The user copy-pastes them or runs them via [the RareCloud CLI](https://github.com/RareCloudio/rarecloud-io/tree/main/cli).
39
+
40
+ ## Install
41
+
42
+ Requires Node.js 20+.
43
+
44
+ ```bash
45
+ npm install -g @rarecloudio/mcp-server
46
+ ```
47
+
48
+ Or run directly with `npx`:
49
+
50
+ ```bash
51
+ npx @rarecloudio/mcp-server
52
+ ```
53
+
54
+ ## Configure
55
+
56
+ Get an API token: **Dashboard → Account → API tokens → New token**. Scope it to read-only (`services:read`, `billing:read`, `account:read`, `catalog:read`). Copy it — shown once.
57
+
58
+ Set the env var:
59
+
60
+ ```bash
61
+ export RARECLOUD_API_TOKEN="rc_pat_..."
62
+ ```
63
+
64
+ Optional, for self-hosted / staging instances:
65
+
66
+ ```bash
67
+ export RARECLOUD_API_ENDPOINT="https://your-instance.example.com"
68
+ ```
69
+
70
+ ## Use with Claude Desktop
71
+
72
+ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
73
+
74
+ ```json
75
+ {
76
+ "mcpServers": {
77
+ "rarecloud": {
78
+ "command": "npx",
79
+ "args": ["-y", "@rarecloudio/mcp-server"],
80
+ "env": {
81
+ "RARECLOUD_API_TOKEN": "rc_pat_..."
82
+ }
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ Restart Claude Desktop. The 20 tools become available under the 🔌 menu.
89
+
90
+ ## Use with Claude Code
91
+
92
+ ```bash
93
+ claude mcp add rarecloud npx -- -y @rarecloudio/mcp-server \
94
+ -e RARECLOUD_API_TOKEN=rc_pat_...
95
+ ```
96
+
97
+ ## Use with Cursor
98
+
99
+ Edit `~/.cursor/mcp.json`:
100
+
101
+ ```json
102
+ {
103
+ "mcpServers": {
104
+ "rarecloud": {
105
+ "command": "npx",
106
+ "args": ["-y", "@rarecloudio/mcp-server"],
107
+ "env": { "RARECLOUD_API_TOKEN": "rc_pat_..." }
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ ## Example prompts
114
+
115
+ Once configured, try:
116
+
117
+ - *"What VPS plans do you offer in Frankfurt?"* → uses `list_catalog_products` + `list_regions`
118
+ - *"List my running servers and their monthly cost"* → `list_services` + per-service spec lookup
119
+ - *"Am I close to any resource limits?"* → `get_account_limits`
120
+ - *"Give me a Terraform config for a 2 vCPU / 4 GB VPS in The Hague"* → `get_catalog_plan` + composition
121
+
122
+ ## Develop locally
123
+
124
+ ```bash
125
+ git clone https://github.com/RareCloudio/rarecloud-io
126
+ cd rarecloud-io/mcp-server
127
+ npm install
128
+ npm run dev # runs from source via tsx
129
+ npm run build # compiles to dist/
130
+ ```
131
+
132
+ Then point Claude Desktop at your local checkout:
133
+
134
+ ```json
135
+ {
136
+ "mcpServers": {
137
+ "rarecloud-dev": {
138
+ "command": "node",
139
+ "args": ["/absolute/path/to/rarecloud-io/mcp-server/dist/index.js"],
140
+ "env": { "RARECLOUD_API_TOKEN": "rc_pat_..." }
141
+ }
142
+ }
143
+ }
144
+ ```
145
+
146
+ ## Security
147
+
148
+ - Tokens never touch shell history (we use env vars, not CLI flags).
149
+ - Each tool maps 1:1 to a RareCloud API endpoint; the MCP server doesn't aggregate or transform data beyond what the API returns.
150
+ - Read-only scope — there is no path for an agent to mutate state via this server, even on a compromised token.
151
+ - Revoke a token at any time: **Dashboard → Account → API tokens**. Revocation is instant, no propagation delay.
152
+
153
+ ## License
154
+
155
+ MIT.
@@ -0,0 +1,23 @@
1
+ export interface RareCloudClientConfig {
2
+ endpoint: string;
3
+ token: string;
4
+ userAgent?: string;
5
+ }
6
+ export interface APIErrorPayload {
7
+ code: string;
8
+ message: string;
9
+ }
10
+ export declare class APIError extends Error {
11
+ readonly code: string;
12
+ constructor(payload: APIErrorPayload);
13
+ }
14
+ export declare class RareCloudClient {
15
+ private readonly config;
16
+ constructor(config: RareCloudClientConfig);
17
+ get<T = unknown>(path: string, query?: Record<string, string | number | undefined>): Promise<T>;
18
+ post<T = unknown>(path: string, body?: unknown): Promise<T>;
19
+ delete<T = unknown>(path: string): Promise<T>;
20
+ private url;
21
+ private do;
22
+ }
23
+ export declare function clientFromEnv(): RareCloudClient;
package/dist/client.js ADDED
@@ -0,0 +1,83 @@
1
+ // Tiny HTTP client for the RareCloud /v1 surface. Same envelope shape
2
+ // the CLI + Terraform provider use: { ok: true, data: ... } or
3
+ // { ok: false, error: { code, message } }.
4
+ export class APIError extends Error {
5
+ code;
6
+ constructor(payload) {
7
+ super(`[${payload.code}] ${payload.message}`);
8
+ this.code = payload.code;
9
+ }
10
+ }
11
+ export class RareCloudClient {
12
+ config;
13
+ constructor(config) {
14
+ this.config = config;
15
+ }
16
+ async get(path, query) {
17
+ const url = this.url(path, query);
18
+ return this.do('GET', url);
19
+ }
20
+ async post(path, body) {
21
+ return this.do('POST', this.url(path), body);
22
+ }
23
+ async delete(path) {
24
+ return this.do('DELETE', this.url(path));
25
+ }
26
+ url(path, query) {
27
+ const url = new URL(this.config.endpoint + path);
28
+ if (query) {
29
+ for (const [k, v] of Object.entries(query)) {
30
+ if (v === undefined || v === null || v === '')
31
+ continue;
32
+ url.searchParams.set(k, String(v));
33
+ }
34
+ }
35
+ return url.toString();
36
+ }
37
+ async do(method, url, body) {
38
+ const headers = {
39
+ Authorization: `Bearer ${this.config.token}`,
40
+ 'User-Agent': this.config.userAgent ?? 'rarecloud-mcp/0.1.0',
41
+ };
42
+ if (body !== undefined)
43
+ headers['Content-Type'] = 'application/json';
44
+ let resp;
45
+ try {
46
+ resp = await fetch(url, {
47
+ method,
48
+ headers,
49
+ body: body !== undefined ? JSON.stringify(body) : undefined,
50
+ });
51
+ }
52
+ catch (e) {
53
+ throw new APIError({ code: 'NETWORK_ERROR', message: e.message });
54
+ }
55
+ const text = await resp.text();
56
+ let env;
57
+ try {
58
+ env = JSON.parse(text);
59
+ }
60
+ catch {
61
+ throw new APIError({
62
+ code: 'INVALID_RESPONSE',
63
+ message: `Non-JSON response (HTTP ${resp.status}): ${text.slice(0, 200)}`,
64
+ });
65
+ }
66
+ if (!env.ok) {
67
+ throw new APIError(env.error ?? { code: 'UNKNOWN', message: `HTTP ${resp.status}` });
68
+ }
69
+ return (env.data ?? {});
70
+ }
71
+ }
72
+ export function clientFromEnv() {
73
+ const endpoint = process.env.RARECLOUD_API_ENDPOINT?.replace(/\/$/, '') ?? 'https://api.rarecloud.io';
74
+ const token = process.env.RARECLOUD_API_TOKEN;
75
+ if (!token) {
76
+ throw new APIError({
77
+ code: 'MISSING_TOKEN',
78
+ message: 'Set RARECLOUD_API_TOKEN to your personal access token (Dashboard → Account → API tokens).',
79
+ });
80
+ }
81
+ return new RareCloudClient({ endpoint, token });
82
+ }
83
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,+DAA+D;AAC/D,2CAA2C;AAa3C,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,CAAS;IACtB,YAAY,OAAwB;QAClC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;CACF;AAQD,MAAM,OAAO,eAAe;IACG;IAA7B,YAA6B,MAA6B;QAA7B,WAAM,GAAN,MAAM,CAAuB;IAAG,CAAC;IAE9D,KAAK,CAAC,GAAG,CAAc,IAAY,EAAE,KAAmD;QACtF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC,EAAE,CAAI,KAAK,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,IAAI,CAAc,IAAY,EAAE,IAAc;QAClD,OAAO,IAAI,CAAC,EAAE,CAAI,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,MAAM,CAAc,IAAY;QACpC,OAAO,IAAI,CAAC,EAAE,CAAI,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9C,CAAC;IAEO,GAAG,CAAC,IAAY,EAAE,KAAmD;QAC3E,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;QACjD,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3C,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE;oBAAE,SAAS;gBACxD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAEO,KAAK,CAAC,EAAE,CAAI,MAAc,EAAE,GAAW,EAAE,IAAc;QAC7D,MAAM,OAAO,GAA2B;YACtC,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;YAC5C,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,qBAAqB;SAC7D,CAAC;QACF,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAErE,IAAI,IAAc,CAAC;QACnB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBACtB,MAAM;gBACN,OAAO;gBACP,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;aAC5D,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,QAAQ,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAG,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/E,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,GAAgB,CAAC;QACrB,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgB,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,QAAQ,CAAC;gBACjB,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,2BAA2B,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;aAC1E,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,IAAI,IAAK,EAAQ,CAAM,CAAC;IACtC,CAAC;CACF;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,0BAA0B,CAAC;IACtG,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,QAAQ,CAAC;YACjB,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,2FAA2F;SACrG,CAAC,CAAC;IACL,CAAC;IACD,OAAO,IAAI,eAAe,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ // MCP server for RareCloud — exposes a read-only tool surface that lets
3
+ // AI agents (Claude Code, Claude Desktop, Cursor, custom agents) inspect
4
+ // a RareCloud account and reason about it.
5
+ //
6
+ // Design notes:
7
+ // - READ-ONLY by design. No create / destroy / mutate tools yet.
8
+ // Mutating actions need the derived-token + plan-and-approve flow
9
+ // described in docs/ideas/2026-05-22-agent-ai-architecture.md.
10
+ // Until that ships, the agent suggests and the user confirms in
11
+ // dashboard / CLI / Terraform.
12
+ // - Authenticates with a personal access token (Dashboard → Account →
13
+ // API tokens). Scope the token to *:read for safety.
14
+ // - Speaks stdio. To use with Claude Desktop, see README.md for the
15
+ // mcpServers config snippet.
16
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
17
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
18
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
19
+ import { clientFromEnv } from './client.js';
20
+ import { TOOLS, findTool } from './tools/index.js';
21
+ const SERVER_NAME = 'rarecloud';
22
+ const SERVER_VERSION = '0.1.0';
23
+ async function main() {
24
+ // Validate config early so the user sees a clear error instead of
25
+ // every tool call failing with the same MISSING_TOKEN.
26
+ try {
27
+ clientFromEnv();
28
+ }
29
+ catch (e) {
30
+ process.stderr.write(`${e.message}\n`);
31
+ process.exit(1);
32
+ }
33
+ const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
34
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
35
+ tools: TOOLS.map((t) => ({
36
+ name: t.name,
37
+ description: t.description,
38
+ inputSchema: t.inputSchema,
39
+ })),
40
+ }));
41
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
42
+ const tool = findTool(req.params.name);
43
+ if (!tool) {
44
+ return {
45
+ content: [{ type: 'text', text: `Unknown tool: ${req.params.name}` }],
46
+ isError: true,
47
+ };
48
+ }
49
+ const client = clientFromEnv();
50
+ const args = (req.params.arguments ?? {});
51
+ const result = await tool.handler(client, args);
52
+ // ServerResult is a union over sync/async/task shapes — our handlers
53
+ // are always sync-completion (content + isError). Cast keeps the
54
+ // SDK happy without forcing every tool to declare task metadata.
55
+ return result;
56
+ });
57
+ const transport = new StdioServerTransport();
58
+ await server.connect(transport);
59
+ // Stay alive until stdio closes.
60
+ process.stderr.write(`${SERVER_NAME} MCP server v${SERVER_VERSION} ready (${TOOLS.length} tools)\n`);
61
+ }
62
+ main().catch((e) => {
63
+ process.stderr.write(`Fatal: ${e.message}\n`);
64
+ process.exit(1);
65
+ });
66
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type ToolDefinition } from './types.js';
2
+ export declare const getAccount: ToolDefinition;
3
+ export declare const listSshKeys: ToolDefinition;
4
+ export declare const getAccountLimits: ToolDefinition;
@@ -0,0 +1,66 @@
1
+ // Account tools — profile, SSH keys, limits. Read-only.
2
+ import { APIError } from '../client.js';
3
+ import { jsonResult, errorResult } from './types.js';
4
+ export const getAccount = {
5
+ name: 'get_account',
6
+ description: 'Get the authenticated user\'s profile: email, name, country, billing currency, account creation date. Use for "what account am I on?" or to confirm identity before suggesting cross-account actions.',
7
+ inputSchema: {
8
+ type: 'object',
9
+ properties: {},
10
+ additionalProperties: false,
11
+ },
12
+ async handler(client) {
13
+ try {
14
+ const data = await client.get('/v1/account');
15
+ return jsonResult(data);
16
+ }
17
+ catch (e) {
18
+ return errorResult(e instanceof APIError ? e.message : e.message);
19
+ }
20
+ },
21
+ };
22
+ export const listSshKeys = {
23
+ name: 'list_ssh_keys',
24
+ description: 'List the SSH keys on a specific server (legacy VPS). SSH keys are per-server in the API, not account-wide. Pass a service_id from list_services.',
25
+ inputSchema: {
26
+ type: 'object',
27
+ properties: {
28
+ service_id: {
29
+ type: 'string',
30
+ description: 'Server ID from list_services.',
31
+ },
32
+ },
33
+ required: ['service_id'],
34
+ additionalProperties: false,
35
+ },
36
+ async handler(client, args) {
37
+ try {
38
+ // There is no account-wide ssh-keys endpoint; keys are per-service.
39
+ const id = args.service_id;
40
+ const data = await client.get(`/v1/services/${encodeURIComponent(id)}/ssh-keys`);
41
+ return jsonResult(data);
42
+ }
43
+ catch (e) {
44
+ return errorResult(e instanceof APIError ? e.message : e.message);
45
+ }
46
+ },
47
+ };
48
+ export const getAccountLimits = {
49
+ name: 'get_account_limits',
50
+ description: 'Get account resource limits and current usage (servers / vCPUs / snapshots / IPs / volumes / DNS zones / etc). Use before recommending a deploy to make sure the user has headroom.',
51
+ inputSchema: {
52
+ type: 'object',
53
+ properties: {},
54
+ additionalProperties: false,
55
+ },
56
+ async handler(client) {
57
+ try {
58
+ const data = await client.get('/v1/account/limits');
59
+ return jsonResult(data);
60
+ }
61
+ catch (e) {
62
+ return errorResult(e instanceof APIError ? e.message : e.message);
63
+ }
64
+ },
65
+ };
66
+ //# sourceMappingURL=account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/tools/account.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAuB,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,CAAC,MAAM,UAAU,GAAmB;IACxC,IAAI,EAAE,aAAa;IACnB,WAAW,EAAE,uMAAuM;IACpN,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,EAAE;QACd,oBAAoB,EAAE,KAAK;KAC5B;IACD,KAAK,CAAC,OAAO,CAAC,MAAM;QAClB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YAC7C,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,WAAW,CAAC,CAAC,YAAY,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAW,CAAC,OAAO,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAmB;IACzC,IAAI,EAAE,eAAe;IACrB,WAAW,EAAE,kJAAkJ;IAC/J,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,+BAA+B;aAC7C;SACF;QACD,QAAQ,EAAE,CAAC,YAAY,CAAC;QACxB,oBAAoB,EAAE,KAAK;KAC5B;IACD,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI;QACxB,IAAI,CAAC;YACH,oEAAoE;YACpE,MAAM,EAAE,GAAG,IAAI,CAAC,UAAoB,CAAC;YACrC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,gBAAgB,kBAAkB,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC;YACjF,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,WAAW,CAAC,CAAC,YAAY,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAW,CAAC,OAAO,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAmB;IAC9C,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EAAE,qLAAqL;IAClM,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,EAAE;QACd,oBAAoB,EAAE,KAAK;KAC5B;IACD,KAAK,CAAC,OAAO,CAAC,MAAM;QAClB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YACpD,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,WAAW,CAAC,CAAC,YAAY,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAW,CAAC,OAAO,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { type ToolDefinition } from './types.js';
2
+ export declare const listInvoices: ToolDefinition;
3
+ export declare const getInvoice: ToolDefinition;
4
+ export declare const getCreditBalance: ToolDefinition;
5
+ export declare const getCreditLedger: ToolDefinition;
@@ -0,0 +1,97 @@
1
+ // Billing tools — invoices, credit balance, payment methods.
2
+ // Read-only. AddFunds / payment-method mutations stay manual.
3
+ import { APIError } from '../client.js';
4
+ import { jsonResult, errorResult } from './types.js';
5
+ export const listInvoices = {
6
+ name: 'list_invoices',
7
+ description: 'List invoices for the authenticated account: number, status (paid / unpaid / cancelled), issued date, total. Use for "summarize my last 6 months of spend" or "which invoices are unpaid".',
8
+ inputSchema: {
9
+ type: 'object',
10
+ properties: {
11
+ status: {
12
+ type: 'string',
13
+ enum: ['unpaid', 'paid', 'cancelled', 'refunded', 'collections'],
14
+ description: 'Optional filter on invoice status.',
15
+ },
16
+ limit: {
17
+ type: 'number',
18
+ description: 'Cap results (default: 50, max: 200).',
19
+ },
20
+ },
21
+ additionalProperties: false,
22
+ },
23
+ async handler(client, args) {
24
+ try {
25
+ const data = await client.get('/v1/billing/invoices', {
26
+ status: args.status,
27
+ limit: args.limit,
28
+ });
29
+ return jsonResult(data);
30
+ }
31
+ catch (e) {
32
+ return errorResult(e instanceof APIError ? e.message : e.message);
33
+ }
34
+ },
35
+ };
36
+ export const getInvoice = {
37
+ name: 'get_invoice',
38
+ description: 'Get full details for a single invoice: every line item, taxes, payment method used, payment timestamp. Use after list_invoices when more detail is needed.',
39
+ inputSchema: {
40
+ type: 'object',
41
+ properties: {
42
+ invoice_id: {
43
+ type: 'string',
44
+ description: 'Invoice ID from list_invoices.',
45
+ },
46
+ },
47
+ required: ['invoice_id'],
48
+ additionalProperties: false,
49
+ },
50
+ async handler(client, args) {
51
+ try {
52
+ const id = args.invoice_id;
53
+ const data = await client.get(`/v1/billing/invoices/${encodeURIComponent(id)}`);
54
+ return jsonResult(data);
55
+ }
56
+ catch (e) {
57
+ return errorResult(e instanceof APIError ? e.message : e.message);
58
+ }
59
+ },
60
+ };
61
+ export const getCreditBalance = {
62
+ name: 'get_credit_balance',
63
+ description: 'Get the current account credit balance (Pattern A v2 prepaid credit). Use for "how much do I have left?", or to check before suggesting actions that would consume credit (cloud-compute hourly billing).',
64
+ inputSchema: {
65
+ type: 'object',
66
+ properties: {},
67
+ additionalProperties: false,
68
+ },
69
+ async handler(client) {
70
+ try {
71
+ const data = await client.get('/v1/billing/credit');
72
+ return jsonResult(data);
73
+ }
74
+ catch (e) {
75
+ return errorResult(e instanceof APIError ? e.message : e.message);
76
+ }
77
+ },
78
+ };
79
+ export const getCreditLedger = {
80
+ name: 'get_credit_ledger',
81
+ description: 'List credit ledger entries (top-ups, voucher redemptions, hourly metering debits, refunds) for the authenticated account. Use to explain "where did my credit go?" or to reconcile a balance.',
82
+ inputSchema: {
83
+ type: 'object',
84
+ properties: {},
85
+ additionalProperties: false,
86
+ },
87
+ async handler(client) {
88
+ try {
89
+ const data = await client.get('/v1/billing/credit/ledger');
90
+ return jsonResult(data);
91
+ }
92
+ catch (e) {
93
+ return errorResult(e instanceof APIError ? e.message : e.message);
94
+ }
95
+ },
96
+ };
97
+ //# 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,5 @@
1
+ import { type ToolDefinition } from './types.js';
2
+ export declare const listCatalogProducts: ToolDefinition;
3
+ export declare const getCatalogPlan: ToolDefinition;
4
+ export declare const listRegions: ToolDefinition;
5
+ export declare const listImages: ToolDefinition;
@@ -0,0 +1,104 @@
1
+ // Catalog tools — un-authed endpoints, useful for "what can we deploy?"
2
+ // queries. Token is still passed so we use the same client; the API
3
+ // ignores it on these routes.
4
+ import { APIError } from '../client.js';
5
+ import { jsonResult, errorResult } from './types.js';
6
+ export const listCatalogProducts = {
7
+ name: 'list_catalog_products',
8
+ description: 'List orderable products from the RareCloud catalog. Use to answer "what plans can I deploy?" Returns SKU, kind (legacy_vps / cloud_compute / proxy / ...), category, display name. Filter by kind or backend to narrow results.',
9
+ inputSchema: {
10
+ type: 'object',
11
+ properties: {
12
+ kind: {
13
+ type: 'string',
14
+ enum: ['legacy_vps', 'cloud_compute', 'cloud_k8s', 'cloud_volume', 'cloud_network', 'dedicated_server', 'proxy', 'hosting', 'domain', 'app_hosting'],
15
+ description: 'Filter by product kind. Most users want legacy_vps (KVM VPS) or cloud_compute (hourly OpenStack VMs).',
16
+ },
17
+ backend: {
18
+ type: 'string',
19
+ enum: ['whmcs', 'virtualizor', 'openstack', 'gardener'],
20
+ description: 'Filter by underlying backend. Usually you do NOT need this — pick by kind instead.',
21
+ },
22
+ category: {
23
+ type: 'string',
24
+ description: 'Filter by category name (e.g. "KVM Servers", "Proxies").',
25
+ },
26
+ },
27
+ additionalProperties: false,
28
+ },
29
+ async handler(client, args) {
30
+ try {
31
+ const data = await client.get('/v1/catalog/products', {
32
+ kind: args.kind,
33
+ backend: args.backend,
34
+ category: args.category,
35
+ });
36
+ return jsonResult(data);
37
+ }
38
+ catch (e) {
39
+ return errorResult(e instanceof APIError ? e.message : e.message);
40
+ }
41
+ },
42
+ };
43
+ export const getCatalogPlan = {
44
+ name: 'get_catalog_plan',
45
+ description: 'Get full details for a single catalog product including all plans (sizes), their specs (vCPU, RAM, disk, bandwidth), pricing for every supported billing cycle, and supported billing tracks. Use before generating a Terraform plan or before recommending a specific SKU.',
46
+ inputSchema: {
47
+ type: 'object',
48
+ properties: {
49
+ sku: {
50
+ type: 'string',
51
+ description: 'Product SKU from list_catalog_products (e.g. "whmcs.kvm-servers-plus-vps").',
52
+ },
53
+ },
54
+ required: ['sku'],
55
+ additionalProperties: false,
56
+ },
57
+ async handler(client, args) {
58
+ try {
59
+ const sku = args.sku;
60
+ const data = await client.get(`/v1/catalog/products/${encodeURIComponent(sku)}`);
61
+ return jsonResult(data);
62
+ }
63
+ catch (e) {
64
+ return errorResult(e instanceof APIError ? e.message : e.message);
65
+ }
66
+ },
67
+ };
68
+ export const listRegions = {
69
+ name: 'list_regions',
70
+ description: 'List available RareCloud datacenter regions. Each region has a slug (e.g. "frankfurt-de"), display name, country code, and which backends can provision there.',
71
+ inputSchema: {
72
+ type: 'object',
73
+ properties: {},
74
+ additionalProperties: false,
75
+ },
76
+ async handler(client) {
77
+ try {
78
+ const data = await client.get('/v1/catalog/regions');
79
+ return jsonResult(data);
80
+ }
81
+ catch (e) {
82
+ return errorResult(e instanceof APIError ? e.message : e.message);
83
+ }
84
+ },
85
+ };
86
+ export const listImages = {
87
+ name: 'list_images',
88
+ description: 'List available OS images (Ubuntu / Debian / Rocky / Windows Server / etc) that can be installed on new servers. Use to validate an image slug before recommending it.',
89
+ inputSchema: {
90
+ type: 'object',
91
+ properties: {},
92
+ additionalProperties: false,
93
+ },
94
+ async handler(client) {
95
+ try {
96
+ const data = await client.get('/v1/catalog/images');
97
+ return jsonResult(data);
98
+ }
99
+ catch (e) {
100
+ return errorResult(e instanceof APIError ? e.message : e.message);
101
+ }
102
+ },
103
+ };
104
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/tools/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ToolDefinition } from './types.js';
2
+ export declare const TOOLS: ToolDefinition[];
3
+ export declare function findTool(name: string): ToolDefinition | undefined;
@@ -0,0 +1,38 @@
1
+ // Single registry of all tools exposed by this MCP server. Keep this file
2
+ // short — adding a new tool = import + push.
3
+ import { listCatalogProducts, getCatalogPlan, listRegions, listImages, } from './catalog.js';
4
+ import { listServices, getService, getServiceMetrics, listBackups, getProvisioningState, listOsTemplates, listUpgradeOptions } from './services.js';
5
+ import { listInvoices, getInvoice, getCreditBalance, getCreditLedger } from './billing.js';
6
+ import { getAccount, listSshKeys, getAccountLimits } from './account.js';
7
+ import { listTickets, getTicket } from './tickets.js';
8
+ export const TOOLS = [
9
+ // Catalog (un-authed surface, "what can I deploy?")
10
+ listCatalogProducts,
11
+ getCatalogPlan,
12
+ listRegions,
13
+ listImages,
14
+ // Services ("what do I have running?")
15
+ listServices,
16
+ getService,
17
+ getServiceMetrics,
18
+ listBackups,
19
+ getProvisioningState,
20
+ listOsTemplates,
21
+ listUpgradeOptions,
22
+ // Billing ("how much am I spending?")
23
+ listInvoices,
24
+ getInvoice,
25
+ getCreditBalance,
26
+ getCreditLedger,
27
+ // Support ("any open tickets?")
28
+ listTickets,
29
+ getTicket,
30
+ // Account ("who am I + what are my keys?")
31
+ getAccount,
32
+ listSshKeys,
33
+ getAccountLimits,
34
+ ];
35
+ export function findTool(name) {
36
+ return TOOLS.find((t) => t.name === name);
37
+ }
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,6CAA6C;AAG7C,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,WAAW,EACX,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,WAAW,EAAE,oBAAoB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACpJ,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC3F,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,CAAC,MAAM,KAAK,GAAqB;IACrC,oDAAoD;IACpD,mBAAmB;IACnB,cAAc;IACd,WAAW;IACX,UAAU;IACV,uCAAuC;IACvC,YAAY;IACZ,UAAU;IACV,iBAAiB;IACjB,WAAW;IACX,oBAAoB;IACpB,eAAe;IACf,kBAAkB;IAClB,sCAAsC;IACtC,YAAY;IACZ,UAAU;IACV,gBAAgB;IAChB,eAAe;IACf,gCAAgC;IAChC,WAAW;IACX,SAAS;IACT,2CAA2C;IAC3C,UAAU;IACV,WAAW;IACX,gBAAgB;CACjB,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type ToolDefinition } from './types.js';
2
+ export declare const listServices: ToolDefinition;
3
+ export declare const getService: ToolDefinition;
4
+ export declare const getServiceMetrics: ToolDefinition;
5
+ export declare const listBackups: ToolDefinition;
6
+ export declare const getProvisioningState: ToolDefinition;
7
+ export declare const listOsTemplates: ToolDefinition;
8
+ export declare const listUpgradeOptions: ToolDefinition;
@@ -0,0 +1,191 @@
1
+ // Services tools — wrap /v1/services. Read-only for v1: list, get,
2
+ // list snapshots. No create/destroy/snapshot-create until we ship the
3
+ // derived-token + plan-and-approve flow described in
4
+ // docs/ideas/2026-05-22-agent-ai-architecture.md.
5
+ import { APIError } from '../client.js';
6
+ import { jsonResult, errorResult } from './types.js';
7
+ export const listServices = {
8
+ name: 'list_services',
9
+ description: 'List all services in the authenticated account: VPS servers, cloud VMs, proxies, hosting, domains. Returns each service\'s id, kind, name, status, IPv4, region, specs, billing cycle. Use to answer "what do I have running?" or to find a service ID for follow-up calls.',
10
+ inputSchema: {
11
+ type: 'object',
12
+ properties: {
13
+ category: {
14
+ type: 'string',
15
+ description: 'Optional filter by service category: "server" (legacy VPS), "cloud-vm", "cloud-k8s", "proxy", "hosting", "domain". Omit to return all.',
16
+ },
17
+ },
18
+ additionalProperties: false,
19
+ },
20
+ async handler(client, args) {
21
+ try {
22
+ // The /v1/services route filters by `category` (not `kind`).
23
+ const data = await client.get('/v1/services', {
24
+ category: args.category,
25
+ });
26
+ return jsonResult(data);
27
+ }
28
+ catch (e) {
29
+ return errorResult(e instanceof APIError ? e.message : e.message);
30
+ }
31
+ },
32
+ };
33
+ export const getService = {
34
+ name: 'get_service',
35
+ description: 'Get full details for a single service by ID: status, network config, billing state, current-month usage. Use when you need more than the list_services summary (e.g. to inspect logs, current cost, attached resources).',
36
+ inputSchema: {
37
+ type: 'object',
38
+ properties: {
39
+ service_id: {
40
+ type: 'string',
41
+ description: 'Service ID from list_services (e.g. "srv_01H8E9...").',
42
+ },
43
+ },
44
+ required: ['service_id'],
45
+ additionalProperties: false,
46
+ },
47
+ async handler(client, args) {
48
+ try {
49
+ const id = args.service_id;
50
+ const data = await client.get(`/v1/services/${encodeURIComponent(id)}`);
51
+ return jsonResult(data);
52
+ }
53
+ catch (e) {
54
+ return errorResult(e instanceof APIError ? e.message : e.message);
55
+ }
56
+ },
57
+ };
58
+ export const getServiceMetrics = {
59
+ name: 'get_service_metrics',
60
+ description: 'Get resource metrics (CPU / RAM / disk / bandwidth time series) for a single service. Use to answer "is my server busy?" or "how much bandwidth have I used?".',
61
+ inputSchema: {
62
+ type: 'object',
63
+ properties: {
64
+ service_id: {
65
+ type: 'string',
66
+ description: 'Service ID from list_services.',
67
+ },
68
+ period: {
69
+ type: 'string',
70
+ enum: ['hour', 'day', 'week', 'month'],
71
+ description: 'Aggregation window (default: hour).',
72
+ },
73
+ },
74
+ required: ['service_id'],
75
+ additionalProperties: false,
76
+ },
77
+ async handler(client, args) {
78
+ try {
79
+ const id = args.service_id;
80
+ const data = await client.get(`/v1/services/${encodeURIComponent(id)}/metrics`, {
81
+ period: args.period,
82
+ });
83
+ return jsonResult(data);
84
+ }
85
+ catch (e) {
86
+ return errorResult(e instanceof APIError ? e.message : e.message);
87
+ }
88
+ },
89
+ };
90
+ export const listBackups = {
91
+ name: 'list_backups',
92
+ description: 'List existing backups for a single legacy VPS server. Use to check whether a recent backup exists before a risky change, or to find a backup ID for restore.',
93
+ inputSchema: {
94
+ type: 'object',
95
+ properties: {
96
+ service_id: {
97
+ type: 'string',
98
+ description: 'Server ID from list_services.',
99
+ },
100
+ },
101
+ required: ['service_id'],
102
+ additionalProperties: false,
103
+ },
104
+ async handler(client, args) {
105
+ try {
106
+ const id = args.service_id;
107
+ // /snapshots doesn't exist in v1; backups is the real read endpoint.
108
+ const data = await client.get(`/v1/services/${encodeURIComponent(id)}/backups`);
109
+ return jsonResult(data);
110
+ }
111
+ catch (e) {
112
+ return errorResult(e instanceof APIError ? e.message : e.message);
113
+ }
114
+ },
115
+ };
116
+ export const getProvisioningState = {
117
+ name: 'get_provisioning_state',
118
+ description: 'Setup state of a pending service: whether its order is paid, whether the VM exists yet, and whether provisioning looks stuck (paid but unprovisioned past the grace period). Use after a deploy to watch it land, or to diagnose a service that stays pending.',
119
+ inputSchema: {
120
+ type: 'object',
121
+ properties: {
122
+ service_id: {
123
+ type: 'string',
124
+ description: 'Service ID from list_services.',
125
+ },
126
+ },
127
+ required: ['service_id'],
128
+ additionalProperties: false,
129
+ },
130
+ async handler(client, args) {
131
+ try {
132
+ const id = args.service_id;
133
+ const data = await client.get(`/v1/services/${encodeURIComponent(id)}/provisioning`);
134
+ return jsonResult(data);
135
+ }
136
+ catch (e) {
137
+ return errorResult(e instanceof APIError ? e.message : e.message);
138
+ }
139
+ },
140
+ };
141
+ export const listOsTemplates = {
142
+ name: 'list_os_templates',
143
+ description: 'Operating systems a legacy VPS can be reinstalled with (Virtualizor templates). Read-only; the reinstall itself is a destructive write and is not exposed as an MCP tool.',
144
+ inputSchema: {
145
+ type: 'object',
146
+ properties: {
147
+ service_id: {
148
+ type: 'string',
149
+ description: 'Server ID from list_services.',
150
+ },
151
+ },
152
+ required: ['service_id'],
153
+ additionalProperties: false,
154
+ },
155
+ async handler(client, args) {
156
+ try {
157
+ const id = args.service_id;
158
+ const data = await client.get(`/v1/services/${encodeURIComponent(id)}/os-templates`);
159
+ return jsonResult(data);
160
+ }
161
+ catch (e) {
162
+ return errorResult(e instanceof APIError ? e.message : e.message);
163
+ }
164
+ },
165
+ };
166
+ export const listUpgradeOptions = {
167
+ name: 'list_upgrade_options',
168
+ description: 'Plans (and billing cycles with prices) a service could be upgraded/downgraded to, from its product group. Read-only; the actual upgrade creates an invoice and is not exposed as an MCP tool.',
169
+ inputSchema: {
170
+ type: 'object',
171
+ properties: {
172
+ service_id: {
173
+ type: 'string',
174
+ description: 'Service ID from list_services.',
175
+ },
176
+ },
177
+ required: ['service_id'],
178
+ additionalProperties: false,
179
+ },
180
+ async handler(client, args) {
181
+ try {
182
+ const id = args.service_id;
183
+ const data = await client.get(`/v1/services/${encodeURIComponent(id)}/upgrade`);
184
+ return jsonResult(data);
185
+ }
186
+ catch (e) {
187
+ return errorResult(e instanceof APIError ? e.message : e.message);
188
+ }
189
+ },
190
+ };
191
+ //# sourceMappingURL=services.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"services.js","sourceRoot":"","sources":["../../src/tools/services.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type ToolDefinition } from './types.js';
2
+ export declare const listTickets: ToolDefinition;
3
+ export declare const getTicket: ToolDefinition;
@@ -0,0 +1,56 @@
1
+ // Support ticket tools — read-only. Opening / replying to tickets stays manual
2
+ // until the write-tool (plan-then-approve) flow ships.
3
+ import { APIError } from '../client.js';
4
+ import { jsonResult, errorResult } from './types.js';
5
+ export const listTickets = {
6
+ name: 'list_tickets',
7
+ description: 'List support tickets for the authenticated account: id, subject, status (open / awaiting-staff / awaiting-client / closed), department, last-updated. Use for "do I have any open tickets?".',
8
+ inputSchema: {
9
+ type: 'object',
10
+ properties: {
11
+ status: {
12
+ type: 'string',
13
+ enum: ['open', 'awaiting-staff', 'awaiting-client', 'closed'],
14
+ description: 'Optional filter on ticket status.',
15
+ },
16
+ },
17
+ additionalProperties: false,
18
+ },
19
+ async handler(client, args) {
20
+ try {
21
+ const data = await client.get('/v1/tickets', {
22
+ status: args.status,
23
+ });
24
+ return jsonResult(data);
25
+ }
26
+ catch (e) {
27
+ return errorResult(e instanceof APIError ? e.message : e.message);
28
+ }
29
+ },
30
+ };
31
+ export const getTicket = {
32
+ name: 'get_ticket',
33
+ description: 'Get a single support ticket with its full message thread. Use after list_tickets to read the conversation or check the latest staff reply.',
34
+ inputSchema: {
35
+ type: 'object',
36
+ properties: {
37
+ ticket_id: {
38
+ type: 'string',
39
+ description: 'Ticket ID from list_tickets.',
40
+ },
41
+ },
42
+ required: ['ticket_id'],
43
+ additionalProperties: false,
44
+ },
45
+ async handler(client, args) {
46
+ try {
47
+ const id = args.ticket_id;
48
+ const data = await client.get(`/v1/tickets/${encodeURIComponent(id)}`);
49
+ return jsonResult(data);
50
+ }
51
+ catch (e) {
52
+ return errorResult(e instanceof APIError ? e.message : e.message);
53
+ }
54
+ },
55
+ };
56
+ //# sourceMappingURL=tickets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tickets.js","sourceRoot":"","sources":["../../src/tools/tickets.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,uDAAuD;AAEvD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAuB,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,CAAC,MAAM,WAAW,GAAmB;IACzC,IAAI,EAAE,cAAc;IACpB,WAAW,EAAE,8LAA8L;IAC3M,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,QAAQ,CAAC;gBAC7D,WAAW,EAAE,mCAAmC;aACjD;SACF;QACD,oBAAoB,EAAE,KAAK;KAC5B;IACD,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI;QACxB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE;gBAC3C,MAAM,EAAE,IAAI,CAAC,MAA4B;aAC1C,CAAC,CAAC;YACH,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,WAAW,CAAC,CAAC,YAAY,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAW,CAAC,OAAO,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAmB;IACvC,IAAI,EAAE,YAAY;IAClB,WAAW,EAAE,4IAA4I;IACzJ,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,SAAS,EAAE;gBACT,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,8BAA8B;aAC5C;SACF;QACD,QAAQ,EAAE,CAAC,WAAW,CAAC;QACvB,oBAAoB,EAAE,KAAK;KAC5B;IACD,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI;QACxB,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,IAAI,CAAC,SAAmB,CAAC;YACpC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,eAAe,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACvE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,WAAW,CAAC,CAAC,YAAY,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,CAAW,CAAC,OAAO,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { RareCloudClient } from '../client.js';
2
+ export interface ToolDefinition {
3
+ name: string;
4
+ description: string;
5
+ inputSchema: {
6
+ type: 'object';
7
+ properties: Record<string, unknown>;
8
+ required?: string[];
9
+ additionalProperties?: boolean;
10
+ };
11
+ handler: (client: RareCloudClient, args: Record<string, unknown>) => Promise<ToolCallResult>;
12
+ }
13
+ export interface ToolCallResult {
14
+ content: Array<{
15
+ type: 'text';
16
+ text: string;
17
+ } | {
18
+ type: 'resource';
19
+ resource: {
20
+ uri: string;
21
+ mimeType?: string;
22
+ text?: string;
23
+ };
24
+ }>;
25
+ isError?: boolean;
26
+ }
27
+ export declare function jsonResult(data: unknown): ToolCallResult;
28
+ export declare function errorResult(message: string): ToolCallResult;
@@ -0,0 +1,21 @@
1
+ // Each MCP tool: a JSON Schema for inputs + a handler that returns the
2
+ // shape MCP expects (`content` array, each block typed).
3
+ //
4
+ // We keep handlers small and uniform — they always return either a JSON
5
+ // payload or a friendly error message. The actual HTTP work lives in
6
+ // client.ts. Tool descriptions are tuned for LLM consumption: lead with
7
+ // the verb, mention the resource, hint at when to use it.
8
+ // Helper: wrap JSON in a text block (the de-facto MCP convention until
9
+ // structured outputs land in the spec).
10
+ export function jsonResult(data) {
11
+ return {
12
+ content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
13
+ };
14
+ }
15
+ export function errorResult(message) {
16
+ return {
17
+ content: [{ type: 'text', text: `Error: ${message}` }],
18
+ isError: true,
19
+ };
20
+ }
21
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,yDAAyD;AACzD,EAAE;AACF,wEAAwE;AACxE,qEAAqE;AACrE,wEAAwE;AACxE,0DAA0D;AA2B1D,uEAAuE;AACvE,wCAAwC;AACxC,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;KACjE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC;QACtD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@rarecloudio/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "Model Context Protocol server for the RareCloud API. Drop into Claude Code / Claude Desktop / Cursor to let AI agents inspect and reason about your RareCloud account.",
5
+ "type": "module",
6
+ "bin": {
7
+ "rarecloud-mcp": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "files": [
11
+ "dist/",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsx src/index.ts",
17
+ "test": "node --test --import tsx/esm src/**/*.test.ts",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "dependencies": {
21
+ "@modelcontextprotocol/sdk": "^1.0.0",
22
+ "zod": "^3.23.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^22.0.0",
26
+ "tsx": "^4.19.0",
27
+ "typescript": "^5.6.0"
28
+ },
29
+ "engines": {
30
+ "node": ">=20.0.0"
31
+ },
32
+ "keywords": [
33
+ "mcp",
34
+ "model-context-protocol",
35
+ "rarecloud",
36
+ "cloud",
37
+ "ai-agents"
38
+ ],
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/RareCloudio/rarecloud-cli"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "homepage": "https://console.rarecloud.io/docs/api"
48
+ }