nworks 0.4.0 → 0.6.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/dist/mcp.js CHANGED
@@ -14060,6 +14060,9 @@ async function listUsers(profile = "default") {
14060
14060
  return { users: result.users ?? [], responseMetaData: result.responseMetaData };
14061
14061
  }
14062
14062
 
14063
+ // src/api/calendar.ts
14064
+ import { randomUUID } from "crypto";
14065
+
14063
14066
  // src/auth/oauth-user.ts
14064
14067
  import { createServer } from "http";
14065
14068
  import { URL as URL2 } from "url";
@@ -14110,50 +14113,177 @@ async function getValidUserToken(profile = "default") {
14110
14113
 
14111
14114
  // src/api/calendar.ts
14112
14115
  var BASE_URL2 = "https://www.worksapis.com/v1.0";
14113
- async function listEvents(fromDateTime, untilDateTime, userId = "me", profile = "default") {
14116
+ async function authedFetch(url2, init, profile) {
14114
14117
  const token = await getValidUserToken(profile);
14118
+ const headers = new Headers(init.headers);
14119
+ headers.set("Authorization", `Bearer ${token}`);
14120
+ return fetch(url2, { ...init, headers });
14121
+ }
14122
+ async function handleError(res) {
14123
+ if (res.status === 401) {
14124
+ throw new AuthError("User token expired. Run `nworks login --user --scope calendar` again.");
14125
+ }
14126
+ let code = "UNKNOWN";
14127
+ let description = `HTTP ${res.status}`;
14128
+ try {
14129
+ const body = await res.json();
14130
+ code = body.code ?? code;
14131
+ description = body.description ?? description;
14132
+ } catch {
14133
+ }
14134
+ throw new ApiError(code, description, res.status);
14135
+ }
14136
+ function generateEventId() {
14137
+ return `event-${randomUUID()}`;
14138
+ }
14139
+ function normalizeDateTime(dt) {
14140
+ const match = dt.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2})([+-]\d{2}:\d{2}|Z)?$/);
14141
+ if (match) {
14142
+ return `${match[1]}:00${match[2] ?? ""}`;
14143
+ }
14144
+ return dt;
14145
+ }
14146
+ async function listEvents(fromDateTime, untilDateTime, userId = "me", profile = "default") {
14115
14147
  const from = encodeURIComponent(fromDateTime);
14116
14148
  const until = encodeURIComponent(untilDateTime);
14117
14149
  const url2 = `${BASE_URL2}/users/${userId}/calendar/events?fromDateTime=${from}&untilDateTime=${until}`;
14118
14150
  if (process.env["NWORKS_VERBOSE"] === "1") {
14119
14151
  console.error(`[nworks] GET ${url2}`);
14120
14152
  }
14121
- const res = await fetch(url2, {
14122
- method: "GET",
14123
- headers: {
14124
- Authorization: `Bearer ${token}`,
14125
- "Content-Type": "application/json"
14126
- }
14127
- });
14128
- if (res.status === 401) {
14129
- throw new AuthError("User token expired. Run `nworks login --user` again.");
14153
+ const res = await authedFetch(url2, { method: "GET" }, profile);
14154
+ if (!res.ok) return handleError(res);
14155
+ const data = await res.json();
14156
+ return { events: data.events ?? [] };
14157
+ }
14158
+ async function createEvent(opts) {
14159
+ const userId = opts.userId ?? "me";
14160
+ const profile = opts.profile ?? "default";
14161
+ const timeZone = opts.timeZone ?? "Asia/Seoul";
14162
+ const eventId = generateEventId();
14163
+ const eventComponent = {
14164
+ eventId,
14165
+ summary: opts.summary,
14166
+ start: { dateTime: normalizeDateTime(opts.start), timeZone },
14167
+ end: { dateTime: normalizeDateTime(opts.end), timeZone }
14168
+ };
14169
+ if (opts.description) eventComponent.description = opts.description;
14170
+ if (opts.location) eventComponent.location = opts.location;
14171
+ if (opts.transparency) eventComponent.transparency = opts.transparency;
14172
+ if (opts.visibility) eventComponent.visibility = opts.visibility;
14173
+ if (opts.attendees) {
14174
+ eventComponent.attendees = opts.attendees.map((a) => ({
14175
+ email: a.email,
14176
+ displayName: a.displayName ?? "",
14177
+ partstat: "NEEDS-ACTION",
14178
+ isOptional: false,
14179
+ isResource: false
14180
+ }));
14130
14181
  }
14131
- if (!res.ok) {
14132
- let code = "UNKNOWN";
14133
- let description = `HTTP ${res.status}`;
14134
- try {
14135
- const errorBody = await res.json();
14136
- code = errorBody.code ?? code;
14137
- description = errorBody.description ?? description;
14138
- } catch {
14139
- }
14140
- throw new ApiError(code, description, res.status);
14182
+ const body = {
14183
+ eventComponents: [eventComponent],
14184
+ sendNotification: opts.sendNotification ?? false
14185
+ };
14186
+ const url2 = `${BASE_URL2}/users/${userId}/calendar/events`;
14187
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14188
+ console.error(`[nworks] POST ${url2}`);
14189
+ console.error(`[nworks] Body: ${JSON.stringify(body, null, 2)}`);
14190
+ }
14191
+ const res = await authedFetch(
14192
+ url2,
14193
+ {
14194
+ method: "POST",
14195
+ headers: { "Content-Type": "application/json" },
14196
+ body: JSON.stringify(body)
14197
+ },
14198
+ profile
14199
+ );
14200
+ if (res.status === 201) {
14201
+ return await res.json();
14202
+ }
14203
+ if (!res.ok) return handleError(res);
14204
+ return await res.json();
14205
+ }
14206
+ async function getEvent(eventId, userId = "me", profile = "default") {
14207
+ const url2 = `${BASE_URL2}/users/${userId}/calendar/events/${eventId}`;
14208
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14209
+ console.error(`[nworks] GET ${url2}`);
14141
14210
  }
14211
+ const res = await authedFetch(url2, { method: "GET" }, profile);
14212
+ if (!res.ok) return handleError(res);
14142
14213
  const data = await res.json();
14143
- return { events: data.events ?? [] };
14214
+ return data.eventComponents[0];
14215
+ }
14216
+ async function updateEvent(opts) {
14217
+ const userId = opts.userId ?? "me";
14218
+ const profile = opts.profile ?? "default";
14219
+ const timeZone = opts.timeZone ?? "Asia/Seoul";
14220
+ const existing = await getEvent(opts.eventId, userId, profile);
14221
+ const eventComponent = {
14222
+ eventId: opts.eventId,
14223
+ summary: opts.summary ?? existing.summary,
14224
+ start: opts.start ? { dateTime: normalizeDateTime(opts.start), timeZone } : existing.start,
14225
+ end: opts.end ? { dateTime: normalizeDateTime(opts.end), timeZone } : existing.end
14226
+ };
14227
+ if (opts.description !== void 0) eventComponent.description = opts.description;
14228
+ else if (existing.description) eventComponent.description = existing.description;
14229
+ if (opts.location !== void 0) eventComponent.location = opts.location;
14230
+ else if (existing.location) eventComponent.location = existing.location;
14231
+ if (opts.transparency !== void 0) eventComponent.transparency = opts.transparency;
14232
+ if (opts.visibility !== void 0) eventComponent.visibility = opts.visibility;
14233
+ if (opts.attendees !== void 0) {
14234
+ eventComponent.attendees = opts.attendees.map((a) => ({
14235
+ email: a.email,
14236
+ displayName: a.displayName ?? "",
14237
+ partstat: "NEEDS-ACTION",
14238
+ isOptional: false,
14239
+ isResource: false
14240
+ }));
14241
+ } else if (existing.attendees) {
14242
+ eventComponent.attendees = existing.attendees;
14243
+ }
14244
+ const body = {
14245
+ eventComponents: [eventComponent],
14246
+ sendNotification: opts.sendNotification ?? false
14247
+ };
14248
+ const url2 = `${BASE_URL2}/users/${userId}/calendar/events/${opts.eventId}`;
14249
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14250
+ console.error(`[nworks] PUT ${url2}`);
14251
+ console.error(`[nworks] Body: ${JSON.stringify(body, null, 2)}`);
14252
+ }
14253
+ const res = await authedFetch(
14254
+ url2,
14255
+ {
14256
+ method: "PUT",
14257
+ headers: { "Content-Type": "application/json" },
14258
+ body: JSON.stringify(body)
14259
+ },
14260
+ profile
14261
+ );
14262
+ if (!res.ok) return handleError(res);
14263
+ }
14264
+ async function deleteEvent(eventId, userId = "me", sendNotification = false, profile = "default") {
14265
+ const params = new URLSearchParams();
14266
+ params.set("sendNotification", String(sendNotification));
14267
+ const url2 = `${BASE_URL2}/users/${userId}/calendar/events/${eventId}?${params.toString()}`;
14268
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14269
+ console.error(`[nworks] DELETE ${url2}`);
14270
+ }
14271
+ const res = await authedFetch(url2, { method: "DELETE" }, profile);
14272
+ if (res.status === 204) return;
14273
+ if (!res.ok) return handleError(res);
14144
14274
  }
14145
14275
 
14146
14276
  // src/api/drive.ts
14147
14277
  import { readFile as readFile3, stat } from "fs/promises";
14148
14278
  import { basename } from "path";
14149
14279
  var BASE_URL3 = "https://www.worksapis.com/v1.0";
14150
- async function authedFetch(url2, init, profile) {
14280
+ async function authedFetch2(url2, init, profile) {
14151
14281
  const token = await getValidUserToken(profile);
14152
14282
  const headers = new Headers(init.headers);
14153
14283
  headers.set("Authorization", `Bearer ${token}`);
14154
14284
  return fetch(url2, { ...init, headers });
14155
14285
  }
14156
- async function handleError(res) {
14286
+ async function handleError2(res) {
14157
14287
  if (res.status === 401) {
14158
14288
  throw new AuthError("User token expired. Run `nworks login --user --scope file` again.");
14159
14289
  }
@@ -14177,8 +14307,8 @@ async function listFiles(userId = "me", folderId, count = 20, cursor, profile =
14177
14307
  if (process.env["NWORKS_VERBOSE"] === "1") {
14178
14308
  console.error(`[nworks] GET ${url2}`);
14179
14309
  }
14180
- const res = await authedFetch(url2, { method: "GET" }, profile);
14181
- if (!res.ok) return handleError(res);
14310
+ const res = await authedFetch2(url2, { method: "GET" }, profile);
14311
+ if (!res.ok) return handleError2(res);
14182
14312
  const data = await res.json();
14183
14313
  return { files: data.files ?? [], responseMetaData: data.responseMetaData };
14184
14314
  }
@@ -14191,7 +14321,7 @@ async function uploadFile(localPath, userId = "me", folderId, overwrite = false,
14191
14321
  if (process.env["NWORKS_VERBOSE"] === "1") {
14192
14322
  console.error(`[nworks] POST ${createUrl} (create upload URL)`);
14193
14323
  }
14194
- const createRes = await authedFetch(
14324
+ const createRes = await authedFetch2(
14195
14325
  createUrl,
14196
14326
  {
14197
14327
  method: "POST",
@@ -14200,7 +14330,7 @@ async function uploadFile(localPath, userId = "me", folderId, overwrite = false,
14200
14330
  },
14201
14331
  profile
14202
14332
  );
14203
- if (!createRes.ok) return handleError(createRes);
14333
+ if (!createRes.ok) return handleError2(createRes);
14204
14334
  const { uploadUrl } = await createRes.json();
14205
14335
  const fileBuffer = await readFile3(localPath);
14206
14336
  const boundary = `----nworks${Date.now()}`;
@@ -14218,7 +14348,7 @@ Content-Type: application/octet-stream\r
14218
14348
  if (process.env["NWORKS_VERBOSE"] === "1") {
14219
14349
  console.error(`[nworks] POST ${uploadUrl} (upload content, ${fileSize} bytes)`);
14220
14350
  }
14221
- const uploadRes = await authedFetch(
14351
+ const uploadRes = await authedFetch2(
14222
14352
  uploadUrl,
14223
14353
  {
14224
14354
  method: "POST",
@@ -14227,7 +14357,7 @@ Content-Type: application/octet-stream\r
14227
14357
  },
14228
14358
  profile
14229
14359
  );
14230
- if (!uploadRes.ok) return handleError(uploadRes);
14360
+ if (!uploadRes.ok) return handleError2(uploadRes);
14231
14361
  return await uploadRes.json();
14232
14362
  }
14233
14363
  async function uploadBuffer(fileBuffer, fileName, userId = "me", folderId, overwrite = false, profile = "default") {
@@ -14237,7 +14367,7 @@ async function uploadBuffer(fileBuffer, fileName, userId = "me", folderId, overw
14237
14367
  if (process.env["NWORKS_VERBOSE"] === "1") {
14238
14368
  console.error(`[nworks] POST ${createUrl} (create upload URL for buffer)`);
14239
14369
  }
14240
- const createRes = await authedFetch(
14370
+ const createRes = await authedFetch2(
14241
14371
  createUrl,
14242
14372
  {
14243
14373
  method: "POST",
@@ -14246,7 +14376,7 @@ async function uploadBuffer(fileBuffer, fileName, userId = "me", folderId, overw
14246
14376
  },
14247
14377
  profile
14248
14378
  );
14249
- if (!createRes.ok) return handleError(createRes);
14379
+ if (!createRes.ok) return handleError2(createRes);
14250
14380
  const { uploadUrl } = await createRes.json();
14251
14381
  const boundary = `----nworks${Date.now()}`;
14252
14382
  const header = Buffer.from(
@@ -14263,7 +14393,7 @@ Content-Type: application/octet-stream\r
14263
14393
  if (process.env["NWORKS_VERBOSE"] === "1") {
14264
14394
  console.error(`[nworks] POST ${uploadUrl} (upload buffer, ${fileSize} bytes)`);
14265
14395
  }
14266
- const uploadRes = await authedFetch(
14396
+ const uploadRes = await authedFetch2(
14267
14397
  uploadUrl,
14268
14398
  {
14269
14399
  method: "POST",
@@ -14272,7 +14402,7 @@ Content-Type: application/octet-stream\r
14272
14402
  },
14273
14403
  profile
14274
14404
  );
14275
- if (!uploadRes.ok) return handleError(uploadRes);
14405
+ if (!uploadRes.ok) return handleError2(uploadRes);
14276
14406
  return await uploadRes.json();
14277
14407
  }
14278
14408
  async function downloadFile(fileId, userId = "me", profile = "default") {
@@ -14280,7 +14410,7 @@ async function downloadFile(fileId, userId = "me", profile = "default") {
14280
14410
  if (process.env["NWORKS_VERBOSE"] === "1") {
14281
14411
  console.error(`[nworks] GET ${url2} (get download URL)`);
14282
14412
  }
14283
- const redirectRes = await authedFetch(
14413
+ const redirectRes = await authedFetch2(
14284
14414
  url2,
14285
14415
  { method: "GET", redirect: "manual" },
14286
14416
  profile
@@ -14290,14 +14420,14 @@ async function downloadFile(fileId, userId = "me", profile = "default") {
14290
14420
  }
14291
14421
  const location = redirectRes.headers.get("location");
14292
14422
  if (!location) {
14293
- if (!redirectRes.ok) return handleError(redirectRes);
14423
+ if (!redirectRes.ok) return handleError2(redirectRes);
14294
14424
  throw new ApiError("NO_REDIRECT", "No download URL returned", redirectRes.status);
14295
14425
  }
14296
14426
  if (process.env["NWORKS_VERBOSE"] === "1") {
14297
14427
  console.error(`[nworks] GET ${location} (download content)`);
14298
14428
  }
14299
- const downloadRes = await authedFetch(location, { method: "GET" }, profile);
14300
- if (!downloadRes.ok) return handleError(downloadRes);
14429
+ const downloadRes = await authedFetch2(location, { method: "GET" }, profile);
14430
+ if (!downloadRes.ok) return handleError2(downloadRes);
14301
14431
  const arrayBuffer = await downloadRes.arrayBuffer();
14302
14432
  const buffer = Buffer.from(arrayBuffer);
14303
14433
  const disposition = downloadRes.headers.get("content-disposition");
@@ -14313,13 +14443,13 @@ async function downloadFile(fileId, userId = "me", profile = "default") {
14313
14443
 
14314
14444
  // src/api/mail.ts
14315
14445
  var BASE_URL4 = "https://www.worksapis.com/v1.0";
14316
- async function authedFetch2(url2, init, profile) {
14446
+ async function authedFetch3(url2, init, profile) {
14317
14447
  const token = await getValidUserToken(profile);
14318
14448
  const headers = new Headers(init.headers);
14319
14449
  headers.set("Authorization", `Bearer ${token}`);
14320
14450
  return fetch(url2, { ...init, headers });
14321
14451
  }
14322
- async function handleError2(res) {
14452
+ async function handleError3(res) {
14323
14453
  if (res.status === 401) {
14324
14454
  throw new AuthError("User token expired. Run `nworks login --user --scope mail` again.");
14325
14455
  }
@@ -14348,7 +14478,7 @@ async function sendMail(opts) {
14348
14478
  if (opts.cc) body.cc = opts.cc;
14349
14479
  if (opts.bcc) body.bcc = opts.bcc;
14350
14480
  if (opts.contentType) body.contentType = opts.contentType;
14351
- const res = await authedFetch2(
14481
+ const res = await authedFetch3(
14352
14482
  url2,
14353
14483
  {
14354
14484
  method: "POST",
@@ -14358,7 +14488,7 @@ async function sendMail(opts) {
14358
14488
  profile
14359
14489
  );
14360
14490
  if (res.status === 202) return;
14361
- if (!res.ok) return handleError2(res);
14491
+ if (!res.ok) return handleError3(res);
14362
14492
  }
14363
14493
  async function listMails(folderId = 0, userId = "me", count = 30, cursor, isUnread, profile = "default") {
14364
14494
  const params = new URLSearchParams();
@@ -14369,8 +14499,8 @@ async function listMails(folderId = 0, userId = "me", count = 30, cursor, isUnre
14369
14499
  if (process.env["NWORKS_VERBOSE"] === "1") {
14370
14500
  console.error(`[nworks] GET ${url2}`);
14371
14501
  }
14372
- const res = await authedFetch2(url2, { method: "GET" }, profile);
14373
- if (!res.ok) return handleError2(res);
14502
+ const res = await authedFetch3(url2, { method: "GET" }, profile);
14503
+ if (!res.ok) return handleError3(res);
14374
14504
  const data = await res.json();
14375
14505
  return {
14376
14506
  mails: data.mails ?? [],
@@ -14385,11 +14515,156 @@ async function readMail(mailId, userId = "me", profile = "default") {
14385
14515
  if (process.env["NWORKS_VERBOSE"] === "1") {
14386
14516
  console.error(`[nworks] GET ${url2}`);
14387
14517
  }
14388
- const res = await authedFetch2(url2, { method: "GET" }, profile);
14389
- if (!res.ok) return handleError2(res);
14518
+ const res = await authedFetch3(url2, { method: "GET" }, profile);
14519
+ if (!res.ok) return handleError3(res);
14390
14520
  return await res.json();
14391
14521
  }
14392
14522
 
14523
+ // src/api/task.ts
14524
+ var BASE_URL5 = "https://www.worksapis.com/v1.0";
14525
+ async function authedFetch4(url2, init, profile) {
14526
+ const token = await getValidUserToken(profile);
14527
+ const headers = new Headers(init.headers);
14528
+ headers.set("Authorization", `Bearer ${token}`);
14529
+ return fetch(url2, { ...init, headers });
14530
+ }
14531
+ async function handleError4(res) {
14532
+ if (res.status === 401) {
14533
+ throw new AuthError("User token expired. Run `nworks login --user --scope task` again.");
14534
+ }
14535
+ let code = "UNKNOWN";
14536
+ let description = `HTTP ${res.status}`;
14537
+ try {
14538
+ const body = await res.json();
14539
+ code = body.code ?? code;
14540
+ description = body.description ?? description;
14541
+ } catch {
14542
+ }
14543
+ throw new ApiError(code, description, res.status);
14544
+ }
14545
+ async function resolveUserId(userId, profile) {
14546
+ if (userId !== "me") return userId;
14547
+ const url2 = `${BASE_URL5}/users/me`;
14548
+ const res = await authedFetch4(url2, { method: "GET" }, profile);
14549
+ if (!res.ok) return handleError4(res);
14550
+ const data = await res.json();
14551
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14552
+ console.error(`[nworks] Resolved "me" \u2192 ${data.userId}`);
14553
+ }
14554
+ return data.userId;
14555
+ }
14556
+ async function listTasks(categoryId = "default", userId = "me", count = 50, cursor, status = "ALL", profile = "default") {
14557
+ const params = new URLSearchParams();
14558
+ params.set("categoryId", categoryId);
14559
+ params.set("count", String(count));
14560
+ params.set("status", status);
14561
+ if (cursor) params.set("cursor", cursor);
14562
+ const url2 = `${BASE_URL5}/users/${userId}/tasks?${params.toString()}`;
14563
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14564
+ console.error(`[nworks] GET ${url2}`);
14565
+ }
14566
+ const res = await authedFetch4(url2, { method: "GET" }, profile);
14567
+ if (!res.ok) return handleError4(res);
14568
+ const data = await res.json();
14569
+ return { tasks: data.tasks ?? [], responseMetaData: data.responseMetaData };
14570
+ }
14571
+ async function createTask(opts) {
14572
+ const userId = opts.userId ?? "me";
14573
+ const profile = opts.profile ?? "default";
14574
+ const resolvedUserId = await resolveUserId(userId, profile);
14575
+ const assignorId = opts.assignorId ?? resolvedUserId;
14576
+ const assigneeIds = opts.assigneeIds ?? [resolvedUserId];
14577
+ const body = {
14578
+ assignorId,
14579
+ assignees: assigneeIds.map((id) => ({ assigneeId: id, status: "TODO" })),
14580
+ title: opts.title,
14581
+ content: opts.content ?? "",
14582
+ completionCondition: "ANY_ONE"
14583
+ };
14584
+ if (opts.dueDate) body.dueDate = opts.dueDate;
14585
+ if (opts.categoryId) body.categoryId = opts.categoryId;
14586
+ const url2 = `${BASE_URL5}/users/${userId}/tasks`;
14587
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14588
+ console.error(`[nworks] POST ${url2}`);
14589
+ console.error(`[nworks] Body: ${JSON.stringify(body, null, 2)}`);
14590
+ }
14591
+ const res = await authedFetch4(
14592
+ url2,
14593
+ {
14594
+ method: "POST",
14595
+ headers: { "Content-Type": "application/json" },
14596
+ body: JSON.stringify(body)
14597
+ },
14598
+ profile
14599
+ );
14600
+ if (res.status === 201) {
14601
+ return await res.json();
14602
+ }
14603
+ if (!res.ok) return handleError4(res);
14604
+ return await res.json();
14605
+ }
14606
+ async function updateTask(opts) {
14607
+ const profile = opts.profile ?? "default";
14608
+ const body = {};
14609
+ if (opts.title !== void 0) body.title = opts.title;
14610
+ if (opts.content !== void 0) body.content = opts.content;
14611
+ if (opts.dueDate !== void 0) body.dueDate = opts.dueDate;
14612
+ const url2 = `${BASE_URL5}/tasks/${opts.taskId}`;
14613
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14614
+ console.error(`[nworks] PATCH ${url2}`);
14615
+ }
14616
+ const res = await authedFetch4(
14617
+ url2,
14618
+ {
14619
+ method: "PATCH",
14620
+ headers: { "Content-Type": "application/json" },
14621
+ body: JSON.stringify(body)
14622
+ },
14623
+ profile
14624
+ );
14625
+ if (!res.ok) return handleError4(res);
14626
+ return await res.json();
14627
+ }
14628
+ async function completeTask(taskId, profile = "default") {
14629
+ const url2 = `${BASE_URL5}/tasks/${taskId}/complete`;
14630
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14631
+ console.error(`[nworks] POST ${url2}`);
14632
+ }
14633
+ const res = await authedFetch4(
14634
+ url2,
14635
+ { method: "POST" },
14636
+ profile
14637
+ );
14638
+ if (res.status === 204) return;
14639
+ if (!res.ok) return handleError4(res);
14640
+ }
14641
+ async function incompleteTask(taskId, profile = "default") {
14642
+ const url2 = `${BASE_URL5}/tasks/${taskId}/incomplete`;
14643
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14644
+ console.error(`[nworks] POST ${url2}`);
14645
+ }
14646
+ const res = await authedFetch4(
14647
+ url2,
14648
+ { method: "POST" },
14649
+ profile
14650
+ );
14651
+ if (res.status === 204) return;
14652
+ if (!res.ok) return handleError4(res);
14653
+ }
14654
+ async function deleteTask(taskId, profile = "default") {
14655
+ const url2 = `${BASE_URL5}/tasks/${taskId}`;
14656
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14657
+ console.error(`[nworks] DELETE ${url2}`);
14658
+ }
14659
+ const res = await authedFetch4(
14660
+ url2,
14661
+ { method: "DELETE" },
14662
+ profile
14663
+ );
14664
+ if (res.status === 204) return;
14665
+ if (!res.ok) return handleError4(res);
14666
+ }
14667
+
14393
14668
  // src/mcp/tools.ts
14394
14669
  function registerTools(server) {
14395
14670
  server.tool(
@@ -14500,6 +14775,112 @@ function registerTools(server) {
14500
14775
  }
14501
14776
  }
14502
14777
  );
14778
+ server.tool(
14779
+ "nworks_calendar_create",
14780
+ "\uCE98\uB9B0\uB354 \uC77C\uC815\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4 (User OAuth calendar scope \uD544\uC694)",
14781
+ {
14782
+ summary: external_exports.string().describe("\uC77C\uC815 \uC81C\uBAA9"),
14783
+ start: external_exports.string().describe("\uC2DC\uC791 \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
14784
+ end: external_exports.string().describe("\uC885\uB8CC \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
14785
+ timeZone: external_exports.string().optional().describe("\uD0C0\uC784\uC874 (\uAE30\uBCF8: Asia/Seoul)"),
14786
+ description: external_exports.string().optional().describe("\uC77C\uC815 \uC124\uBA85"),
14787
+ location: external_exports.string().optional().describe("\uC7A5\uC18C"),
14788
+ attendees: external_exports.array(external_exports.object({ email: external_exports.string(), displayName: external_exports.string().optional() })).optional().describe("\uCC38\uC11D\uC790 \uBAA9\uB85D"),
14789
+ sendNotification: external_exports.boolean().optional().describe("\uCC38\uC11D\uC790\uC5D0\uAC8C \uC54C\uB9BC \uBC1C\uC1A1 (\uAE30\uBCF8: false)"),
14790
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
14791
+ },
14792
+ async ({ summary, start, end, timeZone, description, location, attendees, sendNotification, userId }) => {
14793
+ try {
14794
+ const result = await createEvent({
14795
+ summary,
14796
+ start,
14797
+ end,
14798
+ timeZone,
14799
+ description,
14800
+ location,
14801
+ attendees,
14802
+ sendNotification,
14803
+ userId: userId ?? "me"
14804
+ });
14805
+ const event = result.eventComponents?.[0];
14806
+ return {
14807
+ content: [{ type: "text", text: JSON.stringify({ success: true, eventId: event?.eventId, summary: event?.summary, start: event?.start, end: event?.end }) }]
14808
+ };
14809
+ } catch (err) {
14810
+ const error48 = err;
14811
+ return {
14812
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
14813
+ isError: true
14814
+ };
14815
+ }
14816
+ }
14817
+ );
14818
+ server.tool(
14819
+ "nworks_calendar_update",
14820
+ "\uCE98\uB9B0\uB354 \uC77C\uC815\uC744 \uC218\uC815\uD569\uB2C8\uB2E4 (User OAuth calendar scope \uD544\uC694)",
14821
+ {
14822
+ eventId: external_exports.string().describe("\uC77C\uC815 ID"),
14823
+ summary: external_exports.string().optional().describe("\uC0C8 \uC81C\uBAA9"),
14824
+ start: external_exports.string().optional().describe("\uC0C8 \uC2DC\uC791 \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
14825
+ end: external_exports.string().optional().describe("\uC0C8 \uC885\uB8CC \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
14826
+ timeZone: external_exports.string().optional().describe("\uD0C0\uC784\uC874 (\uAE30\uBCF8: Asia/Seoul)"),
14827
+ description: external_exports.string().optional().describe("\uC0C8 \uC124\uBA85"),
14828
+ location: external_exports.string().optional().describe("\uC0C8 \uC7A5\uC18C"),
14829
+ sendNotification: external_exports.boolean().optional().describe("\uCC38\uC11D\uC790\uC5D0\uAC8C \uC54C\uB9BC \uBC1C\uC1A1 (\uAE30\uBCF8: false)"),
14830
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
14831
+ },
14832
+ async ({ eventId, summary, start, end, timeZone, description, location, sendNotification, userId }) => {
14833
+ try {
14834
+ await updateEvent({
14835
+ eventId,
14836
+ summary,
14837
+ start,
14838
+ end,
14839
+ timeZone,
14840
+ description,
14841
+ location,
14842
+ sendNotification,
14843
+ userId: userId ?? "me"
14844
+ });
14845
+ return {
14846
+ content: [{ type: "text", text: JSON.stringify({ success: true, eventId, message: "Event updated" }) }]
14847
+ };
14848
+ } catch (err) {
14849
+ const error48 = err;
14850
+ return {
14851
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
14852
+ isError: true
14853
+ };
14854
+ }
14855
+ }
14856
+ );
14857
+ server.tool(
14858
+ "nworks_calendar_delete",
14859
+ "\uCE98\uB9B0\uB354 \uC77C\uC815\uC744 \uC0AD\uC81C\uD569\uB2C8\uB2E4 (User OAuth calendar scope \uD544\uC694)",
14860
+ {
14861
+ eventId: external_exports.string().describe("\uC0AD\uC81C\uD560 \uC77C\uC815 ID"),
14862
+ sendNotification: external_exports.boolean().optional().describe("\uCC38\uC11D\uC790\uC5D0\uAC8C \uC54C\uB9BC \uBC1C\uC1A1 (\uAE30\uBCF8: false)"),
14863
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
14864
+ },
14865
+ async ({ eventId, sendNotification, userId }) => {
14866
+ try {
14867
+ await deleteEvent(
14868
+ eventId,
14869
+ userId ?? "me",
14870
+ sendNotification ?? false
14871
+ );
14872
+ return {
14873
+ content: [{ type: "text", text: JSON.stringify({ success: true, eventId, message: "Event deleted" }) }]
14874
+ };
14875
+ } catch (err) {
14876
+ const error48 = err;
14877
+ return {
14878
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
14879
+ isError: true
14880
+ };
14881
+ }
14882
+ }
14883
+ );
14503
14884
  server.tool(
14504
14885
  "nworks_drive_list",
14505
14886
  "\uB4DC\uB77C\uC774\uBE0C \uD30C\uC77C/\uD3F4\uB354 \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4 (User OAuth file \uB610\uB294 file.read scope \uD544\uC694)",
@@ -14752,6 +15133,142 @@ function registerTools(server) {
14752
15133
  }
14753
15134
  }
14754
15135
  );
15136
+ server.tool(
15137
+ "nworks_task_list",
15138
+ "\uD560 \uC77C \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4 (User OAuth task \uB610\uB294 task.read scope \uD544\uC694)",
15139
+ {
15140
+ categoryId: external_exports.string().optional().describe("\uCE74\uD14C\uACE0\uB9AC ID (\uAE30\uBCF8: default)"),
15141
+ status: external_exports.enum(["TODO", "ALL"]).optional().describe("\uD544\uD130: TODO \uB610\uB294 ALL (\uAE30\uBCF8: ALL)"),
15142
+ count: external_exports.number().optional().describe("\uD398\uC774\uC9C0\uB2F9 \uD56D\uBAA9 \uC218 (\uAE30\uBCF8: 50, \uCD5C\uB300: 100)"),
15143
+ cursor: external_exports.string().optional().describe("\uD398\uC774\uC9C0\uB124\uC774\uC158 \uCEE4\uC11C"),
15144
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
15145
+ },
15146
+ async ({ categoryId, status, count, cursor, userId }) => {
15147
+ try {
15148
+ const result = await listTasks(
15149
+ categoryId ?? "default",
15150
+ userId ?? "me",
15151
+ count ?? 50,
15152
+ cursor,
15153
+ status ?? "ALL"
15154
+ );
15155
+ const tasks = result.tasks.map((t) => ({
15156
+ taskId: t.taskId,
15157
+ title: t.title,
15158
+ status: t.status,
15159
+ dueDate: t.dueDate,
15160
+ assignor: t.assignorName ?? t.assignorId,
15161
+ created: t.createdTime
15162
+ }));
15163
+ return {
15164
+ content: [{ type: "text", text: JSON.stringify({ tasks, count: tasks.length, nextCursor: result.responseMetaData?.nextCursor ?? null }) }]
15165
+ };
15166
+ } catch (err) {
15167
+ const error48 = err;
15168
+ return {
15169
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
15170
+ isError: true
15171
+ };
15172
+ }
15173
+ }
15174
+ );
15175
+ server.tool(
15176
+ "nworks_task_create",
15177
+ "\uD560 \uC77C\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4 (User OAuth task scope \uD544\uC694). \uAE30\uBCF8\uC801\uC73C\uB85C \uC790\uAE30 \uC790\uC2E0\uC5D0\uAC8C \uD560\uB2F9\uB429\uB2C8\uB2E4.",
15178
+ {
15179
+ title: external_exports.string().describe("\uD560 \uC77C \uC81C\uBAA9"),
15180
+ content: external_exports.string().optional().describe("\uD560 \uC77C \uB0B4\uC6A9"),
15181
+ dueDate: external_exports.string().optional().describe("\uB9C8\uAC10\uC77C (YYYY-MM-DD)"),
15182
+ categoryId: external_exports.string().optional().describe("\uCE74\uD14C\uACE0\uB9AC ID"),
15183
+ assigneeIds: external_exports.array(external_exports.string()).optional().describe("\uB2F4\uB2F9\uC790 user ID \uBAA9\uB85D (\uBBF8\uC9C0\uC815 \uC2DC \uC790\uAE30 \uC790\uC2E0)"),
15184
+ userId: external_exports.string().optional().describe("\uC0DD\uC131\uC790 user ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
15185
+ },
15186
+ async ({ title, content, dueDate, categoryId, assigneeIds, userId }) => {
15187
+ try {
15188
+ const result = await createTask({
15189
+ title,
15190
+ content,
15191
+ dueDate,
15192
+ categoryId,
15193
+ assigneeIds,
15194
+ userId: userId ?? "me"
15195
+ });
15196
+ return {
15197
+ content: [{ type: "text", text: JSON.stringify({ success: true, taskId: result.taskId, title: result.title, status: result.status, dueDate: result.dueDate }) }]
15198
+ };
15199
+ } catch (err) {
15200
+ const error48 = err;
15201
+ return {
15202
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
15203
+ isError: true
15204
+ };
15205
+ }
15206
+ }
15207
+ );
15208
+ server.tool(
15209
+ "nworks_task_update",
15210
+ "\uD560 \uC77C\uC744 \uC218\uC815\uD569\uB2C8\uB2E4 (User OAuth task scope \uD544\uC694). \uC0C1\uD0DC \uBCC0\uACBD(done/todo)\uACFC \uD544\uB4DC \uC218\uC815\uC744 \uBAA8\uB450 \uC9C0\uC6D0\uD569\uB2C8\uB2E4.",
15211
+ {
15212
+ taskId: external_exports.string().describe("\uD560 \uC77C ID"),
15213
+ status: external_exports.enum(["done", "todo"]).optional().describe("\uC0C1\uD0DC \uBCC0\uACBD: done \uB610\uB294 todo"),
15214
+ title: external_exports.string().optional().describe("\uC0C8 \uC81C\uBAA9"),
15215
+ content: external_exports.string().optional().describe("\uC0C8 \uB0B4\uC6A9"),
15216
+ dueDate: external_exports.string().optional().describe("\uC0C8 \uB9C8\uAC10\uC77C (YYYY-MM-DD)")
15217
+ },
15218
+ async ({ taskId, status, title, content, dueDate }) => {
15219
+ try {
15220
+ if (status) {
15221
+ if (status === "done") {
15222
+ await completeTask(taskId);
15223
+ } else {
15224
+ await incompleteTask(taskId);
15225
+ }
15226
+ }
15227
+ if (title !== void 0 || content !== void 0 || dueDate !== void 0) {
15228
+ const result = await updateTask({ taskId, title, content, dueDate });
15229
+ return {
15230
+ content: [{ type: "text", text: JSON.stringify({ success: true, taskId: result.taskId, title: result.title, status: result.status, dueDate: result.dueDate }) }]
15231
+ };
15232
+ }
15233
+ if (status) {
15234
+ return {
15235
+ content: [{ type: "text", text: JSON.stringify({ success: true, taskId, status: status === "done" ? "DONE" : "TODO" }) }]
15236
+ };
15237
+ }
15238
+ return {
15239
+ content: [{ type: "text", text: "Error: status, title, content, dueDate \uC911 \uD558\uB098 \uC774\uC0C1\uC744 \uC9C0\uC815\uD558\uC138\uC694." }],
15240
+ isError: true
15241
+ };
15242
+ } catch (err) {
15243
+ const error48 = err;
15244
+ return {
15245
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
15246
+ isError: true
15247
+ };
15248
+ }
15249
+ }
15250
+ );
15251
+ server.tool(
15252
+ "nworks_task_delete",
15253
+ "\uD560 \uC77C\uC744 \uC0AD\uC81C\uD569\uB2C8\uB2E4 (User OAuth task scope \uD544\uC694)",
15254
+ {
15255
+ taskId: external_exports.string().describe("\uC0AD\uC81C\uD560 \uD560 \uC77C ID")
15256
+ },
15257
+ async ({ taskId }) => {
15258
+ try {
15259
+ await deleteTask(taskId);
15260
+ return {
15261
+ content: [{ type: "text", text: JSON.stringify({ success: true, taskId, message: "Task deleted" }) }]
15262
+ };
15263
+ } catch (err) {
15264
+ const error48 = err;
15265
+ return {
15266
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
15267
+ isError: true
15268
+ };
15269
+ }
15270
+ }
15271
+ );
14755
15272
  server.tool(
14756
15273
  "nworks_whoami",
14757
15274
  "\uD604\uC7AC \uC778\uC99D\uB41C NAVER WORKS \uACC4\uC815 \uC815\uBCF4\uB97C \uD655\uC778\uD569\uB2C8\uB2E4",