nworks-plus 1.3.0 → 1.4.1

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.ja.md CHANGED
@@ -1,8 +1,8 @@
1
- # nworks
1
+ # nworks-plus
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/nworks.svg)](https://www.npmjs.com/package/nworks)
4
- [![license](https://img.shields.io/npm/l/nworks.svg)](LICENSE)
5
- [![npm downloads](https://img.shields.io/npm/dm/nworks.svg)](https://www.npmjs.com/package/nworks)
3
+ [![npm version](https://img.shields.io/npm/v/nworks-plus.svg)](https://www.npmjs.com/package/nworks-plus)
4
+ [![license](https://img.shields.io/npm/l/nworks-plus.svg)](LICENSE)
5
+ [![npm downloads](https://img.shields.io/npm/dm/nworks-plus.svg)](https://www.npmjs.com/package/nworks-plus)
6
6
  [![nworks MCP server](https://glama.ai/mcp/servers/yjcho9317/nworks/badges/score.svg)](https://glama.ai/mcp/servers/yjcho9317/nworks)
7
7
 
8
8
  Featured in [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers)
@@ -19,7 +19,7 @@ LINE WORKS (NAVER WORKS) 初のMCPサーバー。
19
19
  ## クイックスタート
20
20
 
21
21
  ```bash
22
- npm install -g nworks
22
+ npm install -g nworks-plus
23
23
  nworks login --user
24
24
  nworks calendar list
25
25
  ```
@@ -49,8 +49,8 @@ Claude → nworks_mail_read (各)
49
49
  ## インストール
50
50
 
51
51
  ```bash
52
- npx nworks # 直接実行
53
- npm install -g nworks # グローバルインストール
52
+ npx nworks-plus # 直接実行
53
+ npm install -g nworks-plus # グローバルインストール
54
54
  ```
55
55
 
56
56
  ## ログイン
@@ -376,7 +376,7 @@ NWORKS_VERBOSE=1 # デバッグログ
376
376
  "mcpServers": {
377
377
  "nworks": {
378
378
  "command": "npx",
379
- "args": ["-y", "nworks", "mcp"],
379
+ "args": ["-y", "nworks-plus", "mcp"],
380
380
  "env": {
381
381
  "NWORKS_CLIENT_SECRET": "<Client Secret>",
382
382
  "NWORKS_PRIVATE_KEY_PATH": "<Private Keyファイルの絶対パス(Service Account使用時)>"
package/README.ko.md CHANGED
@@ -1,8 +1,8 @@
1
- # nworks
1
+ # nworks-plus
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/nworks.svg)](https://www.npmjs.com/package/nworks)
4
- [![license](https://img.shields.io/npm/l/nworks.svg)](LICENSE)
5
- [![npm downloads](https://img.shields.io/npm/dm/nworks.svg)](https://www.npmjs.com/package/nworks)
3
+ [![npm version](https://img.shields.io/npm/v/nworks-plus.svg)](https://www.npmjs.com/package/nworks-plus)
4
+ [![license](https://img.shields.io/npm/l/nworks-plus.svg)](LICENSE)
5
+ [![npm downloads](https://img.shields.io/npm/dm/nworks-plus.svg)](https://www.npmjs.com/package/nworks-plus)
6
6
  [![nworks MCP server](https://glama.ai/mcp/servers/yjcho9317/nworks/badges/score.svg)](https://glama.ai/mcp/servers/yjcho9317/nworks)
7
7
 
8
8
  Featured in [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers)
@@ -14,12 +14,12 @@ Featured in [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-server
14
14
  </p>
15
15
 
16
16
  NAVER WORKS (LINE WORKS) 최초의 MCP 서버.
17
- 메시지, 캘린더, 드라이브, 메일, 할 일, 게시판 — 28개 도구를 CLI와 AI 에이전트에서 쓸 수 있습니다.
17
+ 메시지, 캘린더, 드라이브, 메일, 할 일, 게시판 — 31개 도구를 CLI와 AI 에이전트에서 쓸 수 있습니다.
18
18
 
19
19
  ## Quickstart
20
20
 
21
21
  ```bash
22
- npm install -g nworks
22
+ npm install -g nworks-plus
23
23
  nworks login --user
24
24
  nworks calendar list
25
25
  ```
@@ -49,8 +49,8 @@ Claude → nworks_mail_read (각각)
49
49
  ## Install
50
50
 
51
51
  ```bash
52
- npx nworks # 바로 실행
53
- npm install -g nworks # 글로벌 설치
52
+ npx nworks-plus # 바로 실행
53
+ npm install -g nworks-plus # 글로벌 설치
54
54
  ```
55
55
 
56
56
  ## 로그인
@@ -104,7 +104,7 @@ nworks login --user --scope "calendar calendar.read file file.read mail mail.rea
104
104
 
105
105
  > **MCP에서 AI 에이전트가 직접 설정하기**: CLI 로그인 없이도 AI 에이전트가 `nworks_setup` → `nworks_login_user` 순서로 호출하면 브라우저 로그인만으로 전체 기능을 사용할 수 있습니다. Client Secret과 Private Key 경로는 MCP 설정의 `env` 필드 또는 시스템 환경변수로 미리 설정해야 합니다.
106
106
 
107
- ### MCP 도구 목록 (28개)
107
+ ### MCP 도구 목록 (31개)
108
108
 
109
109
  | 도구 | 설명 | 필요 인증 |
110
110
  |------|------|----------|
@@ -128,6 +128,9 @@ nworks login --user --scope "calendar calendar.read file file.read mail mail.rea
128
128
  | `nworks_drive_list` | 드라이브 파일/폴더 목록 | User OAuth (file.read) |
129
129
  | `nworks_drive_upload` | 드라이브 파일 업로드 | User OAuth (file) |
130
130
  | `nworks_drive_download` | 드라이브 파일 다운로드 (5MB 초과 시 로컬 저장) | User OAuth (file.read) |
131
+ | `nworks_drive_sharedrive_list` | 팀 공유 드라이브 목록 | User OAuth (file.read) |
132
+ | `nworks_drive_sharedrive_files` | 공유 드라이브 내 파일 목록 | User OAuth (file.read) |
133
+ | `nworks_drive_sharedrive_download` | 공유 드라이브 파일 다운로드 | User OAuth (file.read) |
131
134
  | **메일** | | |
132
135
  | `nworks_mail_send` | 메일 전송 | User OAuth (mail) |
133
136
  | `nworks_mail_list` | 메일 목록 조회 | User OAuth (mail.read) |
@@ -248,6 +251,17 @@ nworks drive download --file-id <fileId>
248
251
 
249
252
  # 다운로드 경로/파일명 지정
250
253
  nworks drive download --file-id <fileId> --out ./downloads --name report.pdf
254
+
255
+ # ── 공유 드라이브 (개인 "내 드라이브"와 별개인 팀 공유 저장소) ──
256
+ # 공유 드라이브 목록 (sharedriveId 확인)
257
+ nworks drive sharedrive-list
258
+
259
+ # 공유 드라이브 파일 목록 (루트 → 폴더 진입)
260
+ nworks drive sharedrive-files --drive <sharedriveId>
261
+ nworks drive sharedrive-files --drive <sharedriveId> --folder <fileId>
262
+
263
+ # 공유 드라이브 파일 다운로드
264
+ nworks drive sharedrive-download --drive <sharedriveId> --file-id <fileId> --out ./downloads
251
265
  ```
252
266
 
253
267
  ### 메일 (User OAuth)
@@ -399,7 +413,7 @@ NWORKS_VERBOSE=1 # 디버그 로깅
399
413
  "mcpServers": {
400
414
  "nworks": {
401
415
  "command": "npx",
402
- "args": ["-y", "nworks", "mcp"],
416
+ "args": ["-y", "nworks-plus", "mcp"],
403
417
  "env": {
404
418
  "NWORKS_CLIENT_SECRET": "<Client Secret>",
405
419
  "NWORKS_PRIVATE_KEY_PATH": "<Private Key 파일 절대 경로 (Service Account 사용 시)>"
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # nworks
1
+ # nworks-plus
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/nworks.svg)](https://www.npmjs.com/package/nworks)
4
- [![license](https://img.shields.io/npm/l/nworks.svg)](LICENSE)
5
- [![npm downloads](https://img.shields.io/npm/dm/nworks.svg)](https://www.npmjs.com/package/nworks)
3
+ [![npm version](https://img.shields.io/npm/v/nworks-plus.svg)](https://www.npmjs.com/package/nworks-plus)
4
+ [![license](https://img.shields.io/npm/l/nworks-plus.svg)](LICENSE)
5
+ [![npm downloads](https://img.shields.io/npm/dm/nworks-plus.svg)](https://www.npmjs.com/package/nworks-plus)
6
6
  [![nworks MCP server](https://glama.ai/mcp/servers/yjcho9317/nworks/badges/score.svg)](https://glama.ai/mcp/servers/yjcho9317/nworks)
7
7
 
8
8
  Featured in [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers)
@@ -19,7 +19,7 @@ CLI + MCP server — 28 tools covering messages, calendar, drive, mail, tasks, a
19
19
  ## Quickstart
20
20
 
21
21
  ```bash
22
- npm install -g nworks
22
+ npm install -g nworks-plus
23
23
  nworks login --user
24
24
  nworks calendar list
25
25
  ```
@@ -49,8 +49,8 @@ Claude → nworks_mail_read (each)
49
49
  ## Install
50
50
 
51
51
  ```bash
52
- npx nworks # Run directly
53
- npm install -g nworks # Global install
52
+ npx nworks-plus # Run directly
53
+ npm install -g nworks-plus # Global install
54
54
  ```
55
55
 
56
56
  ## Login
@@ -104,7 +104,7 @@ One login enables all 28 tools. No extra env setup needed.
104
104
 
105
105
  > Without CLI login, an AI agent can call `nworks_setup` → `nworks_login_user` to authenticate via browser directly. Client Secret and Private Key path must be pre-configured via MCP config `env` field or system environment variables.
106
106
 
107
- ### MCP Tools (28)
107
+ ### MCP Tools (31)
108
108
 
109
109
  | Tool | Description | Auth |
110
110
  |------|-------------|------|
@@ -128,6 +128,9 @@ One login enables all 28 tools. No extra env setup needed.
128
128
  | `nworks_drive_list` | List drive files/folders | User OAuth (file.read) |
129
129
  | `nworks_drive_upload` | Upload file to drive | User OAuth (file) |
130
130
  | `nworks_drive_download` | Download file (saves locally if >5MB) | User OAuth (file.read) |
131
+ | `nworks_drive_sharedrive_list` | List team shared drives | User OAuth (file.read) |
132
+ | `nworks_drive_sharedrive_files` | List files in a shared drive | User OAuth (file.read) |
133
+ | `nworks_drive_sharedrive_download` | Download a file from a shared drive | User OAuth (file.read) |
131
134
  | **Mail** | | |
132
135
  | `nworks_mail_send` | Send mail | User OAuth (mail) |
133
136
  | `nworks_mail_list` | List mailbox | User OAuth (mail.read) |
@@ -248,6 +251,17 @@ nworks drive download --file-id <fileId>
248
251
 
249
252
  # Specify output path/name
250
253
  nworks drive download --file-id <fileId> --out ./downloads --name report.pdf
254
+
255
+ # ── Shared drives (team storage, separate from personal "My Drive") ──
256
+ # List shared drives (get sharedriveId)
257
+ nworks drive sharedrive-list
258
+
259
+ # List files in a shared drive (root, then drill into a folder)
260
+ nworks drive sharedrive-files --drive <sharedriveId>
261
+ nworks drive sharedrive-files --drive <sharedriveId> --folder <fileId>
262
+
263
+ # Download a file from a shared drive
264
+ nworks drive sharedrive-download --drive <sharedriveId> --file-id <fileId> --out ./downloads
251
265
  ```
252
266
 
253
267
  ### Mail (User OAuth)
@@ -399,7 +413,7 @@ Sensitive values (Client Secret, Private Key path) must be set via MCP config `e
399
413
  "mcpServers": {
400
414
  "nworks": {
401
415
  "command": "npx",
402
- "args": ["-y", "nworks", "mcp"],
416
+ "args": ["-y", "nworks-plus", "mcp"],
403
417
  "env": {
404
418
  "NWORKS_CLIENT_SECRET": "<Client Secret>",
405
419
  "NWORKS_PRIVATE_KEY_PATH": "<Private Key file absolute path (for Service Account)>"
package/dist/index.js CHANGED
@@ -1576,8 +1576,7 @@ Content-Type: application/octet-stream\r
1576
1576
  if (!uploadRes.ok) return handleError2(uploadRes);
1577
1577
  return await uploadRes.json();
1578
1578
  }
1579
- async function downloadFile(fileId, userId = "me", profile = "default") {
1580
- const url = `${BASE_URL3}/users/${sanitizePathSegment(userId)}/drive/files/${sanitizePathSegment(fileId)}/download`;
1579
+ async function downloadViaRedirect(url, profile) {
1581
1580
  if (process.env["NWORKS_VERBOSE"] === "1") {
1582
1581
  console.error(`[nworks] GET ${url} (get download URL)`);
1583
1582
  }
@@ -1612,6 +1611,48 @@ async function downloadFile(fileId, userId = "me", profile = "default") {
1612
1611
  }
1613
1612
  return { buffer, fileName };
1614
1613
  }
1614
+ async function downloadFile(fileId, userId = "me", profile = "default") {
1615
+ const url = `${BASE_URL3}/users/${sanitizePathSegment(userId)}/drive/files/${sanitizePathSegment(fileId)}/download`;
1616
+ return downloadViaRedirect(url, profile);
1617
+ }
1618
+ async function listSharedDrives(count = 100, cursor, profile = "default") {
1619
+ const params = new URLSearchParams();
1620
+ params.set("count", String(count));
1621
+ if (cursor) params.set("cursor", cursor);
1622
+ const url = `${BASE_URL3}/sharedrives?${params.toString()}`;
1623
+ if (process.env["NWORKS_VERBOSE"] === "1") {
1624
+ console.error(`[nworks] GET ${url}`);
1625
+ }
1626
+ const res = await authedFetch2(url, { method: "GET" }, profile);
1627
+ if (!res.ok) return handleError2(res);
1628
+ const data = await res.json();
1629
+ const raw = data.sharedDrives ?? data.sharedrives ?? [];
1630
+ const sharedDrives = raw.map((d) => ({
1631
+ sharedriveId: d.sharedriveId,
1632
+ name: d.name ?? d.sharedriveName ?? "",
1633
+ permissionType: d.permissionType
1634
+ }));
1635
+ return { sharedDrives, responseMetaData: data.responseMetaData };
1636
+ }
1637
+ async function listSharedDriveFiles(sharedriveId, folderId, count = 20, cursor, profile = "default") {
1638
+ const base = `${BASE_URL3}/sharedrives/${sanitizePathSegment(sharedriveId)}/files`;
1639
+ const path = folderId ? `${base}/${sanitizePathSegment(folderId)}/children` : base;
1640
+ const params = new URLSearchParams();
1641
+ params.set("count", String(count));
1642
+ if (cursor) params.set("cursor", cursor);
1643
+ const url = `${path}?${params.toString()}`;
1644
+ if (process.env["NWORKS_VERBOSE"] === "1") {
1645
+ console.error(`[nworks] GET ${url}`);
1646
+ }
1647
+ const res = await authedFetch2(url, { method: "GET" }, profile);
1648
+ if (!res.ok) return handleError2(res);
1649
+ const data = await res.json();
1650
+ return { files: data.files ?? [], responseMetaData: data.responseMetaData };
1651
+ }
1652
+ async function downloadSharedDriveFile(sharedriveId, fileId, profile = "default") {
1653
+ const url = `${BASE_URL3}/sharedrives/${sanitizePathSegment(sharedriveId)}/files/${sanitizePathSegment(fileId)}/download`;
1654
+ return downloadViaRedirect(url, profile);
1655
+ }
1615
1656
 
1616
1657
  // src/commands/drive.ts
1617
1658
  var listCommand2 = new Command7("list").description("List files in Drive (requires User OAuth with file or file.read scope)").option("--user <userId>", "Target user ID (default: me)").option("--folder <folderId>", "Folder ID to list (default: root)").option("--count <n>", "Items per page (default: 20)", "20").option("--cursor <cursor>", "Pagination cursor").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
@@ -1705,7 +1746,78 @@ var downloadCommand = new Command7("download").description("Download a file from
1705
1746
  cliError(err, opts, "drive");
1706
1747
  }
1707
1748
  });
1708
- var driveCommand = new Command7("drive").description("Drive operations (requires User OAuth with file scope)").addCommand(listCommand2).addCommand(uploadCommand).addCommand(downloadCommand);
1749
+ var sharedriveListCommand = new Command7("sharedrive-list").description("List shared drives (requires User OAuth with file or file.read scope)").option("--count <n>", "Items per page (default: 100)", "100").option("--cursor <cursor>", "Pagination cursor").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
1750
+ try {
1751
+ const result = await listSharedDrives(
1752
+ parseInt(opts.count, 10),
1753
+ opts.cursor,
1754
+ opts.profile
1755
+ );
1756
+ output(
1757
+ {
1758
+ sharedDrives: result.sharedDrives,
1759
+ count: result.sharedDrives.length,
1760
+ nextCursor: result.responseMetaData?.nextCursor ?? null
1761
+ },
1762
+ opts
1763
+ );
1764
+ } catch (err) {
1765
+ cliError(err, opts, "drive");
1766
+ }
1767
+ });
1768
+ var sharedriveFilesCommand = new Command7("sharedrive-files").description("List files in a shared drive (requires User OAuth with file or file.read scope)").requiredOption("--drive <sharedriveId>", "Shared drive ID (from `drive sharedrive-list`)").option("--folder <fileId>", "Folder ID to list (default: shared drive root)").option("--count <n>", "Items per page (default: 20)", "20").option("--cursor <cursor>", "Pagination cursor").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
1769
+ try {
1770
+ const result = await listSharedDriveFiles(
1771
+ opts.drive,
1772
+ opts.folder,
1773
+ parseInt(opts.count, 10),
1774
+ opts.cursor,
1775
+ opts.profile
1776
+ );
1777
+ const files = result.files.map((f) => ({
1778
+ name: f.fileName,
1779
+ type: f.fileType,
1780
+ size: f.fileSize,
1781
+ modified: f.modifiedTime,
1782
+ fileId: f.fileId
1783
+ }));
1784
+ output(
1785
+ {
1786
+ files,
1787
+ count: files.length,
1788
+ nextCursor: result.responseMetaData?.nextCursor ?? null
1789
+ },
1790
+ opts
1791
+ );
1792
+ } catch (err) {
1793
+ cliError(err, opts, "drive");
1794
+ }
1795
+ });
1796
+ var sharedriveDownloadCommand = new Command7("sharedrive-download").description("Download a file from a shared drive (requires User OAuth with file or file.read scope)").requiredOption("--drive <sharedriveId>", "Shared drive ID (from `drive sharedrive-list`)").requiredOption("--file-id <fileId>", "File ID to download (from `drive sharedrive-files`)").option("--out <path>", "Output directory (default: current directory)").option("--name <filename>", "Output filename (default: original name)").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
1797
+ try {
1798
+ const result = await downloadSharedDriveFile(
1799
+ opts.drive,
1800
+ opts.fileId,
1801
+ opts.profile
1802
+ );
1803
+ const fileName = opts.name ?? result.fileName ?? opts.fileId;
1804
+ const outDir = opts.out ?? process.cwd();
1805
+ const outPath = join2(outDir, fileName);
1806
+ await writeFile2(outPath, result.buffer);
1807
+ output(
1808
+ {
1809
+ success: true,
1810
+ fileName,
1811
+ path: outPath,
1812
+ size: result.buffer.length
1813
+ },
1814
+ opts
1815
+ );
1816
+ } catch (err) {
1817
+ cliError(err, opts, "drive");
1818
+ }
1819
+ });
1820
+ var driveCommand = new Command7("drive").description("Drive operations (requires User OAuth with file scope)").addCommand(listCommand2).addCommand(uploadCommand).addCommand(downloadCommand).addCommand(sharedriveListCommand).addCommand(sharedriveFilesCommand).addCommand(sharedriveDownloadCommand);
1709
1821
 
1710
1822
  // src/commands/mail.ts
1711
1823
  import { writeFile as writeFile3 } from "fs/promises";
@@ -3135,6 +3247,111 @@ OAuth Redirect URI: http://localhost:9876/callback`,
3135
3247
  }
3136
3248
  }
3137
3249
  );
3250
+ server.tool(
3251
+ "nworks_drive_sharedrive_list",
3252
+ "\uD300 \uACF5\uC720 \uB4DC\uB77C\uC774\uBE0C(Shared Drive) \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. \uAC1C\uC778 '\uB0B4 \uB4DC\uB77C\uC774\uBE0C'\uAC00 \uC544\uB2CC \uD300 \uACF5\uC720 \uC800\uC7A5\uC18C\uB97C \uCC3E\uC744 \uB54C \uC0AC\uC6A9. '\uACF5\uC720 \uB4DC\uB77C\uC774\uBE0C \uBCF4\uC5EC\uC918', '\uD300 \uB4DC\uB77C\uC774\uBE0C \uBAA9\uB85D' \uB4F1\uC758 \uC694\uCCAD\uC5D0 \uC0AC\uC6A9. \uBC18\uD658\uB41C sharedriveId\uB85C nworks_drive_sharedrive_files\uB97C \uD638\uCD9C\uD574 \uB0B4\uBD80 \uD30C\uC77C\uC744 \uC870\uD68C\uD558\uC138\uC694. User OAuth \uC778\uC99D \uD544\uC694 (file.read scope)",
3253
+ {
3254
+ count: z.number().optional().describe("\uD398\uC774\uC9C0\uB2F9 \uD56D\uBAA9 \uC218 (\uAE30\uBCF8: 100)"),
3255
+ cursor: z.string().optional().describe("\uD398\uC774\uC9C0\uB124\uC774\uC158 \uCEE4\uC11C")
3256
+ },
3257
+ async ({ count, cursor }) => {
3258
+ try {
3259
+ const result = await listSharedDrives(count ?? 100, cursor);
3260
+ return {
3261
+ content: [{ type: "text", text: JSON.stringify({ sharedDrives: result.sharedDrives, count: result.sharedDrives.length, hasMore: !!result.responseMetaData?.nextCursor, nextCursor: result.responseMetaData?.nextCursor ?? null }) }]
3262
+ };
3263
+ } catch (err) {
3264
+ return {
3265
+ content: [{ type: "text", text: mcpErrorHint(err, "drive.list") }],
3266
+ isError: true
3267
+ };
3268
+ }
3269
+ }
3270
+ );
3271
+ server.tool(
3272
+ "nworks_drive_sharedrive_files",
3273
+ "\uD2B9\uC815 \uACF5\uC720 \uB4DC\uB77C\uC774\uBE0C(Shared Drive) \uC548\uC758 \uD30C\uC77C/\uD3F4\uB354 \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. sharedriveId\uB294 nworks_drive_sharedrive_list\uB85C \uC870\uD68C \uAC00\uB2A5. folderId\uB97C \uC9C0\uC815\uD558\uBA74 \uD574\uB2F9 \uD3F4\uB354 \uB0B4\uBD80\uB97C, \uBBF8\uC9C0\uC815 \uC2DC \uACF5\uC720 \uB4DC\uB77C\uC774\uBE0C \uB8E8\uD2B8\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4. User OAuth \uC778\uC99D \uD544\uC694 (file.read scope)",
3274
+ {
3275
+ sharedriveId: z.string().describe("\uACF5\uC720 \uB4DC\uB77C\uC774\uBE0C ID (nworks_drive_sharedrive_list\uB85C \uC870\uD68C \uAC00\uB2A5)"),
3276
+ folderId: z.string().optional().describe("\uD3F4\uB354 ID (\uBBF8\uC9C0\uC815 \uC2DC \uACF5\uC720 \uB4DC\uB77C\uC774\uBE0C \uB8E8\uD2B8)"),
3277
+ count: z.number().optional().describe("\uD398\uC774\uC9C0\uB2F9 \uD56D\uBAA9 \uC218 (\uAE30\uBCF8: 20, \uCD5C\uB300: 200)"),
3278
+ cursor: z.string().optional().describe("\uD398\uC774\uC9C0\uB124\uC774\uC158 \uCEE4\uC11C")
3279
+ },
3280
+ async ({ sharedriveId, folderId, count, cursor }) => {
3281
+ try {
3282
+ const result = await listSharedDriveFiles(sharedriveId, folderId, count ?? 20, cursor);
3283
+ const files = result.files.map((f) => ({
3284
+ fileId: f.fileId,
3285
+ name: f.fileName,
3286
+ type: f.fileType,
3287
+ size: f.fileSize,
3288
+ modified: f.modifiedTime,
3289
+ path: f.filePath
3290
+ }));
3291
+ return {
3292
+ content: [{ type: "text", text: JSON.stringify({ files, count: files.length, hasMore: !!result.responseMetaData?.nextCursor, nextCursor: result.responseMetaData?.nextCursor ?? null }) }]
3293
+ };
3294
+ } catch (err) {
3295
+ return {
3296
+ content: [{ type: "text", text: mcpErrorHint(err, "drive.list") }],
3297
+ isError: true
3298
+ };
3299
+ }
3300
+ }
3301
+ );
3302
+ server.tool(
3303
+ "nworks_drive_sharedrive_download",
3304
+ "\uACF5\uC720 \uB4DC\uB77C\uC774\uBE0C(Shared Drive)\uC758 \uD30C\uC77C\uC744 \uB2E4\uC6B4\uB85C\uB4DC\uD569\uB2C8\uB2E4. sharedriveId\xB7fileId\uB294 nworks_drive_sharedrive_files\uB85C \uC870\uD68C \uAC00\uB2A5. outputDir\uC744 \uC9C0\uC815\uD558\uBA74 \uB85C\uCEEC\uC5D0 \uD30C\uC77C\uB85C \uC800\uC7A5\uD558\uACE0, \uBBF8\uC9C0\uC815 \uC2DC \uD30C\uC77C \uB0B4\uC6A9\uC744 \uC9C1\uC811 \uBC18\uD658\uD569\uB2C8\uB2E4 (\uD14D\uC2A4\uD2B8\uB294 text, \uBC14\uC774\uB108\uB9AC\uB294 base64). 5MB \uCD08\uACFC \uD30C\uC77C\uC740 \uBC18\uB4DC\uC2DC outputDir\uB97C \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4. User OAuth \uC778\uC99D \uD544\uC694 (file.read scope)",
3305
+ {
3306
+ sharedriveId: z.string().describe("\uACF5\uC720 \uB4DC\uB77C\uC774\uBE0C ID (nworks_drive_sharedrive_list\uB85C \uC870\uD68C \uAC00\uB2A5)"),
3307
+ fileId: z.string().describe("\uB2E4\uC6B4\uB85C\uB4DC\uD560 \uD30C\uC77C ID (nworks_drive_sharedrive_files\uB85C \uC870\uD68C \uAC00\uB2A5)"),
3308
+ outputDir: z.string().optional().describe("\uC800\uC7A5 \uB514\uB809\uD1A0\uB9AC (\uC9C0\uC815 \uC2DC \uD30C\uC77C\uB85C \uC800\uC7A5, \uBBF8\uC9C0\uC815 \uC2DC \uB0B4\uC6A9\uC744 \uC9C1\uC811 \uBC18\uD658)"),
3309
+ outputName: z.string().optional().describe("\uC800\uC7A5 \uD30C\uC77C\uBA85 (\uBBF8\uC9C0\uC815 \uC2DC \uC6D0\uBCF8 \uD30C\uC77C\uBA85)")
3310
+ },
3311
+ async ({ sharedriveId, fileId, outputDir, outputName }) => {
3312
+ try {
3313
+ const result = await downloadSharedDriveFile(sharedriveId, fileId);
3314
+ const fileName = outputName ?? result.fileName ?? fileId;
3315
+ if (outputDir) {
3316
+ const { writeFile: writeFile4 } = await import("fs/promises");
3317
+ const { join: join4 } = await import("path");
3318
+ const safeDir = validateLocalPath(outputDir);
3319
+ const safeName = sanitizeFileName(fileName);
3320
+ const outPath = join4(safeDir, safeName);
3321
+ validateLocalPath(outPath, safeDir);
3322
+ await writeFile4(outPath, result.buffer);
3323
+ return {
3324
+ content: [{ type: "text", text: JSON.stringify({ success: true, fileName, path: outPath, size: result.buffer.length }) }]
3325
+ };
3326
+ }
3327
+ const MAX_INLINE_SIZE = 5 * 1024 * 1024;
3328
+ if (result.buffer.length > MAX_INLINE_SIZE) {
3329
+ const sizeMB = (result.buffer.length / (1024 * 1024)).toFixed(1);
3330
+ return {
3331
+ content: [{ type: "text", text: JSON.stringify({ error: true, message: `\uD30C\uC77C\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${sizeMB}MB). outputDir\uB97C \uC9C0\uC815\uD574\uC11C \uB85C\uCEEC\uC5D0 \uC800\uC7A5\uD558\uC138\uC694.`, fileName, size: result.buffer.length }) }],
3332
+ isError: true
3333
+ };
3334
+ }
3335
+ const textExtensions = /\.(txt|md|csv|json|xml|html|htm|css|js|ts|jsx|tsx|yaml|yml|toml|ini|cfg|conf|log|sh|bash|zsh|py|rb|java|go|rs|c|cpp|h|hpp|sql|graphql|env|gitignore|dockerignore|editorconfig)$/i;
3336
+ const isText = textExtensions.test(fileName);
3337
+ if (isText) {
3338
+ const text = result.buffer.toString("utf-8");
3339
+ return {
3340
+ content: [{ type: "text", text: JSON.stringify({ success: true, fileName, size: result.buffer.length, encoding: "text", content: text }) }]
3341
+ };
3342
+ }
3343
+ const base64 = result.buffer.toString("base64");
3344
+ return {
3345
+ content: [{ type: "text", text: JSON.stringify({ success: true, fileName, size: result.buffer.length, encoding: "base64", content: base64 }) }]
3346
+ };
3347
+ } catch (err) {
3348
+ return {
3349
+ content: [{ type: "text", text: mcpErrorHint(err, "drive.download") }],
3350
+ isError: true
3351
+ };
3352
+ }
3353
+ }
3354
+ );
3138
3355
  server.tool(
3139
3356
  "nworks_mail_send",
3140
3357
  "NAVER WORKS \uBA54\uC77C\uC744 \uC804\uC1A1\uD569\uB2C8\uB2E4. '\uBA54\uC77C \uBCF4\uB0B4\uC918', '\uC774\uBA54\uC77C \uC791\uC131\uD574\uC918' \uB4F1\uC758 \uC694\uCCAD\uC5D0 \uC0AC\uC6A9. \uBE44\uB3D9\uAE30 \uC804\uC1A1(\uC131\uACF5 \uC2DC 202). User OAuth \uC778\uC99D \uD544\uC694 (mail scope)",