asc-mcp 1.0.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,2813 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
7
+
8
+ // src/utils/errors.ts
9
+ function getSensitivePatterns() {
10
+ return [
11
+ /Bearer [A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+/gi,
12
+ // JWT tokens
13
+ /-----BEGIN.*?-----[\s\S]*?-----END.*?-----/g,
14
+ // PEM keys
15
+ /[A-Za-z0-9]{8}-[A-Za-z0-9]{4}-[A-Za-z0-9]{4}-[A-Za-z0-9]{4}-[A-Za-z0-9]{12}/gi
16
+ // UUIDs (issuer IDs)
17
+ ];
18
+ }
19
+ function sanitizeMessage(message) {
20
+ let sanitized = message;
21
+ for (const pattern of getSensitivePatterns()) {
22
+ sanitized = sanitized.replace(pattern, "[REDACTED]");
23
+ }
24
+ return sanitized;
25
+ }
26
+ var ASCError = class extends Error {
27
+ code;
28
+ status;
29
+ details;
30
+ constructor(message, code, status, details) {
31
+ super(sanitizeMessage(message));
32
+ this.name = "ASCError";
33
+ this.code = code;
34
+ this.status = status;
35
+ this.details = details;
36
+ }
37
+ toJSON() {
38
+ return {
39
+ success: false,
40
+ error: {
41
+ code: this.code,
42
+ message: this.message,
43
+ details: this.details
44
+ }
45
+ };
46
+ }
47
+ };
48
+ var AuthError = class extends ASCError {
49
+ constructor(message, details) {
50
+ super(message, "AUTH_ERROR", 401, details);
51
+ this.name = "AuthError";
52
+ }
53
+ };
54
+ var RateLimitError = class extends ASCError {
55
+ retryAfter;
56
+ constructor(retryAfter, details) {
57
+ super(`Rate limit exceeded. Retry after ${retryAfter} seconds.`, "RATE_LIMIT", 429, details);
58
+ this.name = "RateLimitError";
59
+ this.retryAfter = retryAfter;
60
+ }
61
+ toJSON() {
62
+ return {
63
+ success: false,
64
+ error: {
65
+ code: this.code,
66
+ message: this.message,
67
+ details: this.details,
68
+ retryAfter: this.retryAfter
69
+ }
70
+ };
71
+ }
72
+ };
73
+ var ValidationError = class extends ASCError {
74
+ field;
75
+ constructor(message, field) {
76
+ super(message, "VALIDATION_ERROR", 400);
77
+ this.name = "ValidationError";
78
+ this.field = field;
79
+ }
80
+ toJSON() {
81
+ return {
82
+ success: false,
83
+ error: {
84
+ code: this.code,
85
+ message: this.message,
86
+ details: this.details,
87
+ field: this.field
88
+ }
89
+ };
90
+ }
91
+ };
92
+ var ConfigError = class extends ASCError {
93
+ constructor(message) {
94
+ super(message, "CONFIG_ERROR", 500);
95
+ this.name = "ConfigError";
96
+ }
97
+ };
98
+ function parseAPIError(status, body) {
99
+ const errors = body?.errors;
100
+ if (status === 401) {
101
+ return new AuthError("Authentication failed", errors);
102
+ }
103
+ if (status === 429) {
104
+ return new RateLimitError(60, errors);
105
+ }
106
+ if (status === 404) {
107
+ const detail = errors?.[0]?.detail ?? "Resource not found";
108
+ return new ASCError(detail, "NOT_FOUND", 404, errors);
109
+ }
110
+ if (status === 403) {
111
+ return new ASCError(
112
+ "Access forbidden. Check your API key permissions.",
113
+ "FORBIDDEN",
114
+ 403,
115
+ errors
116
+ );
117
+ }
118
+ if (status === 409) {
119
+ const detail = errors?.[0]?.detail ?? "Conflict with current state";
120
+ return new ASCError(detail, "CONFLICT", 409, errors);
121
+ }
122
+ const message = errors?.[0]?.detail ?? `API request failed with status ${status}`;
123
+ return new ASCError(message, "API_ERROR", status, errors);
124
+ }
125
+ function formatErrorResponse(error) {
126
+ if (error instanceof ASCError) {
127
+ return error.toJSON();
128
+ }
129
+ if (error instanceof Error) {
130
+ return {
131
+ success: false,
132
+ error: {
133
+ code: "INTERNAL_ERROR",
134
+ message: sanitizeMessage(error.message)
135
+ }
136
+ };
137
+ }
138
+ return {
139
+ success: false,
140
+ error: {
141
+ code: "UNKNOWN_ERROR",
142
+ message: "An unknown error occurred"
143
+ }
144
+ };
145
+ }
146
+
147
+ // src/api/client.ts
148
+ var BASE_URL = "https://api.appstoreconnect.apple.com/v1";
149
+ var DEFAULT_TIMEOUT_MS = 3e4;
150
+ var MAX_RETRIES = 3;
151
+ var INITIAL_RETRY_DELAY_MS = 1e3;
152
+ var RATE_LIMIT_WINDOW_MS = 6e4;
153
+ var MAX_REQUESTS_PER_WINDOW = 50;
154
+ var AppStoreConnectClient = class {
155
+ tokenManager;
156
+ baseUrl;
157
+ rateLimiter = { timestamps: [] };
158
+ constructor(tokenManager, baseUrl = BASE_URL) {
159
+ this.tokenManager = tokenManager;
160
+ this.baseUrl = baseUrl;
161
+ }
162
+ /**
163
+ * Make a GET request
164
+ */
165
+ async get(path2, params) {
166
+ return this.request({ method: "GET", path: path2, params });
167
+ }
168
+ /**
169
+ * Make a POST request
170
+ */
171
+ async post(path2, body) {
172
+ return this.request({ method: "POST", path: path2, body });
173
+ }
174
+ /**
175
+ * Make a PATCH request
176
+ */
177
+ async patch(path2, body) {
178
+ return this.request({ method: "PATCH", path: path2, body });
179
+ }
180
+ /**
181
+ * Make a DELETE request
182
+ */
183
+ async delete(path2, body) {
184
+ await this.request({ method: "DELETE", path: path2, body });
185
+ }
186
+ /**
187
+ * Make a raw request (for custom operations like file uploads)
188
+ */
189
+ async rawRequest(url, options) {
190
+ const controller = new AbortController();
191
+ const timeoutId = setTimeout(() => controller.abort(), options.timeout ?? DEFAULT_TIMEOUT_MS);
192
+ try {
193
+ const response = await fetch(url, {
194
+ method: options.method,
195
+ headers: options.headers,
196
+ body: options.body,
197
+ signal: controller.signal
198
+ });
199
+ return response;
200
+ } finally {
201
+ clearTimeout(timeoutId);
202
+ }
203
+ }
204
+ /**
205
+ * Paginate through all results
206
+ */
207
+ async *paginate(path2, params, maxItems) {
208
+ let cursor;
209
+ let itemsYielded = 0;
210
+ do {
211
+ const queryParams = { ...params };
212
+ if (cursor) {
213
+ if (cursor.includes("?")) {
214
+ try {
215
+ const url = new URL(cursor);
216
+ for (const [key, value] of url.searchParams) {
217
+ queryParams[key] = value;
218
+ }
219
+ } catch {
220
+ const queryString = cursor.split("?")[1];
221
+ if (queryString) {
222
+ const searchParams = new URLSearchParams(queryString);
223
+ for (const [key, value] of searchParams) {
224
+ queryParams[key] = value;
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+ const response = await this.get(path2, queryParams);
231
+ for (const item of response.data) {
232
+ yield item;
233
+ itemsYielded++;
234
+ if (maxItems && itemsYielded >= maxItems) {
235
+ return;
236
+ }
237
+ }
238
+ cursor = response.links?.next;
239
+ } while (cursor);
240
+ }
241
+ /**
242
+ * Core request method with retry logic
243
+ */
244
+ async request(options) {
245
+ await this.enforceRateLimit();
246
+ let lastError;
247
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
248
+ try {
249
+ return await this.executeRequest(options);
250
+ } catch (error) {
251
+ lastError = error instanceof Error ? error : new Error(String(error));
252
+ if (error instanceof ASCError && error.status >= 400 && error.status < 500) {
253
+ if (error instanceof RateLimitError) {
254
+ const waitMs = error.retryAfter * 1e3;
255
+ await this.sleep(waitMs);
256
+ continue;
257
+ }
258
+ throw error;
259
+ }
260
+ if (attempt < MAX_RETRIES - 1) {
261
+ const delay = INITIAL_RETRY_DELAY_MS * 2 ** attempt;
262
+ await this.sleep(delay);
263
+ }
264
+ }
265
+ }
266
+ throw lastError ?? new Error("Request failed after max retries");
267
+ }
268
+ /**
269
+ * Execute a single HTTP request
270
+ */
271
+ async executeRequest(options) {
272
+ const token = await this.tokenManager.getToken();
273
+ const url = this.buildUrl(options.path, options.params);
274
+ const headers = {
275
+ Authorization: `Bearer ${token}`
276
+ };
277
+ if (options.body) {
278
+ headers["Content-Type"] = "application/json";
279
+ }
280
+ const controller = new AbortController();
281
+ const timeoutId = setTimeout(() => controller.abort(), options.timeout ?? DEFAULT_TIMEOUT_MS);
282
+ try {
283
+ const response = await fetch(url, {
284
+ method: options.method,
285
+ headers,
286
+ body: options.body ? JSON.stringify(options.body) : void 0,
287
+ signal: controller.signal
288
+ });
289
+ clearTimeout(timeoutId);
290
+ if (response.status === 429) {
291
+ const retryAfter = Number.parseInt(response.headers.get("Retry-After") ?? "60", 10);
292
+ throw new RateLimitError(retryAfter);
293
+ }
294
+ if (response.status === 204) {
295
+ return void 0;
296
+ }
297
+ const body = await response.json();
298
+ if (!response.ok) {
299
+ throw parseAPIError(response.status, body);
300
+ }
301
+ return body;
302
+ } catch (error) {
303
+ clearTimeout(timeoutId);
304
+ if (error instanceof ASCError) {
305
+ throw error;
306
+ }
307
+ if (error instanceof Error) {
308
+ if (error.name === "AbortError") {
309
+ throw new ASCError("Request timed out", "TIMEOUT", 408);
310
+ }
311
+ throw new ASCError(error.message, "NETWORK_ERROR", 0);
312
+ }
313
+ throw new ASCError("Unknown error occurred", "UNKNOWN", 0);
314
+ }
315
+ }
316
+ /**
317
+ * Build URL with query parameters
318
+ */
319
+ buildUrl(path2, params) {
320
+ const url = new URL(path2.startsWith("/") ? path2.slice(1) : path2, `${this.baseUrl}/`);
321
+ if (params) {
322
+ for (const [key, value] of Object.entries(params)) {
323
+ if (value !== void 0) {
324
+ url.searchParams.append(key, String(value));
325
+ }
326
+ }
327
+ }
328
+ return url.toString();
329
+ }
330
+ /**
331
+ * Self-imposed rate limiting.
332
+ * Records the request timestamp before checking to prevent off-by-one errors.
333
+ */
334
+ async enforceRateLimit() {
335
+ const now = Date.now();
336
+ this.rateLimiter.timestamps = this.rateLimiter.timestamps.filter(
337
+ (ts) => now - ts < RATE_LIMIT_WINDOW_MS
338
+ );
339
+ if (this.rateLimiter.timestamps.length >= MAX_REQUESTS_PER_WINDOW) {
340
+ const oldestTimestamp = this.rateLimiter.timestamps[0];
341
+ if (oldestTimestamp) {
342
+ const waitTime = RATE_LIMIT_WINDOW_MS - (now - oldestTimestamp);
343
+ if (waitTime > 0) {
344
+ await this.sleep(waitTime);
345
+ const newNow = Date.now();
346
+ this.rateLimiter.timestamps = this.rateLimiter.timestamps.filter(
347
+ (ts) => newNow - ts < RATE_LIMIT_WINDOW_MS
348
+ );
349
+ }
350
+ }
351
+ }
352
+ this.rateLimiter.timestamps.push(Date.now());
353
+ }
354
+ /**
355
+ * Sleep helper
356
+ */
357
+ sleep(ms) {
358
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
359
+ }
360
+ };
361
+ function createClient(tokenManager) {
362
+ return new AppStoreConnectClient(tokenManager);
363
+ }
364
+
365
+ // src/auth/jwt.ts
366
+ import * as fs from "fs/promises";
367
+ import * as path from "path";
368
+ import { SignJWT, importPKCS8 } from "jose";
369
+ var TOKEN_LIFETIME_SECONDS = 15 * 60;
370
+ var TOKEN_REFRESH_BUFFER_SECONDS = 5 * 60;
371
+ var AUDIENCE = "appstoreconnect-v1";
372
+ var TokenManager = class {
373
+ keyId;
374
+ issuerId;
375
+ privateKeySource;
376
+ cachedToken = null;
377
+ tokenExpiresAt = 0;
378
+ privateKey = null;
379
+ pendingTokenGeneration = null;
380
+ constructor(config) {
381
+ this.keyId = config.keyId;
382
+ this.issuerId = config.issuerId;
383
+ if (config.privateKeyPath) {
384
+ if (config.privateKeyPath.includes("..")) {
385
+ throw new ConfigError("Private key path cannot contain '..'");
386
+ }
387
+ this.privateKeySource = { type: "path", path: config.privateKeyPath };
388
+ } else if (config.privateKeyContent) {
389
+ this.privateKeySource = { type: "content", content: config.privateKeyContent };
390
+ } else {
391
+ throw new ConfigError("Either privateKeyPath or privateKeyContent must be provided");
392
+ }
393
+ }
394
+ /**
395
+ * Get a valid JWT token, generating a new one if needed.
396
+ * Uses a pending promise pattern to prevent concurrent token generation.
397
+ */
398
+ async getToken() {
399
+ const now = Math.floor(Date.now() / 1e3);
400
+ if (this.cachedToken && this.tokenExpiresAt > now + TOKEN_REFRESH_BUFFER_SECONDS) {
401
+ return this.cachedToken;
402
+ }
403
+ if (this.pendingTokenGeneration) {
404
+ return this.pendingTokenGeneration;
405
+ }
406
+ this.pendingTokenGeneration = this.generateToken().finally(() => {
407
+ this.pendingTokenGeneration = null;
408
+ });
409
+ return this.pendingTokenGeneration;
410
+ }
411
+ /**
412
+ * Generate a new JWT token
413
+ */
414
+ async generateToken() {
415
+ const now = Math.floor(Date.now() / 1e3);
416
+ const expiresAt = now + TOKEN_LIFETIME_SECONDS;
417
+ if (!this.privateKey) {
418
+ await this.loadPrivateKey();
419
+ }
420
+ if (!this.privateKey) {
421
+ throw new AuthError("Failed to load private key");
422
+ }
423
+ try {
424
+ const jwt = await new SignJWT({}).setProtectedHeader({
425
+ alg: "ES256",
426
+ kid: this.keyId,
427
+ typ: "JWT"
428
+ }).setIssuer(this.issuerId).setIssuedAt(now).setExpirationTime(expiresAt).setAudience(AUDIENCE).sign(this.privateKey);
429
+ this.cachedToken = jwt;
430
+ this.tokenExpiresAt = expiresAt;
431
+ return jwt;
432
+ } catch (error) {
433
+ throw new AuthError(
434
+ `Failed to generate JWT: ${error instanceof Error ? error.message : "Unknown error"}`
435
+ );
436
+ }
437
+ }
438
+ /**
439
+ * Load the private key from file or content
440
+ */
441
+ async loadPrivateKey() {
442
+ let keyContent;
443
+ if (this.privateKeySource.type === "path") {
444
+ try {
445
+ const absolutePath = path.resolve(this.privateKeySource.path);
446
+ keyContent = await fs.readFile(absolutePath, "utf-8");
447
+ } catch (error) {
448
+ throw new ConfigError(
449
+ `Failed to read private key from ${this.privateKeySource.path}: ${error instanceof Error ? error.message : "Unknown error"}`
450
+ );
451
+ }
452
+ } else {
453
+ keyContent = this.privateKeySource.content;
454
+ }
455
+ keyContent = keyContent.trim();
456
+ if (!keyContent.startsWith("-----BEGIN")) {
457
+ keyContent = `-----BEGIN PRIVATE KEY-----
458
+ ${keyContent}
459
+ -----END PRIVATE KEY-----`;
460
+ }
461
+ try {
462
+ this.privateKey = await importPKCS8(keyContent, "ES256");
463
+ } catch (error) {
464
+ throw new AuthError(
465
+ `Failed to parse private key: ${error instanceof Error ? error.message : "Unknown error"}`
466
+ );
467
+ }
468
+ }
469
+ /**
470
+ * Clear cached token (call on shutdown)
471
+ */
472
+ destroy() {
473
+ this.cachedToken = null;
474
+ this.tokenExpiresAt = 0;
475
+ this.privateKey = null;
476
+ this.pendingTokenGeneration = null;
477
+ }
478
+ /**
479
+ * Check if a token is currently cached and valid
480
+ */
481
+ hasValidToken() {
482
+ const now = Math.floor(Date.now() / 1e3);
483
+ return this.cachedToken !== null && this.tokenExpiresAt > now + TOKEN_REFRESH_BUFFER_SECONDS;
484
+ }
485
+ };
486
+ function createTokenManagerFromEnv() {
487
+ const keyId = process.env.APP_STORE_CONNECT_KEY_ID;
488
+ const issuerId = process.env.APP_STORE_CONNECT_ISSUER_ID;
489
+ const privateKeyPath = process.env.APP_STORE_CONNECT_P8_PATH;
490
+ const privateKeyContent = process.env.APP_STORE_CONNECT_P8_CONTENT;
491
+ if (!keyId) {
492
+ throw new ConfigError("APP_STORE_CONNECT_KEY_ID environment variable is required");
493
+ }
494
+ if (!issuerId) {
495
+ throw new ConfigError("APP_STORE_CONNECT_ISSUER_ID environment variable is required");
496
+ }
497
+ if (!privateKeyPath && !privateKeyContent) {
498
+ throw new ConfigError(
499
+ "Either APP_STORE_CONNECT_P8_PATH or APP_STORE_CONNECT_P8_CONTENT environment variable is required"
500
+ );
501
+ }
502
+ return new TokenManager({
503
+ keyId,
504
+ issuerId,
505
+ privateKeyPath,
506
+ privateKeyContent
507
+ });
508
+ }
509
+
510
+ // src/utils/validation.ts
511
+ import { z } from "zod";
512
+ var appIdSchema = z.string().regex(/^\d+$/, "App ID must be a numeric string").min(1, "App ID is required");
513
+ var versionIdSchema = z.string().regex(/^\d+$/, "Version ID must be a numeric string").min(1, "Version ID is required");
514
+ var localizationIdSchema = z.string().regex(/^\d+$/, "Localization ID must be a numeric string").min(1, "Localization ID is required");
515
+ var buildIdSchema = z.string().min(1, "Build ID is required");
516
+ var betaGroupIdSchema = z.string().min(1, "Beta Group ID is required");
517
+ var localeSchema = z.string().regex(
518
+ /^[a-z]{2}([-_][A-Z][a-z]{3})?([-_][A-Z]{2})?$/,
519
+ "Invalid locale format (e.g., en-US, en_US, ja, zh-Hans)"
520
+ ).min(2, "Locale is required");
521
+ var urlSchema = z.string().url("Must be a valid URL").refine((url) => url.startsWith("https://"), "URL must use HTTPS");
522
+ var optionalUrlSchema = urlSchema.optional();
523
+ var versionStringSchema = z.string().regex(/^(0|[1-9]\d*)(\.(0|[1-9]\d*))*$/, "Invalid version format (e.g., 1.0.0)").min(1, "Version string is required");
524
+ var platformSchema = z.enum(["IOS", "MAC_OS", "TV_OS", "VISION_OS"]);
525
+ var versionStateSchema = z.enum([
526
+ "DEVELOPER_REMOVED_FROM_SALE",
527
+ "DEVELOPER_REJECTED",
528
+ "IN_REVIEW",
529
+ "INVALID_BINARY",
530
+ "METADATA_REJECTED",
531
+ "PENDING_APPLE_RELEASE",
532
+ "PENDING_CONTRACT",
533
+ "PENDING_DEVELOPER_RELEASE",
534
+ "PREPARE_FOR_SUBMISSION",
535
+ "PREORDER_READY_FOR_SALE",
536
+ "PROCESSING_FOR_APP_STORE",
537
+ "READY_FOR_REVIEW",
538
+ "READY_FOR_SALE",
539
+ "REJECTED",
540
+ "REMOVED_FROM_SALE",
541
+ "WAITING_FOR_EXPORT_COMPLIANCE",
542
+ "WAITING_FOR_REVIEW",
543
+ "REPLACED_WITH_NEW_VERSION",
544
+ "NOT_APPLICABLE"
545
+ ]);
546
+ var releaseTypeSchema = z.enum(["MANUAL", "AFTER_APPROVAL", "SCHEDULED"]);
547
+ var paginationSchema = z.object({
548
+ limit: z.number().int().min(1).max(200).optional().default(50),
549
+ cursor: z.string().optional()
550
+ });
551
+ var listAppsInputSchema = z.object({
552
+ limit: z.number().int().min(1).max(200).optional()
553
+ });
554
+ var getAppInputSchema = z.object({
555
+ appId: appIdSchema
556
+ });
557
+ var listAppVersionsInputSchema = z.object({
558
+ appId: appIdSchema,
559
+ platform: platformSchema.optional(),
560
+ versionState: versionStateSchema.optional(),
561
+ limit: z.number().int().min(1).max(200).optional()
562
+ });
563
+ var getAppVersionInputSchema = z.object({
564
+ versionId: versionIdSchema
565
+ });
566
+ var createAppVersionInputSchema = z.object({
567
+ appId: appIdSchema,
568
+ platform: platformSchema,
569
+ versionString: versionStringSchema,
570
+ releaseType: releaseTypeSchema.optional(),
571
+ copyright: z.string().optional(),
572
+ earliestReleaseDate: z.string().datetime().optional()
573
+ });
574
+ var listVersionLocalizationsInputSchema = z.object({
575
+ versionId: versionIdSchema,
576
+ limit: z.number().int().min(1).max(200).optional()
577
+ });
578
+ var getVersionLocalizationInputSchema = z.object({
579
+ localizationId: localizationIdSchema
580
+ });
581
+ var createVersionLocalizationInputSchema = z.object({
582
+ versionId: versionIdSchema,
583
+ locale: localeSchema,
584
+ description: z.string().max(4e3).optional(),
585
+ keywords: z.string().max(100).optional(),
586
+ whatsNew: z.string().max(4e3).optional(),
587
+ promotionalText: z.string().max(170).optional(),
588
+ marketingUrl: optionalUrlSchema,
589
+ supportUrl: optionalUrlSchema
590
+ });
591
+ var updateVersionLocalizationInputSchema = z.object({
592
+ localizationId: localizationIdSchema,
593
+ description: z.string().max(4e3).optional(),
594
+ keywords: z.string().max(100).optional(),
595
+ whatsNew: z.string().max(4e3).optional(),
596
+ promotionalText: z.string().max(170).optional(),
597
+ marketingUrl: optionalUrlSchema,
598
+ supportUrl: optionalUrlSchema
599
+ });
600
+ var deleteVersionLocalizationInputSchema = z.object({
601
+ localizationId: localizationIdSchema
602
+ });
603
+ var getAppInfosInputSchema = z.object({
604
+ appId: appIdSchema,
605
+ limit: z.number().int().min(1).max(200).optional()
606
+ });
607
+ var listAppInfoLocalizationsInputSchema = z.object({
608
+ appInfoId: z.string().min(1, "App Info ID is required"),
609
+ limit: z.number().int().min(1).max(200).optional()
610
+ });
611
+ var updateAppInfoLocalizationInputSchema = z.object({
612
+ localizationId: localizationIdSchema,
613
+ name: z.string().max(30).optional(),
614
+ subtitle: z.string().max(30).optional(),
615
+ privacyPolicyUrl: optionalUrlSchema,
616
+ privacyChoicesUrl: optionalUrlSchema,
617
+ privacyPolicyText: z.string().optional()
618
+ });
619
+ var listBetaGroupsInputSchema = z.object({
620
+ appId: appIdSchema,
621
+ limit: z.number().int().min(1).max(200).optional()
622
+ });
623
+ var listBetaTestersInputSchema = z.object({
624
+ betaGroupId: betaGroupIdSchema,
625
+ limit: z.number().int().min(1).max(200).optional()
626
+ });
627
+ var addBetaTesterInputSchema = z.object({
628
+ betaGroupId: betaGroupIdSchema,
629
+ email: z.string().email("Invalid email address"),
630
+ firstName: z.string().min(1).optional(),
631
+ lastName: z.string().min(1).optional()
632
+ });
633
+ var listScreenshotSetsInputSchema = z.object({
634
+ localizationId: localizationIdSchema,
635
+ limit: z.number().int().min(1).max(200).optional()
636
+ });
637
+ var listScreenshotsInputSchema = z.object({
638
+ screenshotSetId: z.string().min(1, "Screenshot Set ID is required"),
639
+ limit: z.number().int().min(1).max(200).optional()
640
+ });
641
+ var uploadScreenshotInputSchema = z.object({
642
+ screenshotSetId: z.string().min(1, "Screenshot Set ID is required"),
643
+ fileName: z.string().min(1, "File name is required"),
644
+ fileSize: z.number().int().positive("File size must be positive"),
645
+ filePath: z.string().min(1, "File path is required")
646
+ });
647
+ function validateInput(schema, input) {
648
+ const result = schema.safeParse(input);
649
+ if (!result.success) {
650
+ const firstError = result.error.errors[0];
651
+ const field = firstError?.path.join(".");
652
+ const message = firstError?.message ?? "Invalid input";
653
+ throw new ValidationError(message, field || void 0);
654
+ }
655
+ return result.data;
656
+ }
657
+ var bundleIdIdentifierSchema = z.string().regex(
658
+ /^[a-zA-Z][a-zA-Z0-9.-]*$/,
659
+ "Bundle ID must start with a letter and contain only letters, numbers, dots, and hyphens"
660
+ ).min(1, "Bundle ID identifier is required");
661
+ var listBundleIdsInputSchema = z.object({
662
+ limit: z.number().int().min(1).max(200).optional(),
663
+ platform: platformSchema.optional()
664
+ });
665
+ var getBundleIdInputSchema = z.object({
666
+ bundleIdId: z.string().min(1, "Bundle ID ID is required")
667
+ });
668
+ var createBundleIdInputSchema = z.object({
669
+ identifier: bundleIdIdentifierSchema,
670
+ name: z.string().min(1, "Name is required").max(255, "Name must be 255 characters or less"),
671
+ platform: platformSchema
672
+ });
673
+ var updateBundleIdInputSchema = z.object({
674
+ bundleIdId: z.string().min(1, "Bundle ID ID is required"),
675
+ name: z.string().min(1, "Name is required").max(255, "Name must be 255 characters or less")
676
+ });
677
+ var deleteBundleIdInputSchema = z.object({
678
+ bundleIdId: z.string().min(1, "Bundle ID ID is required")
679
+ });
680
+ var deviceStatusSchema = z.enum(["ENABLED", "DISABLED"]);
681
+ var listDevicesInputSchema = z.object({
682
+ limit: z.number().int().min(1).max(200).optional(),
683
+ platform: platformSchema.optional(),
684
+ status: deviceStatusSchema.optional()
685
+ });
686
+ var getDeviceInputSchema = z.object({
687
+ deviceId: z.string().min(1, "Device ID is required")
688
+ });
689
+ var userRoleSchema = z.enum([
690
+ "ADMIN",
691
+ "FINANCE",
692
+ "TECHNICAL",
693
+ "SALES",
694
+ "DEVELOPER",
695
+ "MARKETING",
696
+ "APP_MANAGER",
697
+ "CUSTOMER_SUPPORT",
698
+ "ACCESS_TO_REPORTS",
699
+ "READ_ONLY"
700
+ ]);
701
+ var listUsersInputSchema = z.object({
702
+ limit: z.number().int().min(1).max(200).optional(),
703
+ roles: z.array(userRoleSchema).optional()
704
+ });
705
+ var getUserInputSchema = z.object({
706
+ userId: z.string().min(1, "User ID is required")
707
+ });
708
+ var betaTesterIdSchema = z.string().min(1, "Beta Tester ID is required");
709
+ var removeBetaTesterInputSchema = z.object({
710
+ betaGroupId: betaGroupIdSchema,
711
+ betaTesterId: betaTesterIdSchema
712
+ });
713
+ var listBuildsInputSchema = z.object({
714
+ appId: appIdSchema,
715
+ limit: z.number().int().min(1).max(200).optional()
716
+ });
717
+ var getBuildInputSchema = z.object({
718
+ buildId: buildIdSchema
719
+ });
720
+ var listBetaTesterInvitationsInputSchema = z.object({
721
+ betaGroupId: betaGroupIdSchema,
722
+ limit: z.number().int().min(1).max(200).optional()
723
+ });
724
+ var listAppCategoriesInputSchema = z.object({
725
+ limit: z.number().int().min(1).max(200).optional(),
726
+ platform: platformSchema.optional()
727
+ });
728
+ var getAppPriceScheduleInputSchema = z.object({
729
+ appId: appIdSchema
730
+ });
731
+ var getAppAvailabilityInputSchema = z.object({
732
+ appId: appIdSchema
733
+ });
734
+
735
+ // src/tools/app-info.tools.ts
736
+ async function listAppInfos(client, input) {
737
+ try {
738
+ const params = validateInput(getAppInfosInputSchema, input);
739
+ const response = await client.get(`/apps/${params.appId}/appInfos`, {
740
+ limit: params.limit,
741
+ "fields[appInfos]": "appStoreState,appStoreAgeRating"
742
+ });
743
+ return {
744
+ success: true,
745
+ data: response.data.map((info) => ({
746
+ id: info.id,
747
+ appStoreState: info.attributes.appStoreState,
748
+ appStoreAgeRating: info.attributes.appStoreAgeRating
749
+ })),
750
+ meta: {
751
+ total: response.meta?.paging?.total,
752
+ returned: response.data.length
753
+ }
754
+ };
755
+ } catch (error) {
756
+ return formatErrorResponse(error);
757
+ }
758
+ }
759
+ async function listAppInfoLocalizations(client, input) {
760
+ try {
761
+ const params = validateInput(listAppInfoLocalizationsInputSchema, input);
762
+ const response = await client.get(
763
+ `/appInfos/${params.appInfoId}/appInfoLocalizations`,
764
+ {
765
+ limit: params.limit,
766
+ "fields[appInfoLocalizations]": "locale,name,subtitle,privacyPolicyUrl,privacyChoicesUrl,privacyPolicyText"
767
+ }
768
+ );
769
+ return {
770
+ success: true,
771
+ data: response.data.map((loc) => ({
772
+ id: loc.id,
773
+ locale: loc.attributes.locale,
774
+ name: loc.attributes.name,
775
+ subtitle: loc.attributes.subtitle,
776
+ privacyPolicyUrl: loc.attributes.privacyPolicyUrl,
777
+ privacyChoicesUrl: loc.attributes.privacyChoicesUrl,
778
+ privacyPolicyText: loc.attributes.privacyPolicyText
779
+ })),
780
+ meta: {
781
+ total: response.meta?.paging?.total,
782
+ returned: response.data.length
783
+ }
784
+ };
785
+ } catch (error) {
786
+ return formatErrorResponse(error);
787
+ }
788
+ }
789
+ async function updateAppInfoLocalization(client, input) {
790
+ try {
791
+ const params = validateInput(updateAppInfoLocalizationInputSchema, input);
792
+ const requestBody = {
793
+ data: {
794
+ type: "appInfoLocalizations",
795
+ id: params.localizationId,
796
+ attributes: {
797
+ name: params.name,
798
+ subtitle: params.subtitle,
799
+ privacyPolicyUrl: params.privacyPolicyUrl,
800
+ privacyChoicesUrl: params.privacyChoicesUrl,
801
+ privacyPolicyText: params.privacyPolicyText
802
+ }
803
+ }
804
+ };
805
+ const response = await client.patch(
806
+ `/appInfoLocalizations/${params.localizationId}`,
807
+ requestBody
808
+ );
809
+ const loc = response.data;
810
+ return {
811
+ success: true,
812
+ data: {
813
+ id: loc.id,
814
+ locale: loc.attributes.locale,
815
+ name: loc.attributes.name,
816
+ subtitle: loc.attributes.subtitle,
817
+ privacyPolicyUrl: loc.attributes.privacyPolicyUrl,
818
+ privacyChoicesUrl: loc.attributes.privacyChoicesUrl,
819
+ privacyPolicyText: loc.attributes.privacyPolicyText
820
+ }
821
+ };
822
+ } catch (error) {
823
+ return formatErrorResponse(error);
824
+ }
825
+ }
826
+ var appInfoToolDefinitions = [
827
+ {
828
+ name: "list_app_infos",
829
+ description: "List app info records for an app. Use this to get the appInfoId needed for localization operations.",
830
+ inputSchema: {
831
+ type: "object",
832
+ properties: {
833
+ appId: {
834
+ type: "string",
835
+ description: "The App Store Connect app ID"
836
+ },
837
+ limit: {
838
+ type: "number",
839
+ description: "Maximum number of results to return (1-200)",
840
+ minimum: 1,
841
+ maximum: 200
842
+ }
843
+ },
844
+ required: ["appId"]
845
+ }
846
+ },
847
+ {
848
+ name: "list_app_info_localizations",
849
+ description: "List all localizations for an app info. Returns app name, subtitle, and privacy policy info for each locale.",
850
+ inputSchema: {
851
+ type: "object",
852
+ properties: {
853
+ appInfoId: {
854
+ type: "string",
855
+ description: "The App Info ID (get this from list_app_infos)"
856
+ },
857
+ limit: {
858
+ type: "number",
859
+ description: "Maximum number of localizations to return (1-200)",
860
+ minimum: 1,
861
+ maximum: 200
862
+ }
863
+ },
864
+ required: ["appInfoId"]
865
+ }
866
+ },
867
+ {
868
+ name: "update_app_info_localization",
869
+ description: "Update an app info localization. Use this to change app name, subtitle, or privacy policy URL for a locale.",
870
+ inputSchema: {
871
+ type: "object",
872
+ properties: {
873
+ localizationId: {
874
+ type: "string",
875
+ description: "The app info localization ID to update"
876
+ },
877
+ name: {
878
+ type: "string",
879
+ description: "App name (max 30 characters)"
880
+ },
881
+ subtitle: {
882
+ type: "string",
883
+ description: "App subtitle (max 30 characters)"
884
+ },
885
+ privacyPolicyUrl: {
886
+ type: "string",
887
+ description: "Privacy policy URL (HTTPS only)"
888
+ },
889
+ privacyChoicesUrl: {
890
+ type: "string",
891
+ description: "Privacy choices URL (HTTPS only)"
892
+ },
893
+ privacyPolicyText: {
894
+ type: "string",
895
+ description: "Privacy policy text (for apps without a URL)"
896
+ }
897
+ },
898
+ required: ["localizationId"]
899
+ }
900
+ }
901
+ ];
902
+
903
+ // src/tools/apps.tools.ts
904
+ async function listApps(client, input) {
905
+ try {
906
+ const params = validateInput(listAppsInputSchema, input);
907
+ const response = await client.get("/apps", {
908
+ limit: params.limit,
909
+ "fields[apps]": "name,bundleId,sku,primaryLocale"
910
+ });
911
+ return {
912
+ success: true,
913
+ data: response.data.map((app) => ({
914
+ id: app.id,
915
+ name: app.attributes.name,
916
+ bundleId: app.attributes.bundleId,
917
+ sku: app.attributes.sku,
918
+ primaryLocale: app.attributes.primaryLocale
919
+ })),
920
+ meta: {
921
+ total: response.meta?.paging?.total,
922
+ returned: response.data.length
923
+ }
924
+ };
925
+ } catch (error) {
926
+ return formatErrorResponse(error);
927
+ }
928
+ }
929
+ async function getApp(client, input) {
930
+ try {
931
+ const params = validateInput(getAppInputSchema, input);
932
+ const response = await client.get(`/apps/${params.appId}`, {
933
+ "fields[apps]": "name,bundleId,sku,primaryLocale,contentRightsDeclaration,isOrEverWasMadeForKids",
934
+ include: "appInfos,appStoreVersions"
935
+ });
936
+ const app = response.data;
937
+ return {
938
+ success: true,
939
+ data: {
940
+ id: app.id,
941
+ name: app.attributes.name,
942
+ bundleId: app.attributes.bundleId,
943
+ sku: app.attributes.sku,
944
+ primaryLocale: app.attributes.primaryLocale,
945
+ contentRightsDeclaration: app.attributes.contentRightsDeclaration,
946
+ isOrEverWasMadeForKids: app.attributes.isOrEverWasMadeForKids
947
+ }
948
+ };
949
+ } catch (error) {
950
+ return formatErrorResponse(error);
951
+ }
952
+ }
953
+ var appsToolDefinitions = [
954
+ {
955
+ name: "list_apps",
956
+ description: "List all apps in your App Store Connect account. Returns app IDs, names, bundle IDs, and SKUs.",
957
+ inputSchema: {
958
+ type: "object",
959
+ properties: {
960
+ limit: {
961
+ type: "number",
962
+ description: "Maximum number of apps to return (1-200)",
963
+ minimum: 1,
964
+ maximum: 200
965
+ }
966
+ }
967
+ }
968
+ },
969
+ {
970
+ name: "get_app",
971
+ description: "Get detailed information about a specific app by its ID.",
972
+ inputSchema: {
973
+ type: "object",
974
+ properties: {
975
+ appId: {
976
+ type: "string",
977
+ description: "The App Store Connect app ID (numeric string)"
978
+ }
979
+ },
980
+ required: ["appId"]
981
+ }
982
+ }
983
+ ];
984
+
985
+ // src/tools/beta.tools.ts
986
+ async function listBetaGroups(client, input) {
987
+ try {
988
+ const params = validateInput(listBetaGroupsInputSchema, input);
989
+ const response = await client.get(
990
+ `/apps/${params.appId}/betaGroups`,
991
+ {
992
+ limit: params.limit,
993
+ "fields[betaGroups]": "name,createdDate,isInternalGroup,hasAccessToAllBuilds,publicLinkEnabled,publicLink,feedbackEnabled"
994
+ }
995
+ );
996
+ return {
997
+ success: true,
998
+ data: response.data.map((group) => ({
999
+ id: group.id,
1000
+ name: group.attributes.name,
1001
+ createdDate: group.attributes.createdDate,
1002
+ isInternalGroup: group.attributes.isInternalGroup,
1003
+ hasAccessToAllBuilds: group.attributes.hasAccessToAllBuilds,
1004
+ publicLinkEnabled: group.attributes.publicLinkEnabled,
1005
+ publicLink: group.attributes.publicLink,
1006
+ feedbackEnabled: group.attributes.feedbackEnabled
1007
+ })),
1008
+ meta: {
1009
+ total: response.meta?.paging?.total,
1010
+ returned: response.data.length
1011
+ }
1012
+ };
1013
+ } catch (error) {
1014
+ return formatErrorResponse(error);
1015
+ }
1016
+ }
1017
+ async function listBetaTesters(client, input) {
1018
+ try {
1019
+ const params = validateInput(listBetaTestersInputSchema, input);
1020
+ const response = await client.get(
1021
+ `/betaGroups/${params.betaGroupId}/betaTesters`,
1022
+ {
1023
+ limit: params.limit,
1024
+ "fields[betaTesters]": "firstName,lastName,email,inviteType,state"
1025
+ }
1026
+ );
1027
+ return {
1028
+ success: true,
1029
+ data: response.data.map((tester) => ({
1030
+ id: tester.id,
1031
+ firstName: tester.attributes.firstName,
1032
+ lastName: tester.attributes.lastName,
1033
+ email: tester.attributes.email,
1034
+ inviteType: tester.attributes.inviteType,
1035
+ state: tester.attributes.state
1036
+ })),
1037
+ meta: {
1038
+ total: response.meta?.paging?.total,
1039
+ returned: response.data.length
1040
+ }
1041
+ };
1042
+ } catch (error) {
1043
+ return formatErrorResponse(error);
1044
+ }
1045
+ }
1046
+ async function addBetaTester(client, input) {
1047
+ try {
1048
+ const params = validateInput(addBetaTesterInputSchema, input);
1049
+ const requestBody = {
1050
+ data: {
1051
+ type: "betaTesters",
1052
+ attributes: {
1053
+ email: params.email,
1054
+ firstName: params.firstName,
1055
+ lastName: params.lastName
1056
+ },
1057
+ relationships: {
1058
+ betaGroups: {
1059
+ data: [
1060
+ {
1061
+ type: "betaGroups",
1062
+ id: params.betaGroupId
1063
+ }
1064
+ ]
1065
+ }
1066
+ }
1067
+ }
1068
+ };
1069
+ const response = await client.post("/betaTesters", requestBody);
1070
+ const tester = response.data;
1071
+ return {
1072
+ success: true,
1073
+ data: {
1074
+ id: tester.id,
1075
+ firstName: tester.attributes.firstName,
1076
+ lastName: tester.attributes.lastName,
1077
+ email: tester.attributes.email,
1078
+ inviteType: tester.attributes.inviteType,
1079
+ state: tester.attributes.state
1080
+ }
1081
+ };
1082
+ } catch (error) {
1083
+ return formatErrorResponse(error);
1084
+ }
1085
+ }
1086
+ async function removeBetaTester(client, input) {
1087
+ try {
1088
+ const params = validateInput(removeBetaTesterInputSchema, input);
1089
+ const requestBody = {
1090
+ data: [
1091
+ {
1092
+ type: "betaTesters",
1093
+ id: params.betaTesterId
1094
+ }
1095
+ ]
1096
+ };
1097
+ await client.delete(`/betaGroups/${params.betaGroupId}/relationships/betaTesters`, requestBody);
1098
+ return {
1099
+ success: true,
1100
+ data: {
1101
+ removed: true,
1102
+ betaGroupId: params.betaGroupId,
1103
+ betaTesterId: params.betaTesterId
1104
+ }
1105
+ };
1106
+ } catch (error) {
1107
+ return formatErrorResponse(error);
1108
+ }
1109
+ }
1110
+ var betaToolDefinitions = [
1111
+ {
1112
+ name: "list_beta_groups",
1113
+ description: "List all beta groups for an app. Returns group names, public link info, and settings.",
1114
+ inputSchema: {
1115
+ type: "object",
1116
+ properties: {
1117
+ appId: {
1118
+ type: "string",
1119
+ description: "The App Store Connect app ID"
1120
+ },
1121
+ limit: {
1122
+ type: "number",
1123
+ description: "Maximum number of groups to return (1-200)",
1124
+ minimum: 1,
1125
+ maximum: 200
1126
+ }
1127
+ },
1128
+ required: ["appId"]
1129
+ }
1130
+ },
1131
+ {
1132
+ name: "list_beta_testers",
1133
+ description: "List all beta testers in a specific beta group.",
1134
+ inputSchema: {
1135
+ type: "object",
1136
+ properties: {
1137
+ betaGroupId: {
1138
+ type: "string",
1139
+ description: "The beta group ID"
1140
+ },
1141
+ limit: {
1142
+ type: "number",
1143
+ description: "Maximum number of testers to return (1-200)",
1144
+ minimum: 1,
1145
+ maximum: 200
1146
+ }
1147
+ },
1148
+ required: ["betaGroupId"]
1149
+ }
1150
+ },
1151
+ {
1152
+ name: "add_beta_tester",
1153
+ description: "Add a new beta tester to a beta group by email address.",
1154
+ inputSchema: {
1155
+ type: "object",
1156
+ properties: {
1157
+ betaGroupId: {
1158
+ type: "string",
1159
+ description: "The beta group ID to add the tester to"
1160
+ },
1161
+ email: {
1162
+ type: "string",
1163
+ description: "Email address of the beta tester"
1164
+ },
1165
+ firstName: {
1166
+ type: "string",
1167
+ description: "First name of the beta tester (optional)"
1168
+ },
1169
+ lastName: {
1170
+ type: "string",
1171
+ description: "Last name of the beta tester (optional)"
1172
+ }
1173
+ },
1174
+ required: ["betaGroupId", "email"]
1175
+ }
1176
+ },
1177
+ {
1178
+ name: "remove_beta_tester",
1179
+ description: "Remove a beta tester from a beta group.",
1180
+ inputSchema: {
1181
+ type: "object",
1182
+ properties: {
1183
+ betaGroupId: {
1184
+ type: "string",
1185
+ description: "The beta group ID to remove the tester from"
1186
+ },
1187
+ betaTesterId: {
1188
+ type: "string",
1189
+ description: "The beta tester ID to remove"
1190
+ }
1191
+ },
1192
+ required: ["betaGroupId", "betaTesterId"]
1193
+ }
1194
+ }
1195
+ ];
1196
+
1197
+ // src/tools/builds.tools.ts
1198
+ async function listBuilds(client, input) {
1199
+ try {
1200
+ const params = validateInput(listBuildsInputSchema, input);
1201
+ const response = await client.get(`/apps/${params.appId}/builds`, {
1202
+ limit: params.limit,
1203
+ "fields[builds]": "version,uploadedDate,expirationDate,expired,minOsVersion,processingState,buildAudienceType,usesNonExemptEncryption",
1204
+ sort: "-uploadedDate"
1205
+ });
1206
+ return {
1207
+ success: true,
1208
+ data: response.data.map((build) => ({
1209
+ id: build.id,
1210
+ version: build.attributes.version,
1211
+ uploadedDate: build.attributes.uploadedDate,
1212
+ expirationDate: build.attributes.expirationDate,
1213
+ expired: build.attributes.expired,
1214
+ minOsVersion: build.attributes.minOsVersion,
1215
+ processingState: build.attributes.processingState,
1216
+ buildAudienceType: build.attributes.buildAudienceType,
1217
+ usesNonExemptEncryption: build.attributes.usesNonExemptEncryption
1218
+ })),
1219
+ meta: {
1220
+ total: response.meta?.paging?.total,
1221
+ returned: response.data.length
1222
+ }
1223
+ };
1224
+ } catch (error) {
1225
+ return formatErrorResponse(error);
1226
+ }
1227
+ }
1228
+ async function getBuild(client, input) {
1229
+ try {
1230
+ const params = validateInput(getBuildInputSchema, input);
1231
+ const response = await client.get(`/builds/${params.buildId}`, {
1232
+ "fields[builds]": "version,uploadedDate,expirationDate,expired,minOsVersion,processingState,buildAudienceType,usesNonExemptEncryption"
1233
+ });
1234
+ const build = response.data;
1235
+ return {
1236
+ success: true,
1237
+ data: {
1238
+ id: build.id,
1239
+ version: build.attributes.version,
1240
+ uploadedDate: build.attributes.uploadedDate,
1241
+ expirationDate: build.attributes.expirationDate,
1242
+ expired: build.attributes.expired,
1243
+ minOsVersion: build.attributes.minOsVersion,
1244
+ processingState: build.attributes.processingState,
1245
+ buildAudienceType: build.attributes.buildAudienceType,
1246
+ usesNonExemptEncryption: build.attributes.usesNonExemptEncryption
1247
+ }
1248
+ };
1249
+ } catch (error) {
1250
+ return formatErrorResponse(error);
1251
+ }
1252
+ }
1253
+ async function listBetaTesterInvitations(client, input) {
1254
+ try {
1255
+ const params = validateInput(listBetaTesterInvitationsInputSchema, input);
1256
+ const response = await client.get(
1257
+ `/betaGroups/${params.betaGroupId}/betaTesterInvitations`,
1258
+ {
1259
+ limit: params.limit
1260
+ }
1261
+ );
1262
+ return {
1263
+ success: true,
1264
+ data: response.data.map((invitation) => ({
1265
+ id: invitation.id
1266
+ })),
1267
+ meta: {
1268
+ total: response.meta?.paging?.total,
1269
+ returned: response.data.length
1270
+ }
1271
+ };
1272
+ } catch (error) {
1273
+ return formatErrorResponse(error);
1274
+ }
1275
+ }
1276
+ var buildsToolDefinitions = [
1277
+ {
1278
+ name: "list_builds",
1279
+ description: "List all builds for an app, sorted by upload date (newest first).",
1280
+ inputSchema: {
1281
+ type: "object",
1282
+ properties: {
1283
+ appId: {
1284
+ type: "string",
1285
+ description: "The App Store Connect app ID"
1286
+ },
1287
+ limit: {
1288
+ type: "number",
1289
+ description: "Maximum number of builds to return (1-200)",
1290
+ minimum: 1,
1291
+ maximum: 200
1292
+ }
1293
+ },
1294
+ required: ["appId"]
1295
+ }
1296
+ },
1297
+ {
1298
+ name: "get_build",
1299
+ description: "Get details of a specific build.",
1300
+ inputSchema: {
1301
+ type: "object",
1302
+ properties: {
1303
+ buildId: {
1304
+ type: "string",
1305
+ description: "The build resource ID"
1306
+ }
1307
+ },
1308
+ required: ["buildId"]
1309
+ }
1310
+ },
1311
+ {
1312
+ name: "list_beta_tester_invitations",
1313
+ description: "List pending beta tester invitations for a beta group.",
1314
+ inputSchema: {
1315
+ type: "object",
1316
+ properties: {
1317
+ betaGroupId: {
1318
+ type: "string",
1319
+ description: "The beta group ID"
1320
+ },
1321
+ limit: {
1322
+ type: "number",
1323
+ description: "Maximum number of invitations to return (1-200)",
1324
+ minimum: 1,
1325
+ maximum: 200
1326
+ }
1327
+ },
1328
+ required: ["betaGroupId"]
1329
+ }
1330
+ }
1331
+ ];
1332
+
1333
+ // src/tools/bundle-ids.tools.ts
1334
+ async function listBundleIds(client, input) {
1335
+ try {
1336
+ const params = validateInput(listBundleIdsInputSchema, input);
1337
+ const queryParams = {
1338
+ limit: params.limit,
1339
+ "fields[bundleIds]": "name,identifier,platform,seedId"
1340
+ };
1341
+ if (params.platform) {
1342
+ queryParams["filter[platform]"] = params.platform;
1343
+ }
1344
+ const response = await client.get("/bundleIds", queryParams);
1345
+ return {
1346
+ success: true,
1347
+ data: response.data.map((bundleId) => ({
1348
+ id: bundleId.id,
1349
+ name: bundleId.attributes.name,
1350
+ identifier: bundleId.attributes.identifier,
1351
+ platform: bundleId.attributes.platform,
1352
+ seedId: bundleId.attributes.seedId
1353
+ })),
1354
+ meta: {
1355
+ total: response.meta?.paging?.total,
1356
+ returned: response.data.length
1357
+ }
1358
+ };
1359
+ } catch (error) {
1360
+ return formatErrorResponse(error);
1361
+ }
1362
+ }
1363
+ async function getBundleId(client, input) {
1364
+ try {
1365
+ const params = validateInput(getBundleIdInputSchema, input);
1366
+ const response = await client.get(`/bundleIds/${params.bundleIdId}`, {
1367
+ "fields[bundleIds]": "name,identifier,platform,seedId"
1368
+ });
1369
+ const bundleId = response.data;
1370
+ return {
1371
+ success: true,
1372
+ data: {
1373
+ id: bundleId.id,
1374
+ name: bundleId.attributes.name,
1375
+ identifier: bundleId.attributes.identifier,
1376
+ platform: bundleId.attributes.platform,
1377
+ seedId: bundleId.attributes.seedId
1378
+ }
1379
+ };
1380
+ } catch (error) {
1381
+ return formatErrorResponse(error);
1382
+ }
1383
+ }
1384
+ async function createBundleId(client, input) {
1385
+ try {
1386
+ const params = validateInput(createBundleIdInputSchema, input);
1387
+ const requestBody = {
1388
+ data: {
1389
+ type: "bundleIds",
1390
+ attributes: {
1391
+ identifier: params.identifier,
1392
+ name: params.name,
1393
+ platform: params.platform
1394
+ }
1395
+ }
1396
+ };
1397
+ const response = await client.post("/bundleIds", requestBody);
1398
+ const bundleId = response.data;
1399
+ return {
1400
+ success: true,
1401
+ data: {
1402
+ id: bundleId.id,
1403
+ name: bundleId.attributes.name,
1404
+ identifier: bundleId.attributes.identifier,
1405
+ platform: bundleId.attributes.platform,
1406
+ seedId: bundleId.attributes.seedId
1407
+ }
1408
+ };
1409
+ } catch (error) {
1410
+ return formatErrorResponse(error);
1411
+ }
1412
+ }
1413
+ async function updateBundleId(client, input) {
1414
+ try {
1415
+ const params = validateInput(updateBundleIdInputSchema, input);
1416
+ const requestBody = {
1417
+ data: {
1418
+ type: "bundleIds",
1419
+ id: params.bundleIdId,
1420
+ attributes: {
1421
+ name: params.name
1422
+ }
1423
+ }
1424
+ };
1425
+ const response = await client.patch(
1426
+ `/bundleIds/${params.bundleIdId}`,
1427
+ requestBody
1428
+ );
1429
+ const bundleId = response.data;
1430
+ return {
1431
+ success: true,
1432
+ data: {
1433
+ id: bundleId.id,
1434
+ name: bundleId.attributes.name,
1435
+ identifier: bundleId.attributes.identifier,
1436
+ platform: bundleId.attributes.platform,
1437
+ seedId: bundleId.attributes.seedId
1438
+ }
1439
+ };
1440
+ } catch (error) {
1441
+ return formatErrorResponse(error);
1442
+ }
1443
+ }
1444
+ async function deleteBundleId(client, input) {
1445
+ try {
1446
+ const params = validateInput(deleteBundleIdInputSchema, input);
1447
+ await client.delete(`/bundleIds/${params.bundleIdId}`);
1448
+ return {
1449
+ success: true,
1450
+ data: {
1451
+ deleted: true,
1452
+ bundleIdId: params.bundleIdId
1453
+ }
1454
+ };
1455
+ } catch (error) {
1456
+ return formatErrorResponse(error);
1457
+ }
1458
+ }
1459
+ var bundleIdsToolDefinitions = [
1460
+ {
1461
+ name: "list_bundle_ids",
1462
+ description: "List all bundle IDs registered in App Store Connect. Can filter by platform.",
1463
+ inputSchema: {
1464
+ type: "object",
1465
+ properties: {
1466
+ limit: {
1467
+ type: "number",
1468
+ description: "Maximum number of bundle IDs to return (1-200)",
1469
+ minimum: 1,
1470
+ maximum: 200
1471
+ },
1472
+ platform: {
1473
+ type: "string",
1474
+ enum: ["IOS", "MAC_OS", "TV_OS", "VISION_OS"],
1475
+ description: "Filter by platform"
1476
+ }
1477
+ },
1478
+ required: []
1479
+ }
1480
+ },
1481
+ {
1482
+ name: "get_bundle_id",
1483
+ description: "Get details of a specific bundle ID.",
1484
+ inputSchema: {
1485
+ type: "object",
1486
+ properties: {
1487
+ bundleIdId: {
1488
+ type: "string",
1489
+ description: "The bundle ID resource ID"
1490
+ }
1491
+ },
1492
+ required: ["bundleIdId"]
1493
+ }
1494
+ },
1495
+ {
1496
+ name: "create_bundle_id",
1497
+ description: "Register a new bundle ID in App Store Connect. The identifier must be unique and follow reverse-domain notation (e.g., com.example.app).",
1498
+ inputSchema: {
1499
+ type: "object",
1500
+ properties: {
1501
+ identifier: {
1502
+ type: "string",
1503
+ description: "The bundle identifier (e.g., com.example.app)"
1504
+ },
1505
+ name: {
1506
+ type: "string",
1507
+ description: "A name for the bundle ID"
1508
+ },
1509
+ platform: {
1510
+ type: "string",
1511
+ enum: ["IOS", "MAC_OS", "TV_OS", "VISION_OS"],
1512
+ description: "The platform for this bundle ID"
1513
+ }
1514
+ },
1515
+ required: ["identifier", "name", "platform"]
1516
+ }
1517
+ },
1518
+ {
1519
+ name: "update_bundle_id",
1520
+ description: "Update a bundle ID's name. Note: The identifier cannot be changed.",
1521
+ inputSchema: {
1522
+ type: "object",
1523
+ properties: {
1524
+ bundleIdId: {
1525
+ type: "string",
1526
+ description: "The bundle ID resource ID"
1527
+ },
1528
+ name: {
1529
+ type: "string",
1530
+ description: "The new name for the bundle ID"
1531
+ }
1532
+ },
1533
+ required: ["bundleIdId", "name"]
1534
+ }
1535
+ },
1536
+ {
1537
+ name: "delete_bundle_id",
1538
+ description: "Delete a bundle ID. Note: This cannot be undone and may affect apps using this bundle ID.",
1539
+ inputSchema: {
1540
+ type: "object",
1541
+ properties: {
1542
+ bundleIdId: {
1543
+ type: "string",
1544
+ description: "The bundle ID resource ID to delete"
1545
+ }
1546
+ },
1547
+ required: ["bundleIdId"]
1548
+ }
1549
+ }
1550
+ ];
1551
+
1552
+ // src/tools/categories.tools.ts
1553
+ async function listAppCategories(client, input) {
1554
+ try {
1555
+ const params = validateInput(listAppCategoriesInputSchema, input);
1556
+ const queryParams = {
1557
+ limit: params.limit,
1558
+ "fields[appCategories]": "platforms",
1559
+ include: "subcategories"
1560
+ };
1561
+ if (params.platform) {
1562
+ queryParams["filter[platforms]"] = params.platform;
1563
+ }
1564
+ const response = await client.get("/appCategories", queryParams);
1565
+ const included = response.included ?? [];
1566
+ const subcategoriesMap = /* @__PURE__ */ new Map();
1567
+ for (const sub of included) {
1568
+ if (sub.type === "appCategories") {
1569
+ subcategoriesMap.set(sub.id, sub);
1570
+ }
1571
+ }
1572
+ return {
1573
+ success: true,
1574
+ data: response.data.map((category) => {
1575
+ const subcategoryData = category.relationships?.subcategories?.data;
1576
+ const subcategoryIds = Array.isArray(subcategoryData) ? subcategoryData.map((s) => s.id) : [];
1577
+ return {
1578
+ id: category.id,
1579
+ platforms: category.attributes.platforms,
1580
+ subcategories: subcategoryIds.map((id) => {
1581
+ const sub = subcategoriesMap.get(id);
1582
+ return sub ? {
1583
+ id: sub.id,
1584
+ platforms: sub.attributes.platforms
1585
+ } : { id };
1586
+ })
1587
+ };
1588
+ }),
1589
+ meta: {
1590
+ total: response.meta?.paging?.total,
1591
+ returned: response.data.length
1592
+ }
1593
+ };
1594
+ } catch (error) {
1595
+ return formatErrorResponse(error);
1596
+ }
1597
+ }
1598
+ async function getAppPriceSchedule(client, input) {
1599
+ try {
1600
+ const params = validateInput(getAppPriceScheduleInputSchema, input);
1601
+ const response = await client.get(
1602
+ `/apps/${params.appId}/appPriceSchedule`,
1603
+ {
1604
+ include: "baseTerritory,manualPrices,automaticPrices"
1605
+ }
1606
+ );
1607
+ const schedule = response.data;
1608
+ const included = response.included ?? [];
1609
+ let baseTerritory;
1610
+ const baseTerritoryData = schedule.relationships?.baseTerritory?.data;
1611
+ if (baseTerritoryData && !Array.isArray(baseTerritoryData)) {
1612
+ const territory = included.find(
1613
+ (item) => item.type === "territories" && item.id === baseTerritoryData.id
1614
+ );
1615
+ if (territory) {
1616
+ baseTerritory = {
1617
+ id: territory.id,
1618
+ currency: territory.attributes.currency
1619
+ };
1620
+ }
1621
+ }
1622
+ return {
1623
+ success: true,
1624
+ data: {
1625
+ id: schedule.id,
1626
+ baseTerritory,
1627
+ hasManualPrices: schedule.relationships?.manualPrices?.data !== void 0 && (Array.isArray(schedule.relationships.manualPrices.data) ? schedule.relationships.manualPrices.data.length > 0 : true),
1628
+ hasAutomaticPrices: schedule.relationships?.automaticPrices?.data !== void 0 && (Array.isArray(schedule.relationships.automaticPrices.data) ? schedule.relationships.automaticPrices.data.length > 0 : true)
1629
+ }
1630
+ };
1631
+ } catch (error) {
1632
+ return formatErrorResponse(error);
1633
+ }
1634
+ }
1635
+ async function getAppAvailability(client, input) {
1636
+ try {
1637
+ const params = validateInput(getAppAvailabilityInputSchema, input);
1638
+ const response = await client.get(
1639
+ `/apps/${params.appId}/appAvailability`,
1640
+ {
1641
+ include: "availableTerritories",
1642
+ "fields[territories]": "currency"
1643
+ }
1644
+ );
1645
+ const availability = response.data;
1646
+ const included = response.included ?? [];
1647
+ const territories = included.filter((item) => item.type === "territories").map((territory) => ({
1648
+ id: territory.id,
1649
+ currency: territory.attributes.currency
1650
+ }));
1651
+ return {
1652
+ success: true,
1653
+ data: {
1654
+ id: availability.id,
1655
+ availableInNewTerritories: availability.attributes.availableInNewTerritories,
1656
+ territories,
1657
+ territoryCount: territories.length
1658
+ }
1659
+ };
1660
+ } catch (error) {
1661
+ return formatErrorResponse(error);
1662
+ }
1663
+ }
1664
+ var categoriesToolDefinitions = [
1665
+ {
1666
+ name: "list_app_categories",
1667
+ description: "List all app categories available in the App Store. Can filter by platform.",
1668
+ inputSchema: {
1669
+ type: "object",
1670
+ properties: {
1671
+ limit: {
1672
+ type: "number",
1673
+ description: "Maximum number of categories to return (1-200)",
1674
+ minimum: 1,
1675
+ maximum: 200
1676
+ },
1677
+ platform: {
1678
+ type: "string",
1679
+ enum: ["IOS", "MAC_OS", "TV_OS", "VISION_OS"],
1680
+ description: "Filter by platform"
1681
+ }
1682
+ },
1683
+ required: []
1684
+ }
1685
+ },
1686
+ {
1687
+ name: "get_app_price_schedule",
1688
+ description: "Get the price schedule for an app, including pricing information.",
1689
+ inputSchema: {
1690
+ type: "object",
1691
+ properties: {
1692
+ appId: {
1693
+ type: "string",
1694
+ description: "The App Store Connect app ID"
1695
+ }
1696
+ },
1697
+ required: ["appId"]
1698
+ }
1699
+ },
1700
+ {
1701
+ name: "get_app_availability",
1702
+ description: "Get app availability information, including which territories the app is available in.",
1703
+ inputSchema: {
1704
+ type: "object",
1705
+ properties: {
1706
+ appId: {
1707
+ type: "string",
1708
+ description: "The App Store Connect app ID"
1709
+ }
1710
+ },
1711
+ required: ["appId"]
1712
+ }
1713
+ }
1714
+ ];
1715
+
1716
+ // src/tools/devices.tools.ts
1717
+ async function listDevices(client, input) {
1718
+ try {
1719
+ const params = validateInput(listDevicesInputSchema, input);
1720
+ const queryParams = {
1721
+ limit: params.limit,
1722
+ "fields[devices]": "name,platform,udid,deviceClass,status,model,addedDate"
1723
+ };
1724
+ if (params.platform) {
1725
+ queryParams["filter[platform]"] = params.platform;
1726
+ }
1727
+ if (params.status) {
1728
+ queryParams["filter[status]"] = params.status;
1729
+ }
1730
+ const response = await client.get("/devices", queryParams);
1731
+ return {
1732
+ success: true,
1733
+ data: response.data.map((device) => ({
1734
+ id: device.id,
1735
+ name: device.attributes.name,
1736
+ platform: device.attributes.platform,
1737
+ udid: device.attributes.udid,
1738
+ deviceClass: device.attributes.deviceClass,
1739
+ status: device.attributes.status,
1740
+ model: device.attributes.model,
1741
+ addedDate: device.attributes.addedDate
1742
+ })),
1743
+ meta: {
1744
+ total: response.meta?.paging?.total,
1745
+ returned: response.data.length
1746
+ }
1747
+ };
1748
+ } catch (error) {
1749
+ return formatErrorResponse(error);
1750
+ }
1751
+ }
1752
+ async function getDevice(client, input) {
1753
+ try {
1754
+ const params = validateInput(getDeviceInputSchema, input);
1755
+ const response = await client.get(`/devices/${params.deviceId}`, {
1756
+ "fields[devices]": "name,platform,udid,deviceClass,status,model,addedDate"
1757
+ });
1758
+ const device = response.data;
1759
+ return {
1760
+ success: true,
1761
+ data: {
1762
+ id: device.id,
1763
+ name: device.attributes.name,
1764
+ platform: device.attributes.platform,
1765
+ udid: device.attributes.udid,
1766
+ deviceClass: device.attributes.deviceClass,
1767
+ status: device.attributes.status,
1768
+ model: device.attributes.model,
1769
+ addedDate: device.attributes.addedDate
1770
+ }
1771
+ };
1772
+ } catch (error) {
1773
+ return formatErrorResponse(error);
1774
+ }
1775
+ }
1776
+ var devicesToolDefinitions = [
1777
+ {
1778
+ name: "list_devices",
1779
+ description: "List all registered devices in App Store Connect. Can filter by platform and status.",
1780
+ inputSchema: {
1781
+ type: "object",
1782
+ properties: {
1783
+ limit: {
1784
+ type: "number",
1785
+ description: "Maximum number of devices to return (1-200)",
1786
+ minimum: 1,
1787
+ maximum: 200
1788
+ },
1789
+ platform: {
1790
+ type: "string",
1791
+ enum: ["IOS", "MAC_OS", "TV_OS", "VISION_OS"],
1792
+ description: "Filter by platform"
1793
+ },
1794
+ status: {
1795
+ type: "string",
1796
+ enum: ["ENABLED", "DISABLED"],
1797
+ description: "Filter by device status"
1798
+ }
1799
+ },
1800
+ required: []
1801
+ }
1802
+ },
1803
+ {
1804
+ name: "get_device",
1805
+ description: "Get details of a specific registered device.",
1806
+ inputSchema: {
1807
+ type: "object",
1808
+ properties: {
1809
+ deviceId: {
1810
+ type: "string",
1811
+ description: "The device resource ID"
1812
+ }
1813
+ },
1814
+ required: ["deviceId"]
1815
+ }
1816
+ }
1817
+ ];
1818
+
1819
+ // src/tools/localizations.tools.ts
1820
+ async function listVersionLocalizations(client, input) {
1821
+ try {
1822
+ const params = validateInput(listVersionLocalizationsInputSchema, input);
1823
+ const response = await client.get(
1824
+ `/appStoreVersions/${params.versionId}/appStoreVersionLocalizations`,
1825
+ {
1826
+ limit: params.limit,
1827
+ "fields[appStoreVersionLocalizations]": "locale,description,keywords,marketingUrl,promotionalText,supportUrl,whatsNew"
1828
+ }
1829
+ );
1830
+ return {
1831
+ success: true,
1832
+ data: response.data.map((loc) => ({
1833
+ id: loc.id,
1834
+ locale: loc.attributes.locale,
1835
+ description: loc.attributes.description,
1836
+ keywords: loc.attributes.keywords,
1837
+ marketingUrl: loc.attributes.marketingUrl,
1838
+ promotionalText: loc.attributes.promotionalText,
1839
+ supportUrl: loc.attributes.supportUrl,
1840
+ whatsNew: loc.attributes.whatsNew
1841
+ })),
1842
+ meta: {
1843
+ total: response.meta?.paging?.total,
1844
+ returned: response.data.length
1845
+ }
1846
+ };
1847
+ } catch (error) {
1848
+ return formatErrorResponse(error);
1849
+ }
1850
+ }
1851
+ async function getVersionLocalization(client, input) {
1852
+ try {
1853
+ const params = validateInput(getVersionLocalizationInputSchema, input);
1854
+ const response = await client.get(
1855
+ `/appStoreVersionLocalizations/${params.localizationId}`,
1856
+ {
1857
+ "fields[appStoreVersionLocalizations]": "locale,description,keywords,marketingUrl,promotionalText,supportUrl,whatsNew"
1858
+ }
1859
+ );
1860
+ const loc = response.data;
1861
+ return {
1862
+ success: true,
1863
+ data: {
1864
+ id: loc.id,
1865
+ locale: loc.attributes.locale,
1866
+ description: loc.attributes.description,
1867
+ keywords: loc.attributes.keywords,
1868
+ marketingUrl: loc.attributes.marketingUrl,
1869
+ promotionalText: loc.attributes.promotionalText,
1870
+ supportUrl: loc.attributes.supportUrl,
1871
+ whatsNew: loc.attributes.whatsNew
1872
+ }
1873
+ };
1874
+ } catch (error) {
1875
+ return formatErrorResponse(error);
1876
+ }
1877
+ }
1878
+ async function createVersionLocalization(client, input) {
1879
+ try {
1880
+ const params = validateInput(createVersionLocalizationInputSchema, input);
1881
+ const requestBody = {
1882
+ data: {
1883
+ type: "appStoreVersionLocalizations",
1884
+ attributes: {
1885
+ locale: params.locale,
1886
+ description: params.description,
1887
+ keywords: params.keywords,
1888
+ marketingUrl: params.marketingUrl,
1889
+ promotionalText: params.promotionalText,
1890
+ supportUrl: params.supportUrl,
1891
+ whatsNew: params.whatsNew
1892
+ },
1893
+ relationships: {
1894
+ appStoreVersion: {
1895
+ data: {
1896
+ type: "appStoreVersions",
1897
+ id: params.versionId
1898
+ }
1899
+ }
1900
+ }
1901
+ }
1902
+ };
1903
+ const response = await client.post(
1904
+ "/appStoreVersionLocalizations",
1905
+ requestBody
1906
+ );
1907
+ const loc = response.data;
1908
+ return {
1909
+ success: true,
1910
+ data: {
1911
+ id: loc.id,
1912
+ locale: loc.attributes.locale,
1913
+ description: loc.attributes.description,
1914
+ keywords: loc.attributes.keywords,
1915
+ marketingUrl: loc.attributes.marketingUrl,
1916
+ promotionalText: loc.attributes.promotionalText,
1917
+ supportUrl: loc.attributes.supportUrl,
1918
+ whatsNew: loc.attributes.whatsNew
1919
+ }
1920
+ };
1921
+ } catch (error) {
1922
+ return formatErrorResponse(error);
1923
+ }
1924
+ }
1925
+ async function updateVersionLocalization(client, input) {
1926
+ try {
1927
+ const params = validateInput(updateVersionLocalizationInputSchema, input);
1928
+ const requestBody = {
1929
+ data: {
1930
+ type: "appStoreVersionLocalizations",
1931
+ id: params.localizationId,
1932
+ attributes: {
1933
+ description: params.description,
1934
+ keywords: params.keywords,
1935
+ marketingUrl: params.marketingUrl,
1936
+ promotionalText: params.promotionalText,
1937
+ supportUrl: params.supportUrl,
1938
+ whatsNew: params.whatsNew
1939
+ }
1940
+ }
1941
+ };
1942
+ const response = await client.patch(
1943
+ `/appStoreVersionLocalizations/${params.localizationId}`,
1944
+ requestBody
1945
+ );
1946
+ const loc = response.data;
1947
+ return {
1948
+ success: true,
1949
+ data: {
1950
+ id: loc.id,
1951
+ locale: loc.attributes.locale,
1952
+ description: loc.attributes.description,
1953
+ keywords: loc.attributes.keywords,
1954
+ marketingUrl: loc.attributes.marketingUrl,
1955
+ promotionalText: loc.attributes.promotionalText,
1956
+ supportUrl: loc.attributes.supportUrl,
1957
+ whatsNew: loc.attributes.whatsNew
1958
+ }
1959
+ };
1960
+ } catch (error) {
1961
+ return formatErrorResponse(error);
1962
+ }
1963
+ }
1964
+ async function deleteVersionLocalization(client, input) {
1965
+ try {
1966
+ const params = validateInput(deleteVersionLocalizationInputSchema, input);
1967
+ await client.delete(`/appStoreVersionLocalizations/${params.localizationId}`);
1968
+ return {
1969
+ success: true,
1970
+ data: {
1971
+ deleted: true,
1972
+ localizationId: params.localizationId
1973
+ }
1974
+ };
1975
+ } catch (error) {
1976
+ return formatErrorResponse(error);
1977
+ }
1978
+ }
1979
+ var localizationsToolDefinitions = [
1980
+ {
1981
+ name: "list_version_localizations",
1982
+ description: "List all localizations for an App Store version. Returns description, keywords, what's new, and URLs for each locale.",
1983
+ inputSchema: {
1984
+ type: "object",
1985
+ properties: {
1986
+ versionId: {
1987
+ type: "string",
1988
+ description: "The App Store version ID"
1989
+ },
1990
+ limit: {
1991
+ type: "number",
1992
+ description: "Maximum number of localizations to return (1-200)",
1993
+ minimum: 1,
1994
+ maximum: 200
1995
+ }
1996
+ },
1997
+ required: ["versionId"]
1998
+ }
1999
+ },
2000
+ {
2001
+ name: "get_version_localization",
2002
+ description: "Get detailed information about a specific version localization.",
2003
+ inputSchema: {
2004
+ type: "object",
2005
+ properties: {
2006
+ localizationId: {
2007
+ type: "string",
2008
+ description: "The localization ID"
2009
+ }
2010
+ },
2011
+ required: ["localizationId"]
2012
+ }
2013
+ },
2014
+ {
2015
+ name: "create_version_localization",
2016
+ description: "Create a new localization for an App Store version. Add descriptions, keywords, and other metadata in a specific locale.",
2017
+ inputSchema: {
2018
+ type: "object",
2019
+ properties: {
2020
+ versionId: {
2021
+ type: "string",
2022
+ description: "The App Store version ID"
2023
+ },
2024
+ locale: {
2025
+ type: "string",
2026
+ description: "Locale code (e.g., 'en-US', 'ja', 'zh-Hans')"
2027
+ },
2028
+ description: {
2029
+ type: "string",
2030
+ description: "App description (max 4000 characters)"
2031
+ },
2032
+ keywords: {
2033
+ type: "string",
2034
+ description: "Keywords for search (max 100 characters, comma-separated)"
2035
+ },
2036
+ whatsNew: {
2037
+ type: "string",
2038
+ description: "What's new in this version (max 4000 characters)"
2039
+ },
2040
+ promotionalText: {
2041
+ type: "string",
2042
+ description: "Promotional text (max 170 characters)"
2043
+ },
2044
+ marketingUrl: {
2045
+ type: "string",
2046
+ description: "Marketing URL (HTTPS only)"
2047
+ },
2048
+ supportUrl: {
2049
+ type: "string",
2050
+ description: "Support URL (HTTPS only)"
2051
+ }
2052
+ },
2053
+ required: ["versionId", "locale"]
2054
+ }
2055
+ },
2056
+ {
2057
+ name: "update_version_localization",
2058
+ description: "Update an existing version localization. Only provided fields will be updated.",
2059
+ inputSchema: {
2060
+ type: "object",
2061
+ properties: {
2062
+ localizationId: {
2063
+ type: "string",
2064
+ description: "The localization ID to update"
2065
+ },
2066
+ description: {
2067
+ type: "string",
2068
+ description: "App description (max 4000 characters)"
2069
+ },
2070
+ keywords: {
2071
+ type: "string",
2072
+ description: "Keywords for search (max 100 characters, comma-separated)"
2073
+ },
2074
+ whatsNew: {
2075
+ type: "string",
2076
+ description: "What's new in this version (max 4000 characters)"
2077
+ },
2078
+ promotionalText: {
2079
+ type: "string",
2080
+ description: "Promotional text (max 170 characters)"
2081
+ },
2082
+ marketingUrl: {
2083
+ type: "string",
2084
+ description: "Marketing URL (HTTPS only)"
2085
+ },
2086
+ supportUrl: {
2087
+ type: "string",
2088
+ description: "Support URL (HTTPS only)"
2089
+ }
2090
+ },
2091
+ required: ["localizationId"]
2092
+ }
2093
+ },
2094
+ {
2095
+ name: "delete_version_localization",
2096
+ description: "Delete a version localization. Cannot delete the primary locale.",
2097
+ inputSchema: {
2098
+ type: "object",
2099
+ properties: {
2100
+ localizationId: {
2101
+ type: "string",
2102
+ description: "The localization ID to delete"
2103
+ }
2104
+ },
2105
+ required: ["localizationId"]
2106
+ }
2107
+ }
2108
+ ];
2109
+
2110
+ // src/tools/screenshots.tools.ts
2111
+ import * as crypto from "crypto";
2112
+ import * as fs2 from "fs/promises";
2113
+ async function listScreenshotSets(client, input) {
2114
+ try {
2115
+ const params = validateInput(listScreenshotSetsInputSchema, input);
2116
+ const response = await client.get(
2117
+ `/appStoreVersionLocalizations/${params.localizationId}/appScreenshotSets`,
2118
+ {
2119
+ limit: params.limit,
2120
+ "fields[appScreenshotSets]": "screenshotDisplayType"
2121
+ }
2122
+ );
2123
+ return {
2124
+ success: true,
2125
+ data: response.data.map((set) => ({
2126
+ id: set.id,
2127
+ screenshotDisplayType: set.attributes.screenshotDisplayType
2128
+ })),
2129
+ meta: {
2130
+ total: response.meta?.paging?.total,
2131
+ returned: response.data.length
2132
+ }
2133
+ };
2134
+ } catch (error) {
2135
+ return formatErrorResponse(error);
2136
+ }
2137
+ }
2138
+ async function listScreenshots(client, input) {
2139
+ try {
2140
+ const params = validateInput(listScreenshotsInputSchema, input);
2141
+ const response = await client.get(
2142
+ `/appScreenshotSets/${params.screenshotSetId}/appScreenshots`,
2143
+ {
2144
+ limit: params.limit,
2145
+ "fields[appScreenshots]": "fileSize,fileName,sourceFileChecksum,imageAsset,assetDeliveryState"
2146
+ }
2147
+ );
2148
+ return {
2149
+ success: true,
2150
+ data: response.data.map((screenshot) => ({
2151
+ id: screenshot.id,
2152
+ fileName: screenshot.attributes.fileName,
2153
+ fileSize: screenshot.attributes.fileSize,
2154
+ sourceFileChecksum: screenshot.attributes.sourceFileChecksum,
2155
+ imageAsset: screenshot.attributes.imageAsset,
2156
+ assetDeliveryState: screenshot.attributes.assetDeliveryState
2157
+ })),
2158
+ meta: {
2159
+ total: response.meta?.paging?.total,
2160
+ returned: response.data.length
2161
+ }
2162
+ };
2163
+ } catch (error) {
2164
+ return formatErrorResponse(error);
2165
+ }
2166
+ }
2167
+ async function uploadScreenshot(client, input) {
2168
+ try {
2169
+ const params = validateInput(uploadScreenshotInputSchema, input);
2170
+ let fileBuffer;
2171
+ try {
2172
+ fileBuffer = await fs2.readFile(params.filePath);
2173
+ } catch (_error) {
2174
+ throw new ASCError(`Failed to read file: ${params.filePath}`, "FILE_READ_ERROR", 400);
2175
+ }
2176
+ if (fileBuffer.length !== params.fileSize) {
2177
+ throw new ASCError(
2178
+ `File size mismatch: expected ${params.fileSize}, got ${fileBuffer.length}`,
2179
+ "FILE_SIZE_MISMATCH",
2180
+ 400
2181
+ );
2182
+ }
2183
+ const reserveRequest = {
2184
+ data: {
2185
+ type: "appScreenshots",
2186
+ attributes: {
2187
+ fileName: params.fileName,
2188
+ fileSize: params.fileSize
2189
+ },
2190
+ relationships: {
2191
+ appScreenshotSet: {
2192
+ data: {
2193
+ type: "appScreenshotSets",
2194
+ id: params.screenshotSetId
2195
+ }
2196
+ }
2197
+ }
2198
+ }
2199
+ };
2200
+ const reserveResponse = await client.post(
2201
+ "/appScreenshots",
2202
+ reserveRequest
2203
+ );
2204
+ const screenshot = reserveResponse.data;
2205
+ const uploadOperations = screenshot.attributes.uploadOperations;
2206
+ if (!uploadOperations?.length) {
2207
+ throw new ASCError("No upload operations provided", "UPLOAD_ERROR", 500);
2208
+ }
2209
+ for (const operation of uploadOperations) {
2210
+ const chunk = fileBuffer.subarray(operation.offset, operation.offset + operation.length);
2211
+ const headers = {};
2212
+ for (const header of operation.requestHeaders) {
2213
+ headers[header.name] = header.value;
2214
+ }
2215
+ const uploadResponse = await client.rawRequest(operation.url, {
2216
+ method: operation.method,
2217
+ headers,
2218
+ body: chunk
2219
+ });
2220
+ if (!uploadResponse.ok) {
2221
+ throw new ASCError(
2222
+ `Chunk upload failed: ${uploadResponse.status}`,
2223
+ "UPLOAD_ERROR",
2224
+ uploadResponse.status
2225
+ );
2226
+ }
2227
+ }
2228
+ const checksum = crypto.createHash("md5").update(fileBuffer).digest("base64");
2229
+ const commitRequest = {
2230
+ data: {
2231
+ type: "appScreenshots",
2232
+ id: screenshot.id,
2233
+ attributes: {
2234
+ sourceFileChecksum: checksum,
2235
+ uploaded: true
2236
+ }
2237
+ }
2238
+ };
2239
+ const commitResponse = await client.patch(
2240
+ `/appScreenshots/${screenshot.id}`,
2241
+ commitRequest
2242
+ );
2243
+ const finalScreenshot = commitResponse.data;
2244
+ return {
2245
+ success: true,
2246
+ data: {
2247
+ id: finalScreenshot.id,
2248
+ fileName: finalScreenshot.attributes.fileName,
2249
+ fileSize: finalScreenshot.attributes.fileSize,
2250
+ sourceFileChecksum: finalScreenshot.attributes.sourceFileChecksum,
2251
+ assetDeliveryState: finalScreenshot.attributes.assetDeliveryState
2252
+ }
2253
+ };
2254
+ } catch (error) {
2255
+ return formatErrorResponse(error);
2256
+ }
2257
+ }
2258
+ var screenshotsToolDefinitions = [
2259
+ {
2260
+ name: "list_screenshot_sets",
2261
+ description: "List all screenshot sets for a version localization. Each set represents a different display type (device size).",
2262
+ inputSchema: {
2263
+ type: "object",
2264
+ properties: {
2265
+ localizationId: {
2266
+ type: "string",
2267
+ description: "The version localization ID"
2268
+ },
2269
+ limit: {
2270
+ type: "number",
2271
+ description: "Maximum number of sets to return (1-200)",
2272
+ minimum: 1,
2273
+ maximum: 200
2274
+ }
2275
+ },
2276
+ required: ["localizationId"]
2277
+ }
2278
+ },
2279
+ {
2280
+ name: "list_screenshots",
2281
+ description: "List all screenshots in a screenshot set.",
2282
+ inputSchema: {
2283
+ type: "object",
2284
+ properties: {
2285
+ screenshotSetId: {
2286
+ type: "string",
2287
+ description: "The screenshot set ID"
2288
+ },
2289
+ limit: {
2290
+ type: "number",
2291
+ description: "Maximum number of screenshots to return (1-200)",
2292
+ minimum: 1,
2293
+ maximum: 200
2294
+ }
2295
+ },
2296
+ required: ["screenshotSetId"]
2297
+ }
2298
+ },
2299
+ {
2300
+ name: "upload_screenshot",
2301
+ description: "Upload a new screenshot to a screenshot set. Provide the local file path, and this tool will handle the multi-step upload process.",
2302
+ inputSchema: {
2303
+ type: "object",
2304
+ properties: {
2305
+ screenshotSetId: {
2306
+ type: "string",
2307
+ description: "The screenshot set ID to upload to"
2308
+ },
2309
+ fileName: {
2310
+ type: "string",
2311
+ description: "Name for the screenshot file"
2312
+ },
2313
+ fileSize: {
2314
+ type: "number",
2315
+ description: "Size of the file in bytes"
2316
+ },
2317
+ filePath: {
2318
+ type: "string",
2319
+ description: "Local path to the screenshot file"
2320
+ }
2321
+ },
2322
+ required: ["screenshotSetId", "fileName", "fileSize", "filePath"]
2323
+ }
2324
+ }
2325
+ ];
2326
+
2327
+ // src/tools/users.tools.ts
2328
+ async function listUsers(client, input) {
2329
+ try {
2330
+ const params = validateInput(listUsersInputSchema, input);
2331
+ const queryParams = {
2332
+ limit: params.limit,
2333
+ "fields[users]": "username,firstName,lastName,email,roles,allAppsVisible,provisioningAllowed"
2334
+ };
2335
+ if (params.roles && params.roles.length > 0) {
2336
+ queryParams["filter[roles]"] = params.roles.join(",");
2337
+ }
2338
+ const response = await client.get("/users", queryParams);
2339
+ return {
2340
+ success: true,
2341
+ data: response.data.map((user) => ({
2342
+ id: user.id,
2343
+ username: user.attributes.username,
2344
+ firstName: user.attributes.firstName,
2345
+ lastName: user.attributes.lastName,
2346
+ email: user.attributes.email,
2347
+ roles: user.attributes.roles,
2348
+ allAppsVisible: user.attributes.allAppsVisible,
2349
+ provisioningAllowed: user.attributes.provisioningAllowed
2350
+ })),
2351
+ meta: {
2352
+ total: response.meta?.paging?.total,
2353
+ returned: response.data.length
2354
+ }
2355
+ };
2356
+ } catch (error) {
2357
+ return formatErrorResponse(error);
2358
+ }
2359
+ }
2360
+ async function getUser(client, input) {
2361
+ try {
2362
+ const params = validateInput(getUserInputSchema, input);
2363
+ const response = await client.get(`/users/${params.userId}`, {
2364
+ "fields[users]": "username,firstName,lastName,email,roles,allAppsVisible,provisioningAllowed"
2365
+ });
2366
+ const user = response.data;
2367
+ return {
2368
+ success: true,
2369
+ data: {
2370
+ id: user.id,
2371
+ username: user.attributes.username,
2372
+ firstName: user.attributes.firstName,
2373
+ lastName: user.attributes.lastName,
2374
+ email: user.attributes.email,
2375
+ roles: user.attributes.roles,
2376
+ allAppsVisible: user.attributes.allAppsVisible,
2377
+ provisioningAllowed: user.attributes.provisioningAllowed
2378
+ }
2379
+ };
2380
+ } catch (error) {
2381
+ return formatErrorResponse(error);
2382
+ }
2383
+ }
2384
+ var usersToolDefinitions = [
2385
+ {
2386
+ name: "list_users",
2387
+ description: "List all users in your App Store Connect team. Can filter by roles.",
2388
+ inputSchema: {
2389
+ type: "object",
2390
+ properties: {
2391
+ limit: {
2392
+ type: "number",
2393
+ description: "Maximum number of users to return (1-200)",
2394
+ minimum: 1,
2395
+ maximum: 200
2396
+ },
2397
+ roles: {
2398
+ type: "array",
2399
+ items: {
2400
+ type: "string",
2401
+ enum: [
2402
+ "ADMIN",
2403
+ "FINANCE",
2404
+ "TECHNICAL",
2405
+ "SALES",
2406
+ "DEVELOPER",
2407
+ "MARKETING",
2408
+ "APP_MANAGER",
2409
+ "CUSTOMER_SUPPORT",
2410
+ "ACCESS_TO_REPORTS",
2411
+ "READ_ONLY"
2412
+ ]
2413
+ },
2414
+ description: "Filter by user roles"
2415
+ }
2416
+ },
2417
+ required: []
2418
+ }
2419
+ },
2420
+ {
2421
+ name: "get_user",
2422
+ description: "Get details of a specific team user.",
2423
+ inputSchema: {
2424
+ type: "object",
2425
+ properties: {
2426
+ userId: {
2427
+ type: "string",
2428
+ description: "The user resource ID"
2429
+ }
2430
+ },
2431
+ required: ["userId"]
2432
+ }
2433
+ }
2434
+ ];
2435
+
2436
+ // src/tools/versions.tools.ts
2437
+ async function listAppVersions(client, input) {
2438
+ try {
2439
+ const params = validateInput(listAppVersionsInputSchema, input);
2440
+ const queryParams = {
2441
+ limit: params.limit,
2442
+ "fields[appStoreVersions]": "platform,versionString,appStoreState,copyright,releaseType,createdDate"
2443
+ };
2444
+ if (params.platform) {
2445
+ queryParams["filter[platform]"] = params.platform;
2446
+ }
2447
+ if (params.versionState) {
2448
+ queryParams["filter[appStoreState]"] = params.versionState;
2449
+ }
2450
+ const response = await client.get(
2451
+ `/apps/${params.appId}/appStoreVersions`,
2452
+ queryParams
2453
+ );
2454
+ return {
2455
+ success: true,
2456
+ data: response.data.map((version) => ({
2457
+ id: version.id,
2458
+ platform: version.attributes.platform,
2459
+ versionString: version.attributes.versionString,
2460
+ state: version.attributes.appStoreState,
2461
+ copyright: version.attributes.copyright,
2462
+ releaseType: version.attributes.releaseType,
2463
+ createdDate: version.attributes.createdDate
2464
+ })),
2465
+ meta: {
2466
+ total: response.meta?.paging?.total,
2467
+ returned: response.data.length
2468
+ }
2469
+ };
2470
+ } catch (error) {
2471
+ return formatErrorResponse(error);
2472
+ }
2473
+ }
2474
+ async function getAppVersion(client, input) {
2475
+ try {
2476
+ const params = validateInput(getAppVersionInputSchema, input);
2477
+ const response = await client.get(
2478
+ `/appStoreVersions/${params.versionId}`,
2479
+ {
2480
+ "fields[appStoreVersions]": "platform,versionString,appStoreState,copyright,releaseType,earliestReleaseDate,usesIdfa,downloadable,createdDate"
2481
+ }
2482
+ );
2483
+ const version = response.data;
2484
+ return {
2485
+ success: true,
2486
+ data: {
2487
+ id: version.id,
2488
+ platform: version.attributes.platform,
2489
+ versionString: version.attributes.versionString,
2490
+ state: version.attributes.appStoreState,
2491
+ copyright: version.attributes.copyright,
2492
+ releaseType: version.attributes.releaseType,
2493
+ earliestReleaseDate: version.attributes.earliestReleaseDate,
2494
+ usesIdfa: version.attributes.usesIdfa,
2495
+ downloadable: version.attributes.downloadable,
2496
+ createdDate: version.attributes.createdDate
2497
+ }
2498
+ };
2499
+ } catch (error) {
2500
+ return formatErrorResponse(error);
2501
+ }
2502
+ }
2503
+ async function createAppVersion(client, input) {
2504
+ try {
2505
+ const params = validateInput(createAppVersionInputSchema, input);
2506
+ const requestBody = {
2507
+ data: {
2508
+ type: "appStoreVersions",
2509
+ attributes: {
2510
+ platform: params.platform,
2511
+ versionString: params.versionString,
2512
+ copyright: params.copyright,
2513
+ releaseType: params.releaseType,
2514
+ earliestReleaseDate: params.earliestReleaseDate
2515
+ },
2516
+ relationships: {
2517
+ app: {
2518
+ data: {
2519
+ type: "apps",
2520
+ id: params.appId
2521
+ }
2522
+ }
2523
+ }
2524
+ }
2525
+ };
2526
+ const response = await client.post(
2527
+ "/appStoreVersions",
2528
+ requestBody
2529
+ );
2530
+ const version = response.data;
2531
+ return {
2532
+ success: true,
2533
+ data: {
2534
+ id: version.id,
2535
+ platform: version.attributes.platform,
2536
+ versionString: version.attributes.versionString,
2537
+ state: version.attributes.appStoreState,
2538
+ copyright: version.attributes.copyright,
2539
+ releaseType: version.attributes.releaseType,
2540
+ createdDate: version.attributes.createdDate
2541
+ }
2542
+ };
2543
+ } catch (error) {
2544
+ return formatErrorResponse(error);
2545
+ }
2546
+ }
2547
+ var versionsToolDefinitions = [
2548
+ {
2549
+ name: "list_app_versions",
2550
+ description: "List all App Store versions for an app. Can filter by platform and version state.",
2551
+ inputSchema: {
2552
+ type: "object",
2553
+ properties: {
2554
+ appId: {
2555
+ type: "string",
2556
+ description: "The App Store Connect app ID"
2557
+ },
2558
+ platform: {
2559
+ type: "string",
2560
+ description: "Filter by platform (IOS, MAC_OS, TV_OS, VISION_OS)",
2561
+ enum: ["IOS", "MAC_OS", "TV_OS", "VISION_OS"]
2562
+ },
2563
+ versionState: {
2564
+ type: "string",
2565
+ description: "Filter by version state (e.g., PREPARE_FOR_SUBMISSION, READY_FOR_SALE)"
2566
+ },
2567
+ limit: {
2568
+ type: "number",
2569
+ description: "Maximum number of versions to return (1-200)",
2570
+ minimum: 1,
2571
+ maximum: 200
2572
+ }
2573
+ },
2574
+ required: ["appId"]
2575
+ }
2576
+ },
2577
+ {
2578
+ name: "get_app_version",
2579
+ description: "Get detailed information about a specific app version.",
2580
+ inputSchema: {
2581
+ type: "object",
2582
+ properties: {
2583
+ versionId: {
2584
+ type: "string",
2585
+ description: "The App Store version ID"
2586
+ }
2587
+ },
2588
+ required: ["versionId"]
2589
+ }
2590
+ },
2591
+ {
2592
+ name: "create_app_version",
2593
+ description: "Create a new App Store version for an app.",
2594
+ inputSchema: {
2595
+ type: "object",
2596
+ properties: {
2597
+ appId: {
2598
+ type: "string",
2599
+ description: "The App Store Connect app ID"
2600
+ },
2601
+ platform: {
2602
+ type: "string",
2603
+ description: "Platform for the version",
2604
+ enum: ["IOS", "MAC_OS", "TV_OS", "VISION_OS"]
2605
+ },
2606
+ versionString: {
2607
+ type: "string",
2608
+ description: "Version number (e.g., '1.0.0', '2.1')"
2609
+ },
2610
+ releaseType: {
2611
+ type: "string",
2612
+ description: "Release type",
2613
+ enum: ["MANUAL", "AFTER_APPROVAL", "SCHEDULED"]
2614
+ },
2615
+ copyright: {
2616
+ type: "string",
2617
+ description: "Copyright text for the version"
2618
+ },
2619
+ earliestReleaseDate: {
2620
+ type: "string",
2621
+ description: "Earliest release date (ISO 8601 format) for SCHEDULED release type"
2622
+ }
2623
+ },
2624
+ required: ["appId", "platform", "versionString"]
2625
+ }
2626
+ }
2627
+ ];
2628
+
2629
+ // src/tools/index.ts
2630
+ var toolHandlers = {
2631
+ // Apps
2632
+ list_apps: listApps,
2633
+ get_app: getApp,
2634
+ // Versions
2635
+ list_app_versions: listAppVersions,
2636
+ get_app_version: getAppVersion,
2637
+ create_app_version: createAppVersion,
2638
+ // Localizations
2639
+ list_version_localizations: listVersionLocalizations,
2640
+ get_version_localization: getVersionLocalization,
2641
+ create_version_localization: createVersionLocalization,
2642
+ update_version_localization: updateVersionLocalization,
2643
+ delete_version_localization: deleteVersionLocalization,
2644
+ // App Info
2645
+ list_app_infos: listAppInfos,
2646
+ list_app_info_localizations: listAppInfoLocalizations,
2647
+ update_app_info_localization: updateAppInfoLocalization,
2648
+ // Beta
2649
+ list_beta_groups: listBetaGroups,
2650
+ list_beta_testers: listBetaTesters,
2651
+ add_beta_tester: addBetaTester,
2652
+ remove_beta_tester: removeBetaTester,
2653
+ // Screenshots
2654
+ list_screenshot_sets: listScreenshotSets,
2655
+ list_screenshots: listScreenshots,
2656
+ upload_screenshot: uploadScreenshot,
2657
+ // Bundle IDs
2658
+ list_bundle_ids: listBundleIds,
2659
+ get_bundle_id: getBundleId,
2660
+ create_bundle_id: createBundleId,
2661
+ update_bundle_id: updateBundleId,
2662
+ delete_bundle_id: deleteBundleId,
2663
+ // Devices
2664
+ list_devices: listDevices,
2665
+ get_device: getDevice,
2666
+ // Users
2667
+ list_users: listUsers,
2668
+ get_user: getUser,
2669
+ // Builds
2670
+ list_builds: listBuilds,
2671
+ get_build: getBuild,
2672
+ list_beta_tester_invitations: listBetaTesterInvitations,
2673
+ // Categories
2674
+ list_app_categories: listAppCategories,
2675
+ get_app_price_schedule: getAppPriceSchedule,
2676
+ get_app_availability: getAppAvailability
2677
+ };
2678
+ var allToolDefinitions = [
2679
+ ...appsToolDefinitions,
2680
+ ...versionsToolDefinitions,
2681
+ ...localizationsToolDefinitions,
2682
+ ...appInfoToolDefinitions,
2683
+ ...betaToolDefinitions,
2684
+ ...screenshotsToolDefinitions,
2685
+ ...bundleIdsToolDefinitions,
2686
+ ...devicesToolDefinitions,
2687
+ ...usersToolDefinitions,
2688
+ ...buildsToolDefinitions,
2689
+ ...categoriesToolDefinitions
2690
+ ];
2691
+ function hasToolHandler(name) {
2692
+ return name in toolHandlers;
2693
+ }
2694
+ async function executeTool(client, name, input) {
2695
+ const handler = toolHandlers[name];
2696
+ if (!handler) {
2697
+ return {
2698
+ success: false,
2699
+ error: {
2700
+ code: "UNKNOWN_TOOL",
2701
+ message: `Unknown tool: ${name}`
2702
+ }
2703
+ };
2704
+ }
2705
+ return handler(client, input);
2706
+ }
2707
+
2708
+ // src/index.ts
2709
+ var SERVER_NAME = "asc-mcp";
2710
+ var SERVER_VERSION = "1.0.0";
2711
+ async function main() {
2712
+ let tokenManager;
2713
+ let client;
2714
+ try {
2715
+ tokenManager = createTokenManagerFromEnv();
2716
+ client = createClient(tokenManager);
2717
+ } catch (error) {
2718
+ if (error instanceof ConfigError) {
2719
+ console.error(`Configuration error: ${error.message}`);
2720
+ console.error("\nRequired environment variables:");
2721
+ console.error(" APP_STORE_CONNECT_KEY_ID - Your API key ID");
2722
+ console.error(" APP_STORE_CONNECT_ISSUER_ID - Your issuer ID");
2723
+ console.error(" APP_STORE_CONNECT_P8_PATH - Path to your .p8 private key file");
2724
+ console.error(" or APP_STORE_CONNECT_P8_CONTENT - Content of your .p8 private key");
2725
+ process.exit(1);
2726
+ }
2727
+ throw error;
2728
+ }
2729
+ const server = new Server(
2730
+ {
2731
+ name: SERVER_NAME,
2732
+ version: SERVER_VERSION
2733
+ },
2734
+ {
2735
+ capabilities: {
2736
+ tools: {}
2737
+ }
2738
+ }
2739
+ );
2740
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
2741
+ return {
2742
+ tools: allToolDefinitions
2743
+ };
2744
+ });
2745
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
2746
+ const { name, arguments: args } = request.params;
2747
+ if (!hasToolHandler(name)) {
2748
+ return {
2749
+ content: [
2750
+ {
2751
+ type: "text",
2752
+ text: JSON.stringify(
2753
+ {
2754
+ success: false,
2755
+ error: {
2756
+ code: "UNKNOWN_TOOL",
2757
+ message: `Unknown tool: ${name}`
2758
+ }
2759
+ },
2760
+ null,
2761
+ 2
2762
+ )
2763
+ }
2764
+ ],
2765
+ isError: true
2766
+ };
2767
+ }
2768
+ try {
2769
+ const result = await executeTool(client, name, args ?? {});
2770
+ const isError = typeof result === "object" && result !== null && "success" in result && result.success === false;
2771
+ return {
2772
+ content: [
2773
+ {
2774
+ type: "text",
2775
+ text: JSON.stringify(result, null, 2)
2776
+ }
2777
+ ],
2778
+ isError
2779
+ };
2780
+ } catch (error) {
2781
+ const errorResponse = formatErrorResponse(error);
2782
+ return {
2783
+ content: [
2784
+ {
2785
+ type: "text",
2786
+ text: JSON.stringify(errorResponse, null, 2)
2787
+ }
2788
+ ],
2789
+ isError: true
2790
+ };
2791
+ }
2792
+ });
2793
+ const shutdown = () => {
2794
+ console.error("Shutting down...");
2795
+ tokenManager.destroy();
2796
+ setTimeout(() => {
2797
+ process.exit(0);
2798
+ }, 100);
2799
+ };
2800
+ process.on("SIGINT", shutdown);
2801
+ process.on("SIGTERM", shutdown);
2802
+ const transport = new StdioServerTransport();
2803
+ await server.connect(transport);
2804
+ console.error(`${SERVER_NAME} v${SERVER_VERSION} started`);
2805
+ }
2806
+ main().catch((error) => {
2807
+ console.error("Fatal error:", error);
2808
+ if (error instanceof Error && error.stack) {
2809
+ console.error(error.stack);
2810
+ }
2811
+ process.exit(1);
2812
+ });
2813
+ //# sourceMappingURL=index.js.map