@chnxthr/knowledge-mcp 0.0.0-stage → 0.3.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.
Files changed (187) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -2
  3. package/dist/bootstrap.d.ts +1 -0
  4. package/dist/bootstrap.js +18 -0
  5. package/dist/bootstrap.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +5 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/core/auth.d.ts +38 -0
  10. package/dist/core/auth.js +93 -0
  11. package/dist/core/auth.js.map +1 -0
  12. package/dist/core/config.d.ts +259 -0
  13. package/dist/core/config.js +172 -0
  14. package/dist/core/config.js.map +1 -0
  15. package/dist/core/context.d.ts +25 -0
  16. package/dist/core/context.js +37 -0
  17. package/dist/core/context.js.map +1 -0
  18. package/dist/core/env.d.ts +2 -0
  19. package/dist/core/env.js +5 -0
  20. package/dist/core/env.js.map +1 -0
  21. package/dist/core/log_mask.d.ts +10 -0
  22. package/dist/core/log_mask.js +88 -0
  23. package/dist/core/log_mask.js.map +1 -0
  24. package/dist/core/oauth.d.ts +46 -0
  25. package/dist/core/oauth.js +507 -0
  26. package/dist/core/oauth.js.map +1 -0
  27. package/dist/core/paths.d.ts +5 -0
  28. package/dist/core/paths.js +47 -0
  29. package/dist/core/paths.js.map +1 -0
  30. package/dist/core/scope.d.ts +28 -0
  31. package/dist/core/scope.js +54 -0
  32. package/dist/core/scope.js.map +1 -0
  33. package/dist/core/upstream.d.ts +30 -0
  34. package/dist/core/upstream.js +164 -0
  35. package/dist/core/upstream.js.map +1 -0
  36. package/dist/index.d.ts +23 -0
  37. package/dist/index.js +21 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/plugin/define.d.ts +144 -0
  40. package/dist/plugin/define.js +44 -0
  41. package/dist/plugin/define.js.map +1 -0
  42. package/dist/plugin/load.d.ts +2 -0
  43. package/dist/plugin/load.js +41 -0
  44. package/dist/plugin/load.js.map +1 -0
  45. package/dist/plugin/ping.d.ts +9 -0
  46. package/dist/plugin/ping.js +29 -0
  47. package/dist/plugin/ping.js.map +1 -0
  48. package/dist/plugin/registry.d.ts +62 -0
  49. package/dist/plugin/registry.js +248 -0
  50. package/dist/plugin/registry.js.map +1 -0
  51. package/dist/plugin/result.d.ts +20 -0
  52. package/dist/plugin/result.js +59 -0
  53. package/dist/plugin/result.js.map +1 -0
  54. package/dist/plugins/drive/chunker.d.ts +28 -0
  55. package/dist/plugins/drive/chunker.js +356 -0
  56. package/dist/plugins/drive/chunker.js.map +1 -0
  57. package/dist/plugins/drive/clean_text.d.ts +13 -0
  58. package/dist/plugins/drive/clean_text.js +31 -0
  59. package/dist/plugins/drive/clean_text.js.map +1 -0
  60. package/dist/plugins/drive/client.d.ts +25 -0
  61. package/dist/plugins/drive/client.js +62 -0
  62. package/dist/plugins/drive/client.js.map +1 -0
  63. package/dist/plugins/drive/embeddings.d.ts +15 -0
  64. package/dist/plugins/drive/embeddings.js +118 -0
  65. package/dist/plugins/drive/embeddings.js.map +1 -0
  66. package/dist/plugins/drive/exclude.d.ts +11 -0
  67. package/dist/plugins/drive/exclude.js +36 -0
  68. package/dist/plugins/drive/exclude.js.map +1 -0
  69. package/dist/plugins/drive/extract_text.d.ts +26 -0
  70. package/dist/plugins/drive/extract_text.js +108 -0
  71. package/dist/plugins/drive/extract_text.js.map +1 -0
  72. package/dist/plugins/drive/get_token.d.ts +5 -0
  73. package/dist/plugins/drive/get_token.js +113 -0
  74. package/dist/plugins/drive/get_token.js.map +1 -0
  75. package/dist/plugins/drive/index.d.ts +5 -0
  76. package/dist/plugins/drive/index.js +72 -0
  77. package/dist/plugins/drive/index.js.map +1 -0
  78. package/dist/plugins/drive/indexer.d.ts +69 -0
  79. package/dist/plugins/drive/indexer.js +204 -0
  80. package/dist/plugins/drive/indexer.js.map +1 -0
  81. package/dist/plugins/drive/mathpix.d.ts +4 -0
  82. package/dist/plugins/drive/mathpix.js +102 -0
  83. package/dist/plugins/drive/mathpix.js.map +1 -0
  84. package/dist/plugins/drive/path.d.ts +40 -0
  85. package/dist/plugins/drive/path.js +87 -0
  86. package/dist/plugins/drive/path.js.map +1 -0
  87. package/dist/plugins/drive/qdrant.d.ts +50 -0
  88. package/dist/plugins/drive/qdrant.js +61 -0
  89. package/dist/plugins/drive/qdrant.js.map +1 -0
  90. package/dist/plugins/drive/reindex.d.ts +2 -0
  91. package/dist/plugins/drive/reindex.js +263 -0
  92. package/dist/plugins/drive/reindex.js.map +1 -0
  93. package/dist/plugins/drive/settings.d.ts +11 -0
  94. package/dist/plugins/drive/settings.js +13 -0
  95. package/dist/plugins/drive/settings.js.map +1 -0
  96. package/dist/plugins/drive/sync.d.ts +16 -0
  97. package/dist/plugins/drive/sync.js +139 -0
  98. package/dist/plugins/drive/sync.js.map +1 -0
  99. package/dist/plugins/drive/sync_state.d.ts +3 -0
  100. package/dist/plugins/drive/sync_state.js +26 -0
  101. package/dist/plugins/drive/sync_state.js.map +1 -0
  102. package/dist/plugins/drive/tools/list_drive_folder.d.ts +104 -0
  103. package/dist/plugins/drive/tools/list_drive_folder.js +221 -0
  104. package/dist/plugins/drive/tools/list_drive_folder.js.map +1 -0
  105. package/dist/plugins/drive/tools/read_drive_file.d.ts +66 -0
  106. package/dist/plugins/drive/tools/read_drive_file.js +206 -0
  107. package/dist/plugins/drive/tools/read_drive_file.js.map +1 -0
  108. package/dist/plugins/drive/tools/search_drive.d.ts +52 -0
  109. package/dist/plugins/drive/tools/search_drive.js +78 -0
  110. package/dist/plugins/drive/tools/search_drive.js.map +1 -0
  111. package/dist/plugins/drive/tools/semantic_search_drive.d.ts +109 -0
  112. package/dist/plugins/drive/tools/semantic_search_drive.js +228 -0
  113. package/dist/plugins/drive/tools/semantic_search_drive.js.map +1 -0
  114. package/dist/plugins/github/index.d.ts +1 -0
  115. package/dist/plugins/github/index.js +27 -0
  116. package/dist/plugins/github/index.js.map +1 -0
  117. package/dist/plugins/github/repo_tree.d.ts +57 -0
  118. package/dist/plugins/github/repo_tree.js +123 -0
  119. package/dist/plugins/github/repo_tree.js.map +1 -0
  120. package/dist/plugins/index.d.ts +11 -0
  121. package/dist/plugins/index.js +7 -0
  122. package/dist/plugins/index.js.map +1 -0
  123. package/dist/plugins/railway/index.d.ts +1 -0
  124. package/dist/plugins/railway/index.js +21 -0
  125. package/dist/plugins/railway/index.js.map +1 -0
  126. package/dist/plugins/railway/tools.d.ts +114 -0
  127. package/dist/plugins/railway/tools.js +165 -0
  128. package/dist/plugins/railway/tools.js.map +1 -0
  129. package/dist/plugins/slack/api.d.ts +63 -0
  130. package/dist/plugins/slack/api.js +280 -0
  131. package/dist/plugins/slack/api.js.map +1 -0
  132. package/dist/plugins/slack/index.d.ts +1 -0
  133. package/dist/plugins/slack/index.js +80 -0
  134. package/dist/plugins/slack/index.js.map +1 -0
  135. package/dist/plugins/slack/tools/slack_files.d.ts +87 -0
  136. package/dist/plugins/slack/tools/slack_files.js +204 -0
  137. package/dist/plugins/slack/tools/slack_files.js.map +1 -0
  138. package/dist/plugins/slack/tools/slack_list_channels.d.ts +46 -0
  139. package/dist/plugins/slack/tools/slack_list_channels.js +197 -0
  140. package/dist/plugins/slack/tools/slack_list_channels.js.map +1 -0
  141. package/dist/plugins/slack/tools/slack_read_channel.d.ts +56 -0
  142. package/dist/plugins/slack/tools/slack_read_channel.js +150 -0
  143. package/dist/plugins/slack/tools/slack_read_channel.js.map +1 -0
  144. package/dist/plugins/slack/tools/slack_search.d.ts +53 -0
  145. package/dist/plugins/slack/tools/slack_search.js +132 -0
  146. package/dist/plugins/slack/tools/slack_search.js.map +1 -0
  147. package/dist/run.d.ts +8 -0
  148. package/dist/run.js +270 -0
  149. package/dist/run.js.map +1 -0
  150. package/dist/server/http.d.ts +5 -0
  151. package/dist/server/http.js +406 -0
  152. package/dist/server/http.js.map +1 -0
  153. package/dist/server/identity.d.ts +17 -0
  154. package/dist/server/identity.js +47 -0
  155. package/dist/server/identity.js.map +1 -0
  156. package/dist/server/mcp.d.ts +7 -0
  157. package/dist/server/mcp.js +55 -0
  158. package/dist/server/mcp.js.map +1 -0
  159. package/dist/server/stdio.d.ts +2 -0
  160. package/dist/server/stdio.js +40 -0
  161. package/dist/server/stdio.js.map +1 -0
  162. package/dist/store/index.d.ts +11 -0
  163. package/dist/store/index.js +31 -0
  164. package/dist/store/index.js.map +1 -0
  165. package/dist/store/keys.d.ts +9 -0
  166. package/dist/store/keys.js +32 -0
  167. package/dist/store/keys.js.map +1 -0
  168. package/dist/store/sqlite.d.ts +38 -0
  169. package/dist/store/sqlite.js +316 -0
  170. package/dist/store/sqlite.js.map +1 -0
  171. package/dist/store/supabase.d.ts +35 -0
  172. package/dist/store/supabase.js +269 -0
  173. package/dist/store/supabase.js.map +1 -0
  174. package/dist/store/supabase_client.d.ts +10 -0
  175. package/dist/store/supabase_client.js +31 -0
  176. package/dist/store/supabase_client.js.map +1 -0
  177. package/dist/store/tool_log.d.ts +12 -0
  178. package/dist/store/tool_log.js +8 -0
  179. package/dist/store/tool_log.js.map +1 -0
  180. package/dist/store/types.d.ts +96 -0
  181. package/dist/store/types.js +2 -0
  182. package/dist/store/types.js.map +1 -0
  183. package/dist/version.d.ts +1 -0
  184. package/dist/version.js +11 -0
  185. package/dist/version.js.map +1 -0
  186. package/package.json +84 -4
  187. package/sql/supabase.sql +185 -0
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Slack plugin — read-only: channel map, channel history with threads, files, and message search.
3
+ *
4
+ * SLACK_BOT_TOKEN (xoxb-…) every tool; the bot only sees channels it was invited to
5
+ * SLACK_USER_TOKEN (xoxp-…) slack_search as well (results are limited to the bot's channels)
6
+ * The two never stand in for each other — see api.ts.
7
+ */
8
+ import { parseArgs } from 'node:util';
9
+ import { definePlugin } from '../../plugin/define.js';
10
+ import { getStore } from '../../store/index.js';
11
+ import { slackBotToken, slackUserToken } from './api.js';
12
+ import { slackListChannelsTool } from './tools/slack_list_channels.js';
13
+ import { slackReadChannelTool } from './tools/slack_read_channel.js';
14
+ import { slackFindFilesTool, slackGetFileTool } from './tools/slack_files.js';
15
+ import { slackSearchTool } from './tools/slack_search.js';
16
+ const BOT = 'set SLACK_BOT_TOKEN (xoxb-…)';
17
+ /** `knowledge-mcp slack channels` — the purpose map that slack_list_channels shows. */
18
+ async function listChannels() {
19
+ const rows = await (await getStore()).listSlackChannels();
20
+ if (!rows.length) {
21
+ console.error('No channels yet. Call slack_list_channels once (it syncs from Slack), then set purposes here.');
22
+ return;
23
+ }
24
+ for (const r of rows) {
25
+ process.stdout.write(`${r.enabled ? 'on ' : 'off'} #${r.channel_name.padEnd(28)} ${(r.channel_id ?? '-').padEnd(12)} ${r.category ?? ''} ${r.purpose}\n`);
26
+ }
27
+ }
28
+ /** `knowledge-mcp slack channel <#name|id> [--purpose P] [--category C] [--on|--off] [--order N]` */
29
+ async function editChannel(args) {
30
+ const { values, positionals } = parseArgs({
31
+ args,
32
+ allowPositionals: true,
33
+ options: {
34
+ purpose: { type: 'string' },
35
+ category: { type: 'string' },
36
+ on: { type: 'boolean' },
37
+ off: { type: 'boolean' },
38
+ order: { type: 'string' },
39
+ },
40
+ });
41
+ const target = positionals[0]?.replace(/^#/, '');
42
+ if (!target)
43
+ throw new Error('usage: knowledge-mcp slack channel <#name|id> [--purpose P] [--category C] [--on|--off] [--order N]');
44
+ const store = await getStore();
45
+ const row = (await store.listSlackChannels()).find((r) => r.channel_id === target || r.channel_name === target);
46
+ if (!row)
47
+ throw new Error(`channel ${target} is not in the map yet — call slack_list_channels once to sync it`);
48
+ const patch = {};
49
+ if (values.purpose !== undefined)
50
+ patch.purpose = values.purpose;
51
+ if (values.category !== undefined)
52
+ patch.category = values.category || null;
53
+ if (values.on)
54
+ patch.enabled = true;
55
+ if (values.off)
56
+ patch.enabled = false;
57
+ if (values.order !== undefined)
58
+ patch.sort_order = Number(values.order);
59
+ await store.updateSlackChannel(row.id, patch);
60
+ console.error(`Updated #${row.channel_name}. slack_list_channels shows it within SLACK_LIST_CACHE_TTL_MS (default 60s).`);
61
+ }
62
+ export const slackPlugin = definePlugin({
63
+ id: 'slack',
64
+ description: 'Slack channels, threads, files and search (read-only)',
65
+ enabled: () => !!slackBotToken() || BOT,
66
+ tools: [
67
+ slackListChannelsTool,
68
+ slackReadChannelTool,
69
+ slackFindFilesTool,
70
+ slackGetFileTool,
71
+ { ...slackSearchTool, enabled: () => !!slackUserToken() || 'set SLACK_USER_TOKEN (xoxp-…) for search' },
72
+ ],
73
+ instructions: 'Slack: start with slack_list_channels to see which channels exist and which the bot can read; ' +
74
+ 'latest discussion → slack_search (sort="timestamp" for recency) or slack_read_channel.',
75
+ commands: {
76
+ channels: { description: 'list the channel purpose map', run: () => listChannels() },
77
+ channel: { description: 'edit one channel: --purpose, --category, --on/--off, --order', run: (args) => editChannel(args) },
78
+ },
79
+ });
80
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/plugins/slack/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * slack_find_files / slack_get_file — Slack 파일 검색 + 다운로드 (read-only).
3
+ *
4
+ * - slack_find_files: query 있으면 search.files(User token), 없으면 files.list(Bot token, 필터).
5
+ * - slack_get_file: files.info 메타 + 텍스트 파일이면 본문 다운로드.
6
+ * 권한 그룹: slack. 채널 범위는 app_users.allowed_slack_channels.
7
+ */
8
+ import { z } from 'zod';
9
+ declare const findInput: z.ZodObject<{
10
+ query: z.ZodOptional<z.ZodString>;
11
+ channel: z.ZodOptional<z.ZodString>;
12
+ user: z.ZodOptional<z.ZodString>;
13
+ count: z.ZodDefault<z.ZodNumber>;
14
+ }, "strip", z.ZodTypeAny, {
15
+ count: number;
16
+ channel?: string | undefined;
17
+ query?: string | undefined;
18
+ user?: string | undefined;
19
+ }, {
20
+ count?: number | undefined;
21
+ channel?: string | undefined;
22
+ query?: string | undefined;
23
+ user?: string | undefined;
24
+ }>;
25
+ export declare const slackFindFilesTool: import("../../../plugin/define.js").Tool<z.ZodObject<{
26
+ query: z.ZodOptional<z.ZodString>;
27
+ channel: z.ZodOptional<z.ZodString>;
28
+ user: z.ZodOptional<z.ZodString>;
29
+ count: z.ZodDefault<z.ZodNumber>;
30
+ }, "strip", z.ZodTypeAny, {
31
+ count: number;
32
+ channel?: string | undefined;
33
+ query?: string | undefined;
34
+ user?: string | undefined;
35
+ }, {
36
+ count?: number | undefined;
37
+ channel?: string | undefined;
38
+ query?: string | undefined;
39
+ user?: string | undefined;
40
+ }>>;
41
+ export declare function handleSlackFindFiles(args: z.infer<typeof findInput>): Promise<{
42
+ content: {
43
+ type: "text";
44
+ text: string;
45
+ }[];
46
+ isError: boolean;
47
+ } | {
48
+ content: {
49
+ type: "text";
50
+ text: string;
51
+ }[];
52
+ isError?: undefined;
53
+ }>;
54
+ declare const getInput: z.ZodObject<{
55
+ fileId: z.ZodString;
56
+ maxBytes: z.ZodDefault<z.ZodNumber>;
57
+ }, "strip", z.ZodTypeAny, {
58
+ fileId: string;
59
+ maxBytes: number;
60
+ }, {
61
+ fileId: string;
62
+ maxBytes?: number | undefined;
63
+ }>;
64
+ export declare const slackGetFileTool: import("../../../plugin/define.js").Tool<z.ZodObject<{
65
+ fileId: z.ZodString;
66
+ maxBytes: z.ZodDefault<z.ZodNumber>;
67
+ }, "strip", z.ZodTypeAny, {
68
+ fileId: string;
69
+ maxBytes: number;
70
+ }, {
71
+ fileId: string;
72
+ maxBytes?: number | undefined;
73
+ }>>;
74
+ export declare function handleSlackGetFile(args: z.infer<typeof getInput>): Promise<{
75
+ content: {
76
+ type: "text";
77
+ text: string;
78
+ }[];
79
+ isError: boolean;
80
+ } | {
81
+ content: {
82
+ type: "text";
83
+ text: string;
84
+ }[];
85
+ isError?: undefined;
86
+ }>;
87
+ export {};
@@ -0,0 +1,204 @@
1
+ /**
2
+ * slack_find_files / slack_get_file — Slack 파일 검색 + 다운로드 (read-only).
3
+ *
4
+ * - slack_find_files: query 있으면 search.files(User token), 없으면 files.list(Bot token, 필터).
5
+ * - slack_get_file: files.info 메타 + 텍스트 파일이면 본문 다운로드.
6
+ * 권한 그룹: slack. 채널 범위는 app_users.allowed_slack_channels.
7
+ */
8
+ import { z } from 'zod';
9
+ import { defineTool } from '../../../plugin/define.js';
10
+ import { getAuth } from '../../../core/context.js';
11
+ import { allowedSlackChannels } from '../../../core/scope.js';
12
+ import { slackApi, downloadSlackFile, isTextualMime, SlackError, loadBotMemberChannelIds, } from '../api.js';
13
+ /* ── slack_find_files ─────────────────────────────────── */
14
+ const findInput = z.object({
15
+ query: z.string().optional().describe('선택. 파일명/내용 키워드 검색 (User token 필요).'),
16
+ channel: z.string().optional().describe('선택. 채널 ID(C…) 로 필터 (목록 모드).'),
17
+ user: z.string().optional().describe('선택. 업로더 user ID 로 필터 (목록 모드).'),
18
+ count: z.number().int().min(1).max(50).default(20).describe('최대 결과 수 (1-50). default 20.'),
19
+ });
20
+ export const slackFindFilesTool = defineTool({
21
+ name: 'slack_find_files',
22
+ description: 'Slack 파일을 검색/목록. query 가 있으면 키워드 검색(User token — 봇이 멤버인 채널에 공유된 파일만), ' +
23
+ '없으면 채널/유저 필터로 목록(Bot token — 봇이 초대된 대화). ' +
24
+ '결과의 fileId 를 slack_get_file 에 넘겨 본문 다운로드.',
25
+ input: findInput,
26
+ annotations: { readOnlyHint: true },
27
+ run: (args) => handleSlackFindFiles(args),
28
+ });
29
+ /** 파일이 공유된 모든 대화 ID (공개 채널 + 비공개 그룹 + DM). 직원 채널 scope 판정용. */
30
+ function fileConversationIds(f) {
31
+ return [...(f.channels ?? []), ...(f.groups ?? []), ...(f.ims ?? [])];
32
+ }
33
+ /**
34
+ * 직원 채널 scope 안에서 이 파일이 보이는가. scope=null(admin·미설정)=전체 허용.
35
+ * 보수적(over-deny): 파일이 공유된 대화가 scope 와 하나도 겹치지 않으면 제한 직원에게 노출하지 않는다.
36
+ */
37
+ function fileInScope(f, scope) {
38
+ if (!scope)
39
+ return true;
40
+ return fileConversationIds(f).some((id) => scope.has(id));
41
+ }
42
+ function mapFile(f) {
43
+ return {
44
+ fileId: f.id,
45
+ name: f.name ?? f.title ?? '(이름 없음)',
46
+ title: f.title ?? null,
47
+ mimetype: f.mimetype ?? null,
48
+ filetype: f.filetype ?? null,
49
+ size: f.size ?? null,
50
+ created: f.created ? new Date(f.created * 1000).toISOString() : null,
51
+ uploader: f.user ?? null,
52
+ channels: f.channels ?? [],
53
+ permalink: f.permalink ?? null,
54
+ };
55
+ }
56
+ export async function handleSlackFindFiles(args) {
57
+ const auth = getAuth();
58
+ const { query, channel, user, count } = args;
59
+ const channelScope = allowedSlackChannels(auth);
60
+ // 채널 scope 제한 직원이 scope 밖 channel 을 명시 필터하면 거부 (slack_read_channel 과 동일).
61
+ if (channelScope && channel && !channelScope.has(channel)) {
62
+ return {
63
+ content: [
64
+ {
65
+ type: 'text',
66
+ text: `[forbidden] 채널 '${channel}' 은 현재 직원 권한으로 접근 불가입니다.`,
67
+ },
68
+ ],
69
+ isError: true,
70
+ };
71
+ }
72
+ try {
73
+ let files;
74
+ let mode;
75
+ if (query && query.trim().length > 0) {
76
+ mode = 'search.files';
77
+ const res = await slackApi('search.files', { query, count }, 'search');
78
+ // search.files 는 User token(xoxp) 권한 — 토큰 소유자의 DM·비공개 채널 파일까지 보인다.
79
+ // 노출 표면 = "봇이 멤버인 채널에 공유된 파일"(slack_search 와 동일 신뢰 경계, 2026-06-19).
80
+ // - 비공개 채널 공유 파일은 file.groups 에 담긴다 — 과거 channels>0
81
+ // 필터는 이를 전부 떨궈 파일 검색이 사실상 0건이었다.
82
+ // - DM 전용 파일(ims): conversations.list 에 없는 D… id → botMemberIds 에 부재 → 자연 제외.
83
+ // - 봇 미초대 비공개 채널 파일: 허가 안 됨 → 제외.
84
+ // files.list(bot token) 모드는 봇이 초대된 대화만 보이므로 이 제한이 자동 적용된다.
85
+ const botMemberIds = await loadBotMemberChannelIds();
86
+ files = (res.files?.matches ?? []).filter((f) => fileConversationIds(f).some((id) => botMemberIds.has(id)));
87
+ }
88
+ else {
89
+ mode = 'files.list';
90
+ const res = await slackApi('files.list', { channel, user, count }, 'bot');
91
+ // files.list 는 봇에게 온 DM 파일까지 돌려준다 — 검색 모드와 같은 경계(봇 멤버 채널에 공유된 파일)로 거른다.
92
+ const botMemberIds = await loadBotMemberChannelIds();
93
+ files = (res.files ?? []).filter((f) => fileConversationIds(f).some((id) => botMemberIds.has(id)));
94
+ }
95
+ // 직원별 Slack 채널 scope 필터 (admin·미설정 = 전체). scope 밖 대화의 파일 제거.
96
+ const visible = files.filter((f) => fileInScope(f, channelScope));
97
+ const filteredOut = files.length - visible.length;
98
+ return {
99
+ content: [
100
+ {
101
+ type: 'text',
102
+ text: JSON.stringify({ mode, count: visible.length, filteredOut, files: visible.map(mapFile) }, null, 2),
103
+ },
104
+ ],
105
+ };
106
+ }
107
+ catch (e) {
108
+ const msg = e instanceof SlackError ? e.message : e instanceof Error ? e.message : String(e);
109
+ return {
110
+ content: [{ type: 'text', text: `[error] ${msg}` }],
111
+ isError: true,
112
+ };
113
+ }
114
+ }
115
+ /* ── slack_get_file ───────────────────────────────────── */
116
+ const getInput = z.object({
117
+ fileId: z.string().min(1).describe('Slack 파일 ID (예: F0123ABCD).'),
118
+ maxBytes: z.number().int().min(1).max(1_000_000).default(200_000).describe('텍스트 본문 최대 바이트 (default 200000).'),
119
+ });
120
+ export const slackGetFileTool = defineTool({
121
+ name: 'slack_get_file',
122
+ description: 'Slack 파일 1개의 메타데이터 + 본문을 가져온다. 텍스트 파일(.md/.txt/.csv/.json/코드)은 ' +
123
+ '본문을 반환하고, 이미지·PDF 등 바이너리는 메타 + 다운로드 URL 만 반환. ' +
124
+ 'fileId 는 slack_find_files 결과에서 얻는다.',
125
+ input: getInput,
126
+ annotations: { readOnlyHint: true },
127
+ run: (args) => handleSlackGetFile(args),
128
+ });
129
+ export async function handleSlackGetFile(args) {
130
+ const auth = getAuth();
131
+ const { fileId, maxBytes } = args;
132
+ const channelScope = allowedSlackChannels(auth);
133
+ try {
134
+ const res = await slackApi('files.info', { file: fileId }, 'bot');
135
+ const f = res.file;
136
+ if (!f) {
137
+ return {
138
+ content: [{ type: 'text', text: `[not found] file ${fileId}` }],
139
+ isError: true,
140
+ };
141
+ }
142
+ // 신뢰 경계: 봇이 멤버인 채널에 공유된 파일만 (DM 전용 파일 차단 — find_files 와 동일).
143
+ const botMemberIds = await loadBotMemberChannelIds();
144
+ if (!fileConversationIds(f).some((id) => botMemberIds.has(id))) {
145
+ return {
146
+ content: [
147
+ {
148
+ type: 'text',
149
+ text: `[forbidden] 파일 '${fileId}' 은 봇이 멤버인 채널에 공유된 파일이 아닙니다.`,
150
+ },
151
+ ],
152
+ isError: true,
153
+ };
154
+ }
155
+ // 직원별 Slack 채널 scope: 이 파일이 보이는 대화가 전부 권한 밖이면 거부.
156
+ if (!fileInScope(f, channelScope)) {
157
+ return {
158
+ content: [
159
+ {
160
+ type: 'text',
161
+ text: `[forbidden] 파일 '${fileId}' 은 현재 직원 권한으로 접근 불가입니다 (허용 채널 밖).`,
162
+ },
163
+ ],
164
+ isError: true,
165
+ };
166
+ }
167
+ const meta = mapFile(f);
168
+ const textual = isTextualMime(f.mimetype, f.filetype);
169
+ const downloadUrl = f.url_private_download;
170
+ if (textual && downloadUrl) {
171
+ const dl = await downloadSlackFile(downloadUrl, { asText: true, maxBytes });
172
+ return {
173
+ content: [
174
+ {
175
+ type: 'text',
176
+ text: JSON.stringify({ ...meta, bytes: dl.bytes, truncated: dl.truncated, content: dl.text ?? '' }, null, 2),
177
+ },
178
+ ],
179
+ };
180
+ }
181
+ // 바이너리 — 메타 + 다운로드 URL (Bearer 인증 필요 안내)
182
+ return {
183
+ content: [
184
+ {
185
+ type: 'text',
186
+ text: JSON.stringify({
187
+ ...meta,
188
+ binary: true,
189
+ note: '바이너리 파일 — 본문 미포함. downloadUrl 은 Authorization: Bearer <SLACK token> 헤더로 받아야 함.',
190
+ downloadUrl: downloadUrl ?? null,
191
+ }, null, 2),
192
+ },
193
+ ],
194
+ };
195
+ }
196
+ catch (e) {
197
+ const msg = e instanceof SlackError ? e.message : e instanceof Error ? e.message : String(e);
198
+ return {
199
+ content: [{ type: 'text', text: `[error] ${msg}` }],
200
+ isError: true,
201
+ };
202
+ }
203
+ }
204
+ //# sourceMappingURL=slack_files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack_files.js","sourceRoot":"","sources":["../../../../src/plugins/slack/tools/slack_files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * slack_list_channels(enabledOnly?) — Slack 채널 지형(용도 매핑) 반환 + 자동 동기화.
3
+ *
4
+ * 데이터: 저장소 `slack_channels`(용도 매핑, single source — SQLite 파일 또는 Supabase)
5
+ * + Slack `conversations.list`(실시간 채널·멤버십) join.
6
+ *
7
+ * 자동 동기화 (호출 시 lazy — 변경분이 있을 때만 write):
8
+ * - 기존 채널 이름 변경 → channel_name 갱신 (channel_id 가 매칭 기준이라 이름이 바뀌어도 안 깨짐)
9
+ * - channel_id 미기록 행 → 이름으로 매칭해 channel_id backfill (최초 1회 정착)
10
+ * - 신규 채널 → enabled=false 로 추가 (사람이 `knowledge-mcp slack channel` 명령이나
11
+ * Supabase 표에서 purpose 를 채우고 켜면 노출)
12
+ * bot_can_read 판정도 channel_id 기준 — 이름 변경에 강건.
13
+ *
14
+ * 봇/챗봇은 Slack 관련 질문에서 이 툴을 먼저 호출해 "어느 채널이 무슨 용도인지,
15
+ * 봇이 읽을 수 있는지(bot_can_read)" 를 파악한 뒤 slack_search(in:#채널) /
16
+ * slack_read_channel 을 타겟팅한다. 두 도구 모두 봇이 멤버인 채널만 돌려준다.
17
+ */
18
+ import { z } from 'zod';
19
+ declare const inputSchema: z.ZodObject<{
20
+ enabledOnly: z.ZodDefault<z.ZodBoolean>;
21
+ }, "strip", z.ZodTypeAny, {
22
+ enabledOnly: boolean;
23
+ }, {
24
+ enabledOnly?: boolean | undefined;
25
+ }>;
26
+ export declare const slackListChannelsTool: import("../../../plugin/define.js").Tool<z.ZodObject<{
27
+ enabledOnly: z.ZodDefault<z.ZodBoolean>;
28
+ }, "strip", z.ZodTypeAny, {
29
+ enabledOnly: boolean;
30
+ }, {
31
+ enabledOnly?: boolean | undefined;
32
+ }>>;
33
+ export declare function handleSlackListChannels(args: z.infer<typeof inputSchema>): Promise<{
34
+ content: {
35
+ type: "text";
36
+ text: string;
37
+ }[];
38
+ isError?: undefined;
39
+ } | {
40
+ content: {
41
+ type: "text";
42
+ text: string;
43
+ }[];
44
+ isError: boolean;
45
+ }>;
46
+ export {};
@@ -0,0 +1,197 @@
1
+ /**
2
+ * slack_list_channels(enabledOnly?) — Slack 채널 지형(용도 매핑) 반환 + 자동 동기화.
3
+ *
4
+ * 데이터: 저장소 `slack_channels`(용도 매핑, single source — SQLite 파일 또는 Supabase)
5
+ * + Slack `conversations.list`(실시간 채널·멤버십) join.
6
+ *
7
+ * 자동 동기화 (호출 시 lazy — 변경분이 있을 때만 write):
8
+ * - 기존 채널 이름 변경 → channel_name 갱신 (channel_id 가 매칭 기준이라 이름이 바뀌어도 안 깨짐)
9
+ * - channel_id 미기록 행 → 이름으로 매칭해 channel_id backfill (최초 1회 정착)
10
+ * - 신규 채널 → enabled=false 로 추가 (사람이 `knowledge-mcp slack channel` 명령이나
11
+ * Supabase 표에서 purpose 를 채우고 켜면 노출)
12
+ * bot_can_read 판정도 channel_id 기준 — 이름 변경에 강건.
13
+ *
14
+ * 봇/챗봇은 Slack 관련 질문에서 이 툴을 먼저 호출해 "어느 채널이 무슨 용도인지,
15
+ * 봇이 읽을 수 있는지(bot_can_read)" 를 파악한 뒤 slack_search(in:#채널) /
16
+ * slack_read_channel 을 타겟팅한다. 두 도구 모두 봇이 멤버인 채널만 돌려준다.
17
+ */
18
+ import { z } from 'zod';
19
+ import { defineTool } from '../../../plugin/define.js';
20
+ import { getAuth } from '../../../core/context.js';
21
+ import { allowedSlackChannels } from '../../../core/scope.js';
22
+ import { getStore } from '../../../store/index.js';
23
+ import { slackApi } from '../api.js';
24
+ const inputSchema = z.object({
25
+ enabledOnly: z
26
+ .boolean()
27
+ .default(true)
28
+ .describe('true(default) 면 활성 채널만. false 면 비활성(미분류 신규 포함) 포함.'),
29
+ });
30
+ export const slackListChannelsTool = defineTool({
31
+ name: 'slack_list_channels',
32
+ description: 'Returns the Slack channel landscape — each channel purpose and whether the bot can read it (bot_can_read). ' +
33
+ 'Slack 관련 질문에서 가장 먼저 호출해 "어느 채널을 검색/읽을지" 판단하라. ' +
34
+ 'bot_can_read=true 면 slack_read_channel·slack_search 로 읽기 가능, false 면 봇을 그 채널에 초대해야 읽거나 검색할 수 있다. ' +
35
+ '호출 시 Slack 실제 채널과 자동 동기화된다(이름 변경·신규 채널 반영). 용도 매핑은 서버 저장소의 slack_channels 에서 관리.',
36
+ input: inputSchema,
37
+ run: (args) => handleSlackListChannels(args),
38
+ });
39
+ // ── 응답 TTL 캐시 ──
40
+ // 이 도구는 매 호출마다 conversations.list(Slack) + 동기화 write + DB read 를 수행한다.
41
+ // 여러 사용자가 Slack 질문을 동시에 던지면 같은 작업이 중복되므로, 짧은 TTL 로 최종 응답을 캐시한다.
42
+ // (채널 추가/이름변경 반영 지연은 최대 TTL 초 — lazy 동기화 특성상 허용 범위)
43
+ const LIST_CACHE_TTL_MS = Number(process.env.SLACK_LIST_CACHE_TTL_MS ?? 60_000);
44
+ const _listCache = new Map();
45
+ /**
46
+ * conversations.list 결과로 slack_channels 동기화 (변경분만 write).
47
+ * graceful — 실패해도 throw 하지 않고 0 카운트 반환 (read 는 계속 진행).
48
+ */
49
+ async function syncChannels(store, rows, live) {
50
+ const byId = new Map();
51
+ const byName = new Map();
52
+ for (const r of rows) {
53
+ if (r.channel_id)
54
+ byId.set(r.channel_id, r);
55
+ byName.set(r.channel_name, r);
56
+ }
57
+ const toInsert = [];
58
+ const toUpdate = [];
59
+ for (const c of live) {
60
+ const hit = byId.get(c.id);
61
+ if (hit) {
62
+ // 이미 id 로 정착된 채널 — 이름이 바뀌었으면 갱신
63
+ if (hit.channel_name !== c.name)
64
+ toUpdate.push({ id: hit.id, patch: { channel_name: c.name } });
65
+ continue;
66
+ }
67
+ const named = byName.get(c.name);
68
+ if (named && !named.channel_id) {
69
+ // 이름은 매칭되나 channel_id 미기록 — 최초 backfill
70
+ toUpdate.push({ id: named.id, patch: { channel_id: c.id } });
71
+ continue;
72
+ }
73
+ if (!named) {
74
+ // DB 에 없는 신규 채널 — 비활성으로 추가 (사람이 purpose 채우면 노출)
75
+ toInsert.push({ channel_id: c.id, channel_name: c.name, purpose: '', category: null, enabled: false, sort_order: 100 });
76
+ }
77
+ }
78
+ let inserted = 0;
79
+ let renamed = 0;
80
+ let linked = 0;
81
+ try {
82
+ if (toInsert.length) {
83
+ await store.insertSlackChannels(toInsert);
84
+ inserted = toInsert.length;
85
+ }
86
+ for (const u of toUpdate) {
87
+ await store.updateSlackChannel(u.id, u.patch);
88
+ if ('channel_name' in u.patch)
89
+ renamed++;
90
+ else
91
+ linked++;
92
+ }
93
+ }
94
+ catch {
95
+ /* sync 실패해도 매핑 read 는 진행 */
96
+ }
97
+ return { inserted, renamed, linked };
98
+ }
99
+ export async function handleSlackListChannels(args) {
100
+ // 저장소 read 의 네트워크 throw 가 [fatal] 로 누수하지 않게 graceful 래핑. 입력은 registry 가 검증했다.
101
+ try {
102
+ return await _handleSlackListChannels(args);
103
+ }
104
+ catch (e) {
105
+ return {
106
+ content: [{ type: 'text', text: `[error] 채널 목록 조회 일시 오류 (${e instanceof Error ? e.message : String(e)}).` }],
107
+ isError: true,
108
+ };
109
+ }
110
+ }
111
+ async function _handleSlackListChannels(args) {
112
+ const auth = getAuth();
113
+ const { enabledOnly } = args;
114
+ const channelScope = allowedSlackChannels(auth);
115
+ // 캐시 hit — Slack 호출·동기화·DB read 전부 skip. scope 있는 직원은 캐시 오염 방지 위해 skip.
116
+ const cacheKey = enabledOnly ? 'enabled' : 'all';
117
+ if (!channelScope) {
118
+ const cached = _listCache.get(cacheKey);
119
+ if (cached && cached.expiresAt > Date.now()) {
120
+ return { content: [{ type: 'text', text: cached.text }] };
121
+ }
122
+ }
123
+ const store = await getStore();
124
+ // 1. 저장소의 현재 매핑 (sync 비교용 — enabled 무관 전체)
125
+ let rows;
126
+ try {
127
+ rows = await store.listSlackChannels();
128
+ }
129
+ catch (e) {
130
+ return {
131
+ content: [{ type: 'text', text: `[error] slack_channels 조회 실패: ${e instanceof Error ? e.message : String(e)}` }],
132
+ isError: true,
133
+ };
134
+ }
135
+ // 2. 실시간 채널 (Slack 안 되면 매핑만 반환 — graceful). cursor 로 끝까지 — Slack 은 limit 보다
136
+ // 적게 주고 cursor 를 줄 수 있어, 첫 페이지만 보면 뒤 채널이 bot_can_read=false 로 잘못 나온다.
137
+ const live = [];
138
+ try {
139
+ let cursor;
140
+ for (let page = 0; page < 15; page++) {
141
+ const r = await slackApi('conversations.list', { types: 'public_channel,private_channel', limit: 1000, exclude_archived: true, cursor }, 'bot');
142
+ for (const c of r.channels ?? []) {
143
+ if (c.id && c.name)
144
+ live.push({ id: c.id, name: c.name, is_member: !!c.is_member });
145
+ }
146
+ cursor = r.response_metadata?.next_cursor || undefined;
147
+ if (!cursor)
148
+ break;
149
+ }
150
+ }
151
+ catch {
152
+ /* slack 토큰/스코프 문제여도 매핑은 반환 */
153
+ }
154
+ // 3. 자동 동기화 (live 가 있을 때만 — 빈 응답으로 DB 를 망가뜨리지 않게)
155
+ let synced = { inserted: 0, renamed: 0, linked: 0 };
156
+ if (live.length > 0) {
157
+ synced = await syncChannels(store, rows, live);
158
+ }
159
+ // 4. 동기화로 바뀐 게 있으면 반영본 재조회
160
+ let outRows = rows;
161
+ if (synced.inserted || synced.renamed || synced.linked) {
162
+ outRows = await store.listSlackChannels().catch(() => rows);
163
+ }
164
+ if (enabledOnly)
165
+ outRows = outRows.filter((r) => r.enabled);
166
+ // 직원별 Slack 채널 scope (admin·미설정 = 전체). channel_id 기준.
167
+ if (channelScope)
168
+ outRows = outRows.filter((r) => r.channel_id && channelScope.has(r.channel_id));
169
+ // 5. bot_can_read — channel_id 기준 (이름 변경에 강건), 미링크 행은 이름 fallback
170
+ const liveById = new Map();
171
+ const liveByName = new Map();
172
+ for (const c of live) {
173
+ liveById.set(c.id, c.is_member);
174
+ liveByName.set(c.name, c.is_member);
175
+ }
176
+ const channels = outRows.map((r) => ({
177
+ channel: `#${r.channel_name}`,
178
+ purpose: r.purpose,
179
+ category: r.category,
180
+ bot_can_read: r.channel_id ? !!liveById.get(r.channel_id) : !!liveByName.get(r.channel_name),
181
+ enabled: r.enabled,
182
+ }));
183
+ const syncNote = synced.inserted || synced.renamed || synced.linked
184
+ ? ` (동기화: 신규 ${synced.inserted}·이름갱신 ${synced.renamed}·id연결 ${synced.linked})`
185
+ : '';
186
+ const text = JSON.stringify({
187
+ count: channels.length,
188
+ note: 'bot_can_read=true → slack_read_channel·slack_search 가능. false → 봇 초대 필요.' + syncNote,
189
+ channels,
190
+ }, null, 2);
191
+ // Slack 응답이 정상일 때만 캐시 (live 비었으면 일시 장애일 수 있어 캐시 안 함 → 다음 호출 재시도)
192
+ if (live.length > 0 && !channelScope) {
193
+ _listCache.set(cacheKey, { text, expiresAt: Date.now() + LIST_CACHE_TTL_MS });
194
+ }
195
+ return { content: [{ type: 'text', text }] };
196
+ }
197
+ //# sourceMappingURL=slack_list_channels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack_list_channels.js","sourceRoot":"","sources":["../../../../src/plugins/slack/tools/slack_list_channels.ts"],"names":[],"mappings":"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"}