@yoonion/mimi-seed-mcp 0.14.0 → 0.14.1
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/admob/cli.js +2 -1
- package/dist/appstore/auth.js +1 -1
- package/dist/appstore/setup-cli.js +4 -1
- package/dist/auth/bigquery-setup-cli.js +4 -1
- package/dist/auth/constants.js +2 -2
- package/dist/auth/google-auth.js +3 -3
- package/dist/auth/playstore-setup-cli.js +4 -1
- package/dist/checks/plan.js +5 -6
- package/dist/firebase/cli.js +2 -1
- package/dist/ga4/cli.js +2 -1
- package/dist/lib/google-errors.js +4 -2
- package/dist/lib/mcp-response.d.ts +10 -0
- package/dist/lib/mcp-response.js +26 -0
- package/dist/registers/admob.js +8 -7
- package/dist/registers/appstore.js +56 -89
- package/dist/registers/auth.js +5 -19
- package/dist/registers/bigquery.js +5 -4
- package/dist/registers/checks.js +2 -1
- package/dist/registers/ci.js +6 -25
- package/dist/registers/facebook.js +3 -7
- package/dist/registers/firebase.js +20 -19
- package/dist/registers/ga4.js +9 -8
- package/dist/registers/googleads.js +4 -13
- package/dist/registers/gsc.js +6 -10
- package/dist/registers/iam.js +3 -2
- package/dist/registers/instagram.js +2 -1
- package/dist/registers/jenkins.js +13 -42
- package/dist/registers/playstore.js +43 -62
- package/dist/registers/threads.js +3 -7
- package/dist/social/profile-store.js +1 -1
- package/dist/video/providers.js +3 -1
- package/package.json +8 -4
package/dist/admob/cli.js
CHANGED
|
@@ -27,7 +27,8 @@ const HELP = `
|
|
|
27
27
|
인증: npx -y @yoonion/mimi-seed-mcp mimi-seed-auth
|
|
28
28
|
`;
|
|
29
29
|
const AD_FORMATS = ['BANNER', 'INTERSTITIAL', 'REWARDED', 'REWARDED_INTERSTITIAL', 'APP_OPEN', 'NATIVE'];
|
|
30
|
-
runDomainCli
|
|
30
|
+
// runDomainCli 는 내부에서 모든 예외를 잡고 항상 process.exit 한다.
|
|
31
|
+
void runDomainCli({
|
|
31
32
|
binName: 'mimi-seed-admob',
|
|
32
33
|
argv: process.argv.slice(2),
|
|
33
34
|
help: HELP,
|
package/dist/appstore/auth.js
CHANGED
|
@@ -47,7 +47,7 @@ export async function generateToken(creds) {
|
|
|
47
47
|
key = await importPKCS8(normalizedKey, 'ES256');
|
|
48
48
|
}
|
|
49
49
|
catch (err) {
|
|
50
|
-
throw new Error(`App Store 개인 키 파싱 실패 — ~/.mimi-seed/appstore.json의 privateKey 형식 확인 필요.\n원인: ${err.message}
|
|
50
|
+
throw new Error(`App Store 개인 키 파싱 실패 — ~/.mimi-seed/appstore.json의 privateKey 형식 확인 필요.\n원인: ${err.message}`, { cause: err });
|
|
51
51
|
}
|
|
52
52
|
// Subtract 60s from iat to tolerate local clock running slightly ahead of Apple servers.
|
|
53
53
|
const iat = Math.floor(Date.now() / 1000) - 60;
|
package/dist/auth/constants.js
CHANGED
|
@@ -25,7 +25,7 @@ export async function getMcpOAuthClient() {
|
|
|
25
25
|
}
|
|
26
26
|
catch (e) {
|
|
27
27
|
const detail = e instanceof Error ? e.message : String(e);
|
|
28
|
-
throw new Error(`mcp-auth-config fetch failed (${WEB_BASE} 접속 불가: ${detail})
|
|
28
|
+
throw new Error(`mcp-auth-config fetch failed (${WEB_BASE} 접속 불가: ${detail})`, { cause: e });
|
|
29
29
|
}
|
|
30
30
|
if (!res.ok)
|
|
31
31
|
throw new Error(`mcp-auth-config fetch failed (${res.status})`);
|
|
@@ -37,7 +37,7 @@ export async function getMcpOAuthClient() {
|
|
|
37
37
|
}
|
|
38
38
|
catch (e) {
|
|
39
39
|
const detail = e instanceof Error ? e.message : String(e);
|
|
40
|
-
throw new Error(`mcp-auth-config fetch failed (응답이 JSON 이 아님: ${detail})
|
|
40
|
+
throw new Error(`mcp-auth-config fetch failed (응답이 JSON 이 아님: ${detail})`, { cause: e });
|
|
41
41
|
}
|
|
42
42
|
// 빈 값 200 응답(서버 env 미설정)을 캐싱하면 프로세스 수명 내내 invalid_client 로 오진된다.
|
|
43
43
|
if (!data || typeof data !== 'object' || !data.clientId || !data.clientSecret) {
|
package/dist/auth/google-auth.js
CHANGED
|
@@ -17,9 +17,6 @@ const LEGACY_TOKEN_DIR = path.join(os.homedir(), '.preseed');
|
|
|
17
17
|
const TOKEN_PATH = path.join(TOKEN_DIR, 'tokens.json');
|
|
18
18
|
const LEGACY_TOKEN_PATH = path.join(LEGACY_TOKEN_DIR, 'tokens.json');
|
|
19
19
|
const CREDENTIALS_PATH = path.join(TOKEN_DIR, 'credentials.json');
|
|
20
|
-
// Default OAuth client for development — users should replace with their own
|
|
21
|
-
const DEFAULT_CLIENT_ID = ''; // Will be set during auth setup
|
|
22
|
-
const DEFAULT_CLIENT_SECRET = '';
|
|
23
20
|
export function getStoredCredentials() {
|
|
24
21
|
if (!fs.existsSync(CREDENTIALS_PATH))
|
|
25
22
|
return null;
|
|
@@ -142,6 +139,9 @@ export function startAuth(clientId, clientSecret, options = {}) {
|
|
|
142
139
|
});
|
|
143
140
|
const wait = new Promise((resolve, reject) => {
|
|
144
141
|
const rejectAuth = (e) => reject(new AuthError(classifyError(e, { phase: 'login' })));
|
|
142
|
+
// 핸들러 전체가 try/catch 로 감싸여 있고 모든 경로가 응답 후 rejectAuth 로 끝난다 —
|
|
143
|
+
// createServer 가 void 를 기대하지만 여기서 새어 나갈 rejection 은 없다.
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
145
145
|
const server = http.createServer(async (req, res) => {
|
|
146
146
|
try {
|
|
147
147
|
const url = new URL(req.url, `http://localhost:9876`);
|
package/dist/checks/plan.js
CHANGED
|
@@ -73,13 +73,13 @@ export async function buildPlayStoreReleasePlan(opts) {
|
|
|
73
73
|
let trackInfo = {
|
|
74
74
|
hasTargetVersion: false,
|
|
75
75
|
};
|
|
76
|
-
let listingPresent = false;
|
|
77
76
|
try {
|
|
78
77
|
await withEdit(auth, packageName, async (editId) => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
// 리스팅 존재 여부는 checkPlayStoreRisks 가 따로 본다. 예전엔 여기서도 한 번 더
|
|
79
|
+
// 받아 listingPresent 에 담았지만 그 값을 읽는 곳이 없어 edit 세션마다 낭비였다.
|
|
80
|
+
const tracks = await publisher()
|
|
81
|
+
.edits.tracks.list({ auth, packageName, editId })
|
|
82
|
+
.catch(() => null);
|
|
83
83
|
const targetTrack = tracks?.data?.tracks?.find((t) => t.track === track);
|
|
84
84
|
const release = targetTrack?.releases?.[0];
|
|
85
85
|
const codes = release?.versionCodes ?? [];
|
|
@@ -88,7 +88,6 @@ export async function buildPlayStoreReleasePlan(opts) {
|
|
|
88
88
|
latestVersionCode: codes[codes.length - 1],
|
|
89
89
|
releaseStatus: release?.status ?? undefined,
|
|
90
90
|
};
|
|
91
|
-
listingPresent = !!listing?.data?.title;
|
|
92
91
|
return null;
|
|
93
92
|
});
|
|
94
93
|
steps.push({ state: 'done', title: 'Google Play 인증 + 트랙 조회 성공' });
|
package/dist/firebase/cli.js
CHANGED
|
@@ -62,7 +62,8 @@ async function getConfig(projectId, appId, platform) {
|
|
|
62
62
|
return firebase.getWebConfig(auth, projectId, appId);
|
|
63
63
|
throw new CliUsageError('--platform 은 android | ios | web 중 하나여야 합니다.');
|
|
64
64
|
}
|
|
65
|
-
runDomainCli
|
|
65
|
+
// runDomainCli 는 내부에서 모든 예외를 잡고 항상 process.exit 한다.
|
|
66
|
+
void runDomainCli({
|
|
66
67
|
binName: 'mimi-seed-firebase',
|
|
67
68
|
argv: process.argv.slice(2),
|
|
68
69
|
help: HELP,
|
package/dist/ga4/cli.js
CHANGED
|
@@ -32,7 +32,8 @@ const HELP = `
|
|
|
32
32
|
⚠️ analytics.edit 스코프 필요 — 처음이면 재로그인:
|
|
33
33
|
npx -y @yoonion/mimi-seed-mcp mimi-seed-auth
|
|
34
34
|
`;
|
|
35
|
-
runDomainCli
|
|
35
|
+
// runDomainCli 는 내부에서 모든 예외를 잡고 항상 process.exit 한다.
|
|
36
|
+
void runDomainCli({
|
|
36
37
|
binName: 'mimi-seed-ga4',
|
|
37
38
|
argv: process.argv.slice(2),
|
|
38
39
|
help: HELP,
|
|
@@ -39,8 +39,10 @@ export function googleErrorDetail(e) {
|
|
|
39
39
|
const any = e;
|
|
40
40
|
const apiErr = any.response?.data?.error;
|
|
41
41
|
const parts = [];
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
// Google 이 message 에 객체를 넣어 보내는 경우가 있다 — String() 이면 '[object Object]'.
|
|
43
|
+
if (apiErr?.message) {
|
|
44
|
+
parts.push(typeof apiErr.message === 'string' ? apiErr.message : JSON.stringify(apiErr.message));
|
|
45
|
+
}
|
|
44
46
|
const reasons = apiErr?.errors ?? any.errors;
|
|
45
47
|
if (Array.isArray(reasons)) {
|
|
46
48
|
for (const r of reasons) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
/** 구조화된 값을 그대로 JSON 으로 반환한다. */
|
|
3
|
+
export declare function jsonResult(value: unknown): CallToolResult;
|
|
4
|
+
/**
|
|
5
|
+
* 사람이 읽을 텍스트 응답.
|
|
6
|
+
*
|
|
7
|
+
* 배열을 주면 개행으로 합친다. 빈 문자열은 **빈 줄로 남는다** — 조건부 줄을
|
|
8
|
+
* 없애고 싶으면 호출부에서 `.filter(Boolean)` 을 명시할 것.
|
|
9
|
+
*/
|
|
10
|
+
export declare function textResult(text: string | readonly string[]): CallToolResult;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// 도구 핸들러 응답 래퍼.
|
|
2
|
+
//
|
|
3
|
+
// register 파일들은 `{ content: [{ type: 'text', text: … }] }` 를 250번 되풀이하고
|
|
4
|
+
// 있었다 — 그중 66곳은 `JSON.stringify(x, null, 2)` 한 줄로 완전히 동일하다.
|
|
5
|
+
// 그 껍데기가 register 를 두껍게 만들어 "register 는 얇게" 규칙이 지켜지는지
|
|
6
|
+
// 눈으로 확인할 수 없게 했다.
|
|
7
|
+
//
|
|
8
|
+
// 의도적으로 두 개만 둔다. 줄 배열을 어떻게 합칠지(빈 줄을 남길지 `.filter(Boolean)`
|
|
9
|
+
// 으로 버릴지)는 **호출부의 의미**다 — 헬퍼가 대신 정하면 빈 줄을 의도한 곳과
|
|
10
|
+
// 조건부 줄을 버리려는 곳이 조용히 뒤바뀐다. 그래서 합치는 것까지만 돕고
|
|
11
|
+
// 필터링은 호출부에 남긴다.
|
|
12
|
+
/** 구조화된 값을 그대로 JSON 으로 반환한다. */
|
|
13
|
+
export function jsonResult(value) {
|
|
14
|
+
return { content: [{ type: 'text', text: JSON.stringify(value, null, 2) }] };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 사람이 읽을 텍스트 응답.
|
|
18
|
+
*
|
|
19
|
+
* 배열을 주면 개행으로 합친다. 빈 문자열은 **빈 줄로 남는다** — 조건부 줄을
|
|
20
|
+
* 없애고 싶으면 호출부에서 `.filter(Boolean)` 을 명시할 것.
|
|
21
|
+
*/
|
|
22
|
+
export function textResult(text) {
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: 'text', text: typeof text === 'string' ? text : text.join('\n') }],
|
|
25
|
+
};
|
|
26
|
+
}
|
package/dist/registers/admob.js
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import * as admob from '../admob/tools.js';
|
|
3
3
|
import { requireAuth } from '../helpers.js';
|
|
4
|
+
import { jsonResult } from '../lib/mcp-response.js';
|
|
4
5
|
export function registerAdmobTools(server) {
|
|
5
6
|
server.tool('admob_list_accounts', 'AdMob 계정 목록 조회', {}, async () => {
|
|
6
7
|
const auth = await requireAuth();
|
|
7
8
|
const accounts = await admob.listAccounts(auth);
|
|
8
|
-
return
|
|
9
|
+
return jsonResult(accounts);
|
|
9
10
|
});
|
|
10
11
|
server.tool('admob_list_apps', 'AdMob에 등록된 앱 목록', { accountId: z.string().describe('AdMob 계정 ID (예: accounts/pub-XXXX)') }, async ({ accountId }) => {
|
|
11
12
|
const auth = await requireAuth();
|
|
12
13
|
const apps = await admob.listApps(auth, accountId);
|
|
13
|
-
return
|
|
14
|
+
return jsonResult(apps);
|
|
14
15
|
});
|
|
15
16
|
server.tool('admob_list_ad_units', 'AdMob 광고 단위 목록', { accountId: z.string().describe('AdMob 계정 ID') }, async ({ accountId }) => {
|
|
16
17
|
const auth = await requireAuth();
|
|
17
18
|
const units = await admob.listAdUnits(auth, accountId);
|
|
18
|
-
return
|
|
19
|
+
return jsonResult(units);
|
|
19
20
|
});
|
|
20
21
|
server.tool('admob_get_today_earnings', '오늘 AdMob 수익 요약', { accountId: z.string().describe('AdMob 계정 ID') }, async ({ accountId }) => {
|
|
21
22
|
const auth = await requireAuth();
|
|
22
23
|
const report = await admob.getTodayEarnings(auth, accountId);
|
|
23
|
-
return
|
|
24
|
+
return jsonResult(report);
|
|
24
25
|
});
|
|
25
26
|
server.tool('admob_get_report', 'AdMob 수익 리포트 (기간 지정)', {
|
|
26
27
|
accountId: z.string().describe('AdMob 계정 ID'),
|
|
@@ -33,7 +34,7 @@ export function registerAdmobTools(server) {
|
|
|
33
34
|
}, async ({ accountId, startYear, startMonth, startDay, endYear, endMonth, endDay }) => {
|
|
34
35
|
const auth = await requireAuth();
|
|
35
36
|
const report = await admob.getNetworkReport(auth, accountId, { year: startYear, month: startMonth, day: startDay }, { year: endYear, month: endMonth, day: endDay });
|
|
36
|
-
return
|
|
37
|
+
return jsonResult(report);
|
|
37
38
|
});
|
|
38
39
|
server.tool('admob_create_app', 'AdMob에 새 앱 등록 (v1beta — Limited Access). appStoreId 를 주면 스토어 게시 앱과 링크(Android=패키지명, iOS=App Store 숫자 ID), 없으면 manual(unlinked) 앱.', {
|
|
39
40
|
accountId: z.string().describe('AdMob 계정 ID'),
|
|
@@ -44,7 +45,7 @@ export function registerAdmobTools(server) {
|
|
|
44
45
|
const auth = await requireAuth();
|
|
45
46
|
try {
|
|
46
47
|
const result = await admob.createApp(auth, accountId, platform, displayName, appStoreId);
|
|
47
|
-
return
|
|
48
|
+
return jsonResult(result);
|
|
48
49
|
}
|
|
49
50
|
catch (err) {
|
|
50
51
|
if (err.code === 403) {
|
|
@@ -75,7 +76,7 @@ export function registerAdmobTools(server) {
|
|
|
75
76
|
const auth = await requireAuth();
|
|
76
77
|
try {
|
|
77
78
|
const result = await admob.createAdUnit(auth, accountId, appId, displayName, adFormat);
|
|
78
|
-
return
|
|
79
|
+
return jsonResult(result);
|
|
79
80
|
}
|
|
80
81
|
catch (err) {
|
|
81
82
|
if (err.code === 403) {
|