@yoonion/mimi-seed-mcp 0.3.31 → 0.3.32
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/appstore/errors.js +2 -0
- package/dist/appstore/tools.d.ts +18 -0
- package/dist/appstore/tools.js +56 -1
- package/dist/registers/appstore.js +25 -0
- package/package.json +1 -1
package/dist/appstore/errors.js
CHANGED
|
@@ -27,6 +27,8 @@ const CODE_HINTS = {
|
|
|
27
27
|
FORBIDDEN_ERROR: 'API Key 권한 부족 — App Store Connect > Users and Access 에서 키 role 확인.',
|
|
28
28
|
// submit 직후 cancel 시도 (큐 진입 후)
|
|
29
29
|
STATE_ERROR: '현재 상태에서 허용되지 않는 작업. cancel_review 라면 큐 진입 후라서 불가 — 새 versionString 으로 우회.',
|
|
30
|
+
// JWT 거부 — 잘못된 자격증명
|
|
31
|
+
NOT_AUTHORIZED: 'API 키가 거부됐어요 — issuerId/keyId/.p8 조합 불일치 가능. appstore_verify_credentials 로 진단하고 mimi-seed auth appstore 로 재등록.',
|
|
30
32
|
};
|
|
31
33
|
/** Apple 표준 에러 응답 파싱 시도. 실패하면 null 반환. */
|
|
32
34
|
function parseAppleErrorBody(body) {
|
package/dist/appstore/tools.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
export declare function apiGet(path: string, params?: Record<string, string>): Promise<any>;
|
|
2
|
+
export interface AppStoreVerifyResult {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
stage: 'creds' | 'sign' | 'auth' | 'api' | 'done';
|
|
5
|
+
message: string;
|
|
6
|
+
httpStatus?: number;
|
|
7
|
+
appCount?: number;
|
|
8
|
+
firstApp?: {
|
|
9
|
+
id: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* appstore.json 자격증명 유효성 단계별 검증 (creds → sign → auth → api).
|
|
15
|
+
* playstore_verify_service_account 의 App Store 대응. 읽기 전용 — GET /apps?limit=1.
|
|
16
|
+
* 파일 존재만 보는 requireAppStoreCreds 와 달리, 잘못된 .p8/keyId/issuerId 를
|
|
17
|
+
* "첫 호출 401" 로 늦게 터지기 전에 setup 단계에서 잡아준다.
|
|
18
|
+
*/
|
|
19
|
+
export declare function verifyAppStoreCredentials(): Promise<AppStoreVerifyResult>;
|
|
2
20
|
export declare function listApps(): Promise<any>;
|
|
3
21
|
export declare function getApp(appId: string): Promise<any>;
|
|
4
22
|
export declare function listVersions(appId: string): Promise<any>;
|
package/dist/appstore/tools.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getAuthHeaders } from './auth.js';
|
|
1
|
+
import { getAuthHeaders, getAppStoreCredentials, generateToken } from './auth.js';
|
|
2
2
|
import { friendlyAppStoreError } from './errors.js';
|
|
3
3
|
/**
|
|
4
4
|
* App Store Connect API v1 래퍼
|
|
@@ -29,6 +29,61 @@ export async function apiGet(path, params) {
|
|
|
29
29
|
}
|
|
30
30
|
return res.json();
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* appstore.json 자격증명 유효성 단계별 검증 (creds → sign → auth → api).
|
|
34
|
+
* playstore_verify_service_account 의 App Store 대응. 읽기 전용 — GET /apps?limit=1.
|
|
35
|
+
* 파일 존재만 보는 requireAppStoreCreds 와 달리, 잘못된 .p8/keyId/issuerId 를
|
|
36
|
+
* "첫 호출 401" 로 늦게 터지기 전에 setup 단계에서 잡아준다.
|
|
37
|
+
*/
|
|
38
|
+
export async function verifyAppStoreCredentials() {
|
|
39
|
+
const creds = getAppStoreCredentials();
|
|
40
|
+
if (!creds) {
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
stage: 'creds',
|
|
44
|
+
message: '~/.mimi-seed/appstore.json 이 없습니다. `mimi-seed auth appstore` 로 등록하세요.',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
let token;
|
|
48
|
+
try {
|
|
49
|
+
token = await generateToken(creds);
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
stage: 'sign',
|
|
55
|
+
message: `JWT 서명 실패 — .p8 privateKey / keyId 확인 필요.\n${e.message}`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
let res;
|
|
59
|
+
try {
|
|
60
|
+
res = await fetch(`${BASE}/apps?limit=1`, { headers: { Authorization: `Bearer ${token}` } });
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
return { ok: false, stage: 'api', message: `App Store API 연결 실패: ${e.message}` };
|
|
64
|
+
}
|
|
65
|
+
if (res.status === 401 || res.status === 403) {
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
stage: 'auth',
|
|
69
|
+
httpStatus: res.status,
|
|
70
|
+
message: 'Apple이 인증을 거부했어요 — issuerId / keyId / .p8 조합이 틀렸거나 키 권한이 부족합니다.\n`mimi-seed auth appstore` 로 재등록하세요.',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
const body = await res.text();
|
|
75
|
+
return { ok: false, stage: 'api', httpStatus: res.status, message: `App Store API ${res.status}: ${body.slice(0, 200)}` };
|
|
76
|
+
}
|
|
77
|
+
const data = (await res.json());
|
|
78
|
+
const first = data.data?.[0];
|
|
79
|
+
return {
|
|
80
|
+
ok: true,
|
|
81
|
+
stage: 'done',
|
|
82
|
+
message: '인증 유효',
|
|
83
|
+
appCount: data.meta?.paging?.total,
|
|
84
|
+
firstApp: first ? { id: first.id, name: first.attributes?.name } : undefined,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
32
87
|
async function apiPatch(path, body) {
|
|
33
88
|
const headers = await getAuthHeaders();
|
|
34
89
|
if (!headers)
|
|
@@ -10,6 +10,31 @@ export function registerAppstoreTools(server) {
|
|
|
10
10
|
const apps = await appstore.listApps();
|
|
11
11
|
return { content: [{ type: 'text', text: JSON.stringify(apps, null, 2) }] };
|
|
12
12
|
});
|
|
13
|
+
server.tool('appstore_verify_credentials', 'App Store Connect API 키(appstore.json) 유효성 검증 — JWT 서명 + GET /apps 호출로 creds/sign/auth/api 단계별 진단. 첫 도구 호출에서 401로 늦게 터지기 전에 setup 직후 확인용. 인자 없음.', {}, async () => {
|
|
14
|
+
const r = await appstore.verifyAppStoreCredentials();
|
|
15
|
+
if (r.ok) {
|
|
16
|
+
return {
|
|
17
|
+
content: [{
|
|
18
|
+
type: 'text',
|
|
19
|
+
text: [
|
|
20
|
+
'✓ App Store Connect 인증 유효',
|
|
21
|
+
r.appCount != null ? ` 접근 가능 앱: ${r.appCount}개` : '',
|
|
22
|
+
r.firstApp ? ` 예: ${r.firstApp.name ?? r.firstApp.id}` : '',
|
|
23
|
+
].filter(Boolean).join('\n'),
|
|
24
|
+
}],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
content: [{
|
|
29
|
+
type: 'text',
|
|
30
|
+
text: [
|
|
31
|
+
`✗ 검증 실패 (stage: ${r.stage}${r.httpStatus ? `, HTTP ${r.httpStatus}` : ''})`,
|
|
32
|
+
'',
|
|
33
|
+
r.message,
|
|
34
|
+
].join('\n'),
|
|
35
|
+
}],
|
|
36
|
+
};
|
|
37
|
+
});
|
|
13
38
|
server.tool('appstore_get_app', 'App Store Connect 앱 상세 정보', { appId: z.string().describe('앱 ID (숫자)') }, async ({ appId }) => {
|
|
14
39
|
const app = await appstore.getApp(appId);
|
|
15
40
|
return { content: [{ type: 'text', text: JSON.stringify(app, null, 2) }] };
|
package/package.json
CHANGED