@yoonion/mimi-seed-mcp 0.9.2 → 0.9.4
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/auth/google-auth.js +4 -0
- package/dist/ga4/cli.js +3 -1
- package/dist/ga4/tools.d.ts +10 -1
- package/dist/ga4/tools.js +10 -1
- package/dist/playstore/tools.js +9 -2
- package/dist/registers/ga4.js +2 -1
- package/dist/registers/playstore.js +1 -1
- package/package.json +1 -1
package/dist/auth/google-auth.js
CHANGED
|
@@ -15,6 +15,10 @@ const SCOPES = [
|
|
|
15
15
|
'https://www.googleapis.com/auth/adwords', // Google Ads API (googleads_* tools)
|
|
16
16
|
'https://www.googleapis.com/auth/webmasters', // Search Console API (gsc_* tools, 사이트맵 제출 포함)
|
|
17
17
|
'https://www.googleapis.com/auth/analytics.edit', // GA4 Analytics Admin API (ga4_* tools — property/data stream 생성·조회)
|
|
18
|
+
// GA4 **Data API**(analyticsdata — ga4_run_report)는 analytics.edit 을 받지 않는다.
|
|
19
|
+
// Admin API 전용 스코프이기 때문. 이게 빠져 있으면 property 목록은 보이는데 리포트만
|
|
20
|
+
// 403 으로 막혀서, "API 가 꺼졌나" 로 오진하기 쉽다. (2026-07 실사고)
|
|
21
|
+
'https://www.googleapis.com/auth/analytics.readonly',
|
|
18
22
|
];
|
|
19
23
|
// Primary config dir. Legacy `~/.preseed` is read as a fallback during the
|
|
20
24
|
// rebrand so existing auth sessions don't force a re-login; new writes go to
|
package/dist/ga4/cli.js
CHANGED
|
@@ -53,7 +53,9 @@ runDomainCli({
|
|
|
53
53
|
bundleId: flag(p, 'bundle'),
|
|
54
54
|
}),
|
|
55
55
|
streams: async (p) => ga4.listDataStreams(await requireAuth(ga4.GA4_SCOPE), requireFlag(p, 'property')),
|
|
56
|
-
report: async (p) =>
|
|
56
|
+
report: async (p) =>
|
|
57
|
+
// Data API 는 analytics.readonly — Admin 스코프(analytics.edit)로는 403 이다
|
|
58
|
+
ga4.runReport(await requireAuth(ga4.GA4_DATA_SCOPE), requireFlag(p, 'property'), {
|
|
57
59
|
startDate: requireFlag(p, 'start'),
|
|
58
60
|
endDate: requireFlag(p, 'end'),
|
|
59
61
|
dimensions: flagList(p, 'dimensions'),
|
package/dist/ga4/tools.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import type { OAuth2Client } from 'google-auth-library';
|
|
2
|
-
/**
|
|
2
|
+
/** Admin API(analyticsadmin — property/data stream 생성·조회)가 요구하는 스코프. */
|
|
3
3
|
export declare const GA4_SCOPE = "https://www.googleapis.com/auth/analytics.edit";
|
|
4
|
+
/**
|
|
5
|
+
* Data API(analyticsdata — runReport)가 요구하는 스코프.
|
|
6
|
+
*
|
|
7
|
+
* ⚠️ analytics.edit 은 **Admin API 전용**이라 Data API 가 받아주지 않는다. 둘을 같은
|
|
8
|
+
* 스코프로 묶으면 pre-flight 는 통과시켜 놓고 구글이 403 을 던져서, "API 가 꺼졌나"로
|
|
9
|
+
* 오진하게 된다 — property 목록은 멀쩡히 보이는데 리포트만 막히는 형태라 더 헷갈린다.
|
|
10
|
+
* (2026-07 실사고: 맑음 D1 지표를 뽑으려다 Analytics API 활성화 문제로 오인.)
|
|
11
|
+
*/
|
|
12
|
+
export declare const GA4_DATA_SCOPE = "https://www.googleapis.com/auth/analytics.readonly";
|
|
4
13
|
export type Ga4Auth = OAuth2Client;
|
|
5
14
|
export type DataStreamPlatform = 'web' | 'android' | 'ios';
|
|
6
15
|
/** '123' | 'accounts/123' → 'accounts/123' */
|
package/dist/ga4/tools.js
CHANGED
|
@@ -11,8 +11,17 @@ import { google } from 'googleapis';
|
|
|
11
11
|
*/
|
|
12
12
|
const admin = () => google.analyticsadmin('v1beta');
|
|
13
13
|
const data = () => google.analyticsdata('v1beta');
|
|
14
|
-
/**
|
|
14
|
+
/** Admin API(analyticsadmin — property/data stream 생성·조회)가 요구하는 스코프. */
|
|
15
15
|
export const GA4_SCOPE = 'https://www.googleapis.com/auth/analytics.edit';
|
|
16
|
+
/**
|
|
17
|
+
* Data API(analyticsdata — runReport)가 요구하는 스코프.
|
|
18
|
+
*
|
|
19
|
+
* ⚠️ analytics.edit 은 **Admin API 전용**이라 Data API 가 받아주지 않는다. 둘을 같은
|
|
20
|
+
* 스코프로 묶으면 pre-flight 는 통과시켜 놓고 구글이 403 을 던져서, "API 가 꺼졌나"로
|
|
21
|
+
* 오진하게 된다 — property 목록은 멀쩡히 보이는데 리포트만 막히는 형태라 더 헷갈린다.
|
|
22
|
+
* (2026-07 실사고: 맑음 D1 지표를 뽑으려다 Analytics API 활성화 문제로 오인.)
|
|
23
|
+
*/
|
|
24
|
+
export const GA4_DATA_SCOPE = 'https://www.googleapis.com/auth/analytics.readonly';
|
|
16
25
|
// ─── 경로 정규화 (순수 함수 — 테스트 대상) ───
|
|
17
26
|
/** '123' | 'accounts/123' → 'accounts/123' */
|
|
18
27
|
export function normalizeAccountName(accountId) {
|
package/dist/playstore/tools.js
CHANGED
|
@@ -141,13 +141,20 @@ export async function getListing(auth, packageName, language = 'ko-KR') {
|
|
|
141
141
|
}
|
|
142
142
|
// ─── 스토어 리스팅 수정 ───
|
|
143
143
|
export async function updateListing(auth, packageName, language, data) {
|
|
144
|
+
// ⚠️ edits.listings.update 는 리소스를 **통째로 교체**한다(PUT). requestBody 에 없는 필드는
|
|
145
|
+
// 지워진다 — 특히 `video`(스토어 프로모 영상)는 이 함수의 시그니처에 아예 없으므로,
|
|
146
|
+
// "제목만 바꾸기"가 조용히 앱의 프로모 영상을 삭제한다. patch 는 넘긴 필드만 부분 갱신한다.
|
|
147
|
+
//
|
|
148
|
+
// undefined 를 걸러내는 이유: 호출자가 title 만 준 경우 { shortDescription: undefined } 가
|
|
149
|
+
// 그대로 실려 나가면 patch 라도 해당 필드를 비우려는 의도로 해석될 여지가 있다.
|
|
150
|
+
const requestBody = Object.fromEntries(Object.entries(data).filter(([, v]) => v !== undefined));
|
|
144
151
|
return withEdit(auth, packageName, async (editId) => {
|
|
145
|
-
const updated = await publisher().edits.listings.
|
|
152
|
+
const updated = await publisher().edits.listings.patch({
|
|
146
153
|
auth,
|
|
147
154
|
packageName,
|
|
148
155
|
editId,
|
|
149
156
|
language,
|
|
150
|
-
requestBody
|
|
157
|
+
requestBody,
|
|
151
158
|
});
|
|
152
159
|
return updated.data;
|
|
153
160
|
}, true);
|
package/dist/registers/ga4.js
CHANGED
|
@@ -78,7 +78,8 @@ export function registerGa4Tools(server) {
|
|
|
78
78
|
dimensions: z.string().optional().describe('쉼표 구분 dimension (예: date,country). 생략 시 전체 합계'),
|
|
79
79
|
metrics: z.string().optional().describe('쉼표 구분 metric (기본 activeUsers,eventCount)'),
|
|
80
80
|
}, async ({ propertyId, startDate, endDate, dimensions, metrics }) => {
|
|
81
|
-
|
|
81
|
+
// Data API 는 analytics.readonly 를 요구한다 — Admin 스코프로 검사하면 통과시켜 놓고 403 이 난다
|
|
82
|
+
const auth = await requireAuth(ga4Raw.GA4_DATA_SCOPE);
|
|
82
83
|
const report = await ga4.runReport(auth, propertyId, {
|
|
83
84
|
startDate,
|
|
84
85
|
endDate,
|
|
@@ -59,7 +59,7 @@ export function registerPlaystoreTools(server) {
|
|
|
59
59
|
const listing = await playstore.getListing(auth, packageName, language);
|
|
60
60
|
return { content: [{ type: 'text', text: JSON.stringify(listing, null, 2) }] };
|
|
61
61
|
});
|
|
62
|
-
server.tool('playstore_update_listing', 'Google Play 스토어 리스팅 수정 (제목, 설명문 변경). ⚠️ Play API 편집과 Console 동시 편집은 충돌 — Console에서 같은 리스팅을 편집·미게시 중이면 그 변경이 덮어써질 수 있음.', {
|
|
62
|
+
server.tool('playstore_update_listing', 'Google Play 스토어 리스팅 수정 (제목, 설명문 변경). 넘긴 필드만 부분 갱신(edits.listings.patch) — 생략한 필드와 프로모 영상(video)은 보존된다. ⚠️ Play API 편집과 Console 동시 편집은 충돌 — Console에서 같은 리스팅을 편집·미게시 중이면 그 변경이 덮어써질 수 있음.', {
|
|
63
63
|
packageName: z.string().describe('패키지명'),
|
|
64
64
|
language: z.string().describe('언어 코드 (예: ko-KR, en-US)'),
|
|
65
65
|
title: z.string().optional().describe('앱 제목 (30자 이내)'),
|
package/package.json
CHANGED