horizon-mcp 0.0.0-development

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 (99) hide show
  1. package/README.md +138 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +7 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/prompts/debug-connection.d.ts +3 -0
  7. package/dist/prompts/debug-connection.d.ts.map +1 -0
  8. package/dist/prompts/debug-connection.js +17 -0
  9. package/dist/prompts/debug-connection.js.map +1 -0
  10. package/dist/prompts/explain-feature.d.ts +3 -0
  11. package/dist/prompts/explain-feature.d.ts.map +1 -0
  12. package/dist/prompts/explain-feature.js +32 -0
  13. package/dist/prompts/explain-feature.js.map +1 -0
  14. package/dist/prompts/index.d.ts +6 -0
  15. package/dist/prompts/index.d.ts.map +1 -0
  16. package/dist/prompts/index.js +14 -0
  17. package/dist/prompts/index.js.map +1 -0
  18. package/dist/prompts/integrate-feature.d.ts +3 -0
  19. package/dist/prompts/integrate-feature.d.ts.map +1 -0
  20. package/dist/prompts/integrate-feature.js +35 -0
  21. package/dist/prompts/integrate-feature.js.map +1 -0
  22. package/dist/prompts/setup-auth.d.ts +3 -0
  23. package/dist/prompts/setup-auth.d.ts.map +1 -0
  24. package/dist/prompts/setup-auth.js +26 -0
  25. package/dist/prompts/setup-auth.js.map +1 -0
  26. package/dist/resources/index.d.ts +6 -0
  27. package/dist/resources/index.d.ts.map +1 -0
  28. package/dist/resources/index.js +208 -0
  29. package/dist/resources/index.js.map +1 -0
  30. package/dist/server.d.ts +3 -0
  31. package/dist/server.d.ts.map +1 -0
  32. package/dist/server.js +15 -0
  33. package/dist/server.js.map +1 -0
  34. package/dist/tools/__tests__/api-client.test.d.ts +2 -0
  35. package/dist/tools/__tests__/api-client.test.d.ts.map +1 -0
  36. package/dist/tools/__tests__/api-client.test.js +156 -0
  37. package/dist/tools/__tests__/api-client.test.js.map +1 -0
  38. package/dist/tools/api-client.d.ts +25 -0
  39. package/dist/tools/api-client.d.ts.map +1 -0
  40. package/dist/tools/api-client.js +78 -0
  41. package/dist/tools/api-client.js.map +1 -0
  42. package/dist/tools/auth.d.ts +3 -0
  43. package/dist/tools/auth.d.ts.map +1 -0
  44. package/dist/tools/auth.js +123 -0
  45. package/dist/tools/auth.js.map +1 -0
  46. package/dist/tools/cloud-save.d.ts +3 -0
  47. package/dist/tools/cloud-save.d.ts.map +1 -0
  48. package/dist/tools/cloud-save.js +50 -0
  49. package/dist/tools/cloud-save.js.map +1 -0
  50. package/dist/tools/connection.d.ts +3 -0
  51. package/dist/tools/connection.d.ts.map +1 -0
  52. package/dist/tools/connection.js +20 -0
  53. package/dist/tools/connection.js.map +1 -0
  54. package/dist/tools/feedback.d.ts +3 -0
  55. package/dist/tools/feedback.d.ts.map +1 -0
  56. package/dist/tools/feedback.js +36 -0
  57. package/dist/tools/feedback.js.map +1 -0
  58. package/dist/tools/gift-codes.d.ts +3 -0
  59. package/dist/tools/gift-codes.d.ts.map +1 -0
  60. package/dist/tools/gift-codes.js +52 -0
  61. package/dist/tools/gift-codes.js.map +1 -0
  62. package/dist/tools/index.d.ts +6 -0
  63. package/dist/tools/index.d.ts.map +1 -0
  64. package/dist/tools/index.js +24 -0
  65. package/dist/tools/index.js.map +1 -0
  66. package/dist/tools/leaderboard.d.ts +3 -0
  67. package/dist/tools/leaderboard.d.ts.map +1 -0
  68. package/dist/tools/leaderboard.js +96 -0
  69. package/dist/tools/leaderboard.js.map +1 -0
  70. package/dist/tools/news.d.ts +3 -0
  71. package/dist/tools/news.d.ts.map +1 -0
  72. package/dist/tools/news.js +29 -0
  73. package/dist/tools/news.js.map +1 -0
  74. package/dist/tools/remote-config.d.ts +3 -0
  75. package/dist/tools/remote-config.d.ts.map +1 -0
  76. package/dist/tools/remote-config.js +42 -0
  77. package/dist/tools/remote-config.js.map +1 -0
  78. package/dist/tools/tool-helpers.d.ts +24 -0
  79. package/dist/tools/tool-helpers.d.ts.map +1 -0
  80. package/dist/tools/tool-helpers.js +54 -0
  81. package/dist/tools/tool-helpers.js.map +1 -0
  82. package/dist/tools/user-logs.d.ts +3 -0
  83. package/dist/tools/user-logs.d.ts.map +1 -0
  84. package/dist/tools/user-logs.js +30 -0
  85. package/dist/tools/user-logs.js.map +1 -0
  86. package/package.json +52 -0
  87. package/src/resources/api/app-api.md +495 -0
  88. package/src/resources/docs/auth.md +280 -0
  89. package/src/resources/docs/cloud-save.md +180 -0
  90. package/src/resources/docs/feedback.md +126 -0
  91. package/src/resources/docs/gift-codes.md +153 -0
  92. package/src/resources/docs/leaderboard.md +201 -0
  93. package/src/resources/docs/news.md +114 -0
  94. package/src/resources/docs/overview.md +80 -0
  95. package/src/resources/docs/remote-config.md +149 -0
  96. package/src/resources/docs/user-logs.md +144 -0
  97. package/src/resources/quickstart/godot.md +224 -0
  98. package/src/resources/quickstart/unity.md +317 -0
  99. package/src/resources/quickstart/unreal.md +390 -0
@@ -0,0 +1,156 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { HorizonApiClient, HorizonApiError, createApiClientFromEnv, } from "../api-client.js";
3
+ // ---------------------------------------------------------------------------
4
+ // Helpers
5
+ // ---------------------------------------------------------------------------
6
+ function mockFetchResponse(body, init) {
7
+ const status = init?.status ?? 200;
8
+ const ok = init?.ok ?? (status >= 200 && status < 300);
9
+ return vi.fn().mockResolvedValue({
10
+ ok,
11
+ status,
12
+ json: async () => body,
13
+ text: async () => typeof body === "string" ? body : JSON.stringify(body),
14
+ });
15
+ }
16
+ // ---------------------------------------------------------------------------
17
+ // HorizonApiClient
18
+ // ---------------------------------------------------------------------------
19
+ describe("HorizonApiClient", () => {
20
+ const originalFetch = globalThis.fetch;
21
+ afterEach(() => {
22
+ globalThis.fetch = originalFetch;
23
+ });
24
+ it("throws if no API key is provided", () => {
25
+ expect(() => new HorizonApiClient("", "https://example.com")).toThrow("API key must not be empty");
26
+ });
27
+ it("GET sends X-API-Key header", async () => {
28
+ const fakeFetch = mockFetchResponse({ ok: true });
29
+ vi.stubGlobal("fetch", fakeFetch);
30
+ const client = new HorizonApiClient("test-key", "https://example.com");
31
+ await client.get("/api/v1/test");
32
+ expect(fakeFetch).toHaveBeenCalledTimes(1);
33
+ const [url, options] = fakeFetch.mock.calls[0];
34
+ expect(url).toBe("https://example.com/api/v1/test");
35
+ expect(options.method).toBe("GET");
36
+ expect(options.headers["X-API-Key"]).toBe("test-key");
37
+ expect(options.headers["Content-Type"]).toBe("application/json");
38
+ });
39
+ it("GET appends query params", async () => {
40
+ const fakeFetch = mockFetchResponse({ items: [] });
41
+ vi.stubGlobal("fetch", fakeFetch);
42
+ const client = new HorizonApiClient("key", "https://example.com");
43
+ await client.get("/api/v1/items", { limit: "10", offset: "0" });
44
+ const [url] = fakeFetch.mock.calls[0];
45
+ expect(url).toContain("?");
46
+ expect(url).toContain("limit=10");
47
+ expect(url).toContain("offset=0");
48
+ });
49
+ it("POST sends JSON body and correct headers", async () => {
50
+ const fakeFetch = mockFetchResponse({ id: "123" });
51
+ vi.stubGlobal("fetch", fakeFetch);
52
+ const client = new HorizonApiClient("my-key", "https://example.com");
53
+ const payload = { name: "test", value: 42 };
54
+ await client.post("/api/v1/create", payload);
55
+ expect(fakeFetch).toHaveBeenCalledTimes(1);
56
+ const [url, options] = fakeFetch.mock.calls[0];
57
+ expect(url).toBe("https://example.com/api/v1/create");
58
+ expect(options.method).toBe("POST");
59
+ expect(options.headers["X-API-Key"]).toBe("my-key");
60
+ expect(options.headers["Content-Type"]).toBe("application/json");
61
+ expect(options.body).toBe(JSON.stringify(payload));
62
+ });
63
+ it("POST without body sends undefined body", async () => {
64
+ const fakeFetch = mockFetchResponse({ done: true });
65
+ vi.stubGlobal("fetch", fakeFetch);
66
+ const client = new HorizonApiClient("key", "https://example.com");
67
+ await client.post("/api/v1/action");
68
+ const [, options] = fakeFetch.mock.calls[0];
69
+ expect(options.body).toBeUndefined();
70
+ });
71
+ it("throws HorizonApiError on non-ok response (GET)", async () => {
72
+ const fakeFetch = mockFetchResponse("Not Found", {
73
+ status: 404,
74
+ ok: false,
75
+ });
76
+ vi.stubGlobal("fetch", fakeFetch);
77
+ const client = new HorizonApiClient("key", "https://example.com");
78
+ await expect(client.get("/api/v1/missing")).rejects.toThrow(HorizonApiError);
79
+ await expect(client.get("/api/v1/missing")).rejects.toMatchObject({
80
+ status: 404,
81
+ body: "Not Found",
82
+ });
83
+ });
84
+ it("throws HorizonApiError on non-ok response (POST)", async () => {
85
+ const fakeFetch = mockFetchResponse("Bad Request", {
86
+ status: 400,
87
+ ok: false,
88
+ });
89
+ vi.stubGlobal("fetch", fakeFetch);
90
+ const client = new HorizonApiClient("key", "https://example.com");
91
+ await expect(client.post("/api/v1/create", { bad: true })).rejects.toThrow(HorizonApiError);
92
+ await expect(client.post("/api/v1/create", { bad: true })).rejects.toMatchObject({
93
+ status: 400,
94
+ body: "Bad Request",
95
+ });
96
+ });
97
+ it("handles network errors", async () => {
98
+ const fakeFetch = vi
99
+ .fn()
100
+ .mockRejectedValue(new TypeError("fetch failed"));
101
+ vi.stubGlobal("fetch", fakeFetch);
102
+ const client = new HorizonApiClient("key", "https://example.com");
103
+ await expect(client.get("/api/v1/test")).rejects.toThrow("fetch failed");
104
+ await expect(client.post("/api/v1/test")).rejects.toThrow("fetch failed");
105
+ });
106
+ it("strips trailing slash from base URL", async () => {
107
+ const fakeFetch = mockFetchResponse({});
108
+ vi.stubGlobal("fetch", fakeFetch);
109
+ const client = new HorizonApiClient("key", "https://example.com/");
110
+ await client.get("/api/v1/test");
111
+ const [url] = fakeFetch.mock.calls[0];
112
+ expect(url).toBe("https://example.com/api/v1/test");
113
+ });
114
+ });
115
+ // ---------------------------------------------------------------------------
116
+ // createApiClientFromEnv
117
+ // ---------------------------------------------------------------------------
118
+ describe("createApiClientFromEnv", () => {
119
+ const originalEnv = { ...process.env };
120
+ beforeEach(() => {
121
+ delete process.env.HORIZON_API_KEY;
122
+ delete process.env.HORIZON_BASE_URL;
123
+ });
124
+ afterEach(() => {
125
+ process.env = { ...originalEnv };
126
+ });
127
+ it("returns null when HORIZON_API_KEY is not set", () => {
128
+ expect(createApiClientFromEnv()).toBeNull();
129
+ });
130
+ it("returns a client when HORIZON_API_KEY is set", () => {
131
+ process.env.HORIZON_API_KEY = "some-key";
132
+ const client = createApiClientFromEnv();
133
+ expect(client).toBeInstanceOf(HorizonApiClient);
134
+ });
135
+ it("uses HORIZON_BASE_URL when provided", async () => {
136
+ process.env.HORIZON_API_KEY = "some-key";
137
+ process.env.HORIZON_BASE_URL = "https://custom.example.com";
138
+ const fakeFetch = mockFetchResponse({});
139
+ vi.stubGlobal("fetch", fakeFetch);
140
+ const client = createApiClientFromEnv();
141
+ await client.get("/test");
142
+ const [url] = fakeFetch.mock.calls[0];
143
+ expect(url).toBe("https://custom.example.com/test");
144
+ globalThis.fetch = globalThis.fetch; // restored in afterEach via vi.stubGlobal
145
+ });
146
+ it("defaults to https://horizon.pm when HORIZON_BASE_URL is not set", async () => {
147
+ process.env.HORIZON_API_KEY = "some-key";
148
+ const fakeFetch = mockFetchResponse({});
149
+ vi.stubGlobal("fetch", fakeFetch);
150
+ const client = createApiClientFromEnv();
151
+ await client.get("/test");
152
+ const [url] = fakeFetch.mock.calls[0];
153
+ expect(url).toBe("https://horizon.pm/test");
154
+ });
155
+ });
156
+ //# sourceMappingURL=api-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/api-client.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * HTTP API client for the horizOn App API.
3
+ *
4
+ * All requests include the X-API-Key header and Content-Type: application/json.
5
+ * Non-ok responses throw HorizonApiError with the HTTP status and body.
6
+ */
7
+ export declare class HorizonApiError extends Error {
8
+ readonly status: number;
9
+ readonly body: string;
10
+ constructor(status: number, body: string);
11
+ }
12
+ export declare class HorizonApiClient {
13
+ private readonly apiKey;
14
+ private readonly baseUrl;
15
+ constructor(apiKey: string, baseUrl: string);
16
+ get<T>(path: string, params?: Record<string, string>): Promise<T>;
17
+ post<T>(path: string, body?: unknown): Promise<T>;
18
+ }
19
+ /**
20
+ * Creates an API client from environment variables.
21
+ * Returns null if HORIZON_API_KEY is not set.
22
+ * Uses HORIZON_BASE_URL or defaults to "https://horizon.pm".
23
+ */
24
+ export declare function createApiClientFromEnv(): HorizonApiClient | null;
25
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/tools/api-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,qBAAa,eAAgB,SAAQ,KAAK;aAEtB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;gBADZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM;CAK/B;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IASrC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAyBjE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;CAiBxD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,GAAG,IAAI,CAOhE"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * HTTP API client for the horizOn App API.
3
+ *
4
+ * All requests include the X-API-Key header and Content-Type: application/json.
5
+ * Non-ok responses throw HorizonApiError with the HTTP status and body.
6
+ */
7
+ export class HorizonApiError extends Error {
8
+ status;
9
+ body;
10
+ constructor(status, body) {
11
+ super(`horizOn API error (${status}): ${body}`);
12
+ this.status = status;
13
+ this.body = body;
14
+ this.name = "HorizonApiError";
15
+ }
16
+ }
17
+ export class HorizonApiClient {
18
+ apiKey;
19
+ baseUrl;
20
+ constructor(apiKey, baseUrl) {
21
+ if (!apiKey) {
22
+ throw new Error("API key must not be empty");
23
+ }
24
+ this.apiKey = apiKey;
25
+ // Strip trailing slash so path concatenation is predictable
26
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
27
+ }
28
+ async get(path, params) {
29
+ let url = `${this.baseUrl}${path}`;
30
+ if (params) {
31
+ const qs = new URLSearchParams(params).toString();
32
+ if (qs) {
33
+ url += `?${qs}`;
34
+ }
35
+ }
36
+ const response = await fetch(url, {
37
+ method: "GET",
38
+ headers: {
39
+ "X-API-Key": this.apiKey,
40
+ "Content-Type": "application/json",
41
+ },
42
+ });
43
+ if (!response.ok) {
44
+ const body = await response.text();
45
+ throw new HorizonApiError(response.status, body);
46
+ }
47
+ return (await response.json());
48
+ }
49
+ async post(path, body) {
50
+ const response = await fetch(`${this.baseUrl}${path}`, {
51
+ method: "POST",
52
+ headers: {
53
+ "X-API-Key": this.apiKey,
54
+ "Content-Type": "application/json",
55
+ },
56
+ body: body !== undefined ? JSON.stringify(body) : undefined,
57
+ });
58
+ if (!response.ok) {
59
+ const responseBody = await response.text();
60
+ throw new HorizonApiError(response.status, responseBody);
61
+ }
62
+ return (await response.json());
63
+ }
64
+ }
65
+ /**
66
+ * Creates an API client from environment variables.
67
+ * Returns null if HORIZON_API_KEY is not set.
68
+ * Uses HORIZON_BASE_URL or defaults to "https://horizon.pm".
69
+ */
70
+ export function createApiClientFromEnv() {
71
+ const apiKey = process.env.HORIZON_API_KEY;
72
+ if (!apiKey) {
73
+ return null;
74
+ }
75
+ const baseUrl = process.env.HORIZON_BASE_URL || "https://horizon.pm";
76
+ return new HorizonApiClient(apiKey, baseUrl);
77
+ }
78
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/tools/api-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerAuthTools(server: McpServer): void;
3
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/tools/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0HzD"}
@@ -0,0 +1,123 @@
1
+ import { z } from "zod/v4";
2
+ import { createApiClientFromEnv } from "./api-client.js";
3
+ import { noApiKeyResponse, errorResponse, jsonResponse } from "./tool-helpers.js";
4
+ export function registerAuthTools(server) {
5
+ // --- Sign up anonymously ---
6
+ server.registerTool("horizon_signup_anonymous", {
7
+ title: "Sign Up Anonymously",
8
+ description: "Creates a new anonymous user account on horizOn with a display name.",
9
+ inputSchema: {
10
+ displayName: z.string().max(30).describe("Display name for the anonymous user (max 30 characters)"),
11
+ },
12
+ }, async ({ displayName }) => {
13
+ const client = createApiClientFromEnv();
14
+ if (!client)
15
+ return noApiKeyResponse();
16
+ try {
17
+ const result = await client.post("/api/v1/app/user-management/signup", {
18
+ type: "ANONYMOUS",
19
+ username: displayName,
20
+ });
21
+ return jsonResponse(result);
22
+ }
23
+ catch (error) {
24
+ return errorResponse(error);
25
+ }
26
+ });
27
+ // --- Sign up with email ---
28
+ server.registerTool("horizon_signup_email", {
29
+ title: "Sign Up with Email",
30
+ description: "Creates a new user account on horizOn with email and password.",
31
+ inputSchema: {
32
+ email: z.string().email().max(40).describe("Email address (max 40 characters)"),
33
+ password: z.string().min(4).max(32).describe("Password (4-32 characters)"),
34
+ displayName: z.string().max(30).describe("Display name (max 30 characters)"),
35
+ },
36
+ }, async ({ email, password, displayName }) => {
37
+ const client = createApiClientFromEnv();
38
+ if (!client)
39
+ return noApiKeyResponse();
40
+ try {
41
+ const result = await client.post("/api/v1/app/user-management/signup", {
42
+ type: "EMAIL",
43
+ email,
44
+ password,
45
+ username: displayName,
46
+ });
47
+ return jsonResponse(result);
48
+ }
49
+ catch (error) {
50
+ return errorResponse(error);
51
+ }
52
+ });
53
+ // --- Sign in with email ---
54
+ server.registerTool("horizon_signin_email", {
55
+ title: "Sign In with Email",
56
+ description: "Signs in an existing user on horizOn using email and password.",
57
+ inputSchema: {
58
+ email: z.string().email().describe("Email address"),
59
+ password: z.string().describe("Password"),
60
+ },
61
+ }, async ({ email, password }) => {
62
+ const client = createApiClientFromEnv();
63
+ if (!client)
64
+ return noApiKeyResponse();
65
+ try {
66
+ const result = await client.post("/api/v1/app/user-management/signin", {
67
+ type: "EMAIL",
68
+ email,
69
+ password,
70
+ });
71
+ return jsonResponse(result);
72
+ }
73
+ catch (error) {
74
+ return errorResponse(error);
75
+ }
76
+ });
77
+ // --- Sign in anonymously ---
78
+ server.registerTool("horizon_signin_anonymous", {
79
+ title: "Sign In Anonymously",
80
+ description: "Signs in an anonymous user on horizOn using their anonymous token.",
81
+ inputSchema: {
82
+ anonymousToken: z.string().max(32).describe("Anonymous token (max 32 characters)"),
83
+ },
84
+ }, async ({ anonymousToken }) => {
85
+ const client = createApiClientFromEnv();
86
+ if (!client)
87
+ return noApiKeyResponse();
88
+ try {
89
+ const result = await client.post("/api/v1/app/user-management/signin", {
90
+ type: "ANONYMOUS",
91
+ anonymousToken,
92
+ });
93
+ return jsonResponse(result);
94
+ }
95
+ catch (error) {
96
+ return errorResponse(error);
97
+ }
98
+ });
99
+ // --- Check auth ---
100
+ server.registerTool("horizon_check_auth", {
101
+ title: "Check Authentication",
102
+ description: "Checks whether a user session is still valid on horizOn.",
103
+ inputSchema: {
104
+ userId: z.string().uuid().describe("User ID (UUID)"),
105
+ sessionToken: z.string().max(256).describe("Session token (max 256 characters)"),
106
+ },
107
+ }, async ({ userId, sessionToken }) => {
108
+ const client = createApiClientFromEnv();
109
+ if (!client)
110
+ return noApiKeyResponse();
111
+ try {
112
+ const result = await client.post("/api/v1/app/user-management/check-auth", {
113
+ userId,
114
+ sessionToken,
115
+ });
116
+ return jsonResponse(result);
117
+ }
118
+ catch (error) {
119
+ return errorResponse(error);
120
+ }
121
+ });
122
+ }
123
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/tools/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCloudSaveTools(server: McpServer): void;
3
+ //# sourceMappingURL=cloud-save.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-save.d.ts","sourceRoot":"","sources":["../../src/tools/cloud-save.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA8C9D"}
@@ -0,0 +1,50 @@
1
+ import { z } from "zod/v4";
2
+ import { createApiClientFromEnv } from "./api-client.js";
3
+ import { noApiKeyResponse, errorResponse, jsonResponse } from "./tool-helpers.js";
4
+ export function registerCloudSaveTools(server) {
5
+ // --- Save cloud data ---
6
+ server.registerTool("horizon_save_cloud_data", {
7
+ title: "Save Cloud Data",
8
+ description: "Saves cloud data for a user on horizOn. Data is a string (max 300,000 characters).",
9
+ inputSchema: {
10
+ userId: z.string().uuid().describe("User ID (UUID)"),
11
+ data: z.string().max(300000).describe("Save data string (max 300,000 characters)"),
12
+ },
13
+ }, async ({ userId, data }) => {
14
+ const client = createApiClientFromEnv();
15
+ if (!client)
16
+ return noApiKeyResponse();
17
+ try {
18
+ const result = await client.post("/api/v1/app/cloud-save/save", {
19
+ userId,
20
+ saveData: data,
21
+ });
22
+ return jsonResponse(result);
23
+ }
24
+ catch (error) {
25
+ return errorResponse(error);
26
+ }
27
+ });
28
+ // --- Load cloud data ---
29
+ server.registerTool("horizon_load_cloud_data", {
30
+ title: "Load Cloud Data",
31
+ description: "Loads cloud save data for a user from horizOn.",
32
+ inputSchema: {
33
+ userId: z.string().uuid().describe("User ID (UUID)"),
34
+ },
35
+ }, async ({ userId }) => {
36
+ const client = createApiClientFromEnv();
37
+ if (!client)
38
+ return noApiKeyResponse();
39
+ try {
40
+ const result = await client.post("/api/v1/app/cloud-save/load", {
41
+ userId,
42
+ });
43
+ return jsonResponse(result);
44
+ }
45
+ catch (error) {
46
+ return errorResponse(error);
47
+ }
48
+ });
49
+ }
50
+ //# sourceMappingURL=cloud-save.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-save.js","sourceRoot":"","sources":["../../src/tools/cloud-save.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAElF,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,0BAA0B;IAC1B,MAAM,CAAC,YAAY,CAAC,yBAAyB,EAAE;QAC7C,KAAK,EAAE,iBAAiB;QACxB,WAAW,EACT,oFAAoF;QACtF,WAAW,EAAE;YACX,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACpD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,2CAA2C,CAAC;SACnF;KACF,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE;QAC5B,MAAM,MAAM,GAAG,sBAAsB,EAAE,CAAC;QACxC,IAAI,CAAC,MAAM;YAAE,OAAO,gBAAgB,EAAE,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,6BAA6B,EAAE;gBAC9D,MAAM;gBACN,QAAQ,EAAE,IAAI;aACf,CAAC,CAAC;YACH,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,MAAM,CAAC,YAAY,CAAC,yBAAyB,EAAE;QAC7C,KAAK,EAAE,iBAAiB;QACxB,WAAW,EACT,gDAAgD;QAClD,WAAW,EAAE;YACX,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;SACrD;KACF,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QACtB,MAAM,MAAM,GAAG,sBAAsB,EAAE,CAAC;QACxC,IAAI,CAAC,MAAM;YAAE,OAAO,gBAAgB,EAAE,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,6BAA6B,EAAE;gBAC9D,MAAM;aACP,CAAC,CAAC;YACH,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerConnectionTools(server: McpServer): void;
3
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/tools/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgB/D"}
@@ -0,0 +1,20 @@
1
+ import { createApiClientFromEnv } from "./api-client.js";
2
+ import { noApiKeyResponse, errorResponse, jsonResponse } from "./tool-helpers.js";
3
+ export function registerConnectionTools(server) {
4
+ server.registerTool("horizon_test_connection", {
5
+ title: "Test Connection",
6
+ description: "Tests the connection to the horizOn API by fetching all remote configs as a health check. Returns success or failure.",
7
+ }, async () => {
8
+ const client = createApiClientFromEnv();
9
+ if (!client)
10
+ return noApiKeyResponse();
11
+ try {
12
+ const result = await client.get("/api/v1/app/remote-config/all");
13
+ return jsonResponse({ success: true, data: result });
14
+ }
15
+ catch (error) {
16
+ return errorResponse(error);
17
+ }
18
+ });
19
+ }
20
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/tools/connection.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAElF,MAAM,UAAU,uBAAuB,CAAC,MAAiB;IACvD,MAAM,CAAC,YAAY,CAAC,yBAAyB,EAAE;QAC7C,KAAK,EAAE,iBAAiB;QACxB,WAAW,EACT,uHAAuH;KAC1H,EAAE,KAAK,IAAI,EAAE;QACZ,MAAM,MAAM,GAAG,sBAAsB,EAAE,CAAC;QACxC,IAAI,CAAC,MAAM;YAAE,OAAO,gBAAgB,EAAE,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;YACjE,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerFeedbackTools(server: McpServer): void;
3
+ //# sourceMappingURL=feedback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback.d.ts","sourceRoot":"","sources":["../../src/tools/feedback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6B7D"}
@@ -0,0 +1,36 @@
1
+ import { z } from "zod/v4";
2
+ import { createApiClientFromEnv } from "./api-client.js";
3
+ import { noApiKeyResponse, errorResponse, jsonResponse } from "./tool-helpers.js";
4
+ export function registerFeedbackTools(server) {
5
+ server.registerTool("horizon_submit_feedback", {
6
+ title: "Submit Feedback",
7
+ description: "Submits user feedback to horizOn with a title, message, and optional category, email, and device info.",
8
+ inputSchema: {
9
+ userId: z.string().uuid().describe("User ID (UUID)"),
10
+ title: z.string().min(1).max(100).describe("Feedback title (1-100 characters)"),
11
+ message: z.string().min(1).max(2048).describe("Feedback message (1-2048 characters)"),
12
+ category: z.string().max(50).optional().describe("Feedback category (max 50 characters)"),
13
+ email: z.string().email().optional().describe("Contact email address"),
14
+ deviceInfo: z.string().max(500).optional().describe("Device information (max 500 characters)"),
15
+ },
16
+ }, async ({ userId, title, message, category, email, deviceInfo }) => {
17
+ const client = createApiClientFromEnv();
18
+ if (!client)
19
+ return noApiKeyResponse();
20
+ try {
21
+ const body = { userId, title, message };
22
+ if (category !== undefined)
23
+ body.category = category;
24
+ if (email !== undefined)
25
+ body.email = email;
26
+ if (deviceInfo !== undefined)
27
+ body.deviceInfo = deviceInfo;
28
+ const result = await client.post("/api/v1/app/user-feedback/submit", body);
29
+ return jsonResponse(result);
30
+ }
31
+ catch (error) {
32
+ return errorResponse(error);
33
+ }
34
+ });
35
+ }
36
+ //# sourceMappingURL=feedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback.js","sourceRoot":"","sources":["../../src/tools/feedback.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAElF,MAAM,UAAU,qBAAqB,CAAC,MAAiB;IACrD,MAAM,CAAC,YAAY,CAAC,yBAAyB,EAAE;QAC7C,KAAK,EAAE,iBAAiB;QACxB,WAAW,EACT,wGAAwG;QAC1G,WAAW,EAAE;YACX,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACpD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,mCAAmC,CAAC;YAC/E,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,sCAAsC,CAAC;YACrF,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;YACzF,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YACtE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;SAC/F;KACF,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE;QACnE,MAAM,MAAM,GAAG,sBAAsB,EAAE,CAAC;QACxC,IAAI,CAAC,MAAM;YAAE,OAAO,gBAAgB,EAAE,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,IAAI,GAA4B,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;YACjE,IAAI,QAAQ,KAAK,SAAS;gBAAE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;YACrD,IAAI,KAAK,KAAK,SAAS;gBAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;YAC5C,IAAI,UAAU,KAAK,SAAS;gBAAE,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;YAE3D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,kCAAkC,EAAE,IAAI,CAAC,CAAC;YAC3E,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerGiftCodeTools(server: McpServer): void;
3
+ //# sourceMappingURL=gift-codes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gift-codes.d.ts","sourceRoot":"","sources":["../../src/tools/gift-codes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgD7D"}
@@ -0,0 +1,52 @@
1
+ import { z } from "zod/v4";
2
+ import { createApiClientFromEnv } from "./api-client.js";
3
+ import { noApiKeyResponse, errorResponse, jsonResponse } from "./tool-helpers.js";
4
+ export function registerGiftCodeTools(server) {
5
+ // --- Validate gift code ---
6
+ server.registerTool("horizon_validate_gift_code", {
7
+ title: "Validate Gift Code",
8
+ description: "Validates a gift code on horizOn without redeeming it. Checks if the code is valid for the given user.",
9
+ inputSchema: {
10
+ code: z.string().max(50).describe("Gift code to validate (max 50 characters)"),
11
+ userId: z.string().uuid().describe("User ID (UUID)"),
12
+ },
13
+ }, async ({ code, userId }) => {
14
+ const client = createApiClientFromEnv();
15
+ if (!client)
16
+ return noApiKeyResponse();
17
+ try {
18
+ const result = await client.post("/api/v1/app/gift-codes/validate", {
19
+ code,
20
+ userId,
21
+ });
22
+ return jsonResponse(result);
23
+ }
24
+ catch (error) {
25
+ return errorResponse(error);
26
+ }
27
+ });
28
+ // --- Redeem gift code ---
29
+ server.registerTool("horizon_redeem_gift_code", {
30
+ title: "Redeem Gift Code",
31
+ description: "Redeems a gift code on horizOn for the given user.",
32
+ inputSchema: {
33
+ code: z.string().max(50).describe("Gift code to redeem (max 50 characters)"),
34
+ userId: z.string().uuid().describe("User ID (UUID)"),
35
+ },
36
+ }, async ({ code, userId }) => {
37
+ const client = createApiClientFromEnv();
38
+ if (!client)
39
+ return noApiKeyResponse();
40
+ try {
41
+ const result = await client.post("/api/v1/app/gift-codes/redeem", {
42
+ code,
43
+ userId,
44
+ });
45
+ return jsonResponse(result);
46
+ }
47
+ catch (error) {
48
+ return errorResponse(error);
49
+ }
50
+ });
51
+ }
52
+ //# sourceMappingURL=gift-codes.js.map