nworks 0.6.2 → 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 +178 -127
- package/dist/index.js +419 -28
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +275 -18
- package/dist/mcp.js.map +1 -1
- package/package.json +8 -5
package/dist/mcp.js
CHANGED
|
@@ -13811,20 +13811,15 @@ async function ensureConfigDir() {
|
|
|
13811
13811
|
function getCredentialsFromEnv() {
|
|
13812
13812
|
const clientId = process.env["NWORKS_CLIENT_ID"];
|
|
13813
13813
|
const clientSecret = process.env["NWORKS_CLIENT_SECRET"];
|
|
13814
|
-
|
|
13815
|
-
|
|
13816
|
-
|
|
13817
|
-
|
|
13818
|
-
|
|
13819
|
-
|
|
13820
|
-
|
|
13821
|
-
|
|
13822
|
-
|
|
13823
|
-
botId,
|
|
13824
|
-
domainId: process.env["NWORKS_DOMAIN_ID"]
|
|
13825
|
-
};
|
|
13826
|
-
}
|
|
13827
|
-
return null;
|
|
13814
|
+
if (!clientId || !clientSecret) return null;
|
|
13815
|
+
return {
|
|
13816
|
+
clientId,
|
|
13817
|
+
clientSecret,
|
|
13818
|
+
serviceAccount: process.env["NWORKS_SERVICE_ACCOUNT"],
|
|
13819
|
+
privateKeyPath: process.env["NWORKS_PRIVATE_KEY_PATH"],
|
|
13820
|
+
botId: process.env["NWORKS_BOT_ID"],
|
|
13821
|
+
domainId: process.env["NWORKS_DOMAIN_ID"]
|
|
13822
|
+
};
|
|
13828
13823
|
}
|
|
13829
13824
|
async function loadCredentials(profile = "default") {
|
|
13830
13825
|
const envCreds = getCredentialsFromEnv();
|
|
@@ -13891,6 +13886,11 @@ async function saveUserToken(token, profile = "default") {
|
|
|
13891
13886
|
import { readFile as readFile2 } from "fs/promises";
|
|
13892
13887
|
import jwt2 from "jsonwebtoken";
|
|
13893
13888
|
async function createJWT(creds) {
|
|
13889
|
+
if (!creds.serviceAccount || !creds.privateKeyPath) {
|
|
13890
|
+
throw new AuthError(
|
|
13891
|
+
"Service Account credentials required for bot authentication.\nRun `nworks login` with --service-account and --private-key flags."
|
|
13892
|
+
);
|
|
13893
|
+
}
|
|
13894
13894
|
const privateKey = await readFile2(creds.privateKeyPath, "utf-8");
|
|
13895
13895
|
const now = Math.floor(Date.now() / 1e3);
|
|
13896
13896
|
const payload = {
|
|
@@ -14018,6 +14018,11 @@ function buildContent(opts) {
|
|
|
14018
14018
|
async function send(opts) {
|
|
14019
14019
|
const profile = opts.profile ?? "default";
|
|
14020
14020
|
const creds = await loadCredentials(profile);
|
|
14021
|
+
if (!creds.botId) {
|
|
14022
|
+
throw new Error(
|
|
14023
|
+
"Bot ID is required for sending messages.\nRun `nworks login` with --bot-id flag to set up bot credentials."
|
|
14024
|
+
);
|
|
14025
|
+
}
|
|
14021
14026
|
const content = buildContent(opts);
|
|
14022
14027
|
const body = { content };
|
|
14023
14028
|
if (opts.to) {
|
|
@@ -14042,6 +14047,11 @@ async function send(opts) {
|
|
|
14042
14047
|
}
|
|
14043
14048
|
async function listMembers(channelId, profile = "default") {
|
|
14044
14049
|
const creds = await loadCredentials(profile);
|
|
14050
|
+
if (!creds.botId) {
|
|
14051
|
+
throw new Error(
|
|
14052
|
+
"Bot ID is required for listing channel members.\nRun `nworks login` with --bot-id flag to set up bot credentials."
|
|
14053
|
+
);
|
|
14054
|
+
}
|
|
14045
14055
|
const result = await request({
|
|
14046
14056
|
method: "GET",
|
|
14047
14057
|
path: `/bots/${creds.botId}/channels/${channelId}/members`,
|
|
@@ -14211,7 +14221,9 @@ async function getEvent(eventId, userId = "me", profile = "default") {
|
|
|
14211
14221
|
const res = await authedFetch(url2, { method: "GET" }, profile);
|
|
14212
14222
|
if (!res.ok) return handleError(res);
|
|
14213
14223
|
const data = await res.json();
|
|
14214
|
-
|
|
14224
|
+
const event = data.eventComponents[0];
|
|
14225
|
+
if (!event) throw new ApiError("NOT_FOUND", "Event not found", 404);
|
|
14226
|
+
return event;
|
|
14215
14227
|
}
|
|
14216
14228
|
async function updateEvent(opts) {
|
|
14217
14229
|
const userId = opts.userId ?? "me";
|
|
@@ -14665,6 +14677,114 @@ async function deleteTask(taskId, profile = "default") {
|
|
|
14665
14677
|
if (!res.ok) return handleError4(res);
|
|
14666
14678
|
}
|
|
14667
14679
|
|
|
14680
|
+
// src/api/board.ts
|
|
14681
|
+
var BASE_URL6 = "https://www.worksapis.com/v1.0";
|
|
14682
|
+
async function authedFetch5(url2, init, profile) {
|
|
14683
|
+
const token = await getValidUserToken(profile);
|
|
14684
|
+
const headers = new Headers(init.headers);
|
|
14685
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
14686
|
+
return fetch(url2, { ...init, headers });
|
|
14687
|
+
}
|
|
14688
|
+
async function handleError5(res) {
|
|
14689
|
+
if (res.status === 401) {
|
|
14690
|
+
throw new AuthError("User token expired. Run `nworks login --user --scope board` again.");
|
|
14691
|
+
}
|
|
14692
|
+
let code = "UNKNOWN";
|
|
14693
|
+
let description = `HTTP ${res.status}`;
|
|
14694
|
+
try {
|
|
14695
|
+
const body = await res.json();
|
|
14696
|
+
code = body.code ?? code;
|
|
14697
|
+
description = body.description ?? description;
|
|
14698
|
+
} catch {
|
|
14699
|
+
}
|
|
14700
|
+
throw new ApiError(code, description, res.status);
|
|
14701
|
+
}
|
|
14702
|
+
function safeParseJson(text) {
|
|
14703
|
+
const safe = text.replace(
|
|
14704
|
+
/"((?:board|post|domain|user)Id)"\s*:\s*(\d{16,})/g,
|
|
14705
|
+
'"$1":"$2"'
|
|
14706
|
+
);
|
|
14707
|
+
return JSON.parse(safe);
|
|
14708
|
+
}
|
|
14709
|
+
async function listBoards(count = 20, cursor, profile = "default") {
|
|
14710
|
+
const params = new URLSearchParams();
|
|
14711
|
+
params.set("count", String(count));
|
|
14712
|
+
if (cursor) params.set("cursor", cursor);
|
|
14713
|
+
const url2 = `${BASE_URL6}/boards?${params.toString()}`;
|
|
14714
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
14715
|
+
console.error(`[nworks] GET ${url2}`);
|
|
14716
|
+
}
|
|
14717
|
+
const res = await authedFetch5(url2, { method: "GET" }, profile);
|
|
14718
|
+
if (!res.ok) return handleError5(res);
|
|
14719
|
+
const text = await res.text();
|
|
14720
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
14721
|
+
console.error(`[nworks] Response: ${text}`);
|
|
14722
|
+
}
|
|
14723
|
+
const data = safeParseJson(text);
|
|
14724
|
+
return { boards: data.boards ?? [], responseMetaData: data.responseMetaData };
|
|
14725
|
+
}
|
|
14726
|
+
async function listPosts(boardId, count = 20, cursor, profile = "default") {
|
|
14727
|
+
const params = new URLSearchParams();
|
|
14728
|
+
params.set("count", String(count));
|
|
14729
|
+
if (cursor) params.set("cursor", cursor);
|
|
14730
|
+
const url2 = `${BASE_URL6}/boards/${boardId}/posts?${params.toString()}`;
|
|
14731
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
14732
|
+
console.error(`[nworks] GET ${url2}`);
|
|
14733
|
+
}
|
|
14734
|
+
const res = await authedFetch5(url2, { method: "GET" }, profile);
|
|
14735
|
+
if (!res.ok) return handleError5(res);
|
|
14736
|
+
const text = await res.text();
|
|
14737
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
14738
|
+
console.error(`[nworks] Response: ${text}`);
|
|
14739
|
+
}
|
|
14740
|
+
const data = safeParseJson(text);
|
|
14741
|
+
return { posts: data.posts ?? [], responseMetaData: data.responseMetaData };
|
|
14742
|
+
}
|
|
14743
|
+
async function readPost(boardId, postId, profile = "default") {
|
|
14744
|
+
const url2 = `${BASE_URL6}/boards/${boardId}/posts/${postId}`;
|
|
14745
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
14746
|
+
console.error(`[nworks] GET ${url2}`);
|
|
14747
|
+
}
|
|
14748
|
+
const res = await authedFetch5(url2, { method: "GET" }, profile);
|
|
14749
|
+
if (!res.ok) return handleError5(res);
|
|
14750
|
+
const text = await res.text();
|
|
14751
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
14752
|
+
console.error(`[nworks] Response: ${text}`);
|
|
14753
|
+
}
|
|
14754
|
+
return safeParseJson(text);
|
|
14755
|
+
}
|
|
14756
|
+
async function createPost(opts) {
|
|
14757
|
+
const profile = opts.profile ?? "default";
|
|
14758
|
+
const body = {
|
|
14759
|
+
title: opts.title,
|
|
14760
|
+
body: opts.body ?? ""
|
|
14761
|
+
};
|
|
14762
|
+
if (opts.enableComment !== void 0) body.enableComment = opts.enableComment;
|
|
14763
|
+
if (opts.sendNotifications !== void 0) body.sendNotifications = opts.sendNotifications;
|
|
14764
|
+
const url2 = `${BASE_URL6}/boards/${opts.boardId}/posts`;
|
|
14765
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
14766
|
+
console.error(`[nworks] POST ${url2}`);
|
|
14767
|
+
console.error(`[nworks] Body: ${JSON.stringify(body, null, 2)}`);
|
|
14768
|
+
}
|
|
14769
|
+
const res = await authedFetch5(
|
|
14770
|
+
url2,
|
|
14771
|
+
{
|
|
14772
|
+
method: "POST",
|
|
14773
|
+
headers: { "Content-Type": "application/json" },
|
|
14774
|
+
body: JSON.stringify(body)
|
|
14775
|
+
},
|
|
14776
|
+
profile
|
|
14777
|
+
);
|
|
14778
|
+
if (res.status === 201 || res.ok) {
|
|
14779
|
+
const text = await res.text();
|
|
14780
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
14781
|
+
console.error(`[nworks] Response: ${text}`);
|
|
14782
|
+
}
|
|
14783
|
+
return safeParseJson(text);
|
|
14784
|
+
}
|
|
14785
|
+
return handleError5(res);
|
|
14786
|
+
}
|
|
14787
|
+
|
|
14668
14788
|
// src/mcp/tools.ts
|
|
14669
14789
|
function registerTools(server) {
|
|
14670
14790
|
server.tool(
|
|
@@ -15270,6 +15390,127 @@ function registerTools(server) {
|
|
|
15270
15390
|
}
|
|
15271
15391
|
}
|
|
15272
15392
|
);
|
|
15393
|
+
server.tool(
|
|
15394
|
+
"nworks_board_list",
|
|
15395
|
+
"\uAC8C\uC2DC\uD310 \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4 (User OAuth board \uB610\uB294 board.read scope \uD544\uC694)",
|
|
15396
|
+
{
|
|
15397
|
+
count: external_exports.number().optional().describe("\uD398\uC774\uC9C0\uB2F9 \uD56D\uBAA9 \uC218 (\uAE30\uBCF8: 20)"),
|
|
15398
|
+
cursor: external_exports.string().optional().describe("\uD398\uC774\uC9C0\uB124\uC774\uC158 \uCEE4\uC11C")
|
|
15399
|
+
},
|
|
15400
|
+
async ({ count, cursor }) => {
|
|
15401
|
+
try {
|
|
15402
|
+
const result = await listBoards(count ?? 20, cursor);
|
|
15403
|
+
const boards = result.boards.map((b) => ({
|
|
15404
|
+
boardId: b.boardId,
|
|
15405
|
+
boardName: b.boardName,
|
|
15406
|
+
description: b.description ?? ""
|
|
15407
|
+
}));
|
|
15408
|
+
return {
|
|
15409
|
+
content: [{ type: "text", text: JSON.stringify({ boards, count: boards.length, nextCursor: result.responseMetaData?.nextCursor ?? null }) }]
|
|
15410
|
+
};
|
|
15411
|
+
} catch (err) {
|
|
15412
|
+
const error48 = err;
|
|
15413
|
+
return {
|
|
15414
|
+
content: [{ type: "text", text: `Error: ${error48.message}` }],
|
|
15415
|
+
isError: true
|
|
15416
|
+
};
|
|
15417
|
+
}
|
|
15418
|
+
}
|
|
15419
|
+
);
|
|
15420
|
+
server.tool(
|
|
15421
|
+
"nworks_board_posts",
|
|
15422
|
+
"\uAC8C\uC2DC\uD310\uC758 \uAE00 \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4 (User OAuth board \uB610\uB294 board.read scope \uD544\uC694)",
|
|
15423
|
+
{
|
|
15424
|
+
boardId: external_exports.string().describe("\uAC8C\uC2DC\uD310 ID"),
|
|
15425
|
+
count: external_exports.number().optional().describe("\uD398\uC774\uC9C0\uB2F9 \uD56D\uBAA9 \uC218 (\uAE30\uBCF8: 20, \uCD5C\uB300: 40)"),
|
|
15426
|
+
cursor: external_exports.string().optional().describe("\uD398\uC774\uC9C0\uB124\uC774\uC158 \uCEE4\uC11C")
|
|
15427
|
+
},
|
|
15428
|
+
async ({ boardId, count, cursor }) => {
|
|
15429
|
+
try {
|
|
15430
|
+
const result = await listPosts(boardId, count ?? 20, cursor);
|
|
15431
|
+
const posts = result.posts.map((p) => ({
|
|
15432
|
+
postId: p.postId,
|
|
15433
|
+
title: p.title,
|
|
15434
|
+
userName: p.userName ?? "",
|
|
15435
|
+
readCount: p.readCount ?? 0,
|
|
15436
|
+
commentCount: p.commentCount ?? 0,
|
|
15437
|
+
createdTime: p.createdTime ?? ""
|
|
15438
|
+
}));
|
|
15439
|
+
return {
|
|
15440
|
+
content: [{ type: "text", text: JSON.stringify({ posts, count: posts.length, nextCursor: result.responseMetaData?.nextCursor ?? null }) }]
|
|
15441
|
+
};
|
|
15442
|
+
} catch (err) {
|
|
15443
|
+
const error48 = err;
|
|
15444
|
+
return {
|
|
15445
|
+
content: [{ type: "text", text: `Error: ${error48.message}` }],
|
|
15446
|
+
isError: true
|
|
15447
|
+
};
|
|
15448
|
+
}
|
|
15449
|
+
}
|
|
15450
|
+
);
|
|
15451
|
+
server.tool(
|
|
15452
|
+
"nworks_board_read",
|
|
15453
|
+
"\uAC8C\uC2DC\uD310 \uAE00\uC758 \uC0C1\uC138 \uB0B4\uC6A9\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4 (User OAuth board \uB610\uB294 board.read scope \uD544\uC694)",
|
|
15454
|
+
{
|
|
15455
|
+
boardId: external_exports.string().describe("\uAC8C\uC2DC\uD310 ID"),
|
|
15456
|
+
postId: external_exports.string().describe("\uAE00 ID")
|
|
15457
|
+
},
|
|
15458
|
+
async ({ boardId, postId }) => {
|
|
15459
|
+
try {
|
|
15460
|
+
const post = await readPost(boardId, postId);
|
|
15461
|
+
return {
|
|
15462
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
15463
|
+
postId: post.postId,
|
|
15464
|
+
boardId: post.boardId,
|
|
15465
|
+
title: post.title,
|
|
15466
|
+
body: post.body ?? "",
|
|
15467
|
+
userName: post.userName ?? "",
|
|
15468
|
+
readCount: post.readCount ?? 0,
|
|
15469
|
+
commentCount: post.commentCount ?? 0,
|
|
15470
|
+
createdTime: post.createdTime ?? "",
|
|
15471
|
+
updatedTime: post.updatedTime ?? ""
|
|
15472
|
+
}) }]
|
|
15473
|
+
};
|
|
15474
|
+
} catch (err) {
|
|
15475
|
+
const error48 = err;
|
|
15476
|
+
return {
|
|
15477
|
+
content: [{ type: "text", text: `Error: ${error48.message}` }],
|
|
15478
|
+
isError: true
|
|
15479
|
+
};
|
|
15480
|
+
}
|
|
15481
|
+
}
|
|
15482
|
+
);
|
|
15483
|
+
server.tool(
|
|
15484
|
+
"nworks_board_create",
|
|
15485
|
+
"\uAC8C\uC2DC\uD310\uC5D0 \uAE00\uC744 \uC791\uC131\uD569\uB2C8\uB2E4 (User OAuth board scope \uD544\uC694)",
|
|
15486
|
+
{
|
|
15487
|
+
boardId: external_exports.string().describe("\uAC8C\uC2DC\uD310 ID"),
|
|
15488
|
+
title: external_exports.string().describe("\uAE00 \uC81C\uBAA9"),
|
|
15489
|
+
body: external_exports.string().optional().describe("\uAE00 \uBCF8\uBB38"),
|
|
15490
|
+
enableComment: external_exports.boolean().optional().describe("\uB313\uAE00 \uD5C8\uC6A9 (\uAE30\uBCF8: true)"),
|
|
15491
|
+
sendNotifications: external_exports.boolean().optional().describe("\uC54C\uB9BC \uBC1C\uC1A1 (\uAE30\uBCF8: false)")
|
|
15492
|
+
},
|
|
15493
|
+
async ({ boardId, title, body, enableComment, sendNotifications }) => {
|
|
15494
|
+
try {
|
|
15495
|
+
const post = await createPost({
|
|
15496
|
+
boardId,
|
|
15497
|
+
title,
|
|
15498
|
+
body,
|
|
15499
|
+
enableComment,
|
|
15500
|
+
sendNotifications
|
|
15501
|
+
});
|
|
15502
|
+
return {
|
|
15503
|
+
content: [{ type: "text", text: JSON.stringify({ success: true, postId: post.postId, boardId: post.boardId, title: post.title }) }]
|
|
15504
|
+
};
|
|
15505
|
+
} catch (err) {
|
|
15506
|
+
const error48 = err;
|
|
15507
|
+
return {
|
|
15508
|
+
content: [{ type: "text", text: `Error: ${error48.message}` }],
|
|
15509
|
+
isError: true
|
|
15510
|
+
};
|
|
15511
|
+
}
|
|
15512
|
+
}
|
|
15513
|
+
);
|
|
15273
15514
|
server.tool(
|
|
15274
15515
|
"nworks_whoami",
|
|
15275
15516
|
"\uD604\uC7AC \uC778\uC99D\uB41C NAVER WORKS \uACC4\uC815 \uC815\uBCF4\uB97C \uD655\uC778\uD569\uB2C8\uB2E4",
|
|
@@ -15280,9 +15521,9 @@ function registerTools(server) {
|
|
|
15280
15521
|
const token = await loadToken();
|
|
15281
15522
|
const isValid = token ? token.expiresAt > Date.now() / 1e3 : false;
|
|
15282
15523
|
const info = {
|
|
15283
|
-
serviceAccount: creds.serviceAccount,
|
|
15524
|
+
serviceAccount: creds.serviceAccount ?? null,
|
|
15284
15525
|
clientId: creds.clientId,
|
|
15285
|
-
botId: creds.botId,
|
|
15526
|
+
botId: creds.botId ?? null,
|
|
15286
15527
|
tokenValid: isValid
|
|
15287
15528
|
};
|
|
15288
15529
|
return {
|
|
@@ -15301,9 +15542,25 @@ function registerTools(server) {
|
|
|
15301
15542
|
|
|
15302
15543
|
// src/mcp/server.ts
|
|
15303
15544
|
async function startMcpServer() {
|
|
15545
|
+
try {
|
|
15546
|
+
await loadCredentials();
|
|
15547
|
+
} catch {
|
|
15548
|
+
console.error(
|
|
15549
|
+
"[nworks] Authentication required. Run: nworks login"
|
|
15550
|
+
);
|
|
15551
|
+
}
|
|
15552
|
+
try {
|
|
15553
|
+
const userToken = await loadUserToken();
|
|
15554
|
+
if (!userToken) {
|
|
15555
|
+
console.error(
|
|
15556
|
+
"[nworks] User OAuth not configured. Run: nworks login --user"
|
|
15557
|
+
);
|
|
15558
|
+
}
|
|
15559
|
+
} catch {
|
|
15560
|
+
}
|
|
15304
15561
|
const server = new McpServer({
|
|
15305
15562
|
name: "nworks",
|
|
15306
|
-
version: "0.
|
|
15563
|
+
version: "1.0.0"
|
|
15307
15564
|
});
|
|
15308
15565
|
registerTools(server);
|
|
15309
15566
|
const transport = new StdioServerTransport();
|