coupon-moa-mcp 0.1.0 → 0.2.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 +32 -1
- package/bin/coupon-moa-mcp.mjs +9 -1
- package/package.json +1 -1
- package/src/auth.ts +7 -3
- package/src/cli-login.ts +24 -0
- package/src/cli-logout.ts +12 -0
- package/src/index.ts +5 -1
- package/src/ws/ws-server.ts +12 -6
package/README.md
CHANGED
|
@@ -40,9 +40,40 @@ Coupon Moa 어드민 MCP Server. Claude Desktop 또는 Claude Code에서 어드
|
|
|
40
40
|
}
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
### Codex
|
|
44
|
+
|
|
45
|
+
CLI로 추가:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
codex mcp add coupon-moa -- npx -y coupon-moa-mcp
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
또는 `~/.codex/config.toml` (또는 프로젝트 루트 `.codex/config.toml`)에 직접 추가:
|
|
52
|
+
|
|
53
|
+
```toml
|
|
54
|
+
[mcp_servers.coupon-moa]
|
|
55
|
+
command = "npx"
|
|
56
|
+
args = ["-y", "coupon-moa-mcp"]
|
|
57
|
+
|
|
58
|
+
[mcp_servers.coupon-moa.env]
|
|
59
|
+
ADMIN_API_URL = "https://your-api-url.com"
|
|
60
|
+
```
|
|
61
|
+
|
|
43
62
|
## 인증
|
|
44
63
|
|
|
45
|
-
|
|
64
|
+
MCP를 사용하기 전에 먼저 로그인이 필요합니다:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
ADMIN_API_URL=https://your-api-url.com npx coupon-moa-mcp login
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
브라우저가 열리면 Google 로그인 후 토큰이 `~/.coupon-moa-mcp/token.json`에 저장됩니다. 이후 MCP 실행 시 자동으로 사용됩니다.
|
|
71
|
+
|
|
72
|
+
로그아웃:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx coupon-moa-mcp logout
|
|
76
|
+
```
|
|
46
77
|
|
|
47
78
|
## Tools
|
|
48
79
|
|
package/bin/coupon-moa-mcp.mjs
CHANGED
|
@@ -4,4 +4,12 @@ import { pathToFileURL } from 'node:url';
|
|
|
4
4
|
|
|
5
5
|
register('tsx/esm', pathToFileURL('./'));
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const command = process.argv[2];
|
|
8
|
+
|
|
9
|
+
if (command === 'login') {
|
|
10
|
+
await import('../src/cli-login.ts');
|
|
11
|
+
} else if (command === 'logout') {
|
|
12
|
+
await import('../src/cli-logout.ts');
|
|
13
|
+
} else {
|
|
14
|
+
await import('../src/index.ts');
|
|
15
|
+
}
|
package/package.json
CHANGED
package/src/auth.ts
CHANGED
|
@@ -14,16 +14,20 @@ interface StoredTokens {
|
|
|
14
14
|
savedAt: number;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
export async function loadStoredToken(): Promise<string | null> {
|
|
18
|
+
const stored = await loadTokens();
|
|
19
|
+
return stored?.accessToken ?? null;
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
export async function getAccessToken(apiUrl: string): Promise<string> {
|
|
18
23
|
// 저장된 토큰 확인
|
|
19
24
|
const stored = await loadTokens();
|
|
20
25
|
if (stored) {
|
|
21
|
-
// accessToken 유효성은 API 호출 시 확인 — 여기서는 일단 반환
|
|
22
26
|
return stored.accessToken;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
// 토큰 없으면 OAuth 로그인
|
|
26
|
-
console.
|
|
29
|
+
// 토큰 없으면 OAuth 로그인 (CLI login 명령에서만 호출)
|
|
30
|
+
console.log('브라우저에서 로그인해주세요...');
|
|
27
31
|
const tokens = await startOAuthFlow(apiUrl);
|
|
28
32
|
await saveTokens(tokens);
|
|
29
33
|
return tokens.accessToken;
|
package/src/cli-login.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getAccessToken, clearTokens } from './auth.js';
|
|
2
|
+
|
|
3
|
+
const apiUrl = process.env.ADMIN_API_URL || 'http://localhost:8080';
|
|
4
|
+
|
|
5
|
+
console.log('=== coupon-moa-mcp 로그인 ===');
|
|
6
|
+
console.log(`API URL: ${apiUrl}`);
|
|
7
|
+
console.log('');
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
// 기존 토큰 삭제 후 새로 로그인
|
|
11
|
+
await clearTokens();
|
|
12
|
+
const token = await getAccessToken(apiUrl);
|
|
13
|
+
console.log('');
|
|
14
|
+
console.log('로그인 성공! 토큰이 저장되었습니다.');
|
|
15
|
+
console.log(`토큰 미리보기: ${token.slice(0, 20)}...`);
|
|
16
|
+
console.log('');
|
|
17
|
+
console.log('이제 Claude Desktop/Code/Codex에서 coupon-moa MCP를 사용할 수 있습니다.');
|
|
18
|
+
} catch (error) {
|
|
19
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
20
|
+
console.error(`로그인 실패: ${message}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
process.exit(0);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { clearTokens } from './auth.js';
|
|
2
|
+
|
|
3
|
+
try {
|
|
4
|
+
await clearTokens();
|
|
5
|
+
console.log('로그아웃 완료. 저장된 토큰이 삭제되었습니다.');
|
|
6
|
+
} catch (error) {
|
|
7
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
8
|
+
console.error(`로그아웃 실패: ${message}`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
process.exit(0);
|
package/src/index.ts
CHANGED
|
@@ -8,7 +8,11 @@ import {
|
|
|
8
8
|
} from './tools/list-queries.js';
|
|
9
9
|
|
|
10
10
|
const api = new AdminApiClient();
|
|
11
|
-
|
|
11
|
+
try {
|
|
12
|
+
await api.initialize();
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error(`[MCP] ${error instanceof Error ? error.message : error}`);
|
|
15
|
+
}
|
|
12
16
|
|
|
13
17
|
const server = new McpServer({
|
|
14
18
|
name: 'coupon-moa-admin',
|
package/src/ws/ws-server.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { loadStoredToken, refreshAccessToken, clearTokens } from '../auth.js';
|
|
2
2
|
|
|
3
3
|
const DEFAULT_API_URL = 'http://localhost:8080';
|
|
4
4
|
|
|
@@ -11,12 +11,20 @@ export class AdminApiClient {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
async initialize(): Promise<void> {
|
|
14
|
-
|
|
14
|
+
const stored = await loadStoredToken();
|
|
15
|
+
if (stored) {
|
|
16
|
+
this.token = stored;
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
throw new Error(
|
|
21
|
+
'로그인이 필요합니다. 먼저 `npx coupon-moa-mcp login` 을 실행해주세요.'
|
|
22
|
+
);
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
async send(method: string, params: unknown): Promise<unknown> {
|
|
18
26
|
if (!this.token) {
|
|
19
|
-
|
|
27
|
+
throw new Error('로그인이 필요합니다. `npx coupon-moa-mcp login` 을 실행해주세요.');
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
let response = await this.fetchApi(method, params);
|
|
@@ -28,10 +36,8 @@ export class AdminApiClient {
|
|
|
28
36
|
this.token = newToken;
|
|
29
37
|
response = await this.fetchApi(method, params);
|
|
30
38
|
} else {
|
|
31
|
-
// refresh 실패 → 재로그인
|
|
32
39
|
await clearTokens();
|
|
33
|
-
|
|
34
|
-
response = await this.fetchApi(method, params);
|
|
40
|
+
throw new Error('토큰이 만료되었습니다. `npx coupon-moa-mcp login` 으로 다시 로그인해주세요.');
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
43
|
|