gwanli 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp.js +287 -2
- package/package.json +3 -3
package/dist/mcp.js
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
4
4
|
import { z } from "zod";
|
5
|
-
import { loadConfig, addWorkspace, updateWorkspace, deleteWorkspace, OAUTH_BASE_URL, checkWorkspace, indexNotionPages, JobTracker, listFiles, getRecentJobs, getJobById, } from "gwanli-core";
|
5
|
+
import { loadConfig, addWorkspace, updateWorkspace, deleteWorkspace, OAUTH_BASE_URL, checkWorkspace, indexNotionPages, JobTracker, listFiles, getRecentJobs, getJobById, get_db, searchPages, findPagesByPattern, getPageBySlug, } from "gwanli-core";
|
6
6
|
// Create an MCP server
|
7
7
|
const server = new McpServer({
|
8
8
|
name: "gwanli-mcp",
|
@@ -385,7 +385,9 @@ server.registerTool("checkJob", {
|
|
385
385
|
}
|
386
386
|
const jobsList = jobs
|
387
387
|
.map((job) => {
|
388
|
-
const statusText = job.state?.status
|
388
|
+
const statusText = job.state?.status
|
389
|
+
? ` (${job.state.status})`
|
390
|
+
: "";
|
389
391
|
const timeStr = new Date(job.timestamp).toLocaleString();
|
390
392
|
return `- **${job.jobId}**${statusText} - ${timeStr}`;
|
391
393
|
})
|
@@ -412,6 +414,289 @@ server.registerTool("checkJob", {
|
|
412
414
|
};
|
413
415
|
}
|
414
416
|
});
|
417
|
+
// Register grep tool (FTS search)
|
418
|
+
server.registerTool("grep", {
|
419
|
+
description: "Search pages using full-text search with ranking and relevance",
|
420
|
+
inputSchema: {
|
421
|
+
query: z
|
422
|
+
.string()
|
423
|
+
.min(1)
|
424
|
+
.describe("Search query for full-text search across pages and databases"),
|
425
|
+
workspace: z
|
426
|
+
.string()
|
427
|
+
.optional()
|
428
|
+
.describe("Workspace name to search in - defaults to default_search from config"),
|
429
|
+
limit: z
|
430
|
+
.number()
|
431
|
+
.default(10)
|
432
|
+
.describe("Maximum number of results to return - defaults to 10"),
|
433
|
+
offset: z
|
434
|
+
.number()
|
435
|
+
.default(0)
|
436
|
+
.describe("Number of results to skip for pagination - defaults to 0"),
|
437
|
+
includeContent: z
|
438
|
+
.boolean()
|
439
|
+
.default(false)
|
440
|
+
.describe("Include page content in results - defaults to false for performance"),
|
441
|
+
},
|
442
|
+
}, async (args) => {
|
443
|
+
try {
|
444
|
+
const config = loadConfig();
|
445
|
+
if (!config.default_search) {
|
446
|
+
return {
|
447
|
+
content: [
|
448
|
+
{
|
449
|
+
type: "text",
|
450
|
+
text: "No default search configured",
|
451
|
+
},
|
452
|
+
],
|
453
|
+
isError: true,
|
454
|
+
};
|
455
|
+
}
|
456
|
+
const searchWorkspace = args.workspace ?? config.default_search;
|
457
|
+
// Initialize database connection
|
458
|
+
const db = get_db(config.workspace[searchWorkspace].db_path);
|
459
|
+
// Perform search
|
460
|
+
const result = searchPages(db, args.query, {
|
461
|
+
limit: args.limit,
|
462
|
+
offset: args.offset,
|
463
|
+
includeContent: args.includeContent,
|
464
|
+
});
|
465
|
+
db.close();
|
466
|
+
if (result.results.length === 0) {
|
467
|
+
return {
|
468
|
+
content: [
|
469
|
+
{
|
470
|
+
type: "text",
|
471
|
+
text: `No results found for query: "${args.query}"`,
|
472
|
+
},
|
473
|
+
],
|
474
|
+
};
|
475
|
+
}
|
476
|
+
// Format results
|
477
|
+
const formattedResults = result.results
|
478
|
+
.map((page, index) => {
|
479
|
+
const title = page.title || "Untitled";
|
480
|
+
const slug = page.slug || "";
|
481
|
+
const type = page.type?.toUpperCase() || "UNKNOWN";
|
482
|
+
const rank = page.rank ? ` (rank: ${page.rank.toFixed(3)})` : "";
|
483
|
+
let content = `**${index + 1 + args.offset}. [${type}] ${title}**${rank}`;
|
484
|
+
if (slug)
|
485
|
+
content += `\n Slug: \`${slug}\``;
|
486
|
+
if (page.content && args.includeContent) {
|
487
|
+
const preview = page.content.substring(0, 200);
|
488
|
+
content += `\n Preview: ${preview}${page.content.length > 200 ? "..." : ""}`;
|
489
|
+
}
|
490
|
+
content += `\n Updated: ${new Date(page.lastUpdated).toLocaleString()}`;
|
491
|
+
return content;
|
492
|
+
})
|
493
|
+
.join("\n\n");
|
494
|
+
const paginationInfo = `**Results ${args.offset + 1}-${args.offset + result.results.length} of ${result.totalCount}**${result.hasMore ? " (more available)" : ""}`;
|
495
|
+
return {
|
496
|
+
content: [
|
497
|
+
{
|
498
|
+
type: "text",
|
499
|
+
text: `**Search Results for: "${args.query}"**\n\n${paginationInfo}\n\n${formattedResults}`,
|
500
|
+
},
|
501
|
+
],
|
502
|
+
};
|
503
|
+
}
|
504
|
+
catch (error) {
|
505
|
+
return {
|
506
|
+
content: [
|
507
|
+
{
|
508
|
+
type: "text",
|
509
|
+
text: `Error searching pages: ${error instanceof Error ? error.message : String(error)}`,
|
510
|
+
},
|
511
|
+
],
|
512
|
+
isError: true,
|
513
|
+
};
|
514
|
+
}
|
515
|
+
});
|
516
|
+
// Register glob tool (pattern matching)
|
517
|
+
server.registerTool("glob", {
|
518
|
+
description: "Find pages using glob patterns (*, ?, [abc]) on slug or title fields",
|
519
|
+
inputSchema: {
|
520
|
+
pattern: z
|
521
|
+
.string()
|
522
|
+
.min(1)
|
523
|
+
.describe("Glob pattern to match against (e.g., 'project/*', 'meeting-*-notes', '[abc]*')"),
|
524
|
+
field: z
|
525
|
+
.enum(["slug", "title"])
|
526
|
+
.default("slug")
|
527
|
+
.describe("Field to match pattern against - defaults to 'slug'"),
|
528
|
+
workspace: z
|
529
|
+
.string()
|
530
|
+
.optional()
|
531
|
+
.describe("Workspace name to search in - defaults to default_search from config"),
|
532
|
+
limit: z
|
533
|
+
.number()
|
534
|
+
.default(10)
|
535
|
+
.describe("Maximum number of results to return - defaults to 10"),
|
536
|
+
offset: z
|
537
|
+
.number()
|
538
|
+
.default(0)
|
539
|
+
.describe("Number of results to skip for pagination - defaults to 0"),
|
540
|
+
includeContent: z
|
541
|
+
.boolean()
|
542
|
+
.default(false)
|
543
|
+
.describe("Include page content in results - defaults to false for performance"),
|
544
|
+
},
|
545
|
+
}, async (args) => {
|
546
|
+
try {
|
547
|
+
const config = loadConfig();
|
548
|
+
if (!config.default_search) {
|
549
|
+
return {
|
550
|
+
content: [
|
551
|
+
{
|
552
|
+
type: "text",
|
553
|
+
text: "No default search configured",
|
554
|
+
},
|
555
|
+
],
|
556
|
+
isError: true,
|
557
|
+
};
|
558
|
+
}
|
559
|
+
const searchWorkspace = args.workspace ?? config.default_search;
|
560
|
+
// Initialize database connection
|
561
|
+
const db = get_db(config.workspace[searchWorkspace].db_path);
|
562
|
+
// Perform pattern search
|
563
|
+
const result = findPagesByPattern(db, args.pattern, args.field, {
|
564
|
+
limit: args.limit,
|
565
|
+
offset: args.offset,
|
566
|
+
includeContent: args.includeContent,
|
567
|
+
});
|
568
|
+
db.close();
|
569
|
+
if (result.results.length === 0) {
|
570
|
+
return {
|
571
|
+
content: [
|
572
|
+
{
|
573
|
+
type: "text",
|
574
|
+
text: `No results found for pattern: "${args.pattern}" in field: ${args.field}`,
|
575
|
+
},
|
576
|
+
],
|
577
|
+
};
|
578
|
+
}
|
579
|
+
// Format results
|
580
|
+
const formattedResults = result.results
|
581
|
+
.map((page, index) => {
|
582
|
+
const title = page.title || "Untitled";
|
583
|
+
const slug = page.slug || "";
|
584
|
+
const type = page.type?.toUpperCase() || "UNKNOWN";
|
585
|
+
let content = `**${index + 1 + args.offset}. [${type}] ${title}**`;
|
586
|
+
if (slug)
|
587
|
+
content += `\n Slug: \`${slug}\``;
|
588
|
+
if (page.content && args.includeContent) {
|
589
|
+
const preview = page.content.substring(0, 200);
|
590
|
+
content += `\n Preview: ${preview}${page.content.length > 200 ? "..." : ""}`;
|
591
|
+
}
|
592
|
+
content += `\n Updated: ${new Date(page.lastUpdated).toLocaleString()}`;
|
593
|
+
return content;
|
594
|
+
})
|
595
|
+
.join("\n\n");
|
596
|
+
const paginationInfo = `**Results ${args.offset + 1}-${args.offset + result.results.length} of ${result.totalCount}**${result.hasMore ? " (more available)" : ""}`;
|
597
|
+
return {
|
598
|
+
content: [
|
599
|
+
{
|
600
|
+
type: "text",
|
601
|
+
text: `**Pattern Results for: "${args.pattern}" (${args.field})**\n\n${paginationInfo}\n\n${formattedResults}`,
|
602
|
+
},
|
603
|
+
],
|
604
|
+
};
|
605
|
+
}
|
606
|
+
catch (error) {
|
607
|
+
return {
|
608
|
+
content: [
|
609
|
+
{
|
610
|
+
type: "text",
|
611
|
+
text: `Error finding pages by pattern: ${error instanceof Error ? error.message : String(error)}`,
|
612
|
+
},
|
613
|
+
],
|
614
|
+
isError: true,
|
615
|
+
};
|
616
|
+
}
|
617
|
+
});
|
618
|
+
// Register view tool (single page retrieval)
|
619
|
+
server.registerTool("view", {
|
620
|
+
description: "View a specific page by its slug",
|
621
|
+
inputSchema: {
|
622
|
+
slug: z.string().min(1).describe("Slug of the page to view"),
|
623
|
+
workspace: z
|
624
|
+
.string()
|
625
|
+
.optional()
|
626
|
+
.describe("Workspace name to search in - defaults to default_search from config"),
|
627
|
+
includeContent: z
|
628
|
+
.boolean()
|
629
|
+
.default(true)
|
630
|
+
.describe("Include page content in results - defaults to true"),
|
631
|
+
},
|
632
|
+
}, async (args) => {
|
633
|
+
try {
|
634
|
+
const config = loadConfig();
|
635
|
+
if (!config.default_search) {
|
636
|
+
return {
|
637
|
+
content: [
|
638
|
+
{
|
639
|
+
type: "text",
|
640
|
+
text: "No default search configured",
|
641
|
+
},
|
642
|
+
],
|
643
|
+
isError: true,
|
644
|
+
};
|
645
|
+
}
|
646
|
+
const searchWorkspace = args.workspace ?? config.default_search;
|
647
|
+
// Initialize database connection
|
648
|
+
const db = get_db(config.workspace[searchWorkspace].db_path);
|
649
|
+
// Get page by slug
|
650
|
+
const page = getPageBySlug(db, args.slug);
|
651
|
+
db.close();
|
652
|
+
if (!page) {
|
653
|
+
return {
|
654
|
+
content: [
|
655
|
+
{
|
656
|
+
type: "text",
|
657
|
+
text: `Page not found with slug: "${args.slug}"`,
|
658
|
+
},
|
659
|
+
],
|
660
|
+
isError: true,
|
661
|
+
};
|
662
|
+
}
|
663
|
+
// Format page details
|
664
|
+
const title = page.title || "Untitled";
|
665
|
+
const type = page.type?.toUpperCase() || "UNKNOWN";
|
666
|
+
const createdDate = new Date(page.createdAt).toLocaleString();
|
667
|
+
const updatedDate = new Date(page.lastUpdated).toLocaleString();
|
668
|
+
let formattedPage = `**[${type}] ${title}**\n`;
|
669
|
+
formattedPage += `**Slug:** \`${page.slug}\`\n`;
|
670
|
+
formattedPage += `**ID:** \`${page.id}\`\n`;
|
671
|
+
formattedPage += `**Created:** ${createdDate}\n`;
|
672
|
+
formattedPage += `**Updated:** ${updatedDate}\n`;
|
673
|
+
if (page.type === "database" && "properties" in page && page.properties) {
|
674
|
+
formattedPage += `**Properties:** ${JSON.stringify(page.properties, null, 2)}\n`;
|
675
|
+
}
|
676
|
+
if (args.includeContent && "content" in page && page.content) {
|
677
|
+
formattedPage += `\n**Content:**\n${page.content}`;
|
678
|
+
}
|
679
|
+
return {
|
680
|
+
content: [
|
681
|
+
{
|
682
|
+
type: "text",
|
683
|
+
text: formattedPage,
|
684
|
+
},
|
685
|
+
],
|
686
|
+
};
|
687
|
+
}
|
688
|
+
catch (error) {
|
689
|
+
return {
|
690
|
+
content: [
|
691
|
+
{
|
692
|
+
type: "text",
|
693
|
+
text: `Error viewing page: ${error instanceof Error ? error.message : String(error)}`,
|
694
|
+
},
|
695
|
+
],
|
696
|
+
isError: true,
|
697
|
+
};
|
698
|
+
}
|
699
|
+
});
|
415
700
|
// Start receiving messages on stdin and sending messages on stdout
|
416
701
|
const transport = new StdioServerTransport();
|
417
702
|
await server.connect(transport);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "gwanli",
|
3
|
-
"version": "0.4.
|
3
|
+
"version": "0.4.1",
|
4
4
|
"description": "Notion management with AI capabilities - CLI and MCP server",
|
5
5
|
"type": "module",
|
6
6
|
"main": "dist/cli.js",
|
@@ -20,8 +20,8 @@
|
|
20
20
|
"dependencies": {
|
21
21
|
"@hono/node-server": "^1.18.2",
|
22
22
|
"commander": "^11.0.0",
|
23
|
-
"gwanli-core": "{\n gwanli-core: 0.4.
|
24
|
-
"gwanli-mcp": "{\n gwanli-mcp: 0.4.
|
23
|
+
"gwanli-core": "{\n gwanli-core: 0.4.1\n}",
|
24
|
+
"gwanli-mcp": "{\n gwanli-mcp: 0.4.1\n}",
|
25
25
|
"hono": "^4.9.1",
|
26
26
|
"open": "^10.2.0"
|
27
27
|
},
|