@yoonion/mimi-seed-mcp 0.3.14 → 0.3.15
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/tools.js +5 -9
- package/dist/bigquery/tools.d.ts +29 -0
- package/dist/bigquery/tools.js +53 -0
- package/dist/index.js +65 -0
- package/package.json +1 -1
package/dist/appstore/tools.js
CHANGED
|
@@ -395,20 +395,16 @@ async function getVersionAppAndPlatform(versionId) {
|
|
|
395
395
|
return { appId, platform };
|
|
396
396
|
}
|
|
397
397
|
async function findOpenReviewSubmission(appId, platform) {
|
|
398
|
+
// ASC API는 filter[state]=CREATED를 더 이상 허용하지 않음 (READY_FOR_REVIEW, WAITING_FOR_REVIEW 등만 허용).
|
|
399
|
+
// CREATED 상태 submission은 별도로 조회 불가 → 이미 진행 중인 submission만 재사용.
|
|
400
|
+
// 없으면 submitVersionForReview가 새로 생성.
|
|
398
401
|
const data = await apiGet('/reviewSubmissions', {
|
|
399
402
|
'filter[app]': appId,
|
|
400
403
|
'filter[platform]': platform,
|
|
401
|
-
'filter[state]': 'READY_FOR_REVIEW,COMPLETING,UNRESOLVED_ISSUES',
|
|
404
|
+
'filter[state]': 'READY_FOR_REVIEW,WAITING_FOR_REVIEW,COMPLETING,UNRESOLVED_ISSUES',
|
|
402
405
|
'limit': '1',
|
|
403
406
|
});
|
|
404
|
-
|
|
405
|
-
const created = await apiGet('/reviewSubmissions', {
|
|
406
|
-
'filter[app]': appId,
|
|
407
|
-
'filter[platform]': platform,
|
|
408
|
-
'filter[state]': 'CREATED',
|
|
409
|
-
'limit': '1',
|
|
410
|
-
});
|
|
411
|
-
return created?.data?.[0]?.id ?? data?.data?.[0]?.id ?? null;
|
|
407
|
+
return data?.data?.[0]?.id ?? null;
|
|
412
408
|
}
|
|
413
409
|
async function isVersionAttached(submissionId, versionId) {
|
|
414
410
|
const data = await apiGet(`/reviewSubmissions/${submissionId}/items`, {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { OAuth2Client } from 'google-auth-library';
|
|
2
|
+
export declare function runQuery(auth: OAuth2Client, projectId: string, query: string, maxResults?: number): Promise<{
|
|
3
|
+
jobComplete: boolean | null | undefined;
|
|
4
|
+
totalRows: string | null | undefined;
|
|
5
|
+
schema: {
|
|
6
|
+
name: string | null | undefined;
|
|
7
|
+
type: string | null | undefined;
|
|
8
|
+
}[];
|
|
9
|
+
rows: {
|
|
10
|
+
[k: string]: any;
|
|
11
|
+
}[];
|
|
12
|
+
}>;
|
|
13
|
+
export declare function listDatasets(auth: OAuth2Client, projectId: string): Promise<{
|
|
14
|
+
datasetId: string | null | undefined;
|
|
15
|
+
location: string | undefined;
|
|
16
|
+
}[]>;
|
|
17
|
+
export declare function listTables(auth: OAuth2Client, projectId: string, datasetId: string): Promise<{
|
|
18
|
+
tableId: string | null | undefined;
|
|
19
|
+
type: string | undefined;
|
|
20
|
+
}[]>;
|
|
21
|
+
export declare function getTableSchema(auth: OAuth2Client, projectId: string, datasetId: string, tableId: string): Promise<{
|
|
22
|
+
tableId: string;
|
|
23
|
+
schema: {
|
|
24
|
+
name: string | null | undefined;
|
|
25
|
+
type: string | null | undefined;
|
|
26
|
+
mode: string | null | undefined;
|
|
27
|
+
description: string | null | undefined;
|
|
28
|
+
}[] | undefined;
|
|
29
|
+
}>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { google } from 'googleapis';
|
|
2
|
+
const bq = () => google.bigquery('v2');
|
|
3
|
+
// ─── 쿼리 실행 ───
|
|
4
|
+
export async function runQuery(auth, projectId, query, maxResults = 1000) {
|
|
5
|
+
const res = await bq().jobs.query({
|
|
6
|
+
auth,
|
|
7
|
+
projectId,
|
|
8
|
+
requestBody: {
|
|
9
|
+
query,
|
|
10
|
+
useLegacySql: false,
|
|
11
|
+
maxResults,
|
|
12
|
+
timeoutMs: 30000,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
const data = res.data;
|
|
16
|
+
const schema = data.schema?.fields ?? [];
|
|
17
|
+
const rows = (data.rows ?? []).map((row) => Object.fromEntries((row.f ?? []).map((cell, i) => [schema[i]?.name ?? `col${i}`, cell.v])));
|
|
18
|
+
return {
|
|
19
|
+
jobComplete: data.jobComplete,
|
|
20
|
+
totalRows: data.totalRows,
|
|
21
|
+
schema: schema.map((f) => ({ name: f.name, type: f.type })),
|
|
22
|
+
rows,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// ─── 데이터셋 목록 ───
|
|
26
|
+
export async function listDatasets(auth, projectId) {
|
|
27
|
+
const res = await bq().datasets.list({ auth, projectId });
|
|
28
|
+
return (res.data.datasets ?? []).map((d) => ({
|
|
29
|
+
datasetId: d.datasetReference?.datasetId,
|
|
30
|
+
location: d.location,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
// ─── 테이블 목록 ───
|
|
34
|
+
export async function listTables(auth, projectId, datasetId) {
|
|
35
|
+
const res = await bq().tables.list({ auth, projectId, datasetId });
|
|
36
|
+
return (res.data.tables ?? []).map((t) => ({
|
|
37
|
+
tableId: t.tableReference?.tableId,
|
|
38
|
+
type: t.type,
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
// ─── 테이블 스키마 ───
|
|
42
|
+
export async function getTableSchema(auth, projectId, datasetId, tableId) {
|
|
43
|
+
const res = await bq().tables.get({ auth, projectId, datasetId, tableId });
|
|
44
|
+
return {
|
|
45
|
+
tableId,
|
|
46
|
+
schema: res.data.schema?.fields?.map((f) => ({
|
|
47
|
+
name: f.name,
|
|
48
|
+
type: f.type,
|
|
49
|
+
mode: f.mode,
|
|
50
|
+
description: f.description,
|
|
51
|
+
})),
|
|
52
|
+
};
|
|
53
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import * as playstore from './playstore/tools.js';
|
|
|
13
13
|
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
|
+
import * as bigquery from './bigquery/tools.js';
|
|
16
17
|
import { checkPlayStoreRisks, checkAppStoreRisks, formatRisks } from './checks/risks.js';
|
|
17
18
|
import { buildPlayStoreReleasePlan, buildAppStoreReleasePlan } from './checks/plan.js';
|
|
18
19
|
import { validateAppStoreScreenshots, validatePlayStoreScreenshots, formatValidationResults } from './checks/screenshots.js';
|
|
@@ -1407,9 +1408,73 @@ server.tool('generate_review_reply', [
|
|
|
1407
1408
|
return { content: [{ type: 'text', text }] };
|
|
1408
1409
|
});
|
|
1409
1410
|
// ══════════════════════════════════════════════════
|
|
1411
|
+
// BigQuery
|
|
1412
|
+
// ══════════════════════════════════════════════════
|
|
1413
|
+
server.tool('bigquery_run_query', 'BigQuery SQL 쿼리 실행 (SELECT). GA4 analytics_* 테이블 분석에 사용.', {
|
|
1414
|
+
projectId: z.string().describe('GCP 프로젝트 ID (예: ads-coffee)'),
|
|
1415
|
+
query: z.string().describe('실행할 StandardSQL 쿼리'),
|
|
1416
|
+
maxResults: z.number().optional().describe('최대 행 수 (기본 1000)'),
|
|
1417
|
+
}, async ({ projectId, query, maxResults }) => {
|
|
1418
|
+
const auth = requireAuth();
|
|
1419
|
+
const result = await bigquery.runQuery(auth, projectId, query, maxResults ?? 1000);
|
|
1420
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
1421
|
+
});
|
|
1422
|
+
server.tool('bigquery_list_datasets', 'BigQuery 프로젝트의 데이터셋 목록 조회', {
|
|
1423
|
+
projectId: z.string().describe('GCP 프로젝트 ID'),
|
|
1424
|
+
}, async ({ projectId }) => {
|
|
1425
|
+
const auth = requireAuth();
|
|
1426
|
+
const datasets = await bigquery.listDatasets(auth, projectId);
|
|
1427
|
+
return { content: [{ type: 'text', text: JSON.stringify(datasets, null, 2) }] };
|
|
1428
|
+
});
|
|
1429
|
+
server.tool('bigquery_list_tables', 'BigQuery 데이터셋의 테이블 목록 조회', {
|
|
1430
|
+
projectId: z.string().describe('GCP 프로젝트 ID'),
|
|
1431
|
+
datasetId: z.string().describe('데이터셋 ID (예: analytics_530080532)'),
|
|
1432
|
+
}, async ({ projectId, datasetId }) => {
|
|
1433
|
+
const auth = requireAuth();
|
|
1434
|
+
const tables = await bigquery.listTables(auth, projectId, datasetId);
|
|
1435
|
+
return { content: [{ type: 'text', text: JSON.stringify(tables, null, 2) }] };
|
|
1436
|
+
});
|
|
1437
|
+
server.tool('bigquery_get_table_schema', 'BigQuery 테이블 스키마(컬럼 정보) 조회', {
|
|
1438
|
+
projectId: z.string().describe('GCP 프로젝트 ID'),
|
|
1439
|
+
datasetId: z.string().describe('데이터셋 ID'),
|
|
1440
|
+
tableId: z.string().describe('테이블 ID'),
|
|
1441
|
+
}, async ({ projectId, datasetId, tableId }) => {
|
|
1442
|
+
const auth = requireAuth();
|
|
1443
|
+
const schema = await bigquery.getTableSchema(auth, projectId, datasetId, tableId);
|
|
1444
|
+
return { content: [{ type: 'text', text: JSON.stringify(schema, null, 2) }] };
|
|
1445
|
+
});
|
|
1446
|
+
// ══════════════════════════════════════════════════
|
|
1410
1447
|
// Start
|
|
1411
1448
|
// ══════════════════════════════════════════════════
|
|
1449
|
+
// `npx -y @yoonion/mimi-seed-mcp <subcommand>` 처리.
|
|
1450
|
+
// npx는 스코프 패키지의 basename(`mimi-seed-mcp`)을 매치해 이 bin을 실행하므로,
|
|
1451
|
+
// 추가 인자(`mimi-seed-auth` 등)는 여기 argv로 흘러들어온다. 이전엔 MCP 서버가
|
|
1452
|
+
// stdin을 기다리며 영구 hang 됐다 — 이제 sub-CLI로 위임한다.
|
|
1453
|
+
const SUBCOMMANDS = {
|
|
1454
|
+
'mimi-seed-auth': () => import('./auth/cli.js'),
|
|
1455
|
+
'mimi-seed-playstore-auth': () => import('./auth/playstore-setup-cli.js'),
|
|
1456
|
+
'mimi-seed-appstore-auth': () => import('./appstore/setup-cli.js'),
|
|
1457
|
+
};
|
|
1412
1458
|
async function main() {
|
|
1459
|
+
const sub = process.argv[2];
|
|
1460
|
+
if (sub && SUBCOMMANDS[sub]) {
|
|
1461
|
+
// argv에서 subcommand 토큰 제거 후 sub-CLI 모듈 import — 모듈 top-level이 main() 실행
|
|
1462
|
+
process.argv.splice(2, 1);
|
|
1463
|
+
await SUBCOMMANDS[sub]();
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
if (sub && !sub.startsWith('-')) {
|
|
1467
|
+
console.error([
|
|
1468
|
+
`❌ Unknown subcommand: ${sub}`,
|
|
1469
|
+
'',
|
|
1470
|
+
'Available:',
|
|
1471
|
+
...Object.keys(SUBCOMMANDS).map((k) => ` npx -y @yoonion/mimi-seed-mcp ${k}`),
|
|
1472
|
+
'',
|
|
1473
|
+
'Or run the MCP server (no args):',
|
|
1474
|
+
' npx -y @yoonion/mimi-seed-mcp',
|
|
1475
|
+
].join('\n'));
|
|
1476
|
+
process.exit(2);
|
|
1477
|
+
}
|
|
1413
1478
|
const transport = new StdioServerTransport();
|
|
1414
1479
|
await server.connect(transport);
|
|
1415
1480
|
}
|
package/package.json
CHANGED