@yoonion/mimi-seed-mcp 0.13.9 → 0.13.10

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/README.md CHANGED
@@ -84,7 +84,7 @@ export ANTHROPIC_API_KEY=sk-ant-...
84
84
  | AdMob | 7 | `admob_list_apps` / `admob_create_ad_unit` / `admob_get_today_earnings` / `admob_get_report` |
85
85
  | CI/CD (GitHub Actions · GitLab) | 6 | `ci_trigger_build` / `ci_get_build_status` / `ci_list_workflows` / `ci_cancel_build` |
86
86
  | Jenkins (크리덴셜 + 잡) | 10 | `jenkins_create_credential` / `jenkins_upload_keystore` / `jenkins_create_job` / `jenkins_update_job` |
87
- | GA4 | 6 | `ga4_create_property` / `ga4_create_data_stream` / `ga4_run_report` |
87
+ | GA4 | 8 | `ga4_create_property` / `ga4_create_data_stream` / `ga4_plan_bigquery_link` / `ga4_create_bigquery_link` / `ga4_run_report` |
88
88
  | Search Console | 6 | `gsc_inspect_url` / `gsc_search_analytics` / `gsc_submit_sitemap` |
89
89
  | Google Ads | 6 | `googleads_list_campaigns` / `googleads_get_uac_report` / `googleads_get_campaign_report` |
90
90
  | Facebook | 6 | `facebook_post_photo` / `facebook_post_multi_photo` / `facebook_list_pages` |
@@ -16,6 +16,8 @@ export type DataStreamPlatform = 'web' | 'android' | 'ios';
16
16
  export declare function normalizeAccountName(accountId: string): string;
17
17
  /** '123' | 'properties/123' → 'properties/123' */
18
18
  export declare function normalizePropertyName(propertyId: string): string;
19
+ /** 'my-project' | 'projects/my-project' → 'projects/my-project' */
20
+ export declare function normalizeCloudProjectName(projectId: string): string;
19
21
  /**
20
22
  * dataStreams.create 응답에서 클라이언트가 실제로 필요로 하는 식별자를 평탄화한다.
21
23
  * - web stream → measurementId (G-XXXX, gtag/web 연동용)
@@ -76,6 +78,42 @@ export declare function buildDataStreamBody(opts: {
76
78
  webStreamData?: undefined;
77
79
  androidAppStreamData?: undefined;
78
80
  };
81
+ export interface BigQueryLinkOptions {
82
+ projectId: string;
83
+ datasetLocation: string;
84
+ dailyExportEnabled?: boolean;
85
+ streamingExportEnabled?: boolean;
86
+ freshDailyExportEnabled?: boolean;
87
+ includeAdvertisingId?: boolean;
88
+ }
89
+ /** BigQueryLink 생성 요청 본문 조립 (순수 함수 — 테스트 대상). */
90
+ export declare function buildBigQueryLinkBody(opts: BigQueryLinkOptions): {
91
+ project: string;
92
+ datasetLocation: string;
93
+ dailyExportEnabled: boolean;
94
+ streamingExportEnabled: boolean;
95
+ freshDailyExportEnabled: boolean;
96
+ includeAdvertisingId: boolean;
97
+ };
98
+ export declare function flattenBigQueryLink(link: {
99
+ name?: string | null;
100
+ project?: string | null;
101
+ datasetLocation?: string | null;
102
+ createTime?: string | null;
103
+ dailyExportEnabled?: boolean | null;
104
+ streamingExportEnabled?: boolean | null;
105
+ freshDailyExportEnabled?: boolean | null;
106
+ includeAdvertisingId?: boolean | null;
107
+ }): {
108
+ name: string | null;
109
+ project: string | null;
110
+ datasetLocation: string | null;
111
+ createTime: string | null;
112
+ dailyExportEnabled: boolean;
113
+ streamingExportEnabled: boolean;
114
+ freshDailyExportEnabled: boolean;
115
+ includeAdvertisingId: boolean;
116
+ };
79
117
  /** 접근 가능한 GA 계정 + 각 계정의 property 요약. accountId/propertyId 를 찾는 시작점. */
80
118
  export declare function listAccountSummaries(auth: Ga4Auth): Promise<import("googleapis").analyticsadmin_v1beta.Schema$GoogleAnalyticsAdminV1betaAccountSummary[]>;
81
119
  /** 계정 하위 GA4 property 목록. accountId: '123' 또는 'accounts/123'. */
@@ -107,6 +145,83 @@ export declare function listDataStreams(auth: Ga4Auth, propertyId: string): Prom
107
145
  measurementId: string | null;
108
146
  firebaseAppId: string | null;
109
147
  }[]>;
148
+ /** GA4 property 에 연결된 BigQuery export 링크 목록. */
149
+ export declare function listBigQueryLinks(auth: Ga4Auth, propertyId: string): Promise<{
150
+ name: string | null;
151
+ project: string | null;
152
+ datasetLocation: string | null;
153
+ createTime: string | null;
154
+ dailyExportEnabled: boolean;
155
+ streamingExportEnabled: boolean;
156
+ freshDailyExportEnabled: boolean;
157
+ includeAdvertisingId: boolean;
158
+ }[]>;
159
+ /**
160
+ * 생성 전 계획. GA4 property 는 BigQuery 링크가 이미 있으면 중복 생성을 시도하지 않는다.
161
+ * 기존 링크가 있으면 대상 프로젝트가 같아도/달라도 원격 상태를 그대로 보여준다.
162
+ */
163
+ export declare function planBigQueryLink(auth: Ga4Auth, propertyId: string, opts: BigQueryLinkOptions): Promise<{
164
+ ready: boolean;
165
+ action: string;
166
+ property: string;
167
+ requestBody: {
168
+ project: string;
169
+ datasetLocation: string;
170
+ dailyExportEnabled: boolean;
171
+ streamingExportEnabled: boolean;
172
+ freshDailyExportEnabled: boolean;
173
+ includeAdvertisingId: boolean;
174
+ };
175
+ existingLinks: {
176
+ name: string | null;
177
+ project: string | null;
178
+ datasetLocation: string | null;
179
+ createTime: string | null;
180
+ dailyExportEnabled: boolean;
181
+ streamingExportEnabled: boolean;
182
+ freshDailyExportEnabled: boolean;
183
+ includeAdvertisingId: boolean;
184
+ }[];
185
+ }>;
186
+ /** 계획을 다시 검사한 뒤 BigQuery export 링크 생성. */
187
+ export declare function createBigQueryLink(auth: Ga4Auth, propertyId: string, opts: BigQueryLinkOptions): Promise<{
188
+ ready: boolean;
189
+ action: string;
190
+ property: string;
191
+ requestBody: {
192
+ project: string;
193
+ datasetLocation: string;
194
+ dailyExportEnabled: boolean;
195
+ streamingExportEnabled: boolean;
196
+ freshDailyExportEnabled: boolean;
197
+ includeAdvertisingId: boolean;
198
+ };
199
+ existingLinks: {
200
+ name: string | null;
201
+ project: string | null;
202
+ datasetLocation: string | null;
203
+ createTime: string | null;
204
+ dailyExportEnabled: boolean;
205
+ streamingExportEnabled: boolean;
206
+ freshDailyExportEnabled: boolean;
207
+ includeAdvertisingId: boolean;
208
+ }[];
209
+ created: boolean;
210
+ link?: undefined;
211
+ } | {
212
+ created: boolean;
213
+ property: string;
214
+ link: {
215
+ name: string | null;
216
+ project: string | null;
217
+ datasetLocation: string | null;
218
+ createTime: string | null;
219
+ dailyExportEnabled: boolean;
220
+ streamingExportEnabled: boolean;
221
+ freshDailyExportEnabled: boolean;
222
+ includeAdvertisingId: boolean;
223
+ };
224
+ }>;
110
225
  export interface RunReportParams {
111
226
  startDate: string;
112
227
  endDate: string;
package/dist/ga4/tools.js CHANGED
@@ -10,6 +10,7 @@ import { google } from 'googleapis';
10
10
  * (스코프 추가 후 기존 사용자는 1회 재로그인: `npx -y @yoonion/mimi-seed-mcp mimi-seed-auth`)
11
11
  */
12
12
  const admin = () => google.analyticsadmin('v1beta');
13
+ const adminAlpha = () => google.analyticsadmin('v1alpha');
13
14
  const data = () => google.analyticsdata('v1beta');
14
15
  /** Admin API(analyticsadmin — property/data stream 생성·조회)가 요구하는 스코프. */
15
16
  export const GA4_SCOPE = 'https://www.googleapis.com/auth/analytics.edit';
@@ -33,6 +34,11 @@ export function normalizePropertyName(propertyId) {
33
34
  const id = propertyId.trim();
34
35
  return id.startsWith('properties/') ? id : `properties/${id}`;
35
36
  }
37
+ /** 'my-project' | 'projects/my-project' → 'projects/my-project' */
38
+ export function normalizeCloudProjectName(projectId) {
39
+ const id = projectId.trim();
40
+ return id.startsWith('projects/') ? id : `projects/${id}`;
41
+ }
36
42
  /**
37
43
  * dataStreams.create 응답에서 클라이언트가 실제로 필요로 하는 식별자를 평탄화한다.
38
44
  * - web stream → measurementId (G-XXXX, gtag/web 연동용)
@@ -70,6 +76,29 @@ export function buildDataStreamBody(opts) {
70
76
  };
71
77
  }
72
78
  }
79
+ /** BigQueryLink 생성 요청 본문 조립 (순수 함수 — 테스트 대상). */
80
+ export function buildBigQueryLinkBody(opts) {
81
+ return {
82
+ project: normalizeCloudProjectName(opts.projectId),
83
+ datasetLocation: opts.datasetLocation.trim(),
84
+ dailyExportEnabled: opts.dailyExportEnabled ?? true,
85
+ streamingExportEnabled: opts.streamingExportEnabled ?? false,
86
+ freshDailyExportEnabled: opts.freshDailyExportEnabled ?? false,
87
+ includeAdvertisingId: opts.includeAdvertisingId ?? false,
88
+ };
89
+ }
90
+ export function flattenBigQueryLink(link) {
91
+ return {
92
+ name: link.name ?? null,
93
+ project: link.project ?? null,
94
+ datasetLocation: link.datasetLocation ?? null,
95
+ createTime: link.createTime ?? null,
96
+ dailyExportEnabled: link.dailyExportEnabled ?? false,
97
+ streamingExportEnabled: link.streamingExportEnabled ?? false,
98
+ freshDailyExportEnabled: link.freshDailyExportEnabled ?? false,
99
+ includeAdvertisingId: link.includeAdvertisingId ?? false,
100
+ };
101
+ }
73
102
  // ─── 계정/속성 디스커버리 ───
74
103
  /** 접근 가능한 GA 계정 + 각 계정의 property 요약. accountId/propertyId 를 찾는 시작점. */
75
104
  export async function listAccountSummaries(auth) {
@@ -114,6 +143,49 @@ export async function listDataStreams(auth, propertyId) {
114
143
  });
115
144
  return (res.data.dataStreams ?? []).map((d) => flattenDataStream(d));
116
145
  }
146
+ // ─── BigQuery export 링크 ───
147
+ /** GA4 property 에 연결된 BigQuery export 링크 목록. */
148
+ export async function listBigQueryLinks(auth, propertyId) {
149
+ const res = await adminAlpha().properties.bigQueryLinks.list({
150
+ auth,
151
+ parent: normalizePropertyName(propertyId),
152
+ pageSize: 200,
153
+ });
154
+ return (res.data.bigqueryLinks ?? []).map((link) => flattenBigQueryLink(link));
155
+ }
156
+ /**
157
+ * 생성 전 계획. GA4 property 는 BigQuery 링크가 이미 있으면 중복 생성을 시도하지 않는다.
158
+ * 기존 링크가 있으면 대상 프로젝트가 같아도/달라도 원격 상태를 그대로 보여준다.
159
+ */
160
+ export async function planBigQueryLink(auth, propertyId, opts) {
161
+ const property = normalizePropertyName(propertyId);
162
+ const requestBody = buildBigQueryLinkBody(opts);
163
+ const existingLinks = await listBigQueryLinks(auth, property);
164
+ return {
165
+ ready: existingLinks.length === 0,
166
+ action: existingLinks.length === 0 ? 'create' : 'no-op-existing-link',
167
+ property,
168
+ requestBody,
169
+ existingLinks,
170
+ };
171
+ }
172
+ /** 계획을 다시 검사한 뒤 BigQuery export 링크 생성. */
173
+ export async function createBigQueryLink(auth, propertyId, opts) {
174
+ const plan = await planBigQueryLink(auth, propertyId, opts);
175
+ if (!plan.ready) {
176
+ return { created: false, ...plan };
177
+ }
178
+ const res = await adminAlpha().properties.bigQueryLinks.create({
179
+ auth,
180
+ parent: plan.property,
181
+ requestBody: plan.requestBody,
182
+ });
183
+ return {
184
+ created: true,
185
+ property: plan.property,
186
+ link: flattenBigQueryLink(res.data),
187
+ };
188
+ }
117
189
  export async function runReport(auth, propertyId, params) {
118
190
  const res = await data().properties.runReport({
119
191
  auth,
@@ -71,6 +71,45 @@ export function registerGa4Tools(server) {
71
71
  const streams = await ga4.listDataStreams(auth, propertyId);
72
72
  return { content: [{ type: 'text', text: JSON.stringify(streams, null, 2) }] };
73
73
  });
74
+ const bigQueryLinkSchema = {
75
+ propertyId: z.string().trim().min(1).describe(PROPERTY_DESC),
76
+ projectId: z.string().trim().min(1).describe("Google Cloud 프로젝트 ID — 'my-project' 또는 'projects/my-project'"),
77
+ datasetLocation: z.string().trim().min(1).describe("생성할 BigQuery 데이터셋 위치 (예: 'asia-northeast3', 'US', 'EU'). 생성 후 변경 불가"),
78
+ dailyExportEnabled: z.boolean().optional().describe('일일 내보내기 활성화 (기본 true)'),
79
+ streamingExportEnabled: z.boolean().optional().describe('스트리밍 내보내기 활성화 (기본 false, 추가 비용 가능)'),
80
+ freshDailyExportEnabled: z.boolean().optional().describe('Fresh Daily 내보내기 활성화 (기본 false, 지원 속성만 가능)'),
81
+ includeAdvertisingId: z.boolean().optional().describe('모바일 광고 식별자 포함 (기본 false, 개인정보 정책 확인 필요)'),
82
+ };
83
+ server.tool('ga4_plan_bigquery_link', 'GA4 → BigQuery export 링크 생성 계획을 읽기 전용으로 점검. 기존 링크, 대상 프로젝트, 데이터셋 위치와 export 옵션을 반환하며 원격 상태를 변경하지 않는다.', bigQueryLinkSchema, async ({ propertyId, projectId, datasetLocation, dailyExportEnabled, streamingExportEnabled, freshDailyExportEnabled, includeAdvertisingId }) => {
84
+ const auth = await requireAuth(ga4Raw.GA4_SCOPE);
85
+ const plan = await ga4.planBigQueryLink(auth, propertyId, {
86
+ projectId,
87
+ datasetLocation,
88
+ dailyExportEnabled,
89
+ streamingExportEnabled,
90
+ freshDailyExportEnabled,
91
+ includeAdvertisingId,
92
+ });
93
+ return { content: [{ type: 'text', text: JSON.stringify(plan, null, 2) }] };
94
+ });
95
+ server.tool('ga4_create_bigquery_link', 'GA4 → BigQuery export 링크 생성. confirm 생략/false 시 읽기 전용 계획만 반환하고, confirm: true 일 때만 계획을 재검사한 뒤 생성한다. 기존 링크가 있으면 no-op.', {
96
+ ...bigQueryLinkSchema,
97
+ confirm: z.boolean().optional().describe('true 명시 시에만 원격 링크 생성. 생략/false 면 계획만 반환'),
98
+ }, async ({ propertyId, projectId, datasetLocation, dailyExportEnabled, streamingExportEnabled, freshDailyExportEnabled, includeAdvertisingId, confirm }) => {
99
+ const auth = await requireAuth(ga4Raw.GA4_SCOPE);
100
+ const opts = {
101
+ projectId,
102
+ datasetLocation,
103
+ dailyExportEnabled,
104
+ streamingExportEnabled,
105
+ freshDailyExportEnabled,
106
+ includeAdvertisingId,
107
+ };
108
+ const result = confirm
109
+ ? await ga4.createBigQueryLink(auth, propertyId, opts)
110
+ : { preview: true, ...(await ga4.planBigQueryLink(auth, propertyId, opts)) };
111
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
112
+ });
74
113
  server.tool('ga4_run_report', 'GA4 Data API 리포트 (활성 사용자·이벤트 등). dimensions/metrics 는 쉼표 구분 GA4 API 이름.', {
75
114
  propertyId: z.string().describe(PROPERTY_DESC),
76
115
  startDate: z.string().describe("시작일 (YYYY-MM-DD 또는 'NdaysAgo', 'today')"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yoonion/mimi-seed-mcp",
3
- "version": "0.13.9",
3
+ "version": "0.13.10",
4
4
  "description": "Mimi Seed MCP server — Firebase + AdMob + Google Play + App Store management for Claude Code / Codex / Cursor / any MCP client.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$comment": "등록된 MCP 도구 + 도메인 메타데이터(label·credential·summary)의 SSOT. src/__tests__/tool-manifest.test.ts 가 실제 서버 등록 목록과 diff 하고 메타데이터 정합성을 검사한다. 도구 추가/삭제/개명 시 이 파일을 함께 갱신할 것 — 산문 문서에는 정확한 개수를 쓰지 말고 이 파일을 가리킬 것. mimi-seed://tools/catalog 리소스가 이 파일을 그대로 서빙한다.",
3
- "total": 183,
3
+ "total": 185,
4
4
  "domains": {
5
5
  "admob": {
6
6
  "label": "AdMob",
@@ -169,13 +169,15 @@
169
169
  "ga4": {
170
170
  "label": "Google Analytics 4",
171
171
  "credential": "Google OAuth",
172
- "summary": "계정/속성·데이터 스트림 관리, 리포트 실행",
172
+ "summary": "계정/속성·데이터 스트림·BigQuery export 링크 관리, 리포트 실행",
173
173
  "tools": [
174
174
  "ga4_list_account_summaries",
175
175
  "ga4_list_properties",
176
176
  "ga4_create_property",
177
177
  "ga4_create_data_stream",
178
178
  "ga4_list_data_streams",
179
+ "ga4_plan_bigquery_link",
180
+ "ga4_create_bigquery_link",
179
181
  "ga4_run_report"
180
182
  ]
181
183
  },