coupon-moa-mcp 0.3.0 → 0.4.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 +2 -2
- package/dist/{chunk-6Y5YCDLU.js → chunk-S6Y6GLDK.js} +22 -17
- package/dist/cli-login.js +13 -3
- package/dist/cli-logout.js +1 -1
- package/dist/index.js +10 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,10 +64,10 @@ ADMIN_API_URL = "https://your-api-url.com"
|
|
|
64
64
|
MCP를 사용하기 전에 먼저 로그인이 필요합니다:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
|
-
|
|
67
|
+
npx coupon-moa-mcp login --api-url https://your-api-url.com
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
브라우저가 열리면 Google 로그인 후
|
|
70
|
+
브라우저가 열리면 Google 로그인 후 토큰과 API URL이 `~/.coupon-moa-mcp/config.json`에 저장됩니다. 이후 MCP 실행 시 `ADMIN_API_URL` 환경변수 없이도 자동으로 사용됩니다.
|
|
71
71
|
|
|
72
72
|
로그아웃:
|
|
73
73
|
|
|
@@ -5,24 +5,36 @@ import { existsSync } from "fs";
|
|
|
5
5
|
import { homedir } from "os";
|
|
6
6
|
import { join } from "path";
|
|
7
7
|
var TOKEN_DIR = join(homedir(), ".coupon-moa-mcp");
|
|
8
|
-
var TOKEN_FILE = join(TOKEN_DIR, "
|
|
8
|
+
var TOKEN_FILE = join(TOKEN_DIR, "config.json");
|
|
9
9
|
var AUTH_PORT = 9401;
|
|
10
|
+
async function loadStoredConfig() {
|
|
11
|
+
try {
|
|
12
|
+
const content = await readFile(TOKEN_FILE, "utf-8");
|
|
13
|
+
return JSON.parse(content);
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
10
18
|
async function loadStoredToken() {
|
|
11
|
-
const stored = await
|
|
19
|
+
const stored = await loadStoredConfig();
|
|
12
20
|
return stored?.accessToken ?? null;
|
|
13
21
|
}
|
|
22
|
+
async function loadStoredApiUrl() {
|
|
23
|
+
const stored = await loadStoredConfig();
|
|
24
|
+
return stored?.apiUrl ?? null;
|
|
25
|
+
}
|
|
14
26
|
async function getAccessToken(apiUrl) {
|
|
15
|
-
const stored = await
|
|
27
|
+
const stored = await loadStoredConfig();
|
|
16
28
|
if (stored) {
|
|
17
29
|
return stored.accessToken;
|
|
18
30
|
}
|
|
19
31
|
console.log("\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778\uD574\uC8FC\uC138\uC694...");
|
|
20
32
|
const tokens = await startOAuthFlow(apiUrl);
|
|
21
|
-
await
|
|
33
|
+
await saveConfig({ ...tokens, apiUrl });
|
|
22
34
|
return tokens.accessToken;
|
|
23
35
|
}
|
|
24
36
|
async function refreshAccessToken(apiUrl) {
|
|
25
|
-
const stored = await
|
|
37
|
+
const stored = await loadStoredConfig();
|
|
26
38
|
if (!stored?.refreshToken) {
|
|
27
39
|
return null;
|
|
28
40
|
}
|
|
@@ -39,7 +51,7 @@ async function refreshAccessToken(apiUrl) {
|
|
|
39
51
|
if (!accessToken) {
|
|
40
52
|
return null;
|
|
41
53
|
}
|
|
42
|
-
await
|
|
54
|
+
await saveConfig({ ...stored, accessToken, savedAt: Date.now() });
|
|
43
55
|
return accessToken;
|
|
44
56
|
} catch {
|
|
45
57
|
return null;
|
|
@@ -51,19 +63,11 @@ async function clearTokens() {
|
|
|
51
63
|
await unlink(TOKEN_FILE);
|
|
52
64
|
}
|
|
53
65
|
}
|
|
54
|
-
async function
|
|
55
|
-
try {
|
|
56
|
-
const content = await readFile(TOKEN_FILE, "utf-8");
|
|
57
|
-
return JSON.parse(content);
|
|
58
|
-
} catch {
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
async function saveTokens(tokens) {
|
|
66
|
+
async function saveConfig(config) {
|
|
63
67
|
if (!existsSync(TOKEN_DIR)) {
|
|
64
68
|
await mkdir(TOKEN_DIR, { recursive: true });
|
|
65
69
|
}
|
|
66
|
-
await writeFile(TOKEN_FILE, JSON.stringify(
|
|
70
|
+
await writeFile(TOKEN_FILE, JSON.stringify(config, null, 2));
|
|
67
71
|
}
|
|
68
72
|
function startOAuthFlow(apiUrl) {
|
|
69
73
|
return new Promise((resolve, reject) => {
|
|
@@ -89,7 +93,7 @@ function startOAuthFlow(apiUrl) {
|
|
|
89
93
|
});
|
|
90
94
|
server.listen(AUTH_PORT, () => {
|
|
91
95
|
const loginUrl = `${apiUrl}/api/auth/google?redirect=http://localhost:${AUTH_PORT}`;
|
|
92
|
-
console.
|
|
96
|
+
console.log(`\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778\uD574\uC8FC\uC138\uC694: ${loginUrl}`);
|
|
93
97
|
import("child_process").then(({ exec }) => {
|
|
94
98
|
const command = process.platform === "darwin" ? `open "${loginUrl}"` : process.platform === "win32" ? `start "${loginUrl}"` : `xdg-open "${loginUrl}"`;
|
|
95
99
|
exec(command);
|
|
@@ -104,6 +108,7 @@ function startOAuthFlow(apiUrl) {
|
|
|
104
108
|
|
|
105
109
|
export {
|
|
106
110
|
loadStoredToken,
|
|
111
|
+
loadStoredApiUrl,
|
|
107
112
|
getAccessToken,
|
|
108
113
|
refreshAccessToken,
|
|
109
114
|
clearTokens
|
package/dist/cli-login.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
clearTokens,
|
|
3
3
|
getAccessToken
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-S6Y6GLDK.js";
|
|
5
5
|
|
|
6
6
|
// src/cli-login.ts
|
|
7
|
-
|
|
7
|
+
function parseApiUrl() {
|
|
8
|
+
const args = process.argv.slice(3);
|
|
9
|
+
for (let i = 0; i < args.length; i++) {
|
|
10
|
+
if (args[i] === "--api-url" && args[i + 1]) {
|
|
11
|
+
return args[i + 1];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return process.env.ADMIN_API_URL || "http://localhost:8080";
|
|
15
|
+
}
|
|
16
|
+
var apiUrl = parseApiUrl();
|
|
8
17
|
console.log("=== coupon-moa-mcp \uB85C\uADF8\uC778 ===");
|
|
9
18
|
console.log(`API URL: ${apiUrl}`);
|
|
10
19
|
console.log("");
|
|
@@ -12,10 +21,11 @@ try {
|
|
|
12
21
|
await clearTokens();
|
|
13
22
|
const token = await getAccessToken(apiUrl);
|
|
14
23
|
console.log("");
|
|
15
|
-
console.log("\uB85C\uADF8\uC778 \uC131\uACF5! \uD1A0\uD070\uC774 \uC800\uC7A5\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
24
|
+
console.log("\uB85C\uADF8\uC778 \uC131\uACF5! \uD1A0\uD070\uACFC API URL\uC774 \uC800\uC7A5\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
16
25
|
console.log(`\uD1A0\uD070 \uBBF8\uB9AC\uBCF4\uAE30: ${token.slice(0, 20)}...`);
|
|
17
26
|
console.log("");
|
|
18
27
|
console.log("\uC774\uC81C Claude Desktop/Code/Codex\uC5D0\uC11C coupon-moa MCP\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
28
|
+
console.log("(ADMIN_API_URL \uD658\uACBD\uBCC0\uC218 \uC5C6\uC774\uB3C4 \uB3D9\uC791\uD569\uB2C8\uB2E4)");
|
|
19
29
|
} catch (error) {
|
|
20
30
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
21
31
|
console.error(`\uB85C\uADF8\uC778 \uC2E4\uD328: ${message}`);
|
package/dist/cli-logout.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
clearTokens,
|
|
3
|
+
loadStoredApiUrl,
|
|
3
4
|
loadStoredToken,
|
|
4
5
|
refreshAccessToken
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-S6Y6GLDK.js";
|
|
6
7
|
|
|
7
8
|
// src/index.ts
|
|
8
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -11,24 +12,18 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
11
12
|
// src/ws/ws-server.ts
|
|
12
13
|
var DEFAULT_API_URL = "http://localhost:8080";
|
|
13
14
|
var AdminApiClient = class {
|
|
14
|
-
apiUrl;
|
|
15
|
+
apiUrl = null;
|
|
15
16
|
token = null;
|
|
16
|
-
constructor() {
|
|
17
|
-
this.apiUrl = process.env.ADMIN_API_URL || DEFAULT_API_URL;
|
|
18
|
-
}
|
|
19
17
|
async initialize() {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
this.token = await loadStoredToken();
|
|
19
|
+
this.apiUrl = await loadStoredApiUrl() || process.env.ADMIN_API_URL || DEFAULT_API_URL;
|
|
20
|
+
if (!this.token) {
|
|
21
|
+
throw new Error("\uB85C\uADF8\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. `npx coupon-moa-mcp login --api-url <URL>` \uC744 \uC2E4\uD589\uD574\uC8FC\uC138\uC694.");
|
|
24
22
|
}
|
|
25
|
-
throw new Error(
|
|
26
|
-
"\uB85C\uADF8\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uBA3C\uC800 `npx coupon-moa-mcp login` \uC744 \uC2E4\uD589\uD574\uC8FC\uC138\uC694."
|
|
27
|
-
);
|
|
28
23
|
}
|
|
29
24
|
async send(method, params) {
|
|
30
|
-
if (!this.token) {
|
|
31
|
-
throw new Error("\uB85C\uADF8\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. `npx coupon-moa-mcp login
|
|
25
|
+
if (!this.token || !this.apiUrl) {
|
|
26
|
+
throw new Error("\uB85C\uADF8\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. `npx coupon-moa-mcp login --api-url <URL>` \uC744 \uC2E4\uD589\uD574\uC8FC\uC138\uC694.");
|
|
32
27
|
}
|
|
33
28
|
let response = await this.fetchApi(method, params);
|
|
34
29
|
if (response.status === 401) {
|
|
@@ -38,7 +33,7 @@ var AdminApiClient = class {
|
|
|
38
33
|
response = await this.fetchApi(method, params);
|
|
39
34
|
} else {
|
|
40
35
|
await clearTokens();
|
|
41
|
-
throw new Error("\uD1A0\uD070\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. `npx coupon-moa-mcp login
|
|
36
|
+
throw new Error("\uD1A0\uD070\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. `npx coupon-moa-mcp login --api-url <URL>` \uC73C\uB85C \uB2E4\uC2DC \uB85C\uADF8\uC778\uD574\uC8FC\uC138\uC694.");
|
|
42
37
|
}
|
|
43
38
|
}
|
|
44
39
|
if (!response.ok) {
|