@yoonion/mimi-seed-mcp 0.3.10 → 0.3.12

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.
@@ -2,6 +2,30 @@ export declare function apiGet(path: string, params?: Record<string, string>): P
2
2
  export declare function listApps(): Promise<any>;
3
3
  export declare function getApp(appId: string): Promise<any>;
4
4
  export declare function listVersions(appId: string): Promise<any>;
5
+ export type ApplePlatform = 'IOS' | 'MAC_OS' | 'TV_OS' | 'VISION_OS';
6
+ export type AppleReleaseType = 'MANUAL' | 'AFTER_APPROVAL' | 'SCHEDULED';
7
+ export interface CreateVersionInput {
8
+ appId: string;
9
+ versionString: string;
10
+ platform: ApplePlatform;
11
+ copyright?: string;
12
+ releaseType?: AppleReleaseType;
13
+ earliestReleaseDate?: string;
14
+ buildId?: string;
15
+ }
16
+ export declare function createVersion(input: CreateVersionInput): Promise<{
17
+ id: any;
18
+ version: any;
19
+ platform: any;
20
+ state: any;
21
+ releaseType: any;
22
+ createdDate: any;
23
+ }>;
24
+ export declare function attachBuildToVersion(versionId: string, buildId: string): Promise<{
25
+ versionId: string;
26
+ buildId: string;
27
+ ok: boolean;
28
+ }>;
5
29
  export declare function getVersionLocalizations(versionId: string): Promise<any>;
6
30
  export interface LocalizationUpdateFields {
7
31
  whatsNew?: string;
@@ -107,6 +107,46 @@ export async function listVersions(appId) {
107
107
  createdDate: v.attributes?.createdDate,
108
108
  }));
109
109
  }
110
+ export async function createVersion(input) {
111
+ const attributes = {
112
+ platform: input.platform,
113
+ versionString: input.versionString,
114
+ };
115
+ if (input.copyright !== undefined)
116
+ attributes.copyright = input.copyright;
117
+ if (input.releaseType !== undefined)
118
+ attributes.releaseType = input.releaseType;
119
+ if (input.earliestReleaseDate !== undefined)
120
+ attributes.earliestReleaseDate = input.earliestReleaseDate;
121
+ const relationships = {
122
+ app: { data: { type: 'apps', id: input.appId } },
123
+ };
124
+ if (input.buildId) {
125
+ relationships.build = { data: { type: 'builds', id: input.buildId } };
126
+ }
127
+ const created = await apiPost('/appStoreVersions', {
128
+ data: {
129
+ type: 'appStoreVersions',
130
+ attributes,
131
+ relationships,
132
+ },
133
+ });
134
+ return {
135
+ id: created?.data?.id,
136
+ version: created?.data?.attributes?.versionString,
137
+ platform: created?.data?.attributes?.platform,
138
+ state: created?.data?.attributes?.appStoreState ?? created?.data?.attributes?.state,
139
+ releaseType: created?.data?.attributes?.releaseType,
140
+ createdDate: created?.data?.attributes?.createdDate,
141
+ };
142
+ }
143
+ export async function attachBuildToVersion(versionId, buildId) {
144
+ // /relationships/build 엔드포인트는 204 No Content 반환
145
+ await apiPatch(`/appStoreVersions/${versionId}/relationships/build`, {
146
+ data: { type: 'builds', id: buildId },
147
+ });
148
+ return { versionId, buildId, ok: true };
149
+ }
110
150
  // ─── 로컬라이제이션 (메타데이터) ───
111
151
  export async function getVersionLocalizations(versionId) {
112
152
  const data = await apiGet(`/appStoreVersions/${versionId}/appStoreVersionLocalizations`, {
@@ -0,0 +1,20 @@
1
+ import type { OAuth2Client } from 'google-auth-library';
2
+ export type StepState = 'done' | 'pending' | 'blocked';
3
+ export interface PlanStep {
4
+ state: StepState;
5
+ title: string;
6
+ detail?: string;
7
+ tool?: string;
8
+ irreversible?: boolean;
9
+ }
10
+ export declare function buildPlayStoreReleasePlan(opts: {
11
+ auth: OAuth2Client;
12
+ packageName: string;
13
+ versionCode?: string;
14
+ track: 'production' | 'beta' | 'alpha' | 'internal';
15
+ language: string;
16
+ }): Promise<string>;
17
+ export declare function buildAppStoreReleasePlan(opts: {
18
+ appId: string;
19
+ versionString?: string;
20
+ }): Promise<string>;
@@ -0,0 +1,258 @@
1
+ // Release Plan — Play Store / App Store 배포 사전 점검 + AI에게 TodoWrite 지시
2
+ //
3
+ // 이 모듈은 read-only.
4
+ // 실행 도구(playstore_submit_release, appstore_submit_for_review 등)는 호출하지 않고,
5
+ // 현재 상태를 조사해서 markdown 체크리스트로 변환한다.
6
+ // AI 에이전트가 응답을 받아 TodoWrite로 사용자에게 제시한 뒤,
7
+ // 단계별로 기존 *_update_*, *_submit_*, *_check_* 도구를 호출하도록 유도한다.
8
+ import { publisher, withEdit } from '../playstore/tools.js';
9
+ import { apiGet } from '../appstore/tools.js';
10
+ import { checkPlayStoreRisks, checkAppStoreRisks } from './risks.js';
11
+ const TODO_DIRECTIVE = [
12
+ '📋 [AI 지시 — 호출자 에이전트 전용]',
13
+ '',
14
+ '이 응답은 배포 플랜입니다. 다음 절차를 반드시 따르세요:',
15
+ '',
16
+ '1. 아래 체크리스트의 미완료(◻︎) 항목을 TodoWrite 도구로 사용자에게 먼저 보여주세요.',
17
+ '2. 사용자가 진행에 명시적으로 동의하기 전엔 ⚠️ 비가역 도구를 호출하지 마세요',
18
+ ' (playstore_submit_release status=completed, playstore_promote_release status=completed,',
19
+ ' appstore_submit_for_review).',
20
+ '3. 한 단계 끝날 때마다 TodoWrite로 해당 항목을 completed로 표시하세요.',
21
+ '4. 항목 옆에 "도구:" 가 표기된 경우, 그 도구를 직접 호출해서 단계를 실행하세요.',
22
+ '',
23
+ ].join('\n');
24
+ function fmtState(s) {
25
+ return s === 'done' ? '☑︎' : s === 'blocked' ? '⛔' : '◻︎';
26
+ }
27
+ function risksToSteps(risks, toolHint) {
28
+ return risks.map((r) => ({
29
+ state: 'blocked',
30
+ title: `[${r.code}] ${r.title}`,
31
+ detail: r.detail + (r.fixUrl ? ` → ${r.fixUrl}` : ''),
32
+ tool: r.level === 'blocker' ? toolHint : undefined,
33
+ }));
34
+ }
35
+ function renderPlan(ctx, steps) {
36
+ const blocked = steps.filter((s) => s.state === 'blocked').length;
37
+ const pending = steps.filter((s) => s.state === 'pending').length;
38
+ const done = steps.filter((s) => s.state === 'done').length;
39
+ const lines = [];
40
+ lines.push(TODO_DIRECTIVE);
41
+ lines.push(`🎯 ${ctx.platform} 배포 플랜`);
42
+ lines.push('');
43
+ lines.push(` 대상: ${ctx.identity}`);
44
+ lines.push(` 버전: ${ctx.version}`);
45
+ if (ctx.track)
46
+ lines.push(` 트랙: ${ctx.track}`);
47
+ lines.push('');
48
+ lines.push(` 진행률: ☑︎ ${done} ◻︎ ${pending} ⛔ ${blocked}`);
49
+ if (blocked > 0) {
50
+ lines.push(` ⚠️ 블로커 ${blocked}건 — 먼저 해결해야 제출 가능`);
51
+ }
52
+ lines.push('');
53
+ lines.push('체크리스트 (TodoWrite로 사용자에게 제시):');
54
+ lines.push('');
55
+ for (const s of steps) {
56
+ const irr = s.irreversible ? ' ⚠️ 비가역' : '';
57
+ lines.push(`${fmtState(s.state)} ${s.title}${irr}`);
58
+ if (s.detail)
59
+ lines.push(` ${s.detail}`);
60
+ if (s.tool)
61
+ lines.push(` 도구: ${s.tool}`);
62
+ }
63
+ lines.push('');
64
+ lines.push('—');
65
+ lines.push('TodoWrite로 위 미완료 항목을 등록한 후, 사용자 확인을 받고 도구를 단계별로 호출하세요.');
66
+ return lines.join('\n');
67
+ }
68
+ // ─── Play Store ──────────────────────────────────────────────────────
69
+ export async function buildPlayStoreReleasePlan(opts) {
70
+ const { auth, packageName, versionCode, track, language } = opts;
71
+ const steps = [];
72
+ // 1) 인증 + 트랙 / 빌드 상태 조회 (단일 edit session)
73
+ let trackInfo = {
74
+ hasTargetVersion: false,
75
+ };
76
+ let listingPresent = false;
77
+ try {
78
+ await withEdit(auth, packageName, async (editId) => {
79
+ const [tracks, listing] = await Promise.all([
80
+ publisher().edits.tracks.list({ auth, packageName, editId }).catch(() => null),
81
+ publisher().edits.listings.get({ auth, packageName, editId, language }).catch(() => null),
82
+ ]);
83
+ const targetTrack = tracks?.data?.tracks?.find((t) => t.track === track);
84
+ const release = targetTrack?.releases?.[0];
85
+ const codes = release?.versionCodes ?? [];
86
+ trackInfo = {
87
+ hasTargetVersion: versionCode ? codes.includes(versionCode) : codes.length > 0,
88
+ latestVersionCode: codes[codes.length - 1],
89
+ releaseStatus: release?.status ?? undefined,
90
+ };
91
+ listingPresent = !!listing?.data?.title;
92
+ return null;
93
+ });
94
+ steps.push({ state: 'done', title: 'Google Play 인증 + 트랙 조회 성공' });
95
+ }
96
+ catch (e) {
97
+ steps.push({
98
+ state: 'blocked',
99
+ title: 'Google Play 인증 또는 트랙 조회 실패',
100
+ detail: e instanceof Error ? e.message : String(e),
101
+ tool: 'mimi_seed_auth_status',
102
+ });
103
+ return renderPlan({ platform: 'Play Store', identity: packageName, version: versionCode ?? 'latest', track }, steps);
104
+ }
105
+ // 2) 빌드 도착 확인
106
+ if (trackInfo.hasTargetVersion) {
107
+ steps.push({
108
+ state: 'done',
109
+ title: `${track} 트랙에 versionCode ${versionCode ?? trackInfo.latestVersionCode} 도착 확인`,
110
+ detail: `현재 release status: ${trackInfo.releaseStatus ?? 'unknown'}`,
111
+ });
112
+ }
113
+ else {
114
+ steps.push({
115
+ state: 'blocked',
116
+ title: versionCode
117
+ ? `${track} 트랙에 versionCode ${versionCode} 없음`
118
+ : `${track} 트랙에 release 없음`,
119
+ detail: 'Gradle / Fastlane / Play Console 업로드를 먼저 완료하세요.',
120
+ tool: 'playstore_list_tracks',
121
+ });
122
+ }
123
+ // 3) 메타데이터 / 스크린샷 / 정책 위험 점검
124
+ const risks = await checkPlayStoreRisks(auth, packageName, language);
125
+ if (risks.length === 0) {
126
+ steps.push({
127
+ state: 'done',
128
+ title: '제출 위험 점검 통과 (블로커 0)',
129
+ });
130
+ }
131
+ else {
132
+ const blockers = risks.filter((r) => r.level === 'blocker');
133
+ const warnings = risks.filter((r) => r.level === 'warning');
134
+ steps.push({
135
+ state: blockers.length > 0 ? 'blocked' : 'pending',
136
+ title: `제출 위험 ${risks.length}건 (블로커 ${blockers.length} / 경고 ${warnings.length})`,
137
+ detail: '아래 항목을 개별 수정 후 다시 plan 호출 권장',
138
+ tool: 'playstore_check_submission_risks',
139
+ });
140
+ steps.push(...risksToSteps(blockers, 'playstore_update_listing'));
141
+ steps.push(...risksToSteps(warnings, 'playstore_update_listing'));
142
+ }
143
+ // 4) 릴리즈 노트
144
+ steps.push({
145
+ state: 'pending',
146
+ title: `릴리즈 노트 등록/갱신 (${language})`,
147
+ detail: 'AI 노트 초안 + 사용자 확인 후 적용',
148
+ tool: 'playstore_update_release_notes (또는 playstore_update_latest_release_notes)',
149
+ });
150
+ // 5) 제출
151
+ const canSubmit = !risks.some((r) => r.level === 'blocker') && trackInfo.hasTargetVersion;
152
+ steps.push({
153
+ state: canSubmit ? 'pending' : 'blocked',
154
+ title: `${track} 트랙 release status 변경 (draft → completed)`,
155
+ detail: canSubmit
156
+ ? '⚠️ 비가역에 가까움. 사용자 명시 동의 필수.'
157
+ : '블로커 해결 후 가능',
158
+ tool: 'playstore_submit_release',
159
+ irreversible: true,
160
+ });
161
+ return renderPlan({ platform: 'Play Store', identity: packageName, version: versionCode ?? 'latest', track }, steps);
162
+ }
163
+ // ─── App Store ────────────────────────────────────────────────────────
164
+ export async function buildAppStoreReleasePlan(opts) {
165
+ const { appId, versionString } = opts;
166
+ const steps = [];
167
+ // 1) 편집 가능한 버전 조회
168
+ let versionId;
169
+ let versionState;
170
+ let buildAttached = false;
171
+ try {
172
+ const versions = await apiGet(`/apps/${appId}/appStoreVersions`, {
173
+ 'filter[appStoreState]': 'PREPARE_FOR_SUBMISSION,WAITING_FOR_REVIEW,DEVELOPER_REJECTED',
174
+ ...(versionString ? { 'filter[versionString]': versionString } : {}),
175
+ limit: '5',
176
+ }).catch(() => null);
177
+ if (versions?.data?.length) {
178
+ versionId = versions.data[0].id;
179
+ versionState = versions.data[0].attributes?.appStoreState;
180
+ steps.push({
181
+ state: 'done',
182
+ title: `편집 가능한 버전 발견 (${versions.data[0].attributes?.versionString}, state=${versionState})`,
183
+ });
184
+ // build attached?
185
+ const build = await apiGet(`/appStoreVersions/${versionId}/build`).catch(() => null);
186
+ buildAttached = !!build?.data?.id;
187
+ if (buildAttached) {
188
+ steps.push({ state: 'done', title: '버전에 빌드 연결됨' });
189
+ }
190
+ else {
191
+ steps.push({
192
+ state: 'blocked',
193
+ title: '버전에 빌드 미연결',
194
+ detail: 'TestFlight 빌드 처리 완료 후 App Store Connect에서 연결하거나 API로 attach.',
195
+ tool: 'appstore_list_builds',
196
+ });
197
+ }
198
+ }
199
+ else {
200
+ steps.push({
201
+ state: 'blocked',
202
+ title: versionString
203
+ ? `편집 가능한 ${versionString} 버전 없음`
204
+ : '편집 가능한 버전 없음',
205
+ detail: 'App Store Connect에서 새 버전을 생성하세요.',
206
+ });
207
+ }
208
+ }
209
+ catch (e) {
210
+ steps.push({
211
+ state: 'blocked',
212
+ title: 'App Store 인증 또는 버전 조회 실패',
213
+ detail: e instanceof Error ? e.message : String(e),
214
+ tool: 'mimi_seed_auth_status',
215
+ });
216
+ return renderPlan({ platform: 'App Store', identity: appId, version: versionString ?? 'latest' }, steps);
217
+ }
218
+ // 2) 메타데이터 / 스크린샷 / 정책 위험 점검
219
+ const risks = await checkAppStoreRisks(appId);
220
+ if (risks.length === 0) {
221
+ steps.push({ state: 'done', title: '제출 위험 점검 통과 (블로커 0)' });
222
+ }
223
+ else {
224
+ const blockers = risks.filter((r) => r.level === 'blocker');
225
+ const warnings = risks.filter((r) => r.level === 'warning');
226
+ steps.push({
227
+ state: blockers.length > 0 ? 'blocked' : 'pending',
228
+ title: `제출 위험 ${risks.length}건 (블로커 ${blockers.length} / 경고 ${warnings.length})`,
229
+ tool: 'appstore_check_submission_risks',
230
+ });
231
+ steps.push(...risksToSteps(blockers, 'appstore_update_localization'));
232
+ steps.push(...risksToSteps(warnings, 'appstore_update_localization'));
233
+ }
234
+ // 3) What's New / promotional text / reviewer notes
235
+ steps.push({
236
+ state: 'pending',
237
+ title: "What's New (릴리즈 노트) 등록/갱신",
238
+ detail: '버전별 로컬라이제이션마다 입력',
239
+ tool: 'appstore_update_whats_new',
240
+ });
241
+ steps.push({
242
+ state: 'pending',
243
+ title: 'Promotional Text / Description / Keywords 검토',
244
+ tool: 'appstore_update_localization',
245
+ });
246
+ // 4) 제출
247
+ const canSubmit = versionId && buildAttached && !risks.some((r) => r.level === 'blocker');
248
+ steps.push({
249
+ state: canSubmit ? 'pending' : 'blocked',
250
+ title: '심사 제출 (reviewSubmissions v2)',
251
+ detail: canSubmit
252
+ ? '⚠️ 비가역. 제출 후 메타/빌드 변경 불가 (REJECTED 시 재편집).'
253
+ : '블로커 해결 후 가능',
254
+ tool: versionId ? `appstore_submit_for_review (versionId=${versionId})` : 'appstore_submit_for_review',
255
+ irreversible: true,
256
+ });
257
+ return renderPlan({ platform: 'App Store', identity: appId, version: versionString ?? 'latest' }, steps);
258
+ }
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ import * as appstore from './appstore/tools.js';
14
14
  import * as appstoreScreenshots from './appstore/screenshots.js';
15
15
  import * as iam from './iam/tools.js';
16
16
  import { checkPlayStoreRisks, checkAppStoreRisks, formatRisks } from './checks/risks.js';
17
+ import { buildPlayStoreReleasePlan, buildAppStoreReleasePlan } from './checks/plan.js';
17
18
  import { validateAppStoreScreenshots, validatePlayStoreScreenshots, formatValidationResults } from './checks/screenshots.js';
18
19
  import { generateReleaseNotesFromCommits, formatGeneratedNotes } from './ai/notes.js';
19
20
  import { generateReviewReply, formatReviewReply } from './ai/review.js';
@@ -704,6 +705,69 @@ server.tool('appstore_list_versions', 'App Store 버전 목록 (심사 상태
704
705
  const versions = await appstore.listVersions(appId);
705
706
  return { content: [{ type: 'text', text: JSON.stringify(versions, null, 2) }] };
706
707
  });
708
+ server.tool('appstore_create_version', [
709
+ 'App Store 새 버전 레코드 생성 — POST /v1/appStoreVersions.',
710
+ '새 versionString(예: "1.2.3")으로 PREPARE_FOR_SUBMISSION 상태의 버전을 만듦.',
711
+ 'buildId를 함께 주면 생성과 동시에 빌드 연결. 나중에 붙이려면 appstore_attach_build 사용.',
712
+ 'releaseType: MANUAL(개발자가 출시) / AFTER_APPROVAL(승인 후 자동) / SCHEDULED(earliestReleaseDate 필요).',
713
+ ].join(' '), {
714
+ appId: z.string().describe('App Store 앱 ID (appstore_list_apps 결과의 id, 숫자형)'),
715
+ versionString: z.string().describe('버전 문자열 (예: "1.2.3")'),
716
+ platform: z
717
+ .enum(['IOS', 'MAC_OS', 'TV_OS', 'VISION_OS'])
718
+ .default('IOS')
719
+ .describe('플랫폼 (기본 IOS)'),
720
+ copyright: z.string().optional().describe('저작권 표기 (예: "© 2026 Foo Inc.")'),
721
+ releaseType: z
722
+ .enum(['MANUAL', 'AFTER_APPROVAL', 'SCHEDULED'])
723
+ .optional()
724
+ .describe('출시 방식 (생략 시 Apple 기본값)'),
725
+ earliestReleaseDate: z
726
+ .string()
727
+ .optional()
728
+ .describe('SCHEDULED일 때 가장 빠른 출시 시각 (ISO 8601, 예: "2026-05-01T00:00:00Z")'),
729
+ buildId: z
730
+ .string()
731
+ .optional()
732
+ .describe('연결할 빌드 ID (appstore_list_builds 결과). 생략 시 버전만 생성하고 나중에 attach.'),
733
+ }, async ({ appId, versionString, platform, copyright, releaseType, earliestReleaseDate, buildId }) => {
734
+ const result = await appstore.createVersion({
735
+ appId,
736
+ versionString,
737
+ platform,
738
+ copyright,
739
+ releaseType,
740
+ earliestReleaseDate,
741
+ buildId,
742
+ });
743
+ return {
744
+ content: [
745
+ {
746
+ type: 'text',
747
+ text: `✅ 버전 ${versionString} (${platform}) 생성됨${buildId ? ` + 빌드 ${buildId} 연결됨` : ''}.\n\n${JSON.stringify(result, null, 2)}`,
748
+ },
749
+ ],
750
+ };
751
+ });
752
+ server.tool('appstore_attach_build', [
753
+ 'App Store 버전에 업로드된 빌드를 연결 — PATCH /v1/appStoreVersions/{id}/relationships/build.',
754
+ 'TestFlight에 업로드되어 processingState=VALID 상태인 빌드만 연결 가능.',
755
+ '편집 가능한 버전(PREPARE_FOR_SUBMISSION 등)에서만 변경됨.',
756
+ 'buildId는 appstore_list_builds 결과 사용.',
757
+ ].join(' '), {
758
+ versionId: z.string().describe('App Store 버전 ID (appstore_list_versions 또는 appstore_create_version 결과)'),
759
+ buildId: z.string().describe('빌드 ID (appstore_list_builds 결과)'),
760
+ }, async ({ versionId, buildId }) => {
761
+ const result = await appstore.attachBuildToVersion(versionId, buildId);
762
+ return {
763
+ content: [
764
+ {
765
+ type: 'text',
766
+ text: `✅ 빌드 ${buildId}가 버전 ${versionId}에 연결됐어.\n\n${JSON.stringify(result, null, 2)}`,
767
+ },
768
+ ],
769
+ };
770
+ });
707
771
  server.tool('appstore_get_metadata', 'App Store 버전 메타데이터 (설명문, 키워드, What\'s New)', { versionId: z.string().describe('버전 ID') }, async ({ versionId }) => {
708
772
  const localizations = await appstore.getVersionLocalizations(versionId);
709
773
  return { content: [{ type: 'text', text: JSON.stringify(localizations, null, 2) }] };
@@ -1042,6 +1106,42 @@ server.tool('appstore_delete_product', '⚠️ 비가역. App Store IAP 상품
1042
1106
  }
1043
1107
  return { content: [{ type: 'text', text: `✓ ${productId} 삭제 완료` }] };
1044
1108
  });
1109
+ // --- 배포 플랜 (read-only, AI에게 TodoWrite 지시) ---
1110
+ server.tool('playstore_plan_release', [
1111
+ 'Google Play 배포 플랜 — 인증/빌드/메타데이터/스크린샷/정책 위험을 한 번에 점검하고',
1112
+ '체크리스트로 반환합니다 (read-only, 아무것도 변경하지 않음).',
1113
+ '⚠️ AI 호출자 지시: 응답을 받으면 미완료 항목을 반드시 TodoWrite로 사용자에게 먼저 보여주고,',
1114
+ '사용자 동의 후 단계별로 기존 도구(playstore_update_listing, playstore_update_release_notes,',
1115
+ 'playstore_submit_release 등)를 호출하세요. submit_release(status=completed)는 비가역이므로 반드시 명시 동의 필요.',
1116
+ ].join(' '), {
1117
+ packageName: z.string().describe('Android 패키지명 (예: gg.pryzm.coffee)'),
1118
+ versionCode: z.string().optional().describe('확인할 versionCode. 미지정 시 트랙 최신 release 검사'),
1119
+ track: z.enum(['production', 'beta', 'alpha', 'internal']).optional().describe('대상 트랙 (기본: production)'),
1120
+ language: z.string().optional().describe('점검할 리스팅 언어 (기본: ko-KR)'),
1121
+ }, async ({ packageName, versionCode, track, language }) => {
1122
+ const auth = requirePlayStoreAuth();
1123
+ const text = await buildPlayStoreReleasePlan({
1124
+ auth,
1125
+ packageName,
1126
+ versionCode,
1127
+ track: track ?? 'production',
1128
+ language: language ?? 'ko-KR',
1129
+ });
1130
+ return { content: [{ type: 'text', text }] };
1131
+ });
1132
+ server.tool('appstore_plan_release', [
1133
+ 'App Store 배포 플랜 — 편집 가능한 버전/빌드 attach/메타/스크린샷/정책 위험을 한 번에 점검하고',
1134
+ '체크리스트로 반환합니다 (read-only).',
1135
+ '⚠️ AI 호출자 지시: 응답의 미완료 항목을 반드시 TodoWrite로 사용자에게 먼저 보여주고,',
1136
+ '사용자 동의 후 단계별로 기존 도구(appstore_update_localization, appstore_update_whats_new,',
1137
+ 'appstore_submit_for_review 등)를 호출하세요. submit_for_review는 비가역이므로 반드시 명시 동의 필요.',
1138
+ ].join(' '), {
1139
+ appId: z.string().describe('App Store 앱 ID (appstore_list_apps 결과의 id)'),
1140
+ versionString: z.string().optional().describe('대상 버전 (예: 1.3.0). 미지정 시 가장 최근 편집 가능 버전'),
1141
+ }, async ({ appId, versionString }) => {
1142
+ const text = await buildAppStoreReleasePlan({ appId, versionString });
1143
+ return { content: [{ type: 'text', text }] };
1144
+ });
1045
1145
  // --- App Store 심사 제출 (Submit for Review) ---
1046
1146
  server.tool('appstore_submit_for_review', [
1047
1147
  'App Store 버전을 심사에 제출 — 새 reviewSubmissions API 사용 (옛 /appStoreVersionSubmissions는 2024-01 deprecated).',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoonion/mimi-seed-mcp",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "Mimi Seed MCP server — Firebase + AdMob + Google Play + App Store management for Claude Code / Cursor / any MCP client.",
5
5
  "type": "module",
6
6
  "bin": {