@templmf/temp-solf-lmf 0.0.139 → 0.0.140
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/confMCP.ts +639 -0
- package/package.json +1 -1
package/confMCP.ts
ADDED
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
import axios, { AxiosInstance } from "axios";
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import LRUCache from "lru-cache";
|
|
5
|
+
import { htmlToText } from "html-to-text";
|
|
6
|
+
import BM25 from "wink-bm25-text-search";
|
|
7
|
+
|
|
8
|
+
interface ConfluencePage {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
space?: string;
|
|
12
|
+
version?: number;
|
|
13
|
+
url: string;
|
|
14
|
+
content: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface PageResult {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
url: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface SpaceInfo {
|
|
24
|
+
key: string;
|
|
25
|
+
name: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface SearchResult {
|
|
29
|
+
pageId: string;
|
|
30
|
+
title: string;
|
|
31
|
+
url: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface SearchParams {
|
|
35
|
+
keywords: string[];
|
|
36
|
+
spaces?: string[];
|
|
37
|
+
contributors?: string[];
|
|
38
|
+
limit?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface HybridSearchParams {
|
|
42
|
+
question: string;
|
|
43
|
+
spaces?: string[];
|
|
44
|
+
contributors?: string[];
|
|
45
|
+
maxPages?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface AskConfluenceParams {
|
|
49
|
+
question: string;
|
|
50
|
+
spaces?: string[];
|
|
51
|
+
contributors?: string[];
|
|
52
|
+
maxPages?: number;
|
|
53
|
+
maxChunks?: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface TreePage {
|
|
57
|
+
id: string;
|
|
58
|
+
title: string;
|
|
59
|
+
children?: TreePage[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface ScoredPage extends SearchResult {
|
|
63
|
+
score: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface Document {
|
|
67
|
+
pageId: string;
|
|
68
|
+
title: string;
|
|
69
|
+
url: string;
|
|
70
|
+
chunk: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface SearchResultWithScore {
|
|
74
|
+
title: string;
|
|
75
|
+
url: string;
|
|
76
|
+
score: number;
|
|
77
|
+
content: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface ExploreResult {
|
|
81
|
+
page: SearchResult;
|
|
82
|
+
children: { id: string; title: string }[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface ChildPage {
|
|
86
|
+
id: string;
|
|
87
|
+
title: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const server = new Server({
|
|
91
|
+
name: "confluence-7.4.7",
|
|
92
|
+
version: "1.0.0"
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const baseURL = process.env.CONFLUENCE_BASE_URL;
|
|
96
|
+
const authorization = process.env.CONFLUENCE_AUTHORIZATION;
|
|
97
|
+
|
|
98
|
+
if (!baseURL || !authorization) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
"CONFLUENCE_BASE_URL and CONFLUENCE_AUTHORIZATION are required"
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const client: AxiosInstance = axios.create({
|
|
105
|
+
baseURL,
|
|
106
|
+
timeout: 30000,
|
|
107
|
+
headers: {
|
|
108
|
+
Authorization: authorization,
|
|
109
|
+
Accept: "application/json"
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const pageCache = new LRUCache<string, ConfluencePage>({
|
|
114
|
+
max: 100,
|
|
115
|
+
ttl: 1000 * 60 * 10
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
function stripHtml(html = ""): string {
|
|
119
|
+
return html
|
|
120
|
+
.replace(/<[^>]+>/g, " ")
|
|
121
|
+
.replace(/\s+/g, " ")
|
|
122
|
+
.trim();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
interface BuildCqlParams {
|
|
126
|
+
keywords?: string[];
|
|
127
|
+
spaces?: string[];
|
|
128
|
+
contributors?: string[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function buildCql({
|
|
132
|
+
keywords = [],
|
|
133
|
+
spaces = [],
|
|
134
|
+
contributors = []
|
|
135
|
+
}: BuildCqlParams): string {
|
|
136
|
+
const clauses: string[] = [];
|
|
137
|
+
|
|
138
|
+
if (spaces.length) {
|
|
139
|
+
clauses.push(
|
|
140
|
+
"(" +
|
|
141
|
+
spaces
|
|
142
|
+
.map((v) => `space="${v}"`)
|
|
143
|
+
.join(" OR ") +
|
|
144
|
+
")"
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (contributors.length) {
|
|
149
|
+
clauses.push(
|
|
150
|
+
"(" +
|
|
151
|
+
contributors
|
|
152
|
+
.map((v) => `contributor="${v}"`)
|
|
153
|
+
.join(" OR ") +
|
|
154
|
+
")"
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (keywords.length) {
|
|
159
|
+
clauses.push(
|
|
160
|
+
"(" +
|
|
161
|
+
keywords
|
|
162
|
+
.map((v) => `text~"${v}"`)
|
|
163
|
+
.join(" OR ") +
|
|
164
|
+
")"
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return clauses.join(" AND ");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function getPageById(pageId: string): Promise<ConfluencePage> {
|
|
172
|
+
const cached = pageCache.get(pageId);
|
|
173
|
+
|
|
174
|
+
if (cached) {
|
|
175
|
+
return cached;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const { data } = await client.get(
|
|
179
|
+
`/rest/api/content/${pageId}`,
|
|
180
|
+
{
|
|
181
|
+
params: {
|
|
182
|
+
expand: "body.storage,version,space"
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const result: ConfluencePage = {
|
|
188
|
+
id: data.id,
|
|
189
|
+
title: data.title,
|
|
190
|
+
space: data.space?.key,
|
|
191
|
+
version: data.version?.number,
|
|
192
|
+
url: `${baseURL}${data._links?.webui}`,
|
|
193
|
+
content: data.body?.storage?.value || ""
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
pageCache.set(pageId, result);
|
|
197
|
+
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function getPageByTitle(
|
|
202
|
+
title: string,
|
|
203
|
+
space?: string
|
|
204
|
+
): Promise<PageResult | null> {
|
|
205
|
+
const params: Record<string, string> = {
|
|
206
|
+
title,
|
|
207
|
+
expand: "body.storage"
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
if (space) {
|
|
211
|
+
params.spaceKey = space;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const { data } = await client.get(
|
|
215
|
+
"/rest/api/content",
|
|
216
|
+
{
|
|
217
|
+
params
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const page = data.results?.[0];
|
|
222
|
+
|
|
223
|
+
if (!page) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
id: page.id,
|
|
229
|
+
title: page.title,
|
|
230
|
+
url: `${baseURL}${page._links?.webui}`
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function getSpaces(): Promise<SpaceInfo[]> {
|
|
235
|
+
const { data } = await client.get(
|
|
236
|
+
"/rest/api/space"
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
return data.results.map((v: any) => ({
|
|
240
|
+
key: v.key,
|
|
241
|
+
name: v.name
|
|
242
|
+
}));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function searchPages({
|
|
246
|
+
keywords,
|
|
247
|
+
spaces = [],
|
|
248
|
+
contributors = [],
|
|
249
|
+
limit = 10
|
|
250
|
+
}: SearchParams): Promise<SearchResult[]> {
|
|
251
|
+
const cql = buildCql({
|
|
252
|
+
keywords,
|
|
253
|
+
spaces,
|
|
254
|
+
contributors
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const { data } = await client.get(
|
|
258
|
+
"/rest/api/search",
|
|
259
|
+
{
|
|
260
|
+
params: {
|
|
261
|
+
cql,
|
|
262
|
+
limit
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
return data.results.map((item: any) => ({
|
|
268
|
+
pageId: item.content?.id,
|
|
269
|
+
title: item.title,
|
|
270
|
+
url: `${baseURL}${item.url}`
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
server.registerTool(
|
|
275
|
+
"get_page_by_id",
|
|
276
|
+
{
|
|
277
|
+
title: "Get Page By Id",
|
|
278
|
+
description:
|
|
279
|
+
"Get Confluence page content by page id",
|
|
280
|
+
inputSchema: {
|
|
281
|
+
pageId: z.string()
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
async ({ pageId }: { pageId: string }) => {
|
|
285
|
+
return await getPageById(pageId);
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
server.registerTool(
|
|
290
|
+
"get_page_by_title",
|
|
291
|
+
{
|
|
292
|
+
title: "Get Page By Title",
|
|
293
|
+
description:
|
|
294
|
+
"Find page by title",
|
|
295
|
+
inputSchema: {
|
|
296
|
+
title: z.string(),
|
|
297
|
+
space: z.string().optional()
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
async ({ title, space }: { title: string; space?: string }) => {
|
|
301
|
+
return await getPageByTitle(
|
|
302
|
+
title,
|
|
303
|
+
space
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
server.registerTool(
|
|
309
|
+
"get_spaces",
|
|
310
|
+
{
|
|
311
|
+
title: "Get Spaces",
|
|
312
|
+
description:
|
|
313
|
+
"List all spaces"
|
|
314
|
+
},
|
|
315
|
+
async () => {
|
|
316
|
+
return await getSpaces();
|
|
317
|
+
}
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
server.registerTool(
|
|
321
|
+
"search_pages",
|
|
322
|
+
{
|
|
323
|
+
title: "Search Pages",
|
|
324
|
+
description:
|
|
325
|
+
"Search pages by keywords, spaces and contributors",
|
|
326
|
+
inputSchema: {
|
|
327
|
+
keywords: z
|
|
328
|
+
.array(z.string())
|
|
329
|
+
.min(1),
|
|
330
|
+
spaces: z
|
|
331
|
+
.array(z.string())
|
|
332
|
+
.optional(),
|
|
333
|
+
contributors: z
|
|
334
|
+
.array(z.string())
|
|
335
|
+
.optional(),
|
|
336
|
+
limit: z.number().default(10)
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
async (args: SearchParams) => {
|
|
340
|
+
return await searchPages(args);
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
async function getChildren(pageId: string): Promise<ChildPage[]> {
|
|
345
|
+
const { data } = await client.get(
|
|
346
|
+
`/rest/api/content/${pageId}/child/page`
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
return data.results.map((v: any) => ({
|
|
350
|
+
id: v.id,
|
|
351
|
+
title: v.title
|
|
352
|
+
}));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async function buildTree(pageId: string, depth: number): Promise<TreePage> {
|
|
356
|
+
const page = await getPageById(pageId);
|
|
357
|
+
|
|
358
|
+
if (depth <= 0) {
|
|
359
|
+
return {
|
|
360
|
+
id: page.id,
|
|
361
|
+
title: page.title
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const children = await getChildren(pageId);
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
id: page.id,
|
|
369
|
+
title: page.title,
|
|
370
|
+
children: await Promise.all(
|
|
371
|
+
children.map((v) =>
|
|
372
|
+
buildTree(v.id, depth - 1)
|
|
373
|
+
)
|
|
374
|
+
)
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function chunkText(
|
|
379
|
+
text: string,
|
|
380
|
+
size = 1200,
|
|
381
|
+
overlap = 200
|
|
382
|
+
): string[] {
|
|
383
|
+
const chunks: string[] = [];
|
|
384
|
+
|
|
385
|
+
let start = 0;
|
|
386
|
+
|
|
387
|
+
while (start < text.length) {
|
|
388
|
+
chunks.push(
|
|
389
|
+
text.slice(start, start + size)
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
start += size - overlap;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return chunks;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function buildQueries(question: string): string[] {
|
|
399
|
+
const queries = [question];
|
|
400
|
+
|
|
401
|
+
queries.push(
|
|
402
|
+
...question
|
|
403
|
+
.split(/[ ,,、]/)
|
|
404
|
+
.map((v) => v.trim())
|
|
405
|
+
.filter(Boolean)
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
return [...new Set(queries)];
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async function hybridSearch({
|
|
412
|
+
question,
|
|
413
|
+
spaces,
|
|
414
|
+
contributors,
|
|
415
|
+
maxPages = 10
|
|
416
|
+
}: HybridSearchParams): Promise<ScoredPage[]> {
|
|
417
|
+
const queries = buildQueries(question);
|
|
418
|
+
|
|
419
|
+
const weights = [
|
|
420
|
+
1,
|
|
421
|
+
0.8,
|
|
422
|
+
0.6,
|
|
423
|
+
0.4,
|
|
424
|
+
0.2
|
|
425
|
+
];
|
|
426
|
+
|
|
427
|
+
const scoreMap = new Map<string, ScoredPage>();
|
|
428
|
+
|
|
429
|
+
for (let i = 0; i < queries.length; i++) {
|
|
430
|
+
const pages = await searchPages({
|
|
431
|
+
keywords: [queries[i]],
|
|
432
|
+
spaces,
|
|
433
|
+
contributors,
|
|
434
|
+
limit: 20
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
for (const page of pages) {
|
|
438
|
+
const old =
|
|
439
|
+
scoreMap.get(page.pageId) || {
|
|
440
|
+
...page,
|
|
441
|
+
score: 0
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
old.score += weights[i] || 0.1;
|
|
445
|
+
|
|
446
|
+
scoreMap.set(page.pageId, old);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return [...scoreMap.values()]
|
|
451
|
+
.sort((a, b) => b.score - a.score)
|
|
452
|
+
.slice(0, maxPages);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
async function askConfluence({
|
|
456
|
+
question,
|
|
457
|
+
spaces,
|
|
458
|
+
contributors,
|
|
459
|
+
maxPages = 10,
|
|
460
|
+
maxChunks = 15
|
|
461
|
+
}: AskConfluenceParams): Promise<SearchResultWithScore[]> {
|
|
462
|
+
const pages = await hybridSearch({
|
|
463
|
+
question,
|
|
464
|
+
spaces,
|
|
465
|
+
contributors,
|
|
466
|
+
maxPages
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
const documents: Document[] = [];
|
|
470
|
+
|
|
471
|
+
for (const page of pages) {
|
|
472
|
+
const detail = await getPageById(
|
|
473
|
+
page.pageId
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
const text = htmlToText(
|
|
477
|
+
detail.content,
|
|
478
|
+
{
|
|
479
|
+
wordwrap: false
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
const chunks = chunkText(text);
|
|
484
|
+
|
|
485
|
+
chunks.forEach((chunk) => {
|
|
486
|
+
documents.push({
|
|
487
|
+
pageId: detail.id,
|
|
488
|
+
title: detail.title,
|
|
489
|
+
url: detail.url,
|
|
490
|
+
chunk
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const engine = BM25();
|
|
496
|
+
|
|
497
|
+
engine.defineConfig({
|
|
498
|
+
fldWeights: {
|
|
499
|
+
chunk: 1
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
engine.definePrepTasks([]);
|
|
504
|
+
|
|
505
|
+
engine.defineField("chunk");
|
|
506
|
+
|
|
507
|
+
engine.defineRef("id");
|
|
508
|
+
|
|
509
|
+
engine.configure();
|
|
510
|
+
|
|
511
|
+
documents.forEach((doc, index) => {
|
|
512
|
+
engine.addDoc(index, {
|
|
513
|
+
chunk: doc.chunk
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
engine.consolidate();
|
|
518
|
+
|
|
519
|
+
const results = engine.search(
|
|
520
|
+
question,
|
|
521
|
+
maxChunks
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
return results.map((v: [number, number]) => {
|
|
525
|
+
const doc = documents[v[0]];
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
title: doc.title,
|
|
529
|
+
url: doc.url,
|
|
530
|
+
score: v[1],
|
|
531
|
+
content: doc.chunk
|
|
532
|
+
};
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async function exploreConfluence(
|
|
537
|
+
topic: string
|
|
538
|
+
): Promise<ExploreResult[]> {
|
|
539
|
+
const pages = await searchPages({
|
|
540
|
+
keywords: [topic],
|
|
541
|
+
limit: 5
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
const result: ExploreResult[] = [];
|
|
545
|
+
|
|
546
|
+
for (const page of pages) {
|
|
547
|
+
const children = await getChildren(
|
|
548
|
+
page.pageId
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
result.push({
|
|
552
|
+
page,
|
|
553
|
+
children
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return result;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
server.registerTool(
|
|
561
|
+
"get_children",
|
|
562
|
+
{
|
|
563
|
+
title: "Get Children",
|
|
564
|
+
description:
|
|
565
|
+
"Get child pages",
|
|
566
|
+
inputSchema: {
|
|
567
|
+
pageId: z.string()
|
|
568
|
+
}
|
|
569
|
+
},
|
|
570
|
+
async ({ pageId }: { pageId: string }) =>
|
|
571
|
+
getChildren(pageId)
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
server.registerTool(
|
|
575
|
+
"get_page_tree",
|
|
576
|
+
{
|
|
577
|
+
title: "Get Page Tree",
|
|
578
|
+
description:
|
|
579
|
+
"Get page tree recursively",
|
|
580
|
+
inputSchema: {
|
|
581
|
+
rootPageId: z.string(),
|
|
582
|
+
depth: z.number().default(2)
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
async ({
|
|
586
|
+
rootPageId,
|
|
587
|
+
depth
|
|
588
|
+
}: {
|
|
589
|
+
rootPageId: string;
|
|
590
|
+
depth: number;
|
|
591
|
+
}) =>
|
|
592
|
+
buildTree(rootPageId, depth)
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
server.registerTool(
|
|
596
|
+
"ask_confluence",
|
|
597
|
+
{
|
|
598
|
+
title: "Ask Confluence",
|
|
599
|
+
description:
|
|
600
|
+
"Retrieve relevant contexts from Confluence",
|
|
601
|
+
|
|
602
|
+
inputSchema: {
|
|
603
|
+
question: z.string(),
|
|
604
|
+
|
|
605
|
+
spaces: z
|
|
606
|
+
.array(z.string())
|
|
607
|
+
.optional(),
|
|
608
|
+
|
|
609
|
+
contributors: z
|
|
610
|
+
.array(z.string())
|
|
611
|
+
.optional(),
|
|
612
|
+
|
|
613
|
+
maxPages: z
|
|
614
|
+
.number()
|
|
615
|
+
.default(10),
|
|
616
|
+
|
|
617
|
+
maxChunks: z
|
|
618
|
+
.number()
|
|
619
|
+
.default(15)
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
async (args: AskConfluenceParams) =>
|
|
623
|
+
askConfluence(args)
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
server.registerTool(
|
|
627
|
+
"explore_confluence",
|
|
628
|
+
{
|
|
629
|
+
title: "Explore Topic",
|
|
630
|
+
description:
|
|
631
|
+
"Explore topic related pages",
|
|
632
|
+
|
|
633
|
+
inputSchema: {
|
|
634
|
+
topic: z.string()
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
async ({ topic }: { topic: string }) =>
|
|
638
|
+
exploreConfluence(topic)
|
|
639
|
+
);
|