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 CHANGED
@@ -64,10 +64,10 @@ ADMIN_API_URL = "https://your-api-url.com"
64
64
  MCP를 사용하기 전에 먼저 로그인이 필요합니다:
65
65
 
66
66
  ```bash
67
- ADMIN_API_URL=https://your-api-url.com npx coupon-moa-mcp login
67
+ npx coupon-moa-mcp login --api-url https://your-api-url.com
68
68
  ```
69
69
 
70
- 브라우저가 열리면 Google 로그인 후 토큰이 `~/.coupon-moa-mcp/token.json`에 저장됩니다. 이후 MCP 실행 시 자동으로 사용됩니다.
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, "token.json");
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 loadTokens();
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 loadTokens();
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 saveTokens(tokens);
33
+ await saveConfig({ ...tokens, apiUrl });
22
34
  return tokens.accessToken;
23
35
  }
24
36
  async function refreshAccessToken(apiUrl) {
25
- const stored = await loadTokens();
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 saveTokens({ ...stored, accessToken, savedAt: Date.now() });
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 loadTokens() {
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(tokens, null, 2));
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.error(`[MCP] \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778\uD574\uC8FC\uC138\uC694: ${loginUrl}`);
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-6Y5YCDLU.js";
4
+ } from "./chunk-S6Y6GLDK.js";
5
5
 
6
6
  // src/cli-login.ts
7
- var apiUrl = process.env.ADMIN_API_URL || "http://localhost:8080";
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}`);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  clearTokens
3
- } from "./chunk-6Y5YCDLU.js";
3
+ } from "./chunk-S6Y6GLDK.js";
4
4
 
5
5
  // src/cli-logout.ts
6
6
  try {
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-6Y5YCDLU.js";
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
- const stored = await loadStoredToken();
21
- if (stored) {
22
- this.token = stored;
23
- return;
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` \uC744 \uC2E4\uD589\uD574\uC8FC\uC138\uC694.");
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` \uC73C\uB85C \uB2E4\uC2DC \uB85C\uADF8\uC778\uD574\uC8FC\uC138\uC694.");
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coupon-moa-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "MCP server for Coupon Moa admin — push store data, query brands/cities/stores via Claude",
5
5
  "private": false,
6
6
  "type": "module",