nworks 0.7.0 → 1.0.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 +163 -141
- package/dist/index.js +79 -24
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +46 -18
- package/dist/mcp.js.map +1 -1
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -1,85 +1,141 @@
|
|
|
1
1
|
# nworks
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/nworks)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://www.npmjs.com/package/nworks)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
First full MCP server for NAVER WORKS.
|
|
8
|
+
NAVER WORKS API를 스크립트나 AI 에이전트에서 쓰기 쉽게 만든 CLI + MCP 서버입니다.
|
|
9
|
+
|
|
10
|
+
**Automate messages, calendar, drive, mail, tasks, and boards — from CLI or AI agents.**
|
|
11
|
+
|
|
12
|
+
## Quickstart
|
|
6
13
|
|
|
7
14
|
```bash
|
|
8
|
-
|
|
15
|
+
npm install -g nworks
|
|
16
|
+
nworks login --user
|
|
17
|
+
nworks calendar list
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### AI 에이전트가 실제로 이렇게 씁니다
|
|
21
|
+
|
|
9
22
|
```
|
|
23
|
+
User: 오늘 일정 알려줘
|
|
24
|
+
|
|
25
|
+
Claude → nworks_calendar_list
|
|
26
|
+
→ 3건: 스탠드업(10:00), 점심미팅(12:00), 코드리뷰(15:00)
|
|
10
27
|
|
|
11
|
-
|
|
28
|
+
User: 팀 채널에 배포 완료 메시지 보내줘
|
|
29
|
+
|
|
30
|
+
Claude → nworks_message_send
|
|
31
|
+
{ "channel": "C001", "text": "v1.2.0 배포 완료" }
|
|
32
|
+
→ Message sent
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Install
|
|
12
36
|
|
|
13
37
|
```bash
|
|
14
|
-
|
|
38
|
+
npx nworks # 바로 실행
|
|
39
|
+
npm install -g nworks # 글로벌 설치
|
|
15
40
|
```
|
|
16
41
|
|
|
17
|
-
##
|
|
42
|
+
## 로그인
|
|
18
43
|
|
|
19
44
|
```bash
|
|
20
|
-
#
|
|
21
|
-
nworks login \
|
|
22
|
-
--client-id <CLIENT_ID> \
|
|
23
|
-
--client-secret <CLIENT_SECRET> \
|
|
24
|
-
--service-account <SERVICE_ACCOUNT> \
|
|
25
|
-
--private-key <PATH_TO_KEY> \
|
|
26
|
-
--bot-id <BOT_ID>
|
|
27
|
-
|
|
28
|
-
# User OAuth 로그인 (캘린더, 드라이브 등 사용자 API용)
|
|
45
|
+
# User OAuth (캘린더, 드라이브, 메일, 할 일, 게시판)
|
|
29
46
|
nworks login --user --scope "calendar,calendar.read,file,mail,task,board,user.read"
|
|
30
47
|
|
|
48
|
+
# 봇 메시지 전송이 필요한 경우 (Service Account)
|
|
49
|
+
nworks login
|
|
50
|
+
|
|
31
51
|
# 인증 확인
|
|
32
52
|
nworks whoami
|
|
33
53
|
|
|
34
|
-
#
|
|
35
|
-
nworks
|
|
54
|
+
# 로그아웃
|
|
55
|
+
nworks logout
|
|
56
|
+
```
|
|
36
57
|
|
|
37
|
-
|
|
38
|
-
nworks directory members
|
|
58
|
+
> `nworks login --user`는 CLIENT_ID + CLIENT_SECRET만 있으면 됩니다. 환경변수나 기존 설정에 이미 있는 값은 다시 물어보지 않습니다.
|
|
39
59
|
|
|
40
|
-
|
|
41
|
-
nworks calendar list
|
|
60
|
+
> **Developer Console 설정**: User OAuth를 사용하려면 [Developer Console](https://dev.worksmobile.com)에서 Redirect URL에 `http://localhost:9876/callback`을 등록해야 합니다.
|
|
42
61
|
|
|
43
|
-
|
|
44
|
-
nworks calendar create --title "회의" --start "2026-03-14T14:00+09:00" --end "2026-03-14T15:00+09:00"
|
|
62
|
+
---
|
|
45
63
|
|
|
46
|
-
|
|
47
|
-
nworks drive list
|
|
64
|
+
## MCP 서버 (AI 에이전트 연동)
|
|
48
65
|
|
|
49
|
-
|
|
50
|
-
|
|
66
|
+
Claude Desktop, Cursor 등에서 MCP server로 NAVER WORKS 22개 도구를 사용할 수 있습니다.
|
|
67
|
+
메시지 전송, 일정 관리, 파일 업로드, 메일, 할 일, 게시판까지 — AI 에이전트가 NAVER WORKS 워크플로우를 자동화합니다.
|
|
51
68
|
|
|
52
|
-
|
|
53
|
-
nworks mail send --to "user@example.com" --subject "제목" --body "내용"
|
|
69
|
+
### 설정
|
|
54
70
|
|
|
55
|
-
|
|
56
|
-
nworks mail list
|
|
71
|
+
먼저 로그인합니다:
|
|
57
72
|
|
|
58
|
-
|
|
59
|
-
nworks task
|
|
60
|
-
|
|
61
|
-
# 할 일 생성
|
|
62
|
-
nworks task create --title "코드 리뷰" --body "PR #382 리뷰"
|
|
73
|
+
```bash
|
|
74
|
+
nworks login --user --scope "calendar,calendar.read,file,mail,task,board,user.read"
|
|
75
|
+
```
|
|
63
76
|
|
|
64
|
-
|
|
65
|
-
nworks board list
|
|
77
|
+
그리고 MCP 설정에 추가합니다 (`~/.config/claude/claude_desktop_config.json`):
|
|
66
78
|
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"mcpServers": {
|
|
82
|
+
"nworks": {
|
|
83
|
+
"command": "nworks",
|
|
84
|
+
"args": ["mcp"]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
69
88
|
```
|
|
70
89
|
|
|
71
|
-
|
|
90
|
+
끝입니다. 인증 한 번으로 22개 도구 모두 사용 가능 — 별도 env 설정이 필요 없습니다.
|
|
91
|
+
|
|
92
|
+
### MCP 도구 목록 (22개)
|
|
93
|
+
|
|
94
|
+
| 도구 | 설명 | 필요 인증 |
|
|
95
|
+
|------|------|----------|
|
|
96
|
+
| `nworks_message_send` | 사용자/채널에 메시지 전송 | Service Account |
|
|
97
|
+
| `nworks_message_members` | 채널 구성원 조회 | Service Account |
|
|
98
|
+
| `nworks_directory_members` | 조직 구성원 조회 | Service Account |
|
|
99
|
+
| `nworks_calendar_list` | 캘린더 일정 조회 | User OAuth |
|
|
100
|
+
| `nworks_calendar_create` | 캘린더 일정 생성 | User OAuth |
|
|
101
|
+
| `nworks_calendar_update` | 캘린더 일정 수정 | User OAuth |
|
|
102
|
+
| `nworks_calendar_delete` | 캘린더 일정 삭제 | User OAuth |
|
|
103
|
+
| `nworks_drive_list` | 드라이브 파일/폴더 목록 | User OAuth |
|
|
104
|
+
| `nworks_drive_upload` | 드라이브 파일 업로드 | User OAuth |
|
|
105
|
+
| `nworks_drive_download` | 드라이브 파일 다운로드 | User OAuth |
|
|
106
|
+
| `nworks_mail_send` | 메일 전송 | User OAuth |
|
|
107
|
+
| `nworks_mail_list` | 메일 목록 조회 | User OAuth |
|
|
108
|
+
| `nworks_mail_read` | 메일 상세 조회 | User OAuth |
|
|
109
|
+
| `nworks_task_list` | 할 일 목록 조회 | User OAuth |
|
|
110
|
+
| `nworks_task_create` | 할 일 생성 | User OAuth |
|
|
111
|
+
| `nworks_task_update` | 할 일 수정/완료 | User OAuth |
|
|
112
|
+
| `nworks_task_delete` | 할 일 삭제 | User OAuth |
|
|
113
|
+
| `nworks_board_list` | 게시판 목록 조회 | User OAuth |
|
|
114
|
+
| `nworks_board_posts` | 게시판 글 목록 조회 | User OAuth |
|
|
115
|
+
| `nworks_board_read` | 게시판 글 상세 조회 | User OAuth |
|
|
116
|
+
| `nworks_board_create` | 게시판 글 작성 | User OAuth |
|
|
117
|
+
| `nworks_whoami` | 인증 상태 확인 | — |
|
|
118
|
+
|
|
119
|
+
### AI 에이전트 사용 예시
|
|
72
120
|
|
|
73
|
-
|
|
121
|
+
```
|
|
122
|
+
User: 내일 오후 2시에 회의 잡고, 팀 채널에 알려줘
|
|
74
123
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
124
|
+
Claude → nworks_calendar_create
|
|
125
|
+
{ "summary": "회의", "start": "2026-03-15T14:00:00", "end": "2026-03-15T15:00:00" }
|
|
126
|
+
→ Event created
|
|
127
|
+
|
|
128
|
+
Claude → nworks_message_send
|
|
129
|
+
{ "channel": "C001", "text": "내일 14:00 회의가 잡혔습니다" }
|
|
130
|
+
→ Message sent
|
|
81
131
|
```
|
|
82
132
|
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## CLI 사용법
|
|
136
|
+
|
|
137
|
+
> 모든 명령어에 `--json` 지원 (파이프, 스크립트, 에이전트 파싱 용이). `message send`, `mail send`, `drive upload`는 `--dry-run`으로 실제 전송 없이 테스트 가능.
|
|
138
|
+
|
|
83
139
|
### 메시지 (Bot API)
|
|
84
140
|
|
|
85
141
|
```bash
|
|
@@ -107,22 +163,16 @@ nworks message members --channel <channelId>
|
|
|
107
163
|
nworks directory members # 조직 구성원 목록
|
|
108
164
|
```
|
|
109
165
|
|
|
110
|
-
### 캘린더 (User OAuth
|
|
166
|
+
### 캘린더 (User OAuth)
|
|
111
167
|
|
|
112
168
|
```bash
|
|
113
169
|
# 오늘 일정 조회
|
|
114
170
|
nworks calendar list
|
|
115
171
|
|
|
116
|
-
# 기간 지정
|
|
172
|
+
# 기간 지정
|
|
117
173
|
nworks calendar list --from "2026-03-14T00:00:00+09:00" --until "2026-03-14T23:59:59+09:00"
|
|
118
174
|
|
|
119
|
-
# 특정 사용자의 일정
|
|
120
|
-
nworks calendar list --user <userId>
|
|
121
|
-
|
|
122
175
|
# 일정 생성
|
|
123
|
-
nworks calendar create --title "회의" --start "2026-03-14T14:00:00+09:00" --end "2026-03-14T15:00:00+09:00"
|
|
124
|
-
|
|
125
|
-
# 초 생략 가능
|
|
126
176
|
nworks calendar create --title "회의" --start "2026-03-14T14:00+09:00" --end "2026-03-14T15:00+09:00"
|
|
127
177
|
|
|
128
178
|
# 장소/설명 포함
|
|
@@ -136,36 +186,22 @@ nworks calendar create --title "팀 회의" --start "2026-03-14T10:00+09:00" --e
|
|
|
136
186
|
# 일정 수정
|
|
137
187
|
nworks calendar update --id <eventId> --title "변경된 제목"
|
|
138
188
|
|
|
139
|
-
# 시간 변경
|
|
140
|
-
nworks calendar update --id <eventId> --start "2026-03-14T15:00+09:00" --end "2026-03-14T16:00+09:00"
|
|
141
|
-
|
|
142
189
|
# 일정 삭제
|
|
143
190
|
nworks calendar delete --id <eventId>
|
|
144
191
|
```
|
|
145
192
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
### 드라이브 (User OAuth 필요)
|
|
193
|
+
### 드라이브 (User OAuth)
|
|
149
194
|
|
|
150
195
|
```bash
|
|
151
|
-
#
|
|
196
|
+
# 파일/폴더 목록
|
|
152
197
|
nworks drive list
|
|
153
198
|
|
|
154
|
-
#
|
|
155
|
-
nworks drive list --folder <folderId>
|
|
156
|
-
|
|
157
|
-
# 페이지네이션
|
|
158
|
-
nworks drive list --count 50 --cursor <nextCursor>
|
|
159
|
-
|
|
160
|
-
# 파일 업로드 (루트)
|
|
199
|
+
# 파일 업로드
|
|
161
200
|
nworks drive upload --file ./report.pdf
|
|
162
201
|
|
|
163
202
|
# 특정 폴더에 업로드
|
|
164
203
|
nworks drive upload --file ./report.pdf --folder <folderId>
|
|
165
204
|
|
|
166
|
-
# 동일 파일명 덮어쓰기
|
|
167
|
-
nworks drive upload --file ./report.pdf --overwrite
|
|
168
|
-
|
|
169
205
|
# 파일 다운로드
|
|
170
206
|
nworks drive download --file-id <fileId>
|
|
171
207
|
|
|
@@ -173,9 +209,7 @@ nworks drive download --file-id <fileId>
|
|
|
173
209
|
nworks drive download --file-id <fileId> --out ./downloads --name report.pdf
|
|
174
210
|
```
|
|
175
211
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
### 메일 (User OAuth 필요)
|
|
212
|
+
### 메일 (User OAuth)
|
|
179
213
|
|
|
180
214
|
```bash
|
|
181
215
|
# 메일 전송
|
|
@@ -194,12 +228,10 @@ nworks mail list --unread
|
|
|
194
228
|
nworks mail read --id <mailId>
|
|
195
229
|
```
|
|
196
230
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
### 할 일 (User OAuth 필요)
|
|
231
|
+
### 할 일 (User OAuth)
|
|
200
232
|
|
|
201
233
|
```bash
|
|
202
|
-
# 할 일 목록
|
|
234
|
+
# 할 일 목록
|
|
203
235
|
nworks task list
|
|
204
236
|
|
|
205
237
|
# 미완료만 조회
|
|
@@ -214,16 +246,11 @@ nworks task create --title "배포" --due 2026-03-20
|
|
|
214
246
|
# 할 일 완료 처리
|
|
215
247
|
nworks task update --id <taskId> --status done
|
|
216
248
|
|
|
217
|
-
# 할 일 미완료로 되돌리기
|
|
218
|
-
nworks task update --id <taskId> --status todo
|
|
219
|
-
|
|
220
249
|
# 할 일 삭제
|
|
221
250
|
nworks task delete --id <taskId>
|
|
222
251
|
```
|
|
223
252
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
### 게시판 (User OAuth 필요)
|
|
253
|
+
### 게시판 (User OAuth)
|
|
227
254
|
|
|
228
255
|
```bash
|
|
229
256
|
# 게시판 목록
|
|
@@ -242,36 +269,7 @@ nworks board create --board <boardId> --title "공지사항" --body "내용"
|
|
|
242
269
|
nworks board create --board <boardId> --title "공지" --body "내용" --notify --no-comment
|
|
243
270
|
```
|
|
244
271
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
### MCP 서버
|
|
248
|
-
|
|
249
|
-
```bash
|
|
250
|
-
nworks mcp # stdio MCP 서버 시작
|
|
251
|
-
nworks mcp --list-tools # 등록된 tool 목록
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## MCP 서버 연동 (Claude Desktop)
|
|
255
|
-
|
|
256
|
-
`~/.config/claude/claude_desktop_config.json`:
|
|
257
|
-
|
|
258
|
-
```json
|
|
259
|
-
{
|
|
260
|
-
"mcpServers": {
|
|
261
|
-
"nworks": {
|
|
262
|
-
"command": "npx",
|
|
263
|
-
"args": ["-y", "nworks", "mcp"],
|
|
264
|
-
"env": {
|
|
265
|
-
"NWORKS_CLIENT_ID": "<Client ID>",
|
|
266
|
-
"NWORKS_CLIENT_SECRET": "<Client Secret>",
|
|
267
|
-
"NWORKS_SERVICE_ACCOUNT": "<Service Account>",
|
|
268
|
-
"NWORKS_PRIVATE_KEY_PATH": "<Private Key 경로>",
|
|
269
|
-
"NWORKS_BOT_ID": "<Bot ID>"
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
```
|
|
272
|
+
---
|
|
275
273
|
|
|
276
274
|
## OAuth Scope 설정
|
|
277
275
|
|
|
@@ -282,38 +280,29 @@ nworks mcp --list-tools # 등록된 tool 목록
|
|
|
282
280
|
| `bot` | Bot 메시지 전송 | Service Account | `message send` |
|
|
283
281
|
| `bot.read` | Bot 채널/구성원 조회 | Service Account | `message members` |
|
|
284
282
|
| `user.read` | 조직 구성원 조회 | Service Account | `directory members` |
|
|
285
|
-
| `calendar` | 캘린더 쓰기 | User OAuth | `calendar create/update/delete`
|
|
283
|
+
| `calendar` | 캘린더 쓰기 | User OAuth | `calendar create/update/delete` |
|
|
286
284
|
| `calendar.read` | 캘린더 읽기 | User OAuth | `calendar list` |
|
|
287
285
|
| `file` | 드라이브 읽기/쓰기 | User OAuth | `drive list/upload/download` |
|
|
288
286
|
| `file.read` | 드라이브 읽기 전용 | User OAuth | `drive list/download` |
|
|
289
287
|
| `mail` | 메일 읽기/쓰기 | User OAuth | `mail send/list/read` |
|
|
290
288
|
| `mail.read` | 메일 읽기 전용 | User OAuth | `mail list/read` |
|
|
291
|
-
| `task` | 할 일 읽기/쓰기 | User OAuth | `task list/create/update/delete`
|
|
292
|
-
| `task.read` | 할 일 읽기 전용 | User OAuth | `task list`
|
|
289
|
+
| `task` | 할 일 읽기/쓰기 | User OAuth | `task list/create/update/delete` |
|
|
290
|
+
| `task.read` | 할 일 읽기 전용 | User OAuth | `task list` |
|
|
293
291
|
| `board` | 게시판 읽기/쓰기 | User OAuth | `board list/posts/read/create` |
|
|
294
292
|
| `board.read` | 게시판 읽기 전용 | User OAuth | `board list/posts/read` |
|
|
295
293
|
|
|
296
294
|
> **Tip**: scope를 변경한 후에는 토큰을 재발급해야 합니다.
|
|
297
295
|
> ```bash
|
|
298
|
-
> nworks logout && nworks login ...
|
|
296
|
+
> nworks logout && nworks login --user --scope "..."
|
|
299
297
|
> ```
|
|
300
298
|
|
|
301
|
-
> **Developer Console 설정**: User OAuth를 사용하려면 Developer Console에서 Redirect URL에 `http://localhost:9876/callback`을 등록해야 합니다.
|
|
302
|
-
|
|
303
299
|
## 사용 시나리오
|
|
304
300
|
|
|
305
301
|
### CI/CD 배포 알림
|
|
306
302
|
|
|
307
303
|
```bash
|
|
308
304
|
# GitHub Actions에서 배포 완료 후 팀 채널에 알림
|
|
309
|
-
nworks message send --channel $CHANNEL_ID --text "
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### AI 에이전트 메시지
|
|
313
|
-
|
|
314
|
-
```bash
|
|
315
|
-
# Claude Desktop / Cursor에서 MCP tool로 직접 메시지 전송
|
|
316
|
-
# nworks mcp 서버가 nworks_message_send tool을 제공
|
|
305
|
+
nworks message send --channel $CHANNEL_ID --text "v${VERSION} 배포 완료"
|
|
317
306
|
```
|
|
318
307
|
|
|
319
308
|
### 팀 자동화 스크립트
|
|
@@ -321,34 +310,67 @@ nworks message send --channel $CHANNEL_ID --text "✅ v${VERSION} 배포 완료"
|
|
|
321
310
|
```bash
|
|
322
311
|
# 매일 아침 팀원에게 리마인더 전송
|
|
323
312
|
for userId in $(nworks directory members --json | jq -r '.users[].userId'); do
|
|
324
|
-
nworks message send --to "$userId" --text "
|
|
313
|
+
nworks message send --to "$userId" --text "오늘의 스탠드업 10시입니다"
|
|
325
314
|
done
|
|
326
315
|
```
|
|
327
316
|
|
|
328
|
-
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Advanced Configuration
|
|
320
|
+
|
|
321
|
+
### 환경 변수
|
|
322
|
+
|
|
323
|
+
환경변수로 인증 정보를 설정하면 `nworks login` 없이 바로 사용할 수 있습니다.
|
|
329
324
|
|
|
330
325
|
```bash
|
|
326
|
+
# 공통 (필수)
|
|
331
327
|
NWORKS_CLIENT_ID= # 필수
|
|
332
328
|
NWORKS_CLIENT_SECRET= # 필수
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
329
|
+
|
|
330
|
+
# 봇 메시지 전송 시에만 필요 (User OAuth만 쓰면 불필요)
|
|
331
|
+
NWORKS_SERVICE_ACCOUNT= # 봇 전용
|
|
332
|
+
NWORKS_PRIVATE_KEY_PATH= # 봇 전용
|
|
333
|
+
NWORKS_BOT_ID= # 봇 전용
|
|
334
|
+
|
|
335
|
+
# 선택
|
|
336
336
|
NWORKS_DOMAIN_ID= # optional
|
|
337
337
|
NWORKS_SCOPE= # optional (기본: bot bot.read user.read)
|
|
338
338
|
NWORKS_VERBOSE=1 # optional, 디버그 로깅
|
|
339
339
|
```
|
|
340
340
|
|
|
341
|
-
|
|
341
|
+
### MCP 서버에 환경 변수 직접 설정
|
|
342
|
+
|
|
343
|
+
`nworks login` 대신 환경 변수로 직접 설정할 수도 있습니다:
|
|
344
|
+
|
|
345
|
+
```json
|
|
346
|
+
{
|
|
347
|
+
"mcpServers": {
|
|
348
|
+
"nworks": {
|
|
349
|
+
"command": "npx",
|
|
350
|
+
"args": ["-y", "nworks", "mcp"],
|
|
351
|
+
"env": {
|
|
352
|
+
"NWORKS_CLIENT_ID": "<Client ID>",
|
|
353
|
+
"NWORKS_CLIENT_SECRET": "<Client Secret>"
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
봇 메시지도 사용하려면 `NWORKS_SERVICE_ACCOUNT`, `NWORKS_PRIVATE_KEY_PATH`, `NWORKS_BOT_ID`도 추가합니다.
|
|
361
|
+
|
|
362
|
+
---
|
|
342
363
|
|
|
343
364
|
## Roadmap
|
|
344
365
|
|
|
345
366
|
- ~~**v0.1** — 메시지, 조직 구성원, MCP 서버~~
|
|
346
367
|
- ~~**v0.2** — 캘린더 일정 조회 + User OAuth~~
|
|
347
|
-
- ~~**v0.3** — 드라이브 파일
|
|
348
|
-
- ~~**v0.4** — 메일 (`
|
|
349
|
-
- ~~**v0.5** — 할 일 (`
|
|
350
|
-
- ~~**v0.6** — 캘린더 쓰기 (`
|
|
351
|
-
- ~~**v0.7** — 게시판 (`
|
|
368
|
+
- ~~**v0.3** — 드라이브 파일 조회/업로드/다운로드~~
|
|
369
|
+
- ~~**v0.4** — 메일 (`mail send/list/read`)~~
|
|
370
|
+
- ~~**v0.5** — 할 일 (`task list/create/update/delete`)~~
|
|
371
|
+
- ~~**v0.6** — 캘린더 쓰기 (`calendar create/update/delete`)~~
|
|
372
|
+
- ~~**v0.7** — 게시판 (`board list/posts/read/create`)~~
|
|
373
|
+
- **v1.0** — User OAuth 단독 로그인, MCP/CLI 인증 UX 개선, README 재구성
|
|
352
374
|
|
|
353
375
|
## License
|
|
354
376
|
|
package/dist/index.js
CHANGED
|
@@ -52,20 +52,15 @@ async function ensureConfigDir() {
|
|
|
52
52
|
function getCredentialsFromEnv() {
|
|
53
53
|
const clientId = process.env["NWORKS_CLIENT_ID"];
|
|
54
54
|
const clientSecret = process.env["NWORKS_CLIENT_SECRET"];
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
botId,
|
|
65
|
-
domainId: process.env["NWORKS_DOMAIN_ID"]
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
return null;
|
|
55
|
+
if (!clientId || !clientSecret) return null;
|
|
56
|
+
return {
|
|
57
|
+
clientId,
|
|
58
|
+
clientSecret,
|
|
59
|
+
serviceAccount: process.env["NWORKS_SERVICE_ACCOUNT"],
|
|
60
|
+
privateKeyPath: process.env["NWORKS_PRIVATE_KEY_PATH"],
|
|
61
|
+
botId: process.env["NWORKS_BOT_ID"],
|
|
62
|
+
domainId: process.env["NWORKS_DOMAIN_ID"]
|
|
63
|
+
};
|
|
69
64
|
}
|
|
70
65
|
async function loadCredentials(profile = "default") {
|
|
71
66
|
const envCreds = getCredentialsFromEnv();
|
|
@@ -166,6 +161,11 @@ async function clearCredentials(profile = "default") {
|
|
|
166
161
|
import { readFile as readFile2 } from "fs/promises";
|
|
167
162
|
import jwt from "jsonwebtoken";
|
|
168
163
|
async function createJWT(creds) {
|
|
164
|
+
if (!creds.serviceAccount || !creds.privateKeyPath) {
|
|
165
|
+
throw new AuthError(
|
|
166
|
+
"Service Account credentials required for bot authentication.\nRun `nworks login` with --service-account and --private-key flags."
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
169
|
const privateKey = await readFile2(creds.privateKeyPath, "utf-8");
|
|
170
170
|
const now = Math.floor(Date.now() / 1e3);
|
|
171
171
|
const payload = {
|
|
@@ -232,7 +232,7 @@ function buildAuthorizeUrl(clientId, scope, state) {
|
|
|
232
232
|
});
|
|
233
233
|
return `${AUTH_URL2}?${params.toString()}`;
|
|
234
234
|
}
|
|
235
|
-
async function startUserOAuthFlow(
|
|
235
|
+
async function startUserOAuthFlow(_scope, profile = "default") {
|
|
236
236
|
const creds = await loadCredentials(profile);
|
|
237
237
|
const code = await waitForAuthCode();
|
|
238
238
|
return exchangeCodeForToken(code, creds.clientId, creds.clientSecret);
|
|
@@ -431,9 +431,31 @@ var loginCommand = new Command("login").description("Authenticate with NAVER WOR
|
|
|
431
431
|
}
|
|
432
432
|
});
|
|
433
433
|
async function handleUserLogin(scope, profile, opts) {
|
|
434
|
-
|
|
434
|
+
let clientId = opts.clientId;
|
|
435
|
+
let clientSecret = opts.clientSecret;
|
|
436
|
+
try {
|
|
437
|
+
const existing = await loadCredentials(profile);
|
|
438
|
+
if (!clientId) clientId = existing.clientId;
|
|
439
|
+
if (!clientSecret) clientSecret = existing.clientSecret;
|
|
440
|
+
} catch {
|
|
441
|
+
}
|
|
442
|
+
if (process.stdin.isTTY) {
|
|
443
|
+
if (!clientId) clientId = await prompt("Client ID: ");
|
|
444
|
+
if (!clientSecret) clientSecret = await prompt("Client Secret: ");
|
|
445
|
+
}
|
|
446
|
+
if (!clientId || !clientSecret) {
|
|
447
|
+
throw new Error(
|
|
448
|
+
"Client ID and Client Secret are required for User OAuth.\nUse --client-id and --client-secret flags, or set NWORKS_CLIENT_ID / NWORKS_CLIENT_SECRET env vars."
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
try {
|
|
452
|
+
const existing = await loadCredentials(profile);
|
|
453
|
+
await saveCredentials({ ...existing, clientId, clientSecret }, profile);
|
|
454
|
+
} catch {
|
|
455
|
+
await saveCredentials({ clientId, clientSecret }, profile);
|
|
456
|
+
}
|
|
435
457
|
const state = randomBytes(16).toString("hex");
|
|
436
|
-
const authorizeUrl = buildAuthorizeUrl(
|
|
458
|
+
const authorizeUrl = buildAuthorizeUrl(clientId, scope, state);
|
|
437
459
|
console.error(`
|
|
438
460
|
Opening browser for NAVER WORKS login...`);
|
|
439
461
|
console.error(`If the browser does not open, visit this URL:
|
|
@@ -463,6 +485,11 @@ async function handleServiceAccountLogin(opts) {
|
|
|
463
485
|
let botId = opts.botId;
|
|
464
486
|
const domainId = opts.domainId;
|
|
465
487
|
const profile = opts.profile;
|
|
488
|
+
if (!clientId) clientId = process.env["NWORKS_CLIENT_ID"];
|
|
489
|
+
if (!clientSecret) clientSecret = process.env["NWORKS_CLIENT_SECRET"];
|
|
490
|
+
if (!serviceAccount) serviceAccount = process.env["NWORKS_SERVICE_ACCOUNT"];
|
|
491
|
+
if (!privateKeyPath) privateKeyPath = process.env["NWORKS_PRIVATE_KEY_PATH"];
|
|
492
|
+
if (!botId) botId = process.env["NWORKS_BOT_ID"];
|
|
466
493
|
if (process.stdin.isTTY) {
|
|
467
494
|
if (!clientId) clientId = await prompt("Client ID: ");
|
|
468
495
|
if (!clientSecret) clientSecret = await prompt("Client Secret: ");
|
|
@@ -474,7 +501,7 @@ async function handleServiceAccountLogin(opts) {
|
|
|
474
501
|
}
|
|
475
502
|
if (!clientId || !clientSecret || !serviceAccount || !privateKeyPath || !botId) {
|
|
476
503
|
throw new Error(
|
|
477
|
-
"Missing required fields. Use flags or run interactively."
|
|
504
|
+
"Missing required fields. Use flags, environment variables, or run interactively."
|
|
478
505
|
);
|
|
479
506
|
}
|
|
480
507
|
if (!existsSync2(privateKeyPath)) {
|
|
@@ -527,9 +554,9 @@ var whoamiCommand = new Command3("whoami").description("Show current authenticat
|
|
|
527
554
|
output(
|
|
528
555
|
{
|
|
529
556
|
profile,
|
|
530
|
-
serviceAccount: creds.serviceAccount,
|
|
557
|
+
serviceAccount: creds.serviceAccount ?? "(not set)",
|
|
531
558
|
clientId: creds.clientId,
|
|
532
|
-
botId: creds.botId,
|
|
559
|
+
botId: creds.botId ?? "(not set)",
|
|
533
560
|
domainId: creds.domainId ?? "(not set)",
|
|
534
561
|
tokenValid: isValid,
|
|
535
562
|
tokenExpiresAt
|
|
@@ -624,6 +651,11 @@ function buildContent(opts) {
|
|
|
624
651
|
async function send(opts) {
|
|
625
652
|
const profile = opts.profile ?? "default";
|
|
626
653
|
const creds = await loadCredentials(profile);
|
|
654
|
+
if (!creds.botId) {
|
|
655
|
+
throw new Error(
|
|
656
|
+
"Bot ID is required for sending messages.\nRun `nworks login` with --bot-id flag to set up bot credentials."
|
|
657
|
+
);
|
|
658
|
+
}
|
|
627
659
|
const content = buildContent(opts);
|
|
628
660
|
const body = { content };
|
|
629
661
|
if (opts.to) {
|
|
@@ -648,6 +680,11 @@ async function send(opts) {
|
|
|
648
680
|
}
|
|
649
681
|
async function listMembers(channelId, profile = "default") {
|
|
650
682
|
const creds = await loadCredentials(profile);
|
|
683
|
+
if (!creds.botId) {
|
|
684
|
+
throw new Error(
|
|
685
|
+
"Bot ID is required for listing channel members.\nRun `nworks login` with --bot-id flag to set up bot credentials."
|
|
686
|
+
);
|
|
687
|
+
}
|
|
651
688
|
const result = await request({
|
|
652
689
|
method: "GET",
|
|
653
690
|
path: `/bots/${creds.botId}/channels/${channelId}/members`,
|
|
@@ -855,7 +892,9 @@ async function getEvent(eventId, userId = "me", profile = "default") {
|
|
|
855
892
|
const res = await authedFetch(url2, { method: "GET" }, profile);
|
|
856
893
|
if (!res.ok) return handleError(res);
|
|
857
894
|
const data = await res.json();
|
|
858
|
-
|
|
895
|
+
const event = data.eventComponents[0];
|
|
896
|
+
if (!event) throw new ApiError("NOT_FOUND", "Event not found", 404);
|
|
897
|
+
return event;
|
|
859
898
|
}
|
|
860
899
|
async function updateEvent(opts) {
|
|
861
900
|
const userId = opts.userId ?? "me";
|
|
@@ -16481,9 +16520,9 @@ function registerTools(server) {
|
|
|
16481
16520
|
const token = await loadToken();
|
|
16482
16521
|
const isValid = token ? token.expiresAt > Date.now() / 1e3 : false;
|
|
16483
16522
|
const info = {
|
|
16484
|
-
serviceAccount: creds.serviceAccount,
|
|
16523
|
+
serviceAccount: creds.serviceAccount ?? null,
|
|
16485
16524
|
clientId: creds.clientId,
|
|
16486
|
-
botId: creds.botId,
|
|
16525
|
+
botId: creds.botId ?? null,
|
|
16487
16526
|
tokenValid: isValid
|
|
16488
16527
|
};
|
|
16489
16528
|
return {
|
|
@@ -16502,9 +16541,25 @@ function registerTools(server) {
|
|
|
16502
16541
|
|
|
16503
16542
|
// src/mcp/server.ts
|
|
16504
16543
|
async function startMcpServer() {
|
|
16544
|
+
try {
|
|
16545
|
+
await loadCredentials();
|
|
16546
|
+
} catch {
|
|
16547
|
+
console.error(
|
|
16548
|
+
"[nworks] Authentication required. Run: nworks login"
|
|
16549
|
+
);
|
|
16550
|
+
}
|
|
16551
|
+
try {
|
|
16552
|
+
const userToken = await loadUserToken();
|
|
16553
|
+
if (!userToken) {
|
|
16554
|
+
console.error(
|
|
16555
|
+
"[nworks] User OAuth not configured. Run: nworks login --user"
|
|
16556
|
+
);
|
|
16557
|
+
}
|
|
16558
|
+
} catch {
|
|
16559
|
+
}
|
|
16505
16560
|
const server = new McpServer({
|
|
16506
16561
|
name: "nworks",
|
|
16507
|
-
version: "0.
|
|
16562
|
+
version: "1.0.0"
|
|
16508
16563
|
});
|
|
16509
16564
|
registerTools(server);
|
|
16510
16565
|
const transport = new StdioServerTransport();
|