@yoonion/mimi-seed-mcp 0.1.0

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 ADDED
@@ -0,0 +1,86 @@
1
+ # @yoonion/mimi-seed-mcp
2
+
3
+ **Mimi Seed** — Firebase · AdMob · Google Play · App Store Connect를 AI 콘솔에서 관리. Claude Code / Cursor / 기타 MCP 클라이언트에서 한 줄 등록으로 사용.
4
+
5
+ > 이 패키지는 Mimi Seed의 **MCP 서버**만 포함합니다. 웹 콘솔(Next.js 앱)은 <https://mimi-seed.pryzm.gg> — 향후 `@yoonion/mimi-seed`로 별도 배포 예정.
6
+
7
+ ## 설치
8
+
9
+ Claude Code:
10
+
11
+ ```bash
12
+ claude mcp add mimi-seed -- npx -y @yoonion/mimi-seed-mcp
13
+ ```
14
+
15
+ Claude Desktop (`claude_desktop_config.json`):
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "mimi-seed": {
21
+ "command": "npx",
22
+ "args": ["-y", "@yoonion/mimi-seed-mcp"]
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ ## 첫 사용 전 인증
29
+
30
+ ```bash
31
+ npx -y @yoonion/mimi-seed-mcp mimi-seed-auth
32
+ ```
33
+
34
+ 브라우저가 열리면 Google 계정으로 로그인. 토큰은 `~/.mimi-seed/tokens.json`에 저장되고 자동 갱신됨.
35
+
36
+ App Store Connect까지 쓰려면 별도로:
37
+
38
+ ```bash
39
+ npx -y @yoonion/mimi-seed-mcp mimi-seed-appstore-auth
40
+ ```
41
+
42
+ (App Store Connect → Users and Access → Keys에서 API Key 생성 후 Issuer ID / Key ID / .p8 경로 입력)
43
+
44
+ ## 제공 도구 (약 40개)
45
+
46
+ | 영역 | 도구 예시 |
47
+ |------|---------|
48
+ | Firebase | `firebase_list_projects` / `firebase_create_android_app` / `firebase_get_android_config` / `firebase_enable_service` |
49
+ | AdMob | `admob_list_apps` / `admob_create_ad_unit` / `admob_get_today_earnings` / `admob_get_report` |
50
+ | Google Play | `playstore_list_tracks` / `playstore_list_subscriptions` / `playstore_reply_review` / **`playstore_verify_service_account`** |
51
+ | App Store Connect | `appstore_list_apps` / `appstore_list_builds` / `appstore_list_beta_groups` |
52
+ | 상태 | `mimi_seed_auth_status` |
53
+
54
+ ## `playstore_verify_service_account` — 서비스 계정 JSON 검증
55
+
56
+ [onesub](https://github.com/jeonghwanko/onesub) 같은 서버가 Google Play 영수증을 백그라운드로 검증하려면 OAuth가 아니라 **서비스 계정 JSON**이 필요. 이 도구는 JSON을 붙여넣으면 단계별로 확인:
57
+
58
+ 1. **parse** — JSON 구조 + 필수 필드 (`type`, `client_email`, `private_key`, `project_id`)
59
+ 2. **auth** — 실제로 Google에서 OAuth 토큰 받을 수 있는지 (private_key 유효성)
60
+ 3. **api** — 'View financial data' 권한 있는지 (`monetization.subscriptions.list` 실 호출)
61
+
62
+ 실패 단계별로 해결 순서까지 안내. 통과하면 한 줄 JSON으로 변환해서 서버 환경변수에 쓸 수 있음.
63
+
64
+ 예:
65
+
66
+ > (Claude에게) 이 서비스 계정 JSON이 `com.yourapp.id`에 대해 작동하는지 확인해줘:
67
+ > ```json
68
+ > {"type":"service_account", "project_id":"...", ...}
69
+ > ```
70
+
71
+ ## 레거시 호환성
72
+
73
+ 패키지 이름이 `@preseed/mcp-server` / `preseed-mcp` 시절의 데이터는 자동으로 이어받음:
74
+
75
+ - `~/.preseed/tokens.json` 있으면 읽음 (재인증 불필요)
76
+ - `~/.preseed/appstore.json`도 동일
77
+ - 환경변수 `PRESEED_GOOGLE_CLIENT_ID` / `PRESEED_GOOGLE_CLIENT_SECRET` 계속 인식
78
+
79
+ 새로 쓰는 건 `~/.mimi-seed/`.
80
+
81
+ ## Links
82
+
83
+ - 웹 콘솔: https://mimi-seed.pryzm.gg
84
+ - 저장소: https://github.com/jeonghwanko/app-gen
85
+
86
+ MIT © jeonghwanko
@@ -0,0 +1,39 @@
1
+ import type { OAuth2Client } from 'google-auth-library';
2
+ /**
3
+ * AdMob API 래퍼
4
+ * v1: 조회 (stable)
5
+ * v1beta: 생성 (Limited Access — 대부분 계정에서 403, 시도는 함)
6
+ */
7
+ export declare function listAccounts(auth: OAuth2Client): Promise<{
8
+ name: string | null | undefined;
9
+ publisherId: string | null | undefined;
10
+ reportingTimeZone: string | null | undefined;
11
+ currencyCode: string | null | undefined;
12
+ }[]>;
13
+ export declare function listApps(auth: OAuth2Client, accountId: string): Promise<{
14
+ name: any;
15
+ appId: any;
16
+ platform: any;
17
+ linkedAppInfo: any;
18
+ manualAppInfo: any;
19
+ }[]>;
20
+ export declare function listAdUnits(auth: OAuth2Client, accountId: string): Promise<{
21
+ name: any;
22
+ adUnitId: any;
23
+ adFormat: any;
24
+ adTypes: any;
25
+ displayName: any;
26
+ appId: any;
27
+ }[]>;
28
+ export declare function getNetworkReport(auth: OAuth2Client, accountId: string, startDate: {
29
+ year: number;
30
+ month: number;
31
+ day: number;
32
+ }, endDate: {
33
+ year: number;
34
+ month: number;
35
+ day: number;
36
+ }): Promise<import("googleapis").admob_v1.Schema$GenerateNetworkReportResponse>;
37
+ export declare function getTodayEarnings(auth: OAuth2Client, accountId: string): Promise<import("googleapis").admob_v1.Schema$GenerateNetworkReportResponse>;
38
+ export declare function createApp(auth: OAuth2Client, accountId: string, platform: 'ANDROID' | 'IOS', displayName: string): Promise<any>;
39
+ export declare function createAdUnit(auth: OAuth2Client, accountId: string, appId: string, displayName: string, adFormat: 'BANNER' | 'INTERSTITIAL' | 'REWARDED' | 'REWARDED_INTERSTITIAL' | 'APP_OPEN' | 'NATIVE'): Promise<any>;
@@ -0,0 +1,122 @@
1
+ import { google } from 'googleapis';
2
+ /**
3
+ * AdMob API 래퍼
4
+ * v1: 조회 (stable)
5
+ * v1beta: 생성 (Limited Access — 대부분 계정에서 403, 시도는 함)
6
+ */
7
+ // ─── 계정 ───
8
+ export async function listAccounts(auth) {
9
+ const admob = google.admob('v1');
10
+ const res = await admob.accounts.list({ auth, pageSize: 100 });
11
+ return (res.data.account ?? []).map((a) => ({
12
+ name: a.name,
13
+ publisherId: a.publisherId,
14
+ reportingTimeZone: a.reportingTimeZone,
15
+ currencyCode: a.currencyCode,
16
+ }));
17
+ }
18
+ // ─── 앱 ───
19
+ export async function listApps(auth, accountId) {
20
+ const admobApi = google.admob('v1');
21
+ const all = [];
22
+ let pageToken;
23
+ do {
24
+ const res = await admobApi.accounts.apps.list({
25
+ auth,
26
+ parent: accountId,
27
+ pageSize: 100,
28
+ pageToken,
29
+ });
30
+ all.push(...(res.data.apps ?? []));
31
+ pageToken = res.data.nextPageToken ?? undefined;
32
+ } while (pageToken);
33
+ return all.map((a) => ({
34
+ name: a.name,
35
+ appId: a.appId,
36
+ platform: a.platform,
37
+ linkedAppInfo: a.linkedAppInfo,
38
+ manualAppInfo: a.manualAppInfo,
39
+ }));
40
+ }
41
+ // ─── 광고 단위 ───
42
+ export async function listAdUnits(auth, accountId) {
43
+ const admobApi = google.admob('v1');
44
+ const all = [];
45
+ let pageToken;
46
+ do {
47
+ const res = await admobApi.accounts.adUnits.list({
48
+ auth,
49
+ parent: accountId,
50
+ pageSize: 100,
51
+ pageToken,
52
+ });
53
+ all.push(...(res.data.adUnits ?? []));
54
+ pageToken = res.data.nextPageToken ?? undefined;
55
+ } while (pageToken);
56
+ return all.map((u) => ({
57
+ name: u.name,
58
+ adUnitId: u.adUnitId,
59
+ adFormat: u.adFormat,
60
+ adTypes: u.adTypes,
61
+ displayName: u.displayName,
62
+ appId: u.appId,
63
+ }));
64
+ }
65
+ // ─── 수익 리포트 ───
66
+ export async function getNetworkReport(auth, accountId, startDate, endDate) {
67
+ const admob = google.admob('v1');
68
+ const res = await admob.accounts.networkReport.generate({
69
+ auth,
70
+ parent: accountId,
71
+ requestBody: {
72
+ reportSpec: {
73
+ dateRange: { startDate, endDate },
74
+ dimensions: ['APP', 'AD_UNIT', 'DATE'],
75
+ metrics: [
76
+ 'ESTIMATED_EARNINGS',
77
+ 'AD_REQUESTS',
78
+ 'IMPRESSIONS',
79
+ 'CLICKS',
80
+ 'MATCHED_REQUESTS',
81
+ ],
82
+ sortConditions: [{ dimension: 'DATE', order: 'DESCENDING' }],
83
+ },
84
+ },
85
+ });
86
+ return res.data;
87
+ }
88
+ // ─── 오늘 수익 요약 ───
89
+ export async function getTodayEarnings(auth, accountId) {
90
+ const now = new Date();
91
+ const today = { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() };
92
+ return getNetworkReport(auth, accountId, today, today);
93
+ }
94
+ // ─── v1beta: 앱 생성 (Limited Access) ───
95
+ export async function createApp(auth, accountId, platform, displayName) {
96
+ // v1beta — 대부분 계정에서 403. Google Account Manager 승인 필요.
97
+ const admobBeta = google.admob('v1beta');
98
+ const res = await admobBeta.accounts.apps.create({
99
+ auth,
100
+ parent: accountId,
101
+ requestBody: {
102
+ platform,
103
+ manualAppInfo: { displayName },
104
+ },
105
+ });
106
+ return res.data;
107
+ }
108
+ // ─── v1beta: 광고 단위 생성 (Limited Access) ───
109
+ export async function createAdUnit(auth, accountId, appId, displayName, adFormat) {
110
+ const admobBeta = google.admob('v1beta');
111
+ const res = await admobBeta.accounts.adUnits.create({
112
+ auth,
113
+ parent: accountId,
114
+ requestBody: {
115
+ appId,
116
+ displayName,
117
+ adFormat,
118
+ adTypes: ['DISPLAY'],
119
+ },
120
+ });
121
+ return res.data;
122
+ }
@@ -0,0 +1,9 @@
1
+ export interface AppStoreCredentials {
2
+ issuerId: string;
3
+ keyId: string;
4
+ privateKey: string;
5
+ }
6
+ export declare function getAppStoreCredentials(): AppStoreCredentials | null;
7
+ export declare function saveAppStoreCredentials(creds: AppStoreCredentials): void;
8
+ export declare function generateToken(creds: AppStoreCredentials): string;
9
+ export declare function getAuthHeaders(): Record<string, string> | null;
@@ -0,0 +1,53 @@
1
+ import jwt from 'jsonwebtoken';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ // Primary location under ~/.mimi-seed. Legacy ~/.preseed read as fallback
6
+ // during the rebrand window so existing App Store Connect sessions don't
7
+ // force a re-setup.
8
+ const CONFIG_PATH = path.join(os.homedir(), '.mimi-seed', 'appstore.json');
9
+ const LEGACY_CONFIG_PATH = path.join(os.homedir(), '.preseed', 'appstore.json');
10
+ export function getAppStoreCredentials() {
11
+ const pathToRead = fs.existsSync(CONFIG_PATH)
12
+ ? CONFIG_PATH
13
+ : fs.existsSync(LEGACY_CONFIG_PATH)
14
+ ? LEGACY_CONFIG_PATH
15
+ : null;
16
+ if (!pathToRead)
17
+ return null;
18
+ try {
19
+ return JSON.parse(fs.readFileSync(pathToRead, 'utf-8'));
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ export function saveAppStoreCredentials(creds) {
26
+ const dir = path.dirname(CONFIG_PATH);
27
+ if (!fs.existsSync(dir))
28
+ fs.mkdirSync(dir, { recursive: true });
29
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(creds, null, 2), { mode: 0o600 });
30
+ }
31
+ export function generateToken(creds) {
32
+ const now = Math.floor(Date.now() / 1000);
33
+ return jwt.sign({
34
+ iss: creds.issuerId,
35
+ iat: now,
36
+ exp: now + 20 * 60, // 20분
37
+ aud: 'appstoreconnect-v1',
38
+ }, creds.privateKey, {
39
+ algorithm: 'ES256',
40
+ header: {
41
+ alg: 'ES256',
42
+ kid: creds.keyId,
43
+ typ: 'JWT',
44
+ },
45
+ });
46
+ }
47
+ export function getAuthHeaders() {
48
+ const creds = getAppStoreCredentials();
49
+ if (!creds)
50
+ return null;
51
+ const token = generateToken(creds);
52
+ return { Authorization: `Bearer ${token}` };
53
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+ import { saveAppStoreCredentials, getAppStoreCredentials } from './auth.js';
3
+ import readline from 'node:readline';
4
+ import fs from 'node:fs';
5
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
6
+ const ask = (q) => new Promise((r) => rl.question(q, r));
7
+ async function main() {
8
+ console.log('');
9
+ console.log(' 🍎 Mimi Seed — App Store Connect 연결');
10
+ console.log('');
11
+ const existing = getAppStoreCredentials();
12
+ if (existing) {
13
+ console.log(` ✅ 이미 연결됨 (Key ID: ${existing.keyId})`);
14
+ const answer = await ask(' 다시 설정할래? (y/N): ');
15
+ if (answer.toLowerCase() !== 'y') {
16
+ rl.close();
17
+ return;
18
+ }
19
+ }
20
+ console.log(' App Store Connect에서 API Key를 만들어야 해:');
21
+ console.log(' https://appstoreconnect.apple.com/access/integrations/api');
22
+ console.log('');
23
+ console.log(' 1. "키 생성" 클릭');
24
+ console.log(' 2. 이름: Mimi Seed, 역할: Admin');
25
+ console.log(' 3. .p8 파일 다운로드 (1회만 가능!)');
26
+ console.log('');
27
+ const issuerId = await ask(' Issuer ID: ');
28
+ const keyId = await ask(' Key ID: ');
29
+ const p8Path = await ask(' .p8 파일 경로: ');
30
+ const trimmedPath = p8Path.trim().replace(/^["']|["']$/g, '');
31
+ if (!fs.existsSync(trimmedPath)) {
32
+ console.log(` ❌ 파일 없음: ${trimmedPath}`);
33
+ rl.close();
34
+ process.exit(1);
35
+ }
36
+ const privateKey = fs.readFileSync(trimmedPath, 'utf-8');
37
+ saveAppStoreCredentials({
38
+ issuerId: issuerId.trim(),
39
+ keyId: keyId.trim(),
40
+ privateKey,
41
+ });
42
+ console.log('');
43
+ console.log(' ✅ 연결 완료!');
44
+ console.log('');
45
+ console.log(' 이제 Claude Code에서:');
46
+ console.log(' "내 앱스토어 앱 목록 보여줘"');
47
+ console.log(' "TestFlight 빌드 목록 보여줘"');
48
+ console.log('');
49
+ rl.close();
50
+ }
51
+ main();
@@ -0,0 +1,7 @@
1
+ export declare function listApps(): Promise<any>;
2
+ export declare function getApp(appId: string): Promise<any>;
3
+ export declare function listVersions(appId: string): Promise<any>;
4
+ export declare function getVersionLocalizations(versionId: string): Promise<any>;
5
+ export declare function listBuilds(appId: string): Promise<any>;
6
+ export declare function listBetaGroups(appId: string): Promise<any>;
7
+ export declare function getAppInfo(appId: string): Promise<any>;
@@ -0,0 +1,119 @@
1
+ import { getAuthHeaders } from './auth.js';
2
+ /**
3
+ * App Store Connect API v1 래퍼
4
+ * https://developer.apple.com/documentation/appstoreconnectapi
5
+ */
6
+ const BASE = 'https://api.appstoreconnect.apple.com/v1';
7
+ async function apiGet(path, params) {
8
+ const headers = getAuthHeaders();
9
+ if (!headers)
10
+ throw new Error([
11
+ '❌ App Store Connect 인증이 필요해.',
12
+ '',
13
+ '터미널에서 실행:',
14
+ ' cd c:/users/turbo08/app-gen/packages/mcp-server',
15
+ ' npx tsx src/appstore/setup-cli.ts',
16
+ '',
17
+ 'API Key가 필요해:',
18
+ ' App Store Connect > Users and Access > Integrations > Keys',
19
+ ].join('\n'));
20
+ const url = new URL(`${BASE}${path}`);
21
+ if (params) {
22
+ for (const [k, v] of Object.entries(params))
23
+ url.searchParams.set(k, v);
24
+ }
25
+ const res = await fetch(url.toString(), { headers });
26
+ if (!res.ok) {
27
+ const body = await res.text();
28
+ throw new Error(`App Store API ${res.status}: ${body}`);
29
+ }
30
+ return res.json();
31
+ }
32
+ // ─── 앱 ───
33
+ export async function listApps() {
34
+ const data = await apiGet('/apps', {
35
+ 'fields[apps]': 'name,bundleId,sku,primaryLocale,contentRightsDeclaration',
36
+ 'limit': '200',
37
+ });
38
+ return (data.data ?? []).map((a) => ({
39
+ id: a.id,
40
+ name: a.attributes?.name,
41
+ bundleId: a.attributes?.bundleId,
42
+ sku: a.attributes?.sku,
43
+ primaryLocale: a.attributes?.primaryLocale,
44
+ }));
45
+ }
46
+ export async function getApp(appId) {
47
+ const data = await apiGet(`/apps/${appId}`, {
48
+ 'fields[apps]': 'name,bundleId,sku,primaryLocale,contentRightsDeclaration',
49
+ 'include': 'appStoreVersions',
50
+ });
51
+ return data.data;
52
+ }
53
+ // ─── 버전 ───
54
+ export async function listVersions(appId) {
55
+ const data = await apiGet(`/apps/${appId}/appStoreVersions`, {
56
+ 'fields[appStoreVersions]': 'versionString,appStoreState,releaseType,createdDate',
57
+ 'limit': '10',
58
+ });
59
+ return (data.data ?? []).map((v) => ({
60
+ id: v.id,
61
+ version: v.attributes?.versionString,
62
+ state: v.attributes?.appStoreState,
63
+ releaseType: v.attributes?.releaseType,
64
+ createdDate: v.attributes?.createdDate,
65
+ }));
66
+ }
67
+ // ─── 로컬라이제이션 (메타데이터) ───
68
+ export async function getVersionLocalizations(versionId) {
69
+ const data = await apiGet(`/appStoreVersions/${versionId}/appStoreVersionLocalizations`, {
70
+ 'fields[appStoreVersionLocalizations]': 'locale,description,keywords,promotionalText,whatsNew',
71
+ });
72
+ return (data.data ?? []).map((l) => ({
73
+ id: l.id,
74
+ locale: l.attributes?.locale,
75
+ description: l.attributes?.description,
76
+ keywords: l.attributes?.keywords,
77
+ promotionalText: l.attributes?.promotionalText,
78
+ whatsNew: l.attributes?.whatsNew,
79
+ }));
80
+ }
81
+ // ─── 빌드 ───
82
+ export async function listBuilds(appId) {
83
+ const data = await apiGet(`/builds`, {
84
+ 'filter[app]': appId,
85
+ 'fields[builds]': 'version,uploadedDate,processingState,buildAudienceType',
86
+ 'sort': '-uploadedDate',
87
+ 'limit': '10',
88
+ });
89
+ return (data.data ?? []).map((b) => ({
90
+ id: b.id,
91
+ version: b.attributes?.version,
92
+ uploadedDate: b.attributes?.uploadedDate,
93
+ processingState: b.attributes?.processingState,
94
+ }));
95
+ }
96
+ // ─── TestFlight 베타 그룹 ───
97
+ export async function listBetaGroups(appId) {
98
+ const data = await apiGet(`/apps/${appId}/betaGroups`, {
99
+ 'fields[betaGroups]': 'name,isInternalGroup,publicLink,publicLinkEnabled',
100
+ });
101
+ return (data.data ?? []).map((g) => ({
102
+ id: g.id,
103
+ name: g.attributes?.name,
104
+ isInternal: g.attributes?.isInternalGroup,
105
+ publicLink: g.attributes?.publicLink,
106
+ publicLinkEnabled: g.attributes?.publicLinkEnabled,
107
+ }));
108
+ }
109
+ // ─── 앱 정보 (카테고리 등) ───
110
+ export async function getAppInfo(appId) {
111
+ const data = await apiGet(`/apps/${appId}/appInfos`, {
112
+ 'fields[appInfos]': 'appStoreState,appStoreAgeRating,brazilAgeRating',
113
+ });
114
+ return (data.data ?? []).map((i) => ({
115
+ id: i.id,
116
+ state: i.attributes?.appStoreState,
117
+ ageRating: i.attributes?.appStoreAgeRating,
118
+ }));
119
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ import { login, getStoredTokens } from './google-auth.js';
3
+ import { MIMI_SEED_CLIENT_ID, MIMI_SEED_CLIENT_SECRET } from './constants.js';
4
+ import readline from 'node:readline';
5
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
6
+ const ask = (q) => new Promise((r) => rl.question(q, r));
7
+ async function main() {
8
+ console.log('');
9
+ console.log(' ☕ Mimi Seed — Google 계정 연결');
10
+ console.log('');
11
+ const existing = getStoredTokens();
12
+ if (existing) {
13
+ console.log(' ✅ 이미 연결되어 있어.');
14
+ console.log('');
15
+ const answer = await ask(' 다시 연결할래? (y/N): ');
16
+ if (answer.toLowerCase() !== 'y') {
17
+ rl.close();
18
+ return;
19
+ }
20
+ }
21
+ console.log('');
22
+ console.log(' 🌐 브라우저 열게. Google 계정으로 로그인해줘.');
23
+ console.log('');
24
+ try {
25
+ await login(MIMI_SEED_CLIENT_ID, MIMI_SEED_CLIENT_SECRET);
26
+ console.log('');
27
+ console.log(' ✅ 연결 완료!');
28
+ console.log('');
29
+ console.log(' 이제 Claude Code에서 이렇게 쓸 수 있어:');
30
+ console.log(' "내 Firebase 프로젝트 보여줘"');
31
+ console.log(' "새 Android 앱 등록해줘"');
32
+ console.log(' "google-services.json 다운로드해줘"');
33
+ console.log('');
34
+ }
35
+ catch (err) {
36
+ console.error(' ❌ 연결 실패:', err);
37
+ }
38
+ rl.close();
39
+ }
40
+ main();
@@ -0,0 +1,2 @@
1
+ export declare const MIMI_SEED_CLIENT_ID: string;
2
+ export declare const MIMI_SEED_CLIENT_SECRET: string;
@@ -0,0 +1,11 @@
1
+ // Mimi Seed 내장 OAuth 클라이언트
2
+ // 유저가 직접 만들 필요 없음 — `npx -y @yoonion/mimi-seed-mcp mimi-seed-auth`
3
+ // (또는 로컬 dev 중엔 `npm run auth`) 한 줄이면 끝
4
+ // 환경변수 MIMI_SEED_GOOGLE_CLIENT_ID / MIMI_SEED_GOOGLE_CLIENT_SECRET 로 오버라이드 가능
5
+ // (레거시 PRESEED_GOOGLE_CLIENT_ID / PRESEED_GOOGLE_CLIENT_SECRET 도 계속 인식)
6
+ export const MIMI_SEED_CLIENT_ID = process.env.MIMI_SEED_GOOGLE_CLIENT_ID ??
7
+ process.env.PRESEED_GOOGLE_CLIENT_ID ??
8
+ '980022244769-b6qugfe22pvfadg6ccaq699pc7nuju70.apps.googleusercontent.com';
9
+ export const MIMI_SEED_CLIENT_SECRET = process.env.MIMI_SEED_GOOGLE_CLIENT_SECRET ??
10
+ process.env.PRESEED_GOOGLE_CLIENT_SECRET ??
11
+ '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';
@@ -0,0 +1,22 @@
1
+ export interface StoredTokens {
2
+ access_token: string;
3
+ refresh_token: string;
4
+ token_type: string;
5
+ expiry_date: number;
6
+ }
7
+ export declare function getStoredCredentials(): {
8
+ clientId: string;
9
+ clientSecret: string;
10
+ } | null;
11
+ export declare function saveCredentials(clientId: string, clientSecret: string): void;
12
+ export declare function getStoredTokens(): StoredTokens | null;
13
+ export declare function createOAuth2Client(clientId: string, clientSecret: string): import("google-auth-library").OAuth2Client;
14
+ /**
15
+ * Get authenticated OAuth2 client.
16
+ * Returns null if not authenticated yet.
17
+ */
18
+ export declare function getAuthenticatedClient(): ReturnType<typeof createOAuth2Client> | null;
19
+ /**
20
+ * Interactive login — opens browser, waits for callback
21
+ */
22
+ export declare function login(clientId: string, clientSecret: string): Promise<StoredTokens>;