@modootoday/datalab-extension-mcp 1.8.13 → 1.8.16
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.en.md +56 -40
- package/README.md +50 -35
- package/dist/{browsers-M2DRBQB6.js → browsers-NCWXRWHL.js} +2 -2
- package/dist/{chunk-M24B42VT.js → chunk-2XHQ7OBB.js} +20 -8
- package/dist/{chunk-CQRDBJXM.js → chunk-5IYKI4EG.js} +30 -22
- package/dist/{chunk-QHEJF6FK.js → chunk-G34WU5WC.js} +48 -4
- package/dist/cli.js +9 -7
- package/dist/{dist-R3VRQN24.js → dist-TFZ67X6Q.js} +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -22,33 +22,36 @@ Set this up once per computer.
|
|
|
22
22
|
|
|
23
23
|
> A work computer may block installs by policy. Check with your IT team first to avoid wasting time.
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## Four steps
|
|
26
26
|
|
|
27
|
-
| Step | What
|
|
28
|
-
| ----------------- |
|
|
29
|
-
| **1. Turn it on** | Flip a switch and
|
|
30
|
-
| **2. Install** | Run one command and paste the
|
|
31
|
-
| **3.
|
|
27
|
+
| Step | What | Where |
|
|
28
|
+
| ----------------- | -------------------------------------------- | ------------------ |
|
|
29
|
+
| **1. Turn it on** | Flip a switch and copy the **install key** | Extension settings |
|
|
30
|
+
| **2. Install** | Run one command and paste the key | Terminal |
|
|
31
|
+
| **3. Connect** | Register **this browser** on the same screen | Extension settings |
|
|
32
|
+
| **4. Verify** | Ask a test question in your AI app | Your AI app |
|
|
33
|
+
|
|
34
|
+
Steps 1 and 3 are the same screen, which tells you how far along you are — leave it open and follow it down.
|
|
32
35
|
|
|
33
36
|
Feel free to read [Safety](#safety) first if you'd like reassurance before starting.
|
|
34
37
|
|
|
35
38
|
## Step 1 — Turn it on
|
|
36
39
|
|
|
37
40
|
1. Click the DataLab Tools icon at Chrome's top-right to open the **side panel**. (If you don't see it, find it under the puzzle-piece icon.)
|
|
38
|
-
2.
|
|
39
|
-
3. A consent screen appears
|
|
41
|
+
2. Press **설정** (Settings) at the bottom of the panel to open the **extension settings** screen.
|
|
42
|
+
3. Turn on the **"다른 AI 앱에 연결 (MCP)"** (Connect to another AI app) switch. A consent screen appears — read it and agree.
|
|
40
43
|
|
|
41
44
|
> "연결된 AI 앱이 요청하면 네이버 데이터가 해당 앱과 그 AI 제공사 서버로 전달돼요."
|
|
42
45
|
>
|
|
43
46
|
> _(When a connected AI app asks, your Naver data is sent to that app and its AI provider's servers.)_
|
|
44
47
|
|
|
45
|
-
4.
|
|
48
|
+
4. A **numbered 1 · 2 · 3 walkthrough** unfolds below. In box **1 [설치 인증키]** (install key), press **[복사]** (copy) to put the value on the clipboard. ([보기] reveals it if you want to see it.)
|
|
46
49
|
|
|
47
|
-
> "이
|
|
50
|
+
> "이 값은 이 컴퓨터 전용 비밀번호예요. 다른 사람에게 보내지 마세요."
|
|
48
51
|
>
|
|
49
|
-
> _(This
|
|
52
|
+
> _(This value is a password for this computer only. Don't send it to anyone.)_
|
|
50
53
|
|
|
51
|
-
Avoid sharing a screenshot or the copied setup.
|
|
54
|
+
Avoid sharing a screenshot or the copied setup. The install key belongs only in AI host configuration and workbench management. It is **not** the registration code from step 3 — browser registration uses separate codes, each shown once and never again.
|
|
52
55
|
|
|
53
56
|
## Step 2 — Install
|
|
54
57
|
|
|
@@ -58,26 +61,28 @@ One command finds **every** AI app on your computer and connects them automatica
|
|
|
58
61
|
2. **Paste this command and press Enter.** The saved command names an exact bootstrap version. At startup and while idle, the connector checks the canonical release and promotes itself to the current secure, compatible version. Diagnostic environments can opt out with `--no-self-update`.
|
|
59
62
|
|
|
60
63
|
```
|
|
61
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
64
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 install
|
|
62
65
|
```
|
|
63
66
|
|
|
64
67
|
Lines of English scrolling by is normal. This only downloads and runs the connector — it touches nothing else.
|
|
65
68
|
|
|
66
|
-
|
|
69
|
+
The helper runs in four chapters — `[1/4]` check the install key, `[2/4]` find AI programs, `[3/4]` connect, `[4/4] 결과` (results).
|
|
70
|
+
|
|
71
|
+
3. **Paste the install key.** The helper asks:
|
|
67
72
|
|
|
68
|
-
> "복사한
|
|
73
|
+
> "복사한 설치 인증키를 붙여넣어 주세요:"
|
|
69
74
|
>
|
|
70
|
-
> _(Paste the
|
|
75
|
+
> _(Paste the install key you copied here:)_
|
|
71
76
|
|
|
72
|
-
Paste the
|
|
77
|
+
Paste the value from step 1 and press Enter. A malformed paste is asked for once more.
|
|
73
78
|
|
|
74
79
|
4. **Confirm.** It asks exactly one question:
|
|
75
80
|
|
|
76
|
-
> "위 N개 프로그램에 연결할까요?" (
|
|
81
|
+
> "위 N개 프로그램에 연결할까요?" (엔터 = 예 / n = 아니오)
|
|
77
82
|
>
|
|
78
|
-
> _(Connect to the N programs above?)_
|
|
83
|
+
> _(Connect to the N programs above? Enter = yes / n = no)_
|
|
79
84
|
|
|
80
|
-
Press `
|
|
85
|
+
Press Enter. Per-app results follow, then a count: `N개 프로그램에 연결했어요.` If it found 0 apps, install an AI app first and run the same command again.
|
|
81
86
|
|
|
82
87
|
5. When you see this, you're done:
|
|
83
88
|
|
|
@@ -105,7 +110,7 @@ If the command errors with `'npx' is not recognized`, Node.js isn't installed. T
|
|
|
105
110
|
curl -fsSL https://app.datalab.tools/install/mcp.sh | sh
|
|
106
111
|
```
|
|
107
112
|
|
|
108
|
-
When it asks for the
|
|
113
|
+
When it asks for the install key, paste the one from step 1. If you have no AI app yet, you can also pick one to install right there (choosing is optional).
|
|
109
114
|
|
|
110
115
|
> Want to see what it does before running it? Open that URL in a browser — it serves the script itself, in plain text.
|
|
111
116
|
|
|
@@ -114,7 +119,17 @@ When it asks for the management token, paste the one from the Step 1 card. If yo
|
|
|
114
119
|
Apps it **connects automatically**: Claude Desktop, Claude Code, ChatGPT desktop, Codex CLI, Gemini CLI, Cursor, Windsurf, Amazon Q Developer, JetBrains Junie, Kiro.
|
|
115
120
|
Apps it **only prints instructions for** (follow the on-screen guide): VS Code, Zed, Cline, Roo Code, LM Studio, Warp.
|
|
116
121
|
|
|
117
|
-
## Step 3 —
|
|
122
|
+
## Step 3 — Connect
|
|
123
|
+
|
|
124
|
+
Once the install finishes, **box 3 on the extension settings screen** changes on its own. No reload needed.
|
|
125
|
+
|
|
126
|
+
- If a **[이 브라우저 연결하기]** (connect this browser) button is there, press it and you're done — the screen mints the registration code and redeems it for you.
|
|
127
|
+
- If it already reads **"등록됨"** (registered), there is nothing to do.
|
|
128
|
+
- If you **already installed from another browser on this computer**, you get a code box instead of the button. Paste the code made in that browser (see [Putting another Chrome on the workbench](#putting-another-chrome-on-the-workbench)).
|
|
129
|
+
|
|
130
|
+
Registration is once per browser. The connection itself lives only while the side panel is open.
|
|
131
|
+
|
|
132
|
+
## Step 4 — Verify
|
|
118
133
|
|
|
119
134
|
- If the card at the top of the side panel is **green** and reads "연결됨" (connected), you're set. If it isn't green, go to [Troubleshooting](#troubleshooting).
|
|
120
135
|
- Ask something like "How did my blog do last week?" in your AI app; if real numbers come back, everything's done.
|
|
@@ -130,10 +145,10 @@ Apps it **only prints instructions for** (follow the on-screen guide): VS Code,
|
|
|
130
145
|
Installing also places the bundled **writing skills**. An AI app reads the name and description of every installed skill on every start, so the more are installed the less room the conversation has. The default therefore places only the ones that work right away.
|
|
131
146
|
|
|
132
147
|
```
|
|
133
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
134
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
135
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
136
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
148
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 skills list
|
|
149
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 skills status
|
|
150
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 skills attach <name>
|
|
151
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 skills detach <name>
|
|
137
152
|
```
|
|
138
153
|
|
|
139
154
|
- `list` shows everything in this version, name and description.
|
|
@@ -149,21 +164,22 @@ If another tool installed a skill of the same name, it is left alone and said so
|
|
|
149
164
|
|
|
150
165
|
The **workbench** is this computer, where the connector runs. If you use more than one Chrome profile, each gets a registration code of its own and joins the same workbench. This is one computer only — the connector listens on 127.0.0.1, so no other machine can reach it, and a code is never something to send to another person.
|
|
151
166
|
|
|
152
|
-
Make the code
|
|
167
|
+
Make the code in extension settings → **다른 AI 앱에 연결 (MCP)** → expand **[다른 브라우저 추가 (선택)]** (add another browser, optional) below box 3 → [등록 코드 만들기]. Or from the terminal.
|
|
153
168
|
|
|
154
169
|
```
|
|
155
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
156
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
157
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
158
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
170
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 browsers add "Work Chrome"
|
|
171
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 browsers list
|
|
172
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 browsers revoke <handle>
|
|
173
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 browsers repair
|
|
159
174
|
```
|
|
160
175
|
|
|
161
|
-
`add` **asks you to paste the
|
|
162
|
-
install, copied from
|
|
163
|
-
keeps only a fingerprint of it, so it cannot show it to you again.
|
|
176
|
+
`add` **asks you to paste the install key** — the same value you used to
|
|
177
|
+
install, copied from box 1 in settings. It then prints a code **once**. The
|
|
178
|
+
connector keeps only a fingerprint of it, so it cannot show it to you again.
|
|
164
179
|
|
|
165
|
-
|
|
166
|
-
|
|
180
|
+
Open the other browser → extension settings → **다른 AI 앱에 연결 (MCP)** → paste
|
|
181
|
+
that code into **3. 이 브라우저 등록** (register this browser). That browser does
|
|
182
|
+
not make a code of its own.
|
|
167
183
|
|
|
168
184
|
- `list` separates `waiting` (a code exists, nobody has used it) from
|
|
169
185
|
`connected`.
|
|
@@ -208,7 +224,7 @@ The connector is running with the code its AI app handed it at startup. **[다
|
|
|
208
224
|
2. Paste it into the AI app's settings
|
|
209
225
|
3. **Fully quit that AI app and start it again** — not just close the window (Windows: tray icon → quit; Mac: `⌘+Q`; Linux: Quit from the app menu)
|
|
210
226
|
|
|
211
|
-
|
|
227
|
+
Item 3 is what brings the connector back with the install key stored in the AI app. Browser registration codes are not changed by this procedure.
|
|
212
228
|
|
|
213
229
|
### "연결 통로(포트)를 다른 프로그램이 쓰고 있어요."
|
|
214
230
|
|
|
@@ -255,7 +271,7 @@ Start it again after a full quit and it picks the tools up. If they still aren't
|
|
|
255
271
|
- **When the connection disappears** — run the very command you used at the start once more; it safely re-registers. (Some AI apps clean up their settings file and drop the connection.)
|
|
256
272
|
|
|
257
273
|
```
|
|
258
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
274
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 install
|
|
259
275
|
```
|
|
260
276
|
|
|
261
277
|
- **`'npx' is not recognized...`** — Node.js isn't installed. The "No Node.js?" one-liner in Step 2 is the simplest fix.
|
|
@@ -285,7 +301,7 @@ Start it again after a full quit and it picks the tools up. If they still aren't
|
|
|
285
301
|
1. Run this in a terminal (it removes the entries registered in your AI apps).
|
|
286
302
|
|
|
287
303
|
```
|
|
288
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
304
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 uninstall
|
|
289
305
|
```
|
|
290
306
|
|
|
291
307
|
2. Turn **MCP 연결** off in the extension settings. The panel is the bridge, so with it off a leftover config line can look nothing up.
|
|
@@ -301,7 +317,7 @@ Deleting the extension also renders any leftover connector inert. A leftover lin
|
|
|
301
317
|
- **Does the ChatGPT website work?** No. Only the installed desktop app connects.
|
|
302
318
|
- **Do I keep it always on?** No. It connects only while the panel is open.
|
|
303
319
|
- **What data does it read?** Blog stats, keywords, ads — what DataLab Tools shows. Scoped to the Naver account you're logged into in Chrome.
|
|
304
|
-
- **Other computers?** Yes, but set it up once per computer; the
|
|
320
|
+
- **Other computers?** Yes, but set it up once per computer; the install key is per-computer too.
|
|
305
321
|
</details>
|
|
306
322
|
|
|
307
323
|
## Developer appendix
|
package/README.md
CHANGED
|
@@ -20,29 +20,32 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
|
|
|
20
20
|
|
|
21
21
|
> 회사 컴퓨터는 보안 정책 때문에 설치가 막혀 있을 수 있어요. 시작 전에 회사 IT 담당자와 먼저 상의하면 시간을 아낄 수 있어요.
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## 네 단계예요
|
|
24
24
|
|
|
25
|
-
| 단계 | 하는 일
|
|
26
|
-
| ----------- |
|
|
27
|
-
| **1. 켜기** |
|
|
28
|
-
| **2. 설치** | 명령 한 줄을 실행하고
|
|
29
|
-
| **3.
|
|
25
|
+
| 단계 | 하는 일 | 어디서 |
|
|
26
|
+
| ----------- | ----------------------------------------- | --------- |
|
|
27
|
+
| **1. 켜기** | 스위치를 켜고 **설치 인증키**를 복사해요 | 확장 설정 |
|
|
28
|
+
| **2. 설치** | 명령 한 줄을 실행하고 인증키를 붙여넣어요 | 터미널 |
|
|
29
|
+
| **3. 연결** | 설정 화면에서 **이 브라우저**를 등록해요 | 확장 설정 |
|
|
30
|
+
| **4. 확인** | AI 앱에서 시험 질문을 해 봐요 | AI 앱 |
|
|
31
|
+
|
|
32
|
+
1·3단계는 같은 화면에서 이어집니다. 설정 화면이 지금 어디까지 됐는지 알려 주니, 화면을 열어 둔 채로 따라오시면 돼요.
|
|
30
33
|
|
|
31
34
|
시작 전에 안심하고 싶다면 아래 [안전](#안전)을 먼저 읽으셔도 좋아요.
|
|
32
35
|
|
|
33
36
|
## 1단계 — 켜기
|
|
34
37
|
|
|
35
38
|
1. Chrome 오른쪽 위의 데이터랩툴즈 아이콘을 눌러 **사이드 패널**을 열어요. (안 보이면 퍼즐 조각 아이콘에서 찾을 수 있어요.)
|
|
36
|
-
2.
|
|
37
|
-
3. 동의 화면이 나오면 내용을 읽고 동의해요.
|
|
39
|
+
2. 패널 아래쪽 **설정**을 눌러 **확장 설정** 화면을 열어요.
|
|
40
|
+
3. **"다른 AI 앱에 연결 (MCP)"** 스위치를 켜요. 동의 화면이 나오면 내용을 읽고 동의해요.
|
|
38
41
|
|
|
39
42
|
> "연결된 AI 앱이 요청하면 네이버 데이터가 해당 앱과 그 AI 제공사 서버로 전달돼요."
|
|
40
43
|
|
|
41
|
-
4.
|
|
44
|
+
4. 아래에 **1 · 2 · 3 번호가 붙은 안내**가 펼쳐져요. **1번 [설치 인증키]** 칸의 **[복사]** 버튼을 누르세요(눈으로 확인하고 싶으면 [보기]).
|
|
42
45
|
|
|
43
|
-
> "이
|
|
46
|
+
> "이 값은 이 컴퓨터 전용 비밀번호예요. 다른 사람에게 보내지 마세요."
|
|
44
47
|
|
|
45
|
-
캡처 공유·채팅 전송은 피해 주세요.
|
|
48
|
+
캡처 공유·채팅 전송은 피해 주세요. 인증키는 AI 앱 설정과 워크벤치 관리에만 씁니다. **3번의 등록 코드와는 다른 값**이라 등록 코드 칸에 붙여넣으면 안 돼요 — 브라우저마다 별도 등록 코드가 발급되고, 그 코드는 만든 직후 한 번만 보여요.
|
|
46
49
|
|
|
47
50
|
## 2단계 — 설치
|
|
48
51
|
|
|
@@ -52,22 +55,24 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
|
|
|
52
55
|
2. **아래 명령을 붙여넣고 Enter를 눌러요.** 저장되는 명령은 정확한 시작 버전을 적습니다. 커넥터는 시작할 때와 유휴 상태에서 정본 릴리스를 확인해 현재의 안전하고 호환되는 버전으로 승격합니다. 진단 환경에서는 `--no-self-update`로 승격을 끌 수 있어요.
|
|
53
56
|
|
|
54
57
|
```
|
|
55
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
58
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 install
|
|
56
59
|
```
|
|
57
60
|
|
|
58
61
|
영어 글자가 여러 줄 올라오는 건 정상이에요. 이 명령은 커넥터를 내려받아 실행할 뿐, 다른 건 건드리지 않아요.
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
도우미는 네 대목으로 진행돼요 — `[1/4] 설치 인증키 확인`, `[2/4] 연결할 AI 프로그램 찾기`, `[3/4] 연결하는 중`, `[4/4] 결과`.
|
|
64
|
+
|
|
65
|
+
3. **설치 인증키를 붙여넣어요.** 도우미가 이렇게 물어봐요.
|
|
61
66
|
|
|
62
|
-
> "복사한
|
|
67
|
+
> "복사한 설치 인증키를 붙여넣어 주세요:"
|
|
63
68
|
|
|
64
|
-
1
|
|
69
|
+
1단계에서 복사한 값을 붙여넣고 Enter를 눌러요. 형식이 어긋나면 한 번 더 물어봐요.
|
|
65
70
|
|
|
66
71
|
4. **연결할지 확인해요.** 딱 하나만 물어봐요.
|
|
67
72
|
|
|
68
|
-
> "위 N개 프로그램에 연결할까요?" (
|
|
73
|
+
> "위 N개 프로그램에 연결할까요?" (엔터 = 예 / n = 아니오)
|
|
69
74
|
|
|
70
|
-
|
|
75
|
+
엔터를 누르면 앱별 결과와 함께 `N개 프로그램에 연결했어요.` 가 나와요. 찾은 앱이 0개면 AI 앱을 먼저 설치한 뒤 같은 명령을 다시 실행하면 돼요.
|
|
71
76
|
|
|
72
77
|
5. 마지막에 이 안내가 보이면 성공이에요.
|
|
73
78
|
|
|
@@ -93,7 +98,7 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
|
|
|
93
98
|
curl -fsSL https://app.datalab.tools/install/mcp.sh | sh
|
|
94
99
|
```
|
|
95
100
|
|
|
96
|
-
중간에
|
|
101
|
+
중간에 설치 인증키를 물어보면 1단계에서 복사한 값을 붙여넣어요. 아직 AI 앱이 하나도 없으면, 원하는 AI 프로그램을 그 자리에서 골라 설치할 수도 있어요(고르는 건 자유예요).
|
|
97
102
|
|
|
98
103
|
> 실행 전에 내용을 확인하고 싶으면 위 주소를 브라우저에 그대로 열어 보세요 — 받아서 실행할 스크립트 원문이 그대로 보여요.
|
|
99
104
|
|
|
@@ -102,7 +107,17 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
|
|
|
102
107
|
**자동으로 연결되는 앱**: Claude Desktop, Claude Code, ChatGPT 데스크톱, Codex CLI, Gemini CLI, Cursor, Windsurf, Amazon Q Developer, JetBrains Junie, Kiro.
|
|
103
108
|
**안내문만 보여 주는 앱**(화면 안내를 따라 하면 돼요): VS Code, Zed, Cline, Roo Code, LM Studio, Warp.
|
|
104
109
|
|
|
105
|
-
## 3단계 —
|
|
110
|
+
## 3단계 — 연결
|
|
111
|
+
|
|
112
|
+
설치가 끝나면 **확장 설정 화면의 3번 칸**이 바뀌어 있어요. 새로고침하지 않아도 돼요.
|
|
113
|
+
|
|
114
|
+
- **[이 브라우저 연결하기]** 버튼이 보이면 그것만 누르면 끝이에요. 등록 코드를 만들어 붙여넣는 과정을 화면이 대신 해 줘요.
|
|
115
|
+
- **"등록됨"** 이 이미 떠 있으면 할 일이 없어요.
|
|
116
|
+
- 이 컴퓨터의 **다른 브라우저에서 이미 설치**했다면 버튼 대신 등록 코드 칸이 나와요. 그 브라우저에서 만든 코드를 넣으세요(아래 [워크벤치에 크롬 하나 더 올리기](#워크벤치에-크롬-하나-더-올리기)).
|
|
117
|
+
|
|
118
|
+
등록은 브라우저마다 한 번이에요. 실제 연결은 사이드 패널을 열어 둔 동안 이루어져요.
|
|
119
|
+
|
|
120
|
+
## 4단계 — 확인
|
|
106
121
|
|
|
107
122
|
- 사이드 패널 위쪽 카드가 **초록색**이고 "연결됨"이 보이면 된 거예요. 초록색이 아니면 [문제해결](#문제해결)로 가세요.
|
|
108
123
|
- AI 앱에서 "지난주 내 블로그 어땠어?" 처럼 물어보고 실제 수치가 나오면 전부 끝이에요.
|
|
@@ -118,10 +133,10 @@ Claude 같은 AI 앱을 데이터랩툴즈에 연결해 주는 작은 무료 프
|
|
|
118
133
|
설치할 때 **글쓰기 도움말(스킬)** 도 함께 놓입니다. AI 앱은 켜질 때마다 설치된 도움말의 이름과 설명을 전부 읽기 때문에, 많이 깔수록 대화에 쓸 자리가 줄어들어요. 그래서 기본은 **바로 쓸 수 있는 것만** 놓습니다.
|
|
119
134
|
|
|
120
135
|
```
|
|
121
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
122
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
123
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
124
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
136
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 skills list
|
|
137
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 skills status
|
|
138
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 skills attach <이름>
|
|
139
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 skills detach <이름>
|
|
125
140
|
```
|
|
126
141
|
|
|
127
142
|
- `list` 는 이 버전에 들어 있는 전부를 이름과 설명으로 보여 줘요.
|
|
@@ -137,18 +152,18 @@ npx -y @modootoday/datalab-extension-mcp@1.8.13 skills detach <이름>
|
|
|
137
152
|
|
|
138
153
|
**워크벤치**는 커넥터가 도는 이 컴퓨터예요. 크롬 프로필을 여러 개 쓰신다면 프로필마다 등록 코드를 하나씩 만들어 같은 워크벤치에 올릴 수 있어요. **한 컴퓨터 안에서만** 됩니다 — 커넥터는 127.0.0.1 에서만 듣기 때문에 다른 컴퓨터는 닿지 못하고, 코드는 남에게 보낼 것이 아니에요.
|
|
139
154
|
|
|
140
|
-
|
|
155
|
+
확장 설정 → **다른 AI 앱에 연결 (MCP)** → 3번 아래의 **[다른 브라우저 추가 (선택)]** 를 펼쳐 [등록 코드 만들기]를 눌러도 되고, 터미널을 쓰셔도 돼요.
|
|
141
156
|
|
|
142
157
|
```
|
|
143
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
144
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
145
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
146
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
158
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 browsers add "회사 크롬"
|
|
159
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 browsers list
|
|
160
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 browsers revoke <handle>
|
|
161
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 browsers repair
|
|
147
162
|
```
|
|
148
163
|
|
|
149
|
-
`add` 를 실행하면
|
|
164
|
+
`add` 를 실행하면 **설치 인증키를 붙여넣으라고 물어봐요** — 설치할 때와 같은 값이고, 설정 화면 1번의 `복사` 버튼으로 얻습니다. 그러면 브라우저 등록 코드가 **한 번만** 출력돼요. 커넥터는 그 코드를 저장하지 않고 지문만 남기므로 다시 보여 줄 수 없어요.
|
|
150
165
|
|
|
151
|
-
만든 코드는 올릴
|
|
166
|
+
만든 코드는 올릴 크롬을 열고 → 확장 설정 → **다른 AI 앱에 연결 (MCP)** → **3. 이 브라우저 등록** 칸에 붙여넣습니다. 그 브라우저는 자기 코드를 따로 만들지 않아요.
|
|
152
167
|
|
|
153
168
|
- `list` 는 `waiting`(코드는 만들었지만 아직 아무도 안 붙음)과 `connected` 를 구분해서 보여 줘요.
|
|
154
169
|
- `revoke` 뒤에는 그 브라우저가 다시 붙지 못해요. 다시 넣으려면 **`revoke` 한 뒤 새로 `add`** 하세요 — 같은 브라우저에 코드만 새로 줘도 거절돼요.
|
|
@@ -181,7 +196,7 @@ AI 앱이 켜질 때 받은 옛 코드로 커넥터가 돌고 있어서 그래
|
|
|
181
196
|
2. 그 내용을 AI 앱 설정에 붙여넣은 뒤
|
|
182
197
|
3. **그 AI 앱을 완전히 종료했다가 다시 켜요** (창 닫기 말고 — Windows 트레이 종료 / Mac `⌘+Q` / Linux 앱 메뉴 Quit)
|
|
183
198
|
|
|
184
|
-
3번까지 해야 커넥터가 AI 앱 설정에 저장된
|
|
199
|
+
3번까지 해야 커넥터가 AI 앱 설정에 저장된 설치 인증키로 다시 떠요. 브라우저 등록 코드는 이 과정에서 바꾸지 않습니다.
|
|
185
200
|
|
|
186
201
|
### "연결 통로(포트)를 다른 프로그램이 쓰고 있어요."
|
|
187
202
|
|
|
@@ -222,7 +237,7 @@ AI가 "그런 기능은 없다" 고 답하면, **"도구 목록을 확인해 보
|
|
|
222
237
|
- **연결이 사라졌을 때** — 처음 실행했던 그 명령을 한 번 더 실행하면 안전하게 다시 등록돼요. (일부 AI 앱이 설정 파일을 정리하며 연결을 지우기도 해요.)
|
|
223
238
|
|
|
224
239
|
```
|
|
225
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
240
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 install
|
|
226
241
|
```
|
|
227
242
|
|
|
228
243
|
- **`'npx'을(는) 내부 또는 외부 명령...`** — Node.js가 없는 거예요. 위 2단계의 "Node.js가 없다면" 한 줄 설치가 가장 간단해요.
|
|
@@ -242,7 +257,7 @@ AI가 "그런 기능은 없다" 고 답하면, **"도구 목록을 확인해 보
|
|
|
242
257
|
- **조회는 그냥 되고, 고치는 건 허락받아요.** 데이터 조회는 바로 돼요. **네이버 글쓰기 창**과 사진·영상 편집기의 내용을 고치려면 사이드패널이나 설정에서 **AI 편집 허용**을 켜야 해요. 두 가지 중에 고르면 됩니다 — 이번 연결에만 켜면 패널을 닫을 때 다시 꺼지고, **항상 허용**으로 저장하면 매번 켜지 않아도 돼요(끄는 것도 같은 자리에서 합니다). 글쓰기 창에서는 제목·본문 넣기와 되돌리기가 열려요. 본문을 통째로 바꾸거나 사진을 넣는 것, 편집기 창을 닫는 것, 지우기는 **항상 허용에 포함되지 않아요** — 그 설정으로는 열리지 않습니다. 요금이 드는 기능은 어떤 설정으로도 열리지 않고, 실행 전에 **매번** 물어봐요.
|
|
243
258
|
- **발행은 하지 않아요.** 글을 올리거나 공개하는 기능은 연결되지 않습니다 — 고친 내용은 글쓰기 창에 남고, 올릴지는 사용자가 직접 정해요.
|
|
244
259
|
- **아이디·비밀번호·쿠키를 저장하지 않아요.** 네이버에 직접 접속하지도 않아요. 모든 조회는 내가 로그인해 둔 내 브라우저 안에서 일어나요.
|
|
245
|
-
- **내 컴퓨터 안에서만 동작해요.** 밖에서는 접속할 수 없고,
|
|
260
|
+
- **내 컴퓨터 안에서만 동작해요.** 밖에서는 접속할 수 없고, 설치 인증키와 확장 확인이라는 이중 잠금이 걸려 있어요.
|
|
246
261
|
- **데이터가 나가는 방식** — "연결된 AI 앱이 요청하면 네이버 데이터가 해당 앱과 그 AI 제공사 서버로 전달돼요." 이게 AI가 답하는 방식이라 처음 켤 때 미리 동의를 받아요. 전달된 데이터를 얼마나 보관·학습하는지는 각 AI 제공사 정책을 따르니, 쓰시는 AI 앱의 개인정보 정책을 확인해 주세요.
|
|
247
262
|
- **설정에는 정확한 버전이 기록되고, 실행 시 안전한 최신 버전을 확인해요.** 커넥터는 시작할 때와 유휴 상태에서 정본 버전을 확인해 최신 보안·호환 버전으로 승격합니다. 특정 버전을 유지해야 하는 진단 환경은 `--no-self-update`를 사용할 수 있어요.
|
|
248
263
|
- **소스가 전부 공개된 오픈소스예요.** 다만 이미 같은 권한으로 실행 중인 악성코드까지 막아 주지는 못해요 — 이 커넥터의 잠금은 원격·타 출처 접근을 막는 장치예요.
|
|
@@ -252,7 +267,7 @@ AI가 "그런 기능은 없다" 고 답하면, **"도구 목록을 확인해 보
|
|
|
252
267
|
1. 터미널에서 아래 명령을 실행해요(AI 앱에 등록된 설정을 지워 줘요).
|
|
253
268
|
|
|
254
269
|
```
|
|
255
|
-
npx -y @modootoday/datalab-extension-mcp@1.8.
|
|
270
|
+
npx -y @modootoday/datalab-extension-mcp@1.8.16 uninstall
|
|
256
271
|
```
|
|
257
272
|
|
|
258
273
|
2. 확장 설정에서 **MCP 연결을 꺼요.** 패널이 브릿지라, 꺼 두면 어딘가 설정 줄이 남아 있어도 AI 앱이 아무것도 조회할 수 없어요.
|
|
@@ -268,7 +283,7 @@ AI가 "그런 기능은 없다" 고 답하면, **"도구 목록을 확인해 보
|
|
|
268
283
|
- **ChatGPT 웹사이트에서도 되나요?** 아니요. 컴퓨터에 설치한 데스크톱 앱만 연결돼요.
|
|
269
284
|
- **항상 켜 둬야 하나요?** 아니요. 패널이 열려 있을 때만 연결돼요.
|
|
270
285
|
- **어떤 데이터를 읽나요?** 블로그 통계·키워드·광고 등 데이터랩툴즈가 보여 주는 것들이에요. 기준은 지금 Chrome에 로그인해 둔 네이버 계정이에요.
|
|
271
|
-
- **다른 컴퓨터에서도 되나요?** 네. 컴퓨터마다 한 번씩 설치하고,
|
|
286
|
+
- **다른 컴퓨터에서도 되나요?** 네. 컴퓨터마다 한 번씩 설치하고, 설치 인증키도 컴퓨터마다 따로예요.
|
|
272
287
|
</details>
|
|
273
288
|
|
|
274
289
|
## 개발자 부록
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_HOST,
|
|
3
3
|
DEFAULT_PORT
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-G34WU5WC.js";
|
|
5
5
|
import {
|
|
6
6
|
TOKEN_PROMPT_GUIDE,
|
|
7
7
|
TOKEN_PROMPT_QUESTION,
|
|
8
8
|
TOKEN_REQUIRED_NON_INTERACTIVE
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-5IYKI4EG.js";
|
|
10
10
|
import "./chunk-MLKGABMK.js";
|
|
11
11
|
|
|
12
12
|
// src/browsers.ts
|
|
@@ -2,9 +2,10 @@ import {
|
|
|
2
2
|
DEFAULT_HOST,
|
|
3
3
|
DEFAULT_PORT,
|
|
4
4
|
mediaToolDescriptors,
|
|
5
|
+
readTakeoverSecret,
|
|
5
6
|
spawnDaemon,
|
|
6
7
|
tryConnect
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-G34WU5WC.js";
|
|
8
9
|
import {
|
|
9
10
|
__export
|
|
10
11
|
} from "./chunk-MLKGABMK.js";
|
|
@@ -15630,6 +15631,15 @@ async function ensureDaemonRunning(deps = {}) {
|
|
|
15630
15631
|
const readVersion = deps.readVersion ?? defaultReadVersion;
|
|
15631
15632
|
const shutdown = deps.shutdown ?? defaultShutdown;
|
|
15632
15633
|
const checkAuthority = deps.checkAuthority ?? defaultCheckAuthority;
|
|
15634
|
+
const readTakeover = deps.readTakeover ?? (() => readTakeoverSecret());
|
|
15635
|
+
const waitForPortRelease = async () => {
|
|
15636
|
+
for (let i = 0; i < attempts; i += 1) {
|
|
15637
|
+
const socket = await connect(host, port, PROBE_TIMEOUT_MS);
|
|
15638
|
+
if (!socket) break;
|
|
15639
|
+
socket.destroy();
|
|
15640
|
+
await sleep(intervalMs);
|
|
15641
|
+
}
|
|
15642
|
+
};
|
|
15633
15643
|
const spawnAndWait = async () => {
|
|
15634
15644
|
spawn(daemonEntry, ["serve"]);
|
|
15635
15645
|
for (let i = 0; i < attempts; i += 1) {
|
|
@@ -15659,8 +15669,15 @@ async function ensureDaemonRunning(deps = {}) {
|
|
|
15659
15669
|
}
|
|
15660
15670
|
if (!isOlderVersion(running, selfVersion)) {
|
|
15661
15671
|
if (await checkAuthority(base, token)) return;
|
|
15672
|
+
const secret = readTakeover();
|
|
15673
|
+
if (secret !== null && await shutdown(base, secret)) {
|
|
15674
|
+
log("retiring the connector that holds this port with a different token");
|
|
15675
|
+
await waitForPortRelease();
|
|
15676
|
+
await spawnAndWait();
|
|
15677
|
+
return;
|
|
15678
|
+
}
|
|
15662
15679
|
throw new Error(
|
|
15663
|
-
`connector authority conflict on port ${String(port)}; every
|
|
15680
|
+
`connector authority conflict on port ${String(port)}; a connector started with a different DataLab workbench token holds it. Quit every AI app so it idles out (about 5 minutes at zero connections), then start this one again.`
|
|
15664
15681
|
);
|
|
15665
15682
|
}
|
|
15666
15683
|
log(`updating the connector: replacing ${running} with ${selfVersion}`);
|
|
@@ -15673,12 +15690,7 @@ async function ensureDaemonRunning(deps = {}) {
|
|
|
15673
15690
|
`connector authority conflict on port ${String(port)}; the running connector did not accept this workbench token`
|
|
15674
15691
|
);
|
|
15675
15692
|
}
|
|
15676
|
-
|
|
15677
|
-
const socket = await connect(host, port, PROBE_TIMEOUT_MS);
|
|
15678
|
-
if (!socket) break;
|
|
15679
|
-
socket.destroy();
|
|
15680
|
-
await sleep(intervalMs);
|
|
15681
|
-
}
|
|
15693
|
+
await waitForPortRelease();
|
|
15682
15694
|
await spawnAndWait();
|
|
15683
15695
|
}
|
|
15684
15696
|
function createAdapterServer(deps = {}) {
|
|
@@ -5,23 +5,15 @@ import { spawn as nodeSpawn } from "child_process";
|
|
|
5
5
|
import { promises as fs } from "fs";
|
|
6
6
|
import { homedir } from "os";
|
|
7
7
|
import { createInterface } from "readline";
|
|
8
|
-
var
|
|
9
|
-
"\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557",
|
|
10
|
-
"\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557",
|
|
11
|
-
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D",
|
|
12
|
-
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557",
|
|
13
|
-
"\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D",
|
|
14
|
-
"\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u255D",
|
|
15
|
-
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \uB370\uC774\uD130\uB7A9\uD234\uC988 \xB7 datalab.tools"
|
|
16
|
-
];
|
|
8
|
+
var WORDMARK = "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \uB370\uC774\uD130\uB7A9\uD234\uC988 \xB7 datalab.tools";
|
|
17
9
|
function printBanner(out, subtitle) {
|
|
18
10
|
out("");
|
|
19
|
-
|
|
20
|
-
out(row);
|
|
21
|
-
}
|
|
22
|
-
out("");
|
|
11
|
+
out(WORDMARK);
|
|
23
12
|
out(` [\uB370\uC774\uD130\uB7A9\uD234\uC988] ${subtitle}`);
|
|
13
|
+
}
|
|
14
|
+
function printStep(out, step, total, title) {
|
|
24
15
|
out("");
|
|
16
|
+
out(`[${String(step)}/${String(total)}] ${title}`);
|
|
25
17
|
}
|
|
26
18
|
var INSTALL_SUBTITLE = "\uCEE4\uB125\uD130 \uC124\uCE58 \uB3C4\uC6B0\uBBF8";
|
|
27
19
|
var UNINSTALL_SUBTITLE = "\uCEE4\uB125\uD130 \uC815\uB9AC \uB3C4\uC6B0\uBBF8";
|
|
@@ -1059,7 +1051,7 @@ async function placeSkills(io, payloadRoot, detectedIds, version, all = false) {
|
|
|
1059
1051
|
refused.push(`${payload.slug}: ${REFUSAL_MESSAGE[out.refusal ?? ""]}`);
|
|
1060
1052
|
}
|
|
1061
1053
|
}
|
|
1062
|
-
const heldNote = held > 0 ? `
|
|
1054
|
+
const heldNote = held > 0 ? ` (\uC870\uAC74\uBD80 ${held}\uAC1C \uC81C\uC678)` : "";
|
|
1063
1055
|
results.push({
|
|
1064
1056
|
hostId: host.id,
|
|
1065
1057
|
displayName: host.displayName,
|
|
@@ -1115,10 +1107,10 @@ var NO_HOSTS_DETECTED = "\uC5F0\uACB0\uD560 \uC218 \uC788\uB294 AI \uD504\uB85C\
|
|
|
1115
1107
|
var SUPPORTED_APPS_HEADER = "\uC9C0\uC6D0\uD558\uB294 \uD504\uB85C\uADF8\uB7A8\uACFC \uB2E4\uC6B4\uB85C\uB4DC \uC8FC\uC18C\uC608\uC694:";
|
|
1116
1108
|
var AFTER_INSTALL_RETRY = "\uD504\uB85C\uADF8\uB7A8\uC744 \uC124\uCE58\uD55C \uB4A4 \uAC19\uC740 \uBA85\uB839\uC5B4\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694.";
|
|
1117
1109
|
var ALREADY_INSTALLED_HINT = "\uC774\uBBF8 \uC124\uCE58\uD588\uB294\uB370 \uBAA9\uB85D\uC5D0 \uC5C6\uB2E4\uBA74, \uC124\uC815 \uD30C\uC77C\uC774 \uC544\uC9C1 \uB9CC\uB4E4\uC5B4\uC9C0\uC9C0 \uC54A\uC558\uC744 \uC218 \uC788\uC5B4\uC694. \uC124\uC815 \uD30C\uC77C\uC740 \uD504\uB85C\uADF8\uB7A8\uC744 \uC124\uCE58\uD560 \uB54C\uAC00 \uC544\uB2C8\uB77C \uCC98\uC74C \uC4F8 \uB54C \uB9CC\uB4E4\uC5B4\uC838\uC694:\n - ChatGPT \uB370\uC2A4\uD06C\uD1B1: \uC124\uC815 \u2192 MCP \uC11C\uBC84 \u2192 \uC11C\uBC84 \uCD94\uAC00\uB97C \uD55C \uBC88 \uC2E4\uD589\n - VS Code: \uBA85\uB839 \uD314\uB808\uD2B8(Ctrl+Shift+P) \u2192 'MCP: Open User Configuration' \uC2E4\uD589\n\uADF8 \uB4A4 \uAC19\uC740 \uBA85\uB839\uC5B4\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 \uCC3E\uC744 \uC218 \uC788\uC5B4\uC694.";
|
|
1118
|
-
var TOKEN_PROMPT_GUIDE = "\uD06C\uB86C\
|
|
1119
|
-
var TOKEN_PROMPT_QUESTION = "\uBCF5\uC0AC\uD55C \
|
|
1120
|
-
var TOKEN_PROMPT_RETRY = "\
|
|
1121
|
-
var TOKEN_REQUIRED_NON_INTERACTIVE = "\
|
|
1110
|
+
var TOKEN_PROMPT_GUIDE = "\uD06C\uB86C \uD655\uC7A5 \uC124\uC815 \u2192 '\uB2E4\uB978 AI \uC571\uC5D0 \uC5F0\uACB0 (MCP)' \u2192 1\uBC88 [\uC124\uCE58 \uC778\uC99D\uD0A4]\uC758 \uAC12\uC744 \uBCF5\uC0AC\uD574 \uC8FC\uC138\uC694.";
|
|
1111
|
+
var TOKEN_PROMPT_QUESTION = "\uBCF5\uC0AC\uD55C \uC124\uCE58 \uC778\uC99D\uD0A4\uB97C \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694:";
|
|
1112
|
+
var TOKEN_PROMPT_RETRY = "\uC778\uC99D\uD0A4 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC544\uC694. \uC124\uC815 \uD654\uBA74\uC5D0\uC11C \uB2E4\uC2DC \uBCF5\uC0AC\uD574 \uBD99\uC5EC\uB123\uC5B4 \uC8FC\uC138\uC694.";
|
|
1113
|
+
var TOKEN_REQUIRED_NON_INTERACTIVE = "\uC124\uCE58 \uC778\uC99D\uD0A4\uAC00 \uD544\uC694\uD574\uC694. \uD06C\uB86C \uD655\uC7A5 \uC124\uC815 \u2192 '\uB2E4\uB978 AI \uC571\uC5D0 \uC5F0\uACB0 (MCP)' \u2192 2\uBC88 \uBA85\uB839\uC758 [\uBCF5\uC0AC] \uBC84\uD2BC\uC744 \uB20C\uB7EC, \uBCF5\uC0AC\uB41C \uBA85\uB839\uC744 \uADF8\uB300\uB85C \uBD99\uC5EC\uB123\uC5B4 \uC2E4\uD589\uD574 \uC8FC\uC138\uC694.";
|
|
1122
1114
|
var CLI_OFFER_INTRO = "\uC6D0\uD558\uC2DC\uBA74 \uC544\uB798 AI \uD504\uB85C\uADF8\uB7A8 \uC911 \uD558\uB098\uB97C \uC9C0\uAE08 \uBC14\uB85C \uC124\uCE58\uD560 \uC218 \uC788\uC5B4\uC694. Node.js \uAE30\uBC18\uC774\uB77C \uC774 \uC790\uB9AC\uC5D0\uC11C \uC124\uCE58\uB3FC\uC694 \u2014 \uC548 \uD558\uC154\uB3C4 \uAD1C\uCC2E\uC544\uC694.";
|
|
1123
1115
|
var CLI_OFFER_SKIP_LABEL = "0. \uC9C0\uAE08\uC740 \uC124\uCE58\uD558\uC9C0 \uC54A\uAE30";
|
|
1124
1116
|
var CLI_OFFER_QUESTION = "\uC124\uCE58\uD560 \uD504\uB85C\uADF8\uB7A8\uC758 \uBC88\uD638\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694";
|
|
@@ -1589,10 +1581,11 @@ function describeError(error) {
|
|
|
1589
1581
|
if (isPermissionError(error)) {
|
|
1590
1582
|
return PERMISSION_DENIED_HINT;
|
|
1591
1583
|
}
|
|
1584
|
+
const next = " \uAC19\uC740 \uBA85\uB839\uC744 \uD55C \uBC88 \uB354 \uC2E4\uD589\uD574 \uBCF4\uC2DC\uACE0, \uADF8\uB798\uB3C4 \uC548 \uB418\uBA74 \uC54C\uB824\uC8FC\uC138\uC694.";
|
|
1592
1585
|
if (error instanceof Error && error.message !== "") {
|
|
1593
|
-
return `\uC608\uC0C1\uD558\uC9C0 \uBABB\uD55C \uBB38\uC81C\uAC00 \uC0DD\uACBC\uC5B4\uC694
|
|
1586
|
+
return `\uC608\uC0C1\uD558\uC9C0 \uBABB\uD55C \uBB38\uC81C\uAC00 \uC0DD\uACBC\uC5B4\uC694.${next} (${error.message})`;
|
|
1594
1587
|
}
|
|
1595
|
-
return
|
|
1588
|
+
return `\uC608\uC0C1\uD558\uC9C0 \uBABB\uD55C \uBB38\uC81C\uAC00 \uC0DD\uACBC\uC5B4\uC694.${next}`;
|
|
1596
1589
|
}
|
|
1597
1590
|
function answeredNo(answer) {
|
|
1598
1591
|
const normalized = answer.trim().toLowerCase();
|
|
@@ -1789,6 +1782,7 @@ async function resolveInstallCredentials(opts, io) {
|
|
|
1789
1782
|
}
|
|
1790
1783
|
async function runInstall(opts, io) {
|
|
1791
1784
|
printBanner((line) => io.out(line), INSTALL_SUBTITLE);
|
|
1785
|
+
printStep((line) => io.out(line), 1, 4, "\uC124\uCE58 \uC778\uC99D\uD0A4 \uD655\uC778");
|
|
1792
1786
|
const resolved = await resolveInstallCredentials(opts, io);
|
|
1793
1787
|
if (resolved === null) return 1;
|
|
1794
1788
|
const validationError = validateInstallOptions(resolved);
|
|
@@ -1796,6 +1790,7 @@ async function runInstall(opts, io) {
|
|
|
1796
1790
|
io.out(validationError);
|
|
1797
1791
|
return 1;
|
|
1798
1792
|
}
|
|
1793
|
+
io.out(" \uC778\uC99D\uD0A4\uB97C \uD655\uC778\uD588\uC5B4\uC694.");
|
|
1799
1794
|
const entryOpts = {
|
|
1800
1795
|
version: resolved.version,
|
|
1801
1796
|
token: resolved.token,
|
|
@@ -1804,6 +1799,8 @@ async function runInstall(opts, io) {
|
|
|
1804
1799
|
if (resolved.port !== void 0) {
|
|
1805
1800
|
entryOpts.port = resolved.port;
|
|
1806
1801
|
}
|
|
1802
|
+
printStep((line) => io.out(line), 2, 4, "\uC5F0\uACB0\uD560 AI \uD504\uB85C\uADF8\uB7A8 \uCC3E\uAE30");
|
|
1803
|
+
io.out(" \uC124\uCE58\uB41C \uD504\uB85C\uADF8\uB7A8\uC744 \uD655\uC778\uD558\uB294 \uC911\uC774\uC5D0\uC694\u2026");
|
|
1807
1804
|
let detected = filterByRequestedHosts(await detectHosts(io), opts.hosts);
|
|
1808
1805
|
if (detected.length === 0) {
|
|
1809
1806
|
const chosen = await offerCliInstall(io);
|
|
@@ -1828,6 +1825,7 @@ async function runInstall(opts, io) {
|
|
|
1828
1825
|
return 0;
|
|
1829
1826
|
}
|
|
1830
1827
|
}
|
|
1828
|
+
printStep((line) => io.out(line), 3, 4, "\uC5F0\uACB0\uD558\uB294 \uC911");
|
|
1831
1829
|
const results = [];
|
|
1832
1830
|
for (const d of detected) {
|
|
1833
1831
|
try {
|
|
@@ -1868,7 +1866,7 @@ async function runInstall(opts, io) {
|
|
|
1868
1866
|
)
|
|
1869
1867
|
);
|
|
1870
1868
|
}
|
|
1871
|
-
io.out("");
|
|
1869
|
+
printStep((line) => io.out(line), 4, 4, "\uACB0\uACFC");
|
|
1872
1870
|
for (const result of results) {
|
|
1873
1871
|
printResult(io, result);
|
|
1874
1872
|
if (result.status === "success" && result.tier === 1) {
|
|
@@ -1878,6 +1876,16 @@ async function runInstall(opts, io) {
|
|
|
1878
1876
|
}
|
|
1879
1877
|
}
|
|
1880
1878
|
}
|
|
1879
|
+
const connected = results.filter(
|
|
1880
|
+
(r) => r.status === "success" && r.surface !== "skills"
|
|
1881
|
+
).length;
|
|
1882
|
+
const failed = results.filter(
|
|
1883
|
+
(r) => r.status === "failed" && r.tier !== 3
|
|
1884
|
+
).length;
|
|
1885
|
+
io.out("");
|
|
1886
|
+
io.out(
|
|
1887
|
+
failed === 0 ? `${String(connected)}\uAC1C \uD504\uB85C\uADF8\uB7A8\uC5D0 \uC5F0\uACB0\uD588\uC5B4\uC694.` : `${String(connected)}\uAC1C \uC5F0\uACB0, ${String(failed)}\uAC1C \uC2E4\uD328.`
|
|
1888
|
+
);
|
|
1881
1889
|
for (const d of detected) {
|
|
1882
1890
|
if (d.tier === 3 && d.snippet !== void 0) {
|
|
1883
1891
|
await printSnippetBlock(io, d.snippet, entryOpts);
|
|
@@ -2116,7 +2124,7 @@ function createNodeIo() {
|
|
|
2116
2124
|
});
|
|
2117
2125
|
try {
|
|
2118
2126
|
return await new Promise((resolve) => {
|
|
2119
|
-
rl.question(`${question} (
|
|
2127
|
+
rl.question(`${question} (\uC5D4\uD130 = \uC608 / n = \uC544\uB2C8\uC624) `, resolve);
|
|
2120
2128
|
});
|
|
2121
2129
|
} finally {
|
|
2122
2130
|
rl.close();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ../mcp-server/dist/chunk-
|
|
1
|
+
// ../mcp-server/dist/chunk-FEN47OJU.js
|
|
2
2
|
import { createHash } from "crypto";
|
|
3
3
|
import {
|
|
4
4
|
appendFileSync,
|
|
@@ -26,8 +26,13 @@ import { basename, isAbsolute } from "path";
|
|
|
26
26
|
import { createConnection } from "net";
|
|
27
27
|
import { spawn } from "child_process";
|
|
28
28
|
import { closeSync } from "fs";
|
|
29
|
-
import {
|
|
29
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
30
|
+
import { chmodSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
31
|
+
import { homedir as homedir2 } from "os";
|
|
30
32
|
import { join as join4 } from "path";
|
|
33
|
+
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
34
|
+
import { createRequire } from "module";
|
|
35
|
+
import { join as join5 } from "path";
|
|
31
36
|
var __defProp = Object.defineProperty;
|
|
32
37
|
var __export = (target, all) => {
|
|
33
38
|
for (var name in all)
|
|
@@ -17640,7 +17645,8 @@ function createHttpBridge(opts) {
|
|
|
17640
17645
|
if (req.method === "POST" && url2.pathname === "/mcp/shutdown") {
|
|
17641
17646
|
void readJson(req, MAX_BODY_BYTES).then((body) => {
|
|
17642
17647
|
const token = body?.token;
|
|
17643
|
-
|
|
17648
|
+
const presented = typeof token === "string" ? token : "";
|
|
17649
|
+
if (!opts.bridge.verifyRootToken(presented) && !(opts.verifyTakeover?.(presented) ?? false)) {
|
|
17644
17650
|
json2(res, 403, { error: "unauthorized" });
|
|
17645
17651
|
return;
|
|
17646
17652
|
}
|
|
@@ -18140,6 +18146,35 @@ var Lifecycle = class {
|
|
|
18140
18146
|
}
|
|
18141
18147
|
}
|
|
18142
18148
|
};
|
|
18149
|
+
var FILE_NAME3 = "takeover";
|
|
18150
|
+
var OWNER_ONLY = 384;
|
|
18151
|
+
function takeoverPath(home = homedir2()) {
|
|
18152
|
+
return join4(logDir(home), FILE_NAME3);
|
|
18153
|
+
}
|
|
18154
|
+
function writeTakeoverSecret(home = homedir2()) {
|
|
18155
|
+
try {
|
|
18156
|
+
mkdirSync2(logDir(home), { recursive: true });
|
|
18157
|
+
const secret = randomBytes2(32).toString("hex");
|
|
18158
|
+
const path = takeoverPath(home);
|
|
18159
|
+
writeFileSync(path, `${secret}
|
|
18160
|
+
`, { encoding: "utf8", mode: OWNER_ONLY });
|
|
18161
|
+
try {
|
|
18162
|
+
chmodSync(path, OWNER_ONLY);
|
|
18163
|
+
} catch {
|
|
18164
|
+
}
|
|
18165
|
+
return secret;
|
|
18166
|
+
} catch {
|
|
18167
|
+
return null;
|
|
18168
|
+
}
|
|
18169
|
+
}
|
|
18170
|
+
function readTakeoverSecret(home = homedir2()) {
|
|
18171
|
+
try {
|
|
18172
|
+
const raw = readFileSync2(takeoverPath(home), "utf8").trim();
|
|
18173
|
+
return raw === "" ? null : raw;
|
|
18174
|
+
} catch {
|
|
18175
|
+
return null;
|
|
18176
|
+
}
|
|
18177
|
+
}
|
|
18143
18178
|
var NAME = MCP_SERVER_HEALTH_NAME;
|
|
18144
18179
|
function readVersion() {
|
|
18145
18180
|
return createRequire(import.meta.url)("../package.json").version;
|
|
@@ -18156,6 +18191,7 @@ function runDaemon(env, deps = {}) {
|
|
|
18156
18191
|
}
|
|
18157
18192
|
const { port, host, token, extensionIds } = resolved.config;
|
|
18158
18193
|
const version2 = deps.version ?? readVersion();
|
|
18194
|
+
let takeoverSecret = null;
|
|
18159
18195
|
const lifecycle = new Lifecycle({
|
|
18160
18196
|
idleMs: deps.idleMs,
|
|
18161
18197
|
onIdle: () => {
|
|
@@ -18201,7 +18237,7 @@ function runDaemon(env, deps = {}) {
|
|
|
18201
18237
|
log
|
|
18202
18238
|
});
|
|
18203
18239
|
const stateDir = logDir(deps.home);
|
|
18204
|
-
const mediaStage = createMediaStage({ root:
|
|
18240
|
+
const mediaStage = createMediaStage({ root: join5(stateDir, "media") });
|
|
18205
18241
|
const registry2 = createBrowserRegistry({
|
|
18206
18242
|
path: ledgerPath(deps.home),
|
|
18207
18243
|
claimed: claimedBrowsers,
|
|
@@ -18223,6 +18259,7 @@ function runDaemon(env, deps = {}) {
|
|
|
18223
18259
|
release: () => lifecycle.release(),
|
|
18224
18260
|
bump: () => lifecycle.bump()
|
|
18225
18261
|
},
|
|
18262
|
+
verifyTakeover: (presented) => takeoverSecret !== null && presented.length === takeoverSecret.length && timingSafeEqual2(Buffer.from(presented), Buffer.from(takeoverSecret)),
|
|
18226
18263
|
onShutdown: () => {
|
|
18227
18264
|
log("shutdown requested by an updating adapter; stepping aside");
|
|
18228
18265
|
void http.close().finally(() => exit(0));
|
|
@@ -18240,6 +18277,12 @@ function runDaemon(env, deps = {}) {
|
|
|
18240
18277
|
() => {
|
|
18241
18278
|
log(`listening on http://${host}:${port} \u2014 waiting for the side panel`);
|
|
18242
18279
|
lifecycle.start();
|
|
18280
|
+
takeoverSecret = writeTakeoverSecret(deps.home);
|
|
18281
|
+
if (takeoverSecret === null) {
|
|
18282
|
+
log(
|
|
18283
|
+
"could not write the takeover file; a host configured with a different token will not be able to retire this connector automatically"
|
|
18284
|
+
);
|
|
18285
|
+
}
|
|
18243
18286
|
},
|
|
18244
18287
|
() => {
|
|
18245
18288
|
log("another instance already owns the port; exiting");
|
|
@@ -18268,5 +18311,6 @@ export {
|
|
|
18268
18311
|
DEFAULT_HOST,
|
|
18269
18312
|
tryConnect,
|
|
18270
18313
|
spawnDaemon,
|
|
18314
|
+
readTakeoverSecret,
|
|
18271
18315
|
runDaemon
|
|
18272
18316
|
};
|
package/dist/cli.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
dispatchCli,
|
|
4
4
|
runAdapter
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-2XHQ7OBB.js";
|
|
6
6
|
import {
|
|
7
7
|
runDaemon
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-G34WU5WC.js";
|
|
9
9
|
import "./chunk-MLKGABMK.js";
|
|
10
10
|
|
|
11
11
|
// src/cli.ts
|
|
@@ -17,7 +17,7 @@ function log(message) {
|
|
|
17
17
|
`);
|
|
18
18
|
}
|
|
19
19
|
async function runInstaller(sub, argv) {
|
|
20
|
-
const { createNodeIo, runInstall, runUninstall } = await import("./dist-
|
|
20
|
+
const { createNodeIo, runInstall, runUninstall } = await import("./dist-TFZ67X6Q.js");
|
|
21
21
|
const opts = { version: VERSION };
|
|
22
22
|
for (let i = 0; i < argv.length; i += 1) {
|
|
23
23
|
const flag = argv[i];
|
|
@@ -62,8 +62,8 @@ async function main() {
|
|
|
62
62
|
// Loaded lazily, like the installer: the adapter path is what an MCP host
|
|
63
63
|
// spawns and must not pay for a command only an operator types.
|
|
64
64
|
browsers: async (argv) => {
|
|
65
|
-
const { runBrowsers } = await import("./browsers-
|
|
66
|
-
const { createNodeIo } = await import("./dist-
|
|
65
|
+
const { runBrowsers } = await import("./browsers-NCWXRWHL.js");
|
|
66
|
+
const { createNodeIo } = await import("./dist-TFZ67X6Q.js");
|
|
67
67
|
const io = createNodeIo();
|
|
68
68
|
const code = await runBrowsers(argv, {
|
|
69
69
|
env: process.env,
|
|
@@ -88,7 +88,7 @@ async function main() {
|
|
|
88
88
|
attachSkill,
|
|
89
89
|
detachSkill,
|
|
90
90
|
skillsPayloadRoot
|
|
91
|
-
} = await import("./dist-
|
|
91
|
+
} = await import("./dist-TFZ67X6Q.js");
|
|
92
92
|
const io = createNodeIo();
|
|
93
93
|
const root = skillsPayloadRoot();
|
|
94
94
|
const code = await runSkills(argv, {
|
|
@@ -118,5 +118,7 @@ async function main() {
|
|
|
118
118
|
}
|
|
119
119
|
main().catch((err) => {
|
|
120
120
|
log(err instanceof Error ? err.stack ?? err.message : String(err));
|
|
121
|
-
process.
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
const bail = setTimeout(() => process.exit(1), 2e3);
|
|
123
|
+
bail.unref?.();
|
|
122
124
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -50,6 +50,11 @@ interface EnsureRunningDeps {
|
|
|
50
50
|
log?: (message: string) => void;
|
|
51
51
|
attempts?: number;
|
|
52
52
|
intervalMs?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Reads the running daemon's takeover secret from disk. Injected in tests;
|
|
55
|
+
* defaults to the real file.
|
|
56
|
+
*/
|
|
57
|
+
readTakeover?: () => string | null;
|
|
53
58
|
/**
|
|
54
59
|
* This adapter's own version. When set, a running daemon older than this is
|
|
55
60
|
* replaced; one at this version or newer is left alone, so an older adapter
|
package/dist/index.js
CHANGED