searchsocket 0.6.3 → 0.7.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/dist/cli.js +42 -139
- package/dist/index.cjs +41 -138
- package/dist/index.js +41 -138
- package/dist/sveltekit.cjs +41 -138
- package/dist/sveltekit.js +41 -138
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import { Command, Option } from "commander";
|
|
|
12
12
|
// package.json
|
|
13
13
|
var package_default = {
|
|
14
14
|
name: "searchsocket",
|
|
15
|
-
version: "0.
|
|
15
|
+
version: "0.7.0",
|
|
16
16
|
description: "Semantic site search and MCP retrieval for SvelteKit static sites",
|
|
17
17
|
license: "MIT",
|
|
18
18
|
author: "Greg Priday <greg@siteorigin.com>",
|
|
@@ -4494,45 +4494,20 @@ var SearchEngine = class _SearchEngine {
|
|
|
4494
4494
|
function createServer(engine) {
|
|
4495
4495
|
const server = new McpServer({
|
|
4496
4496
|
name: "searchsocket-mcp",
|
|
4497
|
-
version: "0.
|
|
4497
|
+
version: "0.2.0"
|
|
4498
4498
|
});
|
|
4499
4499
|
server.registerTool(
|
|
4500
4500
|
"search",
|
|
4501
4501
|
{
|
|
4502
|
-
description:
|
|
4502
|
+
description: "Searches indexed site content using semantic similarity. Returns ranked results with url, title, snippet, chunkText (full section markdown), score, and routeFile (source file path for editing). Each result includes the best-matching section; set groupBy to 'page' (default) for additional chunk sub-results per page. Use routeFile to locate the source file when editing content. If snippets lack detail, call get_page with the result URL to retrieve the full page markdown.",
|
|
4503
4503
|
inputSchema: {
|
|
4504
|
-
query: z3.string().min(1),
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
maxSubResults: z3.number().int().positive().max(20).optional()
|
|
4512
|
-
},
|
|
4513
|
-
outputSchema: {
|
|
4514
|
-
q: z3.string(),
|
|
4515
|
-
scope: z3.string(),
|
|
4516
|
-
results: z3.array(z3.object({
|
|
4517
|
-
url: z3.string(),
|
|
4518
|
-
title: z3.string(),
|
|
4519
|
-
sectionTitle: z3.string().optional(),
|
|
4520
|
-
snippet: z3.string(),
|
|
4521
|
-
score: z3.number(),
|
|
4522
|
-
routeFile: z3.string(),
|
|
4523
|
-
chunks: z3.array(z3.object({
|
|
4524
|
-
sectionTitle: z3.string().optional(),
|
|
4525
|
-
snippet: z3.string(),
|
|
4526
|
-
headingPath: z3.array(z3.string()),
|
|
4527
|
-
score: z3.number()
|
|
4528
|
-
})).optional()
|
|
4529
|
-
})),
|
|
4530
|
-
meta: z3.object({
|
|
4531
|
-
timingsMs: z3.object({
|
|
4532
|
-
search: z3.number(),
|
|
4533
|
-
total: z3.number()
|
|
4534
|
-
})
|
|
4535
|
-
})
|
|
4504
|
+
query: z3.string().min(1).describe("Search query. Use keywords or natural language, not full sentences."),
|
|
4505
|
+
topK: z3.number().int().positive().max(100).optional().describe("Number of results to return (default: 10, max: 100)"),
|
|
4506
|
+
pathPrefix: z3.string().optional().describe("Filter results to URLs starting with this prefix (e.g. '/docs')"),
|
|
4507
|
+
tags: z3.array(z3.string()).optional().describe("Filter results to pages matching all specified tags"),
|
|
4508
|
+
filters: z3.record(z3.string(), z3.union([z3.string(), z3.number(), z3.boolean()])).optional().describe('Filter by structured page metadata (e.g. {"version": 2})'),
|
|
4509
|
+
groupBy: z3.enum(["page", "chunk"]).optional().describe("'page' (default) groups chunks by page with sub-results; 'chunk' returns individual chunks"),
|
|
4510
|
+
scope: z3.string().optional()
|
|
4536
4511
|
}
|
|
4537
4512
|
},
|
|
4538
4513
|
async (input) => {
|
|
@@ -4543,85 +4518,18 @@ function createServer(engine) {
|
|
|
4543
4518
|
pathPrefix: input.pathPrefix,
|
|
4544
4519
|
tags: input.tags,
|
|
4545
4520
|
filters: input.filters,
|
|
4546
|
-
groupBy: input.groupBy
|
|
4547
|
-
maxSubResults: input.maxSubResults
|
|
4548
|
-
});
|
|
4549
|
-
return {
|
|
4550
|
-
content: [
|
|
4551
|
-
{
|
|
4552
|
-
type: "text",
|
|
4553
|
-
text: JSON.stringify(result, null, 2)
|
|
4554
|
-
}
|
|
4555
|
-
],
|
|
4556
|
-
structuredContent: result
|
|
4557
|
-
};
|
|
4558
|
-
}
|
|
4559
|
-
);
|
|
4560
|
-
server.registerTool(
|
|
4561
|
-
"get_page",
|
|
4562
|
-
{
|
|
4563
|
-
description: "Fetch indexed markdown for a specific path or URL, including frontmatter and routeFile mapping.",
|
|
4564
|
-
inputSchema: {
|
|
4565
|
-
pathOrUrl: z3.string().min(1),
|
|
4566
|
-
scope: z3.string().optional()
|
|
4567
|
-
}
|
|
4568
|
-
},
|
|
4569
|
-
async (input) => {
|
|
4570
|
-
const page = await engine.getPage(input.pathOrUrl, input.scope);
|
|
4571
|
-
return {
|
|
4572
|
-
content: [
|
|
4573
|
-
{
|
|
4574
|
-
type: "text",
|
|
4575
|
-
text: JSON.stringify(page, null, 2)
|
|
4576
|
-
}
|
|
4577
|
-
]
|
|
4578
|
-
};
|
|
4579
|
-
}
|
|
4580
|
-
);
|
|
4581
|
-
server.registerTool(
|
|
4582
|
-
"list_pages",
|
|
4583
|
-
{
|
|
4584
|
-
description: "List indexed pages with optional path prefix filtering and cursor-based pagination. Returns url, title, description, and routeFile for each page. Use nextCursor to fetch subsequent pages.",
|
|
4585
|
-
inputSchema: {
|
|
4586
|
-
pathPrefix: z3.string().optional(),
|
|
4587
|
-
cursor: z3.string().optional(),
|
|
4588
|
-
limit: z3.number().int().positive().max(200).optional(),
|
|
4589
|
-
scope: z3.string().optional()
|
|
4590
|
-
}
|
|
4591
|
-
},
|
|
4592
|
-
async (input) => {
|
|
4593
|
-
const result = await engine.listPages({
|
|
4594
|
-
pathPrefix: input.pathPrefix,
|
|
4595
|
-
cursor: input.cursor,
|
|
4596
|
-
limit: input.limit,
|
|
4597
|
-
scope: input.scope
|
|
4521
|
+
groupBy: input.groupBy
|
|
4598
4522
|
});
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
);
|
|
4609
|
-
server.registerTool(
|
|
4610
|
-
"get_site_structure",
|
|
4611
|
-
{
|
|
4612
|
-
description: "Returns the hierarchical page tree derived from URL paths. Use this to understand site navigation structure, find where pages belong, or scope further operations to a section. Nodes with isIndexed: false are implicit structural parents not directly in the index. Large sites (>2000 pages) return truncated: true.",
|
|
4613
|
-
inputSchema: {
|
|
4614
|
-
pathPrefix: z3.string().optional(),
|
|
4615
|
-
scope: z3.string().optional(),
|
|
4616
|
-
maxPages: z3.number().int().positive().max(2e3).optional()
|
|
4523
|
+
if (result.results.length === 0) {
|
|
4524
|
+
return {
|
|
4525
|
+
content: [
|
|
4526
|
+
{
|
|
4527
|
+
type: "text",
|
|
4528
|
+
text: `No results found for "${input.query}". Try broader keywords or remove filters.`
|
|
4529
|
+
}
|
|
4530
|
+
]
|
|
4531
|
+
};
|
|
4617
4532
|
}
|
|
4618
|
-
},
|
|
4619
|
-
async (input) => {
|
|
4620
|
-
const result = await engine.getSiteStructure({
|
|
4621
|
-
pathPrefix: input.pathPrefix,
|
|
4622
|
-
scope: input.scope,
|
|
4623
|
-
maxPages: input.maxPages
|
|
4624
|
-
});
|
|
4625
4533
|
return {
|
|
4626
4534
|
content: [
|
|
4627
4535
|
{
|
|
@@ -4633,56 +4541,51 @@ function createServer(engine) {
|
|
|
4633
4541
|
}
|
|
4634
4542
|
);
|
|
4635
4543
|
server.registerTool(
|
|
4636
|
-
"
|
|
4544
|
+
"get_page",
|
|
4637
4545
|
{
|
|
4638
|
-
description: "
|
|
4546
|
+
description: "Retrieves the full markdown content and metadata for a specific page by its URL path. Use this after search when snippets lack the detail needed to answer a question. Returns reconstructed page markdown, frontmatter (title, routeFile, tags, link counts, indexedAt), and the source file path. Do NOT use this for discovery \u2014 use search first to find relevant pages.",
|
|
4639
4547
|
inputSchema: {
|
|
4640
|
-
|
|
4548
|
+
path: z3.string().min(1).describe("URL path of the page (e.g. '/docs/auth'). Use a URL from search results."),
|
|
4641
4549
|
scope: z3.string().optional()
|
|
4642
4550
|
}
|
|
4643
4551
|
},
|
|
4644
4552
|
async (input) => {
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4553
|
+
try {
|
|
4554
|
+
const page = await engine.getPage(input.path, input.scope);
|
|
4555
|
+
return {
|
|
4556
|
+
content: [
|
|
4557
|
+
{
|
|
4558
|
+
type: "text",
|
|
4559
|
+
text: JSON.stringify(page, null, 2)
|
|
4560
|
+
}
|
|
4561
|
+
]
|
|
4562
|
+
};
|
|
4563
|
+
} catch {
|
|
4564
|
+
const suggestions = await engine.search({ q: input.path, topK: 3, scope: input.scope });
|
|
4565
|
+
const similar = suggestions.results.map((r) => r.url);
|
|
4651
4566
|
return {
|
|
4652
4567
|
content: [
|
|
4653
4568
|
{
|
|
4654
4569
|
type: "text",
|
|
4655
|
-
text:
|
|
4656
|
-
error: "No matching content found for the given query."
|
|
4657
|
-
})
|
|
4570
|
+
text: similar.length > 0 ? `Page '${input.path}' not found. Similar pages: ${similar.join(", ")}` : `Page '${input.path}' not found. Use search to find the correct URL.`
|
|
4658
4571
|
}
|
|
4659
4572
|
]
|
|
4660
4573
|
};
|
|
4661
4574
|
}
|
|
4662
|
-
const match = result.results[0];
|
|
4663
|
-
const { url, routeFile, sectionTitle, snippet } = match;
|
|
4664
|
-
return {
|
|
4665
|
-
content: [
|
|
4666
|
-
{
|
|
4667
|
-
type: "text",
|
|
4668
|
-
text: JSON.stringify({ url, routeFile, sectionTitle, snippet })
|
|
4669
|
-
}
|
|
4670
|
-
]
|
|
4671
|
-
};
|
|
4672
4575
|
}
|
|
4673
4576
|
);
|
|
4674
4577
|
server.registerTool(
|
|
4675
4578
|
"get_related_pages",
|
|
4676
4579
|
{
|
|
4677
|
-
description: "
|
|
4580
|
+
description: "Finds pages related to a specific page using link graph analysis, semantic similarity, and URL structure. Returns related pages with relationship type (outgoing_link, incoming_link, sibling, semantic) and relevance score. Do NOT use this for general search \u2014 use search instead. Use this only when you already have a specific page URL and need to discover connected content.",
|
|
4678
4581
|
inputSchema: {
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4582
|
+
path: z3.string().min(1).describe("URL path of the source page (e.g. '/docs/auth'). Use a URL from search results."),
|
|
4583
|
+
topK: z3.number().int().positive().max(25).optional().describe("Number of related pages to return (default: 10, max: 25)"),
|
|
4584
|
+
scope: z3.string().optional()
|
|
4682
4585
|
}
|
|
4683
4586
|
},
|
|
4684
4587
|
async (input) => {
|
|
4685
|
-
const result = await engine.getRelatedPages(input.
|
|
4588
|
+
const result = await engine.getRelatedPages(input.path, {
|
|
4686
4589
|
topK: input.topK,
|
|
4687
4590
|
scope: input.scope
|
|
4688
4591
|
});
|
package/dist/index.cjs
CHANGED
|
@@ -21739,45 +21739,20 @@ var SearchEngine = class _SearchEngine {
|
|
|
21739
21739
|
function createServer(engine) {
|
|
21740
21740
|
const server = new mcp_js.McpServer({
|
|
21741
21741
|
name: "searchsocket-mcp",
|
|
21742
|
-
version: "0.
|
|
21742
|
+
version: "0.2.0"
|
|
21743
21743
|
});
|
|
21744
21744
|
server.registerTool(
|
|
21745
21745
|
"search",
|
|
21746
21746
|
{
|
|
21747
|
-
description:
|
|
21747
|
+
description: "Searches indexed site content using semantic similarity. Returns ranked results with url, title, snippet, chunkText (full section markdown), score, and routeFile (source file path for editing). Each result includes the best-matching section; set groupBy to 'page' (default) for additional chunk sub-results per page. Use routeFile to locate the source file when editing content. If snippets lack detail, call get_page with the result URL to retrieve the full page markdown.",
|
|
21748
21748
|
inputSchema: {
|
|
21749
|
-
query: zod.z.string().min(1),
|
|
21750
|
-
|
|
21751
|
-
|
|
21752
|
-
|
|
21753
|
-
|
|
21754
|
-
|
|
21755
|
-
|
|
21756
|
-
maxSubResults: zod.z.number().int().positive().max(20).optional()
|
|
21757
|
-
},
|
|
21758
|
-
outputSchema: {
|
|
21759
|
-
q: zod.z.string(),
|
|
21760
|
-
scope: zod.z.string(),
|
|
21761
|
-
results: zod.z.array(zod.z.object({
|
|
21762
|
-
url: zod.z.string(),
|
|
21763
|
-
title: zod.z.string(),
|
|
21764
|
-
sectionTitle: zod.z.string().optional(),
|
|
21765
|
-
snippet: zod.z.string(),
|
|
21766
|
-
score: zod.z.number(),
|
|
21767
|
-
routeFile: zod.z.string(),
|
|
21768
|
-
chunks: zod.z.array(zod.z.object({
|
|
21769
|
-
sectionTitle: zod.z.string().optional(),
|
|
21770
|
-
snippet: zod.z.string(),
|
|
21771
|
-
headingPath: zod.z.array(zod.z.string()),
|
|
21772
|
-
score: zod.z.number()
|
|
21773
|
-
})).optional()
|
|
21774
|
-
})),
|
|
21775
|
-
meta: zod.z.object({
|
|
21776
|
-
timingsMs: zod.z.object({
|
|
21777
|
-
search: zod.z.number(),
|
|
21778
|
-
total: zod.z.number()
|
|
21779
|
-
})
|
|
21780
|
-
})
|
|
21749
|
+
query: zod.z.string().min(1).describe("Search query. Use keywords or natural language, not full sentences."),
|
|
21750
|
+
topK: zod.z.number().int().positive().max(100).optional().describe("Number of results to return (default: 10, max: 100)"),
|
|
21751
|
+
pathPrefix: zod.z.string().optional().describe("Filter results to URLs starting with this prefix (e.g. '/docs')"),
|
|
21752
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Filter results to pages matching all specified tags"),
|
|
21753
|
+
filters: zod.z.record(zod.z.string(), zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean()])).optional().describe('Filter by structured page metadata (e.g. {"version": 2})'),
|
|
21754
|
+
groupBy: zod.z.enum(["page", "chunk"]).optional().describe("'page' (default) groups chunks by page with sub-results; 'chunk' returns individual chunks"),
|
|
21755
|
+
scope: zod.z.string().optional()
|
|
21781
21756
|
}
|
|
21782
21757
|
},
|
|
21783
21758
|
async (input) => {
|
|
@@ -21788,85 +21763,18 @@ function createServer(engine) {
|
|
|
21788
21763
|
pathPrefix: input.pathPrefix,
|
|
21789
21764
|
tags: input.tags,
|
|
21790
21765
|
filters: input.filters,
|
|
21791
|
-
groupBy: input.groupBy
|
|
21792
|
-
maxSubResults: input.maxSubResults
|
|
21793
|
-
});
|
|
21794
|
-
return {
|
|
21795
|
-
content: [
|
|
21796
|
-
{
|
|
21797
|
-
type: "text",
|
|
21798
|
-
text: JSON.stringify(result, null, 2)
|
|
21799
|
-
}
|
|
21800
|
-
],
|
|
21801
|
-
structuredContent: result
|
|
21802
|
-
};
|
|
21803
|
-
}
|
|
21804
|
-
);
|
|
21805
|
-
server.registerTool(
|
|
21806
|
-
"get_page",
|
|
21807
|
-
{
|
|
21808
|
-
description: "Fetch indexed markdown for a specific path or URL, including frontmatter and routeFile mapping.",
|
|
21809
|
-
inputSchema: {
|
|
21810
|
-
pathOrUrl: zod.z.string().min(1),
|
|
21811
|
-
scope: zod.z.string().optional()
|
|
21812
|
-
}
|
|
21813
|
-
},
|
|
21814
|
-
async (input) => {
|
|
21815
|
-
const page = await engine.getPage(input.pathOrUrl, input.scope);
|
|
21816
|
-
return {
|
|
21817
|
-
content: [
|
|
21818
|
-
{
|
|
21819
|
-
type: "text",
|
|
21820
|
-
text: JSON.stringify(page, null, 2)
|
|
21821
|
-
}
|
|
21822
|
-
]
|
|
21823
|
-
};
|
|
21824
|
-
}
|
|
21825
|
-
);
|
|
21826
|
-
server.registerTool(
|
|
21827
|
-
"list_pages",
|
|
21828
|
-
{
|
|
21829
|
-
description: "List indexed pages with optional path prefix filtering and cursor-based pagination. Returns url, title, description, and routeFile for each page. Use nextCursor to fetch subsequent pages.",
|
|
21830
|
-
inputSchema: {
|
|
21831
|
-
pathPrefix: zod.z.string().optional(),
|
|
21832
|
-
cursor: zod.z.string().optional(),
|
|
21833
|
-
limit: zod.z.number().int().positive().max(200).optional(),
|
|
21834
|
-
scope: zod.z.string().optional()
|
|
21835
|
-
}
|
|
21836
|
-
},
|
|
21837
|
-
async (input) => {
|
|
21838
|
-
const result = await engine.listPages({
|
|
21839
|
-
pathPrefix: input.pathPrefix,
|
|
21840
|
-
cursor: input.cursor,
|
|
21841
|
-
limit: input.limit,
|
|
21842
|
-
scope: input.scope
|
|
21766
|
+
groupBy: input.groupBy
|
|
21843
21767
|
});
|
|
21844
|
-
|
|
21845
|
-
|
|
21846
|
-
|
|
21847
|
-
|
|
21848
|
-
|
|
21849
|
-
|
|
21850
|
-
|
|
21851
|
-
|
|
21852
|
-
|
|
21853
|
-
);
|
|
21854
|
-
server.registerTool(
|
|
21855
|
-
"get_site_structure",
|
|
21856
|
-
{
|
|
21857
|
-
description: "Returns the hierarchical page tree derived from URL paths. Use this to understand site navigation structure, find where pages belong, or scope further operations to a section. Nodes with isIndexed: false are implicit structural parents not directly in the index. Large sites (>2000 pages) return truncated: true.",
|
|
21858
|
-
inputSchema: {
|
|
21859
|
-
pathPrefix: zod.z.string().optional(),
|
|
21860
|
-
scope: zod.z.string().optional(),
|
|
21861
|
-
maxPages: zod.z.number().int().positive().max(2e3).optional()
|
|
21768
|
+
if (result.results.length === 0) {
|
|
21769
|
+
return {
|
|
21770
|
+
content: [
|
|
21771
|
+
{
|
|
21772
|
+
type: "text",
|
|
21773
|
+
text: `No results found for "${input.query}". Try broader keywords or remove filters.`
|
|
21774
|
+
}
|
|
21775
|
+
]
|
|
21776
|
+
};
|
|
21862
21777
|
}
|
|
21863
|
-
},
|
|
21864
|
-
async (input) => {
|
|
21865
|
-
const result = await engine.getSiteStructure({
|
|
21866
|
-
pathPrefix: input.pathPrefix,
|
|
21867
|
-
scope: input.scope,
|
|
21868
|
-
maxPages: input.maxPages
|
|
21869
|
-
});
|
|
21870
21778
|
return {
|
|
21871
21779
|
content: [
|
|
21872
21780
|
{
|
|
@@ -21878,56 +21786,51 @@ function createServer(engine) {
|
|
|
21878
21786
|
}
|
|
21879
21787
|
);
|
|
21880
21788
|
server.registerTool(
|
|
21881
|
-
"
|
|
21789
|
+
"get_page",
|
|
21882
21790
|
{
|
|
21883
|
-
description: "
|
|
21791
|
+
description: "Retrieves the full markdown content and metadata for a specific page by its URL path. Use this after search when snippets lack the detail needed to answer a question. Returns reconstructed page markdown, frontmatter (title, routeFile, tags, link counts, indexedAt), and the source file path. Do NOT use this for discovery \u2014 use search first to find relevant pages.",
|
|
21884
21792
|
inputSchema: {
|
|
21885
|
-
|
|
21793
|
+
path: zod.z.string().min(1).describe("URL path of the page (e.g. '/docs/auth'). Use a URL from search results."),
|
|
21886
21794
|
scope: zod.z.string().optional()
|
|
21887
21795
|
}
|
|
21888
21796
|
},
|
|
21889
21797
|
async (input) => {
|
|
21890
|
-
|
|
21891
|
-
|
|
21892
|
-
|
|
21893
|
-
|
|
21894
|
-
|
|
21895
|
-
|
|
21798
|
+
try {
|
|
21799
|
+
const page = await engine.getPage(input.path, input.scope);
|
|
21800
|
+
return {
|
|
21801
|
+
content: [
|
|
21802
|
+
{
|
|
21803
|
+
type: "text",
|
|
21804
|
+
text: JSON.stringify(page, null, 2)
|
|
21805
|
+
}
|
|
21806
|
+
]
|
|
21807
|
+
};
|
|
21808
|
+
} catch {
|
|
21809
|
+
const suggestions = await engine.search({ q: input.path, topK: 3, scope: input.scope });
|
|
21810
|
+
const similar = suggestions.results.map((r) => r.url);
|
|
21896
21811
|
return {
|
|
21897
21812
|
content: [
|
|
21898
21813
|
{
|
|
21899
21814
|
type: "text",
|
|
21900
|
-
text:
|
|
21901
|
-
error: "No matching content found for the given query."
|
|
21902
|
-
})
|
|
21815
|
+
text: similar.length > 0 ? `Page '${input.path}' not found. Similar pages: ${similar.join(", ")}` : `Page '${input.path}' not found. Use search to find the correct URL.`
|
|
21903
21816
|
}
|
|
21904
21817
|
]
|
|
21905
21818
|
};
|
|
21906
21819
|
}
|
|
21907
|
-
const match = result.results[0];
|
|
21908
|
-
const { url, routeFile, sectionTitle, snippet } = match;
|
|
21909
|
-
return {
|
|
21910
|
-
content: [
|
|
21911
|
-
{
|
|
21912
|
-
type: "text",
|
|
21913
|
-
text: JSON.stringify({ url, routeFile, sectionTitle, snippet })
|
|
21914
|
-
}
|
|
21915
|
-
]
|
|
21916
|
-
};
|
|
21917
21820
|
}
|
|
21918
21821
|
);
|
|
21919
21822
|
server.registerTool(
|
|
21920
21823
|
"get_related_pages",
|
|
21921
21824
|
{
|
|
21922
|
-
description: "
|
|
21825
|
+
description: "Finds pages related to a specific page using link graph analysis, semantic similarity, and URL structure. Returns related pages with relationship type (outgoing_link, incoming_link, sibling, semantic) and relevance score. Do NOT use this for general search \u2014 use search instead. Use this only when you already have a specific page URL and need to discover connected content.",
|
|
21923
21826
|
inputSchema: {
|
|
21924
|
-
|
|
21925
|
-
|
|
21926
|
-
|
|
21827
|
+
path: zod.z.string().min(1).describe("URL path of the source page (e.g. '/docs/auth'). Use a URL from search results."),
|
|
21828
|
+
topK: zod.z.number().int().positive().max(25).optional().describe("Number of related pages to return (default: 10, max: 25)"),
|
|
21829
|
+
scope: zod.z.string().optional()
|
|
21927
21830
|
}
|
|
21928
21831
|
},
|
|
21929
21832
|
async (input) => {
|
|
21930
|
-
const result = await engine.getRelatedPages(input.
|
|
21833
|
+
const result = await engine.getRelatedPages(input.path, {
|
|
21931
21834
|
topK: input.topK,
|
|
21932
21835
|
scope: input.scope
|
|
21933
21836
|
});
|
package/dist/index.js
CHANGED
|
@@ -21727,45 +21727,20 @@ var SearchEngine = class _SearchEngine {
|
|
|
21727
21727
|
function createServer(engine) {
|
|
21728
21728
|
const server = new McpServer({
|
|
21729
21729
|
name: "searchsocket-mcp",
|
|
21730
|
-
version: "0.
|
|
21730
|
+
version: "0.2.0"
|
|
21731
21731
|
});
|
|
21732
21732
|
server.registerTool(
|
|
21733
21733
|
"search",
|
|
21734
21734
|
{
|
|
21735
|
-
description:
|
|
21735
|
+
description: "Searches indexed site content using semantic similarity. Returns ranked results with url, title, snippet, chunkText (full section markdown), score, and routeFile (source file path for editing). Each result includes the best-matching section; set groupBy to 'page' (default) for additional chunk sub-results per page. Use routeFile to locate the source file when editing content. If snippets lack detail, call get_page with the result URL to retrieve the full page markdown.",
|
|
21736
21736
|
inputSchema: {
|
|
21737
|
-
query: z.string().min(1),
|
|
21738
|
-
|
|
21739
|
-
|
|
21740
|
-
|
|
21741
|
-
|
|
21742
|
-
|
|
21743
|
-
|
|
21744
|
-
maxSubResults: z.number().int().positive().max(20).optional()
|
|
21745
|
-
},
|
|
21746
|
-
outputSchema: {
|
|
21747
|
-
q: z.string(),
|
|
21748
|
-
scope: z.string(),
|
|
21749
|
-
results: z.array(z.object({
|
|
21750
|
-
url: z.string(),
|
|
21751
|
-
title: z.string(),
|
|
21752
|
-
sectionTitle: z.string().optional(),
|
|
21753
|
-
snippet: z.string(),
|
|
21754
|
-
score: z.number(),
|
|
21755
|
-
routeFile: z.string(),
|
|
21756
|
-
chunks: z.array(z.object({
|
|
21757
|
-
sectionTitle: z.string().optional(),
|
|
21758
|
-
snippet: z.string(),
|
|
21759
|
-
headingPath: z.array(z.string()),
|
|
21760
|
-
score: z.number()
|
|
21761
|
-
})).optional()
|
|
21762
|
-
})),
|
|
21763
|
-
meta: z.object({
|
|
21764
|
-
timingsMs: z.object({
|
|
21765
|
-
search: z.number(),
|
|
21766
|
-
total: z.number()
|
|
21767
|
-
})
|
|
21768
|
-
})
|
|
21737
|
+
query: z.string().min(1).describe("Search query. Use keywords or natural language, not full sentences."),
|
|
21738
|
+
topK: z.number().int().positive().max(100).optional().describe("Number of results to return (default: 10, max: 100)"),
|
|
21739
|
+
pathPrefix: z.string().optional().describe("Filter results to URLs starting with this prefix (e.g. '/docs')"),
|
|
21740
|
+
tags: z.array(z.string()).optional().describe("Filter results to pages matching all specified tags"),
|
|
21741
|
+
filters: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional().describe('Filter by structured page metadata (e.g. {"version": 2})'),
|
|
21742
|
+
groupBy: z.enum(["page", "chunk"]).optional().describe("'page' (default) groups chunks by page with sub-results; 'chunk' returns individual chunks"),
|
|
21743
|
+
scope: z.string().optional()
|
|
21769
21744
|
}
|
|
21770
21745
|
},
|
|
21771
21746
|
async (input) => {
|
|
@@ -21776,85 +21751,18 @@ function createServer(engine) {
|
|
|
21776
21751
|
pathPrefix: input.pathPrefix,
|
|
21777
21752
|
tags: input.tags,
|
|
21778
21753
|
filters: input.filters,
|
|
21779
|
-
groupBy: input.groupBy
|
|
21780
|
-
maxSubResults: input.maxSubResults
|
|
21781
|
-
});
|
|
21782
|
-
return {
|
|
21783
|
-
content: [
|
|
21784
|
-
{
|
|
21785
|
-
type: "text",
|
|
21786
|
-
text: JSON.stringify(result, null, 2)
|
|
21787
|
-
}
|
|
21788
|
-
],
|
|
21789
|
-
structuredContent: result
|
|
21790
|
-
};
|
|
21791
|
-
}
|
|
21792
|
-
);
|
|
21793
|
-
server.registerTool(
|
|
21794
|
-
"get_page",
|
|
21795
|
-
{
|
|
21796
|
-
description: "Fetch indexed markdown for a specific path or URL, including frontmatter and routeFile mapping.",
|
|
21797
|
-
inputSchema: {
|
|
21798
|
-
pathOrUrl: z.string().min(1),
|
|
21799
|
-
scope: z.string().optional()
|
|
21800
|
-
}
|
|
21801
|
-
},
|
|
21802
|
-
async (input) => {
|
|
21803
|
-
const page = await engine.getPage(input.pathOrUrl, input.scope);
|
|
21804
|
-
return {
|
|
21805
|
-
content: [
|
|
21806
|
-
{
|
|
21807
|
-
type: "text",
|
|
21808
|
-
text: JSON.stringify(page, null, 2)
|
|
21809
|
-
}
|
|
21810
|
-
]
|
|
21811
|
-
};
|
|
21812
|
-
}
|
|
21813
|
-
);
|
|
21814
|
-
server.registerTool(
|
|
21815
|
-
"list_pages",
|
|
21816
|
-
{
|
|
21817
|
-
description: "List indexed pages with optional path prefix filtering and cursor-based pagination. Returns url, title, description, and routeFile for each page. Use nextCursor to fetch subsequent pages.",
|
|
21818
|
-
inputSchema: {
|
|
21819
|
-
pathPrefix: z.string().optional(),
|
|
21820
|
-
cursor: z.string().optional(),
|
|
21821
|
-
limit: z.number().int().positive().max(200).optional(),
|
|
21822
|
-
scope: z.string().optional()
|
|
21823
|
-
}
|
|
21824
|
-
},
|
|
21825
|
-
async (input) => {
|
|
21826
|
-
const result = await engine.listPages({
|
|
21827
|
-
pathPrefix: input.pathPrefix,
|
|
21828
|
-
cursor: input.cursor,
|
|
21829
|
-
limit: input.limit,
|
|
21830
|
-
scope: input.scope
|
|
21754
|
+
groupBy: input.groupBy
|
|
21831
21755
|
});
|
|
21832
|
-
|
|
21833
|
-
|
|
21834
|
-
|
|
21835
|
-
|
|
21836
|
-
|
|
21837
|
-
|
|
21838
|
-
|
|
21839
|
-
|
|
21840
|
-
|
|
21841
|
-
);
|
|
21842
|
-
server.registerTool(
|
|
21843
|
-
"get_site_structure",
|
|
21844
|
-
{
|
|
21845
|
-
description: "Returns the hierarchical page tree derived from URL paths. Use this to understand site navigation structure, find where pages belong, or scope further operations to a section. Nodes with isIndexed: false are implicit structural parents not directly in the index. Large sites (>2000 pages) return truncated: true.",
|
|
21846
|
-
inputSchema: {
|
|
21847
|
-
pathPrefix: z.string().optional(),
|
|
21848
|
-
scope: z.string().optional(),
|
|
21849
|
-
maxPages: z.number().int().positive().max(2e3).optional()
|
|
21756
|
+
if (result.results.length === 0) {
|
|
21757
|
+
return {
|
|
21758
|
+
content: [
|
|
21759
|
+
{
|
|
21760
|
+
type: "text",
|
|
21761
|
+
text: `No results found for "${input.query}". Try broader keywords or remove filters.`
|
|
21762
|
+
}
|
|
21763
|
+
]
|
|
21764
|
+
};
|
|
21850
21765
|
}
|
|
21851
|
-
},
|
|
21852
|
-
async (input) => {
|
|
21853
|
-
const result = await engine.getSiteStructure({
|
|
21854
|
-
pathPrefix: input.pathPrefix,
|
|
21855
|
-
scope: input.scope,
|
|
21856
|
-
maxPages: input.maxPages
|
|
21857
|
-
});
|
|
21858
21766
|
return {
|
|
21859
21767
|
content: [
|
|
21860
21768
|
{
|
|
@@ -21866,56 +21774,51 @@ function createServer(engine) {
|
|
|
21866
21774
|
}
|
|
21867
21775
|
);
|
|
21868
21776
|
server.registerTool(
|
|
21869
|
-
"
|
|
21777
|
+
"get_page",
|
|
21870
21778
|
{
|
|
21871
|
-
description: "
|
|
21779
|
+
description: "Retrieves the full markdown content and metadata for a specific page by its URL path. Use this after search when snippets lack the detail needed to answer a question. Returns reconstructed page markdown, frontmatter (title, routeFile, tags, link counts, indexedAt), and the source file path. Do NOT use this for discovery \u2014 use search first to find relevant pages.",
|
|
21872
21780
|
inputSchema: {
|
|
21873
|
-
|
|
21781
|
+
path: z.string().min(1).describe("URL path of the page (e.g. '/docs/auth'). Use a URL from search results."),
|
|
21874
21782
|
scope: z.string().optional()
|
|
21875
21783
|
}
|
|
21876
21784
|
},
|
|
21877
21785
|
async (input) => {
|
|
21878
|
-
|
|
21879
|
-
|
|
21880
|
-
|
|
21881
|
-
|
|
21882
|
-
|
|
21883
|
-
|
|
21786
|
+
try {
|
|
21787
|
+
const page = await engine.getPage(input.path, input.scope);
|
|
21788
|
+
return {
|
|
21789
|
+
content: [
|
|
21790
|
+
{
|
|
21791
|
+
type: "text",
|
|
21792
|
+
text: JSON.stringify(page, null, 2)
|
|
21793
|
+
}
|
|
21794
|
+
]
|
|
21795
|
+
};
|
|
21796
|
+
} catch {
|
|
21797
|
+
const suggestions = await engine.search({ q: input.path, topK: 3, scope: input.scope });
|
|
21798
|
+
const similar = suggestions.results.map((r) => r.url);
|
|
21884
21799
|
return {
|
|
21885
21800
|
content: [
|
|
21886
21801
|
{
|
|
21887
21802
|
type: "text",
|
|
21888
|
-
text:
|
|
21889
|
-
error: "No matching content found for the given query."
|
|
21890
|
-
})
|
|
21803
|
+
text: similar.length > 0 ? `Page '${input.path}' not found. Similar pages: ${similar.join(", ")}` : `Page '${input.path}' not found. Use search to find the correct URL.`
|
|
21891
21804
|
}
|
|
21892
21805
|
]
|
|
21893
21806
|
};
|
|
21894
21807
|
}
|
|
21895
|
-
const match = result.results[0];
|
|
21896
|
-
const { url, routeFile, sectionTitle, snippet } = match;
|
|
21897
|
-
return {
|
|
21898
|
-
content: [
|
|
21899
|
-
{
|
|
21900
|
-
type: "text",
|
|
21901
|
-
text: JSON.stringify({ url, routeFile, sectionTitle, snippet })
|
|
21902
|
-
}
|
|
21903
|
-
]
|
|
21904
|
-
};
|
|
21905
21808
|
}
|
|
21906
21809
|
);
|
|
21907
21810
|
server.registerTool(
|
|
21908
21811
|
"get_related_pages",
|
|
21909
21812
|
{
|
|
21910
|
-
description: "
|
|
21813
|
+
description: "Finds pages related to a specific page using link graph analysis, semantic similarity, and URL structure. Returns related pages with relationship type (outgoing_link, incoming_link, sibling, semantic) and relevance score. Do NOT use this for general search \u2014 use search instead. Use this only when you already have a specific page URL and need to discover connected content.",
|
|
21911
21814
|
inputSchema: {
|
|
21912
|
-
|
|
21913
|
-
|
|
21914
|
-
|
|
21815
|
+
path: z.string().min(1).describe("URL path of the source page (e.g. '/docs/auth'). Use a URL from search results."),
|
|
21816
|
+
topK: z.number().int().positive().max(25).optional().describe("Number of related pages to return (default: 10, max: 25)"),
|
|
21817
|
+
scope: z.string().optional()
|
|
21915
21818
|
}
|
|
21916
21819
|
},
|
|
21917
21820
|
async (input) => {
|
|
21918
|
-
const result = await engine.getRelatedPages(input.
|
|
21821
|
+
const result = await engine.getRelatedPages(input.path, {
|
|
21919
21822
|
topK: input.topK,
|
|
21920
21823
|
scope: input.scope
|
|
21921
21824
|
});
|
package/dist/sveltekit.cjs
CHANGED
|
@@ -18715,45 +18715,20 @@ var SearchEngine = class _SearchEngine {
|
|
|
18715
18715
|
function createServer(engine) {
|
|
18716
18716
|
const server = new mcp_js.McpServer({
|
|
18717
18717
|
name: "searchsocket-mcp",
|
|
18718
|
-
version: "0.
|
|
18718
|
+
version: "0.2.0"
|
|
18719
18719
|
});
|
|
18720
18720
|
server.registerTool(
|
|
18721
18721
|
"search",
|
|
18722
18722
|
{
|
|
18723
|
-
description:
|
|
18723
|
+
description: "Searches indexed site content using semantic similarity. Returns ranked results with url, title, snippet, chunkText (full section markdown), score, and routeFile (source file path for editing). Each result includes the best-matching section; set groupBy to 'page' (default) for additional chunk sub-results per page. Use routeFile to locate the source file when editing content. If snippets lack detail, call get_page with the result URL to retrieve the full page markdown.",
|
|
18724
18724
|
inputSchema: {
|
|
18725
|
-
query: zod.z.string().min(1),
|
|
18726
|
-
|
|
18727
|
-
|
|
18728
|
-
|
|
18729
|
-
|
|
18730
|
-
|
|
18731
|
-
|
|
18732
|
-
maxSubResults: zod.z.number().int().positive().max(20).optional()
|
|
18733
|
-
},
|
|
18734
|
-
outputSchema: {
|
|
18735
|
-
q: zod.z.string(),
|
|
18736
|
-
scope: zod.z.string(),
|
|
18737
|
-
results: zod.z.array(zod.z.object({
|
|
18738
|
-
url: zod.z.string(),
|
|
18739
|
-
title: zod.z.string(),
|
|
18740
|
-
sectionTitle: zod.z.string().optional(),
|
|
18741
|
-
snippet: zod.z.string(),
|
|
18742
|
-
score: zod.z.number(),
|
|
18743
|
-
routeFile: zod.z.string(),
|
|
18744
|
-
chunks: zod.z.array(zod.z.object({
|
|
18745
|
-
sectionTitle: zod.z.string().optional(),
|
|
18746
|
-
snippet: zod.z.string(),
|
|
18747
|
-
headingPath: zod.z.array(zod.z.string()),
|
|
18748
|
-
score: zod.z.number()
|
|
18749
|
-
})).optional()
|
|
18750
|
-
})),
|
|
18751
|
-
meta: zod.z.object({
|
|
18752
|
-
timingsMs: zod.z.object({
|
|
18753
|
-
search: zod.z.number(),
|
|
18754
|
-
total: zod.z.number()
|
|
18755
|
-
})
|
|
18756
|
-
})
|
|
18725
|
+
query: zod.z.string().min(1).describe("Search query. Use keywords or natural language, not full sentences."),
|
|
18726
|
+
topK: zod.z.number().int().positive().max(100).optional().describe("Number of results to return (default: 10, max: 100)"),
|
|
18727
|
+
pathPrefix: zod.z.string().optional().describe("Filter results to URLs starting with this prefix (e.g. '/docs')"),
|
|
18728
|
+
tags: zod.z.array(zod.z.string()).optional().describe("Filter results to pages matching all specified tags"),
|
|
18729
|
+
filters: zod.z.record(zod.z.string(), zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean()])).optional().describe('Filter by structured page metadata (e.g. {"version": 2})'),
|
|
18730
|
+
groupBy: zod.z.enum(["page", "chunk"]).optional().describe("'page' (default) groups chunks by page with sub-results; 'chunk' returns individual chunks"),
|
|
18731
|
+
scope: zod.z.string().optional()
|
|
18757
18732
|
}
|
|
18758
18733
|
},
|
|
18759
18734
|
async (input) => {
|
|
@@ -18764,85 +18739,18 @@ function createServer(engine) {
|
|
|
18764
18739
|
pathPrefix: input.pathPrefix,
|
|
18765
18740
|
tags: input.tags,
|
|
18766
18741
|
filters: input.filters,
|
|
18767
|
-
groupBy: input.groupBy
|
|
18768
|
-
maxSubResults: input.maxSubResults
|
|
18769
|
-
});
|
|
18770
|
-
return {
|
|
18771
|
-
content: [
|
|
18772
|
-
{
|
|
18773
|
-
type: "text",
|
|
18774
|
-
text: JSON.stringify(result, null, 2)
|
|
18775
|
-
}
|
|
18776
|
-
],
|
|
18777
|
-
structuredContent: result
|
|
18778
|
-
};
|
|
18779
|
-
}
|
|
18780
|
-
);
|
|
18781
|
-
server.registerTool(
|
|
18782
|
-
"get_page",
|
|
18783
|
-
{
|
|
18784
|
-
description: "Fetch indexed markdown for a specific path or URL, including frontmatter and routeFile mapping.",
|
|
18785
|
-
inputSchema: {
|
|
18786
|
-
pathOrUrl: zod.z.string().min(1),
|
|
18787
|
-
scope: zod.z.string().optional()
|
|
18788
|
-
}
|
|
18789
|
-
},
|
|
18790
|
-
async (input) => {
|
|
18791
|
-
const page = await engine.getPage(input.pathOrUrl, input.scope);
|
|
18792
|
-
return {
|
|
18793
|
-
content: [
|
|
18794
|
-
{
|
|
18795
|
-
type: "text",
|
|
18796
|
-
text: JSON.stringify(page, null, 2)
|
|
18797
|
-
}
|
|
18798
|
-
]
|
|
18799
|
-
};
|
|
18800
|
-
}
|
|
18801
|
-
);
|
|
18802
|
-
server.registerTool(
|
|
18803
|
-
"list_pages",
|
|
18804
|
-
{
|
|
18805
|
-
description: "List indexed pages with optional path prefix filtering and cursor-based pagination. Returns url, title, description, and routeFile for each page. Use nextCursor to fetch subsequent pages.",
|
|
18806
|
-
inputSchema: {
|
|
18807
|
-
pathPrefix: zod.z.string().optional(),
|
|
18808
|
-
cursor: zod.z.string().optional(),
|
|
18809
|
-
limit: zod.z.number().int().positive().max(200).optional(),
|
|
18810
|
-
scope: zod.z.string().optional()
|
|
18811
|
-
}
|
|
18812
|
-
},
|
|
18813
|
-
async (input) => {
|
|
18814
|
-
const result = await engine.listPages({
|
|
18815
|
-
pathPrefix: input.pathPrefix,
|
|
18816
|
-
cursor: input.cursor,
|
|
18817
|
-
limit: input.limit,
|
|
18818
|
-
scope: input.scope
|
|
18742
|
+
groupBy: input.groupBy
|
|
18819
18743
|
});
|
|
18820
|
-
|
|
18821
|
-
|
|
18822
|
-
|
|
18823
|
-
|
|
18824
|
-
|
|
18825
|
-
|
|
18826
|
-
|
|
18827
|
-
|
|
18828
|
-
|
|
18829
|
-
);
|
|
18830
|
-
server.registerTool(
|
|
18831
|
-
"get_site_structure",
|
|
18832
|
-
{
|
|
18833
|
-
description: "Returns the hierarchical page tree derived from URL paths. Use this to understand site navigation structure, find where pages belong, or scope further operations to a section. Nodes with isIndexed: false are implicit structural parents not directly in the index. Large sites (>2000 pages) return truncated: true.",
|
|
18834
|
-
inputSchema: {
|
|
18835
|
-
pathPrefix: zod.z.string().optional(),
|
|
18836
|
-
scope: zod.z.string().optional(),
|
|
18837
|
-
maxPages: zod.z.number().int().positive().max(2e3).optional()
|
|
18744
|
+
if (result.results.length === 0) {
|
|
18745
|
+
return {
|
|
18746
|
+
content: [
|
|
18747
|
+
{
|
|
18748
|
+
type: "text",
|
|
18749
|
+
text: `No results found for "${input.query}". Try broader keywords or remove filters.`
|
|
18750
|
+
}
|
|
18751
|
+
]
|
|
18752
|
+
};
|
|
18838
18753
|
}
|
|
18839
|
-
},
|
|
18840
|
-
async (input) => {
|
|
18841
|
-
const result = await engine.getSiteStructure({
|
|
18842
|
-
pathPrefix: input.pathPrefix,
|
|
18843
|
-
scope: input.scope,
|
|
18844
|
-
maxPages: input.maxPages
|
|
18845
|
-
});
|
|
18846
18754
|
return {
|
|
18847
18755
|
content: [
|
|
18848
18756
|
{
|
|
@@ -18854,56 +18762,51 @@ function createServer(engine) {
|
|
|
18854
18762
|
}
|
|
18855
18763
|
);
|
|
18856
18764
|
server.registerTool(
|
|
18857
|
-
"
|
|
18765
|
+
"get_page",
|
|
18858
18766
|
{
|
|
18859
|
-
description: "
|
|
18767
|
+
description: "Retrieves the full markdown content and metadata for a specific page by its URL path. Use this after search when snippets lack the detail needed to answer a question. Returns reconstructed page markdown, frontmatter (title, routeFile, tags, link counts, indexedAt), and the source file path. Do NOT use this for discovery \u2014 use search first to find relevant pages.",
|
|
18860
18768
|
inputSchema: {
|
|
18861
|
-
|
|
18769
|
+
path: zod.z.string().min(1).describe("URL path of the page (e.g. '/docs/auth'). Use a URL from search results."),
|
|
18862
18770
|
scope: zod.z.string().optional()
|
|
18863
18771
|
}
|
|
18864
18772
|
},
|
|
18865
18773
|
async (input) => {
|
|
18866
|
-
|
|
18867
|
-
|
|
18868
|
-
|
|
18869
|
-
|
|
18870
|
-
|
|
18871
|
-
|
|
18774
|
+
try {
|
|
18775
|
+
const page = await engine.getPage(input.path, input.scope);
|
|
18776
|
+
return {
|
|
18777
|
+
content: [
|
|
18778
|
+
{
|
|
18779
|
+
type: "text",
|
|
18780
|
+
text: JSON.stringify(page, null, 2)
|
|
18781
|
+
}
|
|
18782
|
+
]
|
|
18783
|
+
};
|
|
18784
|
+
} catch {
|
|
18785
|
+
const suggestions = await engine.search({ q: input.path, topK: 3, scope: input.scope });
|
|
18786
|
+
const similar = suggestions.results.map((r) => r.url);
|
|
18872
18787
|
return {
|
|
18873
18788
|
content: [
|
|
18874
18789
|
{
|
|
18875
18790
|
type: "text",
|
|
18876
|
-
text:
|
|
18877
|
-
error: "No matching content found for the given query."
|
|
18878
|
-
})
|
|
18791
|
+
text: similar.length > 0 ? `Page '${input.path}' not found. Similar pages: ${similar.join(", ")}` : `Page '${input.path}' not found. Use search to find the correct URL.`
|
|
18879
18792
|
}
|
|
18880
18793
|
]
|
|
18881
18794
|
};
|
|
18882
18795
|
}
|
|
18883
|
-
const match = result.results[0];
|
|
18884
|
-
const { url, routeFile, sectionTitle, snippet } = match;
|
|
18885
|
-
return {
|
|
18886
|
-
content: [
|
|
18887
|
-
{
|
|
18888
|
-
type: "text",
|
|
18889
|
-
text: JSON.stringify({ url, routeFile, sectionTitle, snippet })
|
|
18890
|
-
}
|
|
18891
|
-
]
|
|
18892
|
-
};
|
|
18893
18796
|
}
|
|
18894
18797
|
);
|
|
18895
18798
|
server.registerTool(
|
|
18896
18799
|
"get_related_pages",
|
|
18897
18800
|
{
|
|
18898
|
-
description: "
|
|
18801
|
+
description: "Finds pages related to a specific page using link graph analysis, semantic similarity, and URL structure. Returns related pages with relationship type (outgoing_link, incoming_link, sibling, semantic) and relevance score. Do NOT use this for general search \u2014 use search instead. Use this only when you already have a specific page URL and need to discover connected content.",
|
|
18899
18802
|
inputSchema: {
|
|
18900
|
-
|
|
18901
|
-
|
|
18902
|
-
|
|
18803
|
+
path: zod.z.string().min(1).describe("URL path of the source page (e.g. '/docs/auth'). Use a URL from search results."),
|
|
18804
|
+
topK: zod.z.number().int().positive().max(25).optional().describe("Number of related pages to return (default: 10, max: 25)"),
|
|
18805
|
+
scope: zod.z.string().optional()
|
|
18903
18806
|
}
|
|
18904
18807
|
},
|
|
18905
18808
|
async (input) => {
|
|
18906
|
-
const result = await engine.getRelatedPages(input.
|
|
18809
|
+
const result = await engine.getRelatedPages(input.path, {
|
|
18907
18810
|
topK: input.topK,
|
|
18908
18811
|
scope: input.scope
|
|
18909
18812
|
});
|
package/dist/sveltekit.js
CHANGED
|
@@ -18703,45 +18703,20 @@ var SearchEngine = class _SearchEngine {
|
|
|
18703
18703
|
function createServer(engine) {
|
|
18704
18704
|
const server = new McpServer({
|
|
18705
18705
|
name: "searchsocket-mcp",
|
|
18706
|
-
version: "0.
|
|
18706
|
+
version: "0.2.0"
|
|
18707
18707
|
});
|
|
18708
18708
|
server.registerTool(
|
|
18709
18709
|
"search",
|
|
18710
18710
|
{
|
|
18711
|
-
description:
|
|
18711
|
+
description: "Searches indexed site content using semantic similarity. Returns ranked results with url, title, snippet, chunkText (full section markdown), score, and routeFile (source file path for editing). Each result includes the best-matching section; set groupBy to 'page' (default) for additional chunk sub-results per page. Use routeFile to locate the source file when editing content. If snippets lack detail, call get_page with the result URL to retrieve the full page markdown.",
|
|
18712
18712
|
inputSchema: {
|
|
18713
|
-
query: z.string().min(1),
|
|
18714
|
-
|
|
18715
|
-
|
|
18716
|
-
|
|
18717
|
-
|
|
18718
|
-
|
|
18719
|
-
|
|
18720
|
-
maxSubResults: z.number().int().positive().max(20).optional()
|
|
18721
|
-
},
|
|
18722
|
-
outputSchema: {
|
|
18723
|
-
q: z.string(),
|
|
18724
|
-
scope: z.string(),
|
|
18725
|
-
results: z.array(z.object({
|
|
18726
|
-
url: z.string(),
|
|
18727
|
-
title: z.string(),
|
|
18728
|
-
sectionTitle: z.string().optional(),
|
|
18729
|
-
snippet: z.string(),
|
|
18730
|
-
score: z.number(),
|
|
18731
|
-
routeFile: z.string(),
|
|
18732
|
-
chunks: z.array(z.object({
|
|
18733
|
-
sectionTitle: z.string().optional(),
|
|
18734
|
-
snippet: z.string(),
|
|
18735
|
-
headingPath: z.array(z.string()),
|
|
18736
|
-
score: z.number()
|
|
18737
|
-
})).optional()
|
|
18738
|
-
})),
|
|
18739
|
-
meta: z.object({
|
|
18740
|
-
timingsMs: z.object({
|
|
18741
|
-
search: z.number(),
|
|
18742
|
-
total: z.number()
|
|
18743
|
-
})
|
|
18744
|
-
})
|
|
18713
|
+
query: z.string().min(1).describe("Search query. Use keywords or natural language, not full sentences."),
|
|
18714
|
+
topK: z.number().int().positive().max(100).optional().describe("Number of results to return (default: 10, max: 100)"),
|
|
18715
|
+
pathPrefix: z.string().optional().describe("Filter results to URLs starting with this prefix (e.g. '/docs')"),
|
|
18716
|
+
tags: z.array(z.string()).optional().describe("Filter results to pages matching all specified tags"),
|
|
18717
|
+
filters: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional().describe('Filter by structured page metadata (e.g. {"version": 2})'),
|
|
18718
|
+
groupBy: z.enum(["page", "chunk"]).optional().describe("'page' (default) groups chunks by page with sub-results; 'chunk' returns individual chunks"),
|
|
18719
|
+
scope: z.string().optional()
|
|
18745
18720
|
}
|
|
18746
18721
|
},
|
|
18747
18722
|
async (input) => {
|
|
@@ -18752,85 +18727,18 @@ function createServer(engine) {
|
|
|
18752
18727
|
pathPrefix: input.pathPrefix,
|
|
18753
18728
|
tags: input.tags,
|
|
18754
18729
|
filters: input.filters,
|
|
18755
|
-
groupBy: input.groupBy
|
|
18756
|
-
maxSubResults: input.maxSubResults
|
|
18757
|
-
});
|
|
18758
|
-
return {
|
|
18759
|
-
content: [
|
|
18760
|
-
{
|
|
18761
|
-
type: "text",
|
|
18762
|
-
text: JSON.stringify(result, null, 2)
|
|
18763
|
-
}
|
|
18764
|
-
],
|
|
18765
|
-
structuredContent: result
|
|
18766
|
-
};
|
|
18767
|
-
}
|
|
18768
|
-
);
|
|
18769
|
-
server.registerTool(
|
|
18770
|
-
"get_page",
|
|
18771
|
-
{
|
|
18772
|
-
description: "Fetch indexed markdown for a specific path or URL, including frontmatter and routeFile mapping.",
|
|
18773
|
-
inputSchema: {
|
|
18774
|
-
pathOrUrl: z.string().min(1),
|
|
18775
|
-
scope: z.string().optional()
|
|
18776
|
-
}
|
|
18777
|
-
},
|
|
18778
|
-
async (input) => {
|
|
18779
|
-
const page = await engine.getPage(input.pathOrUrl, input.scope);
|
|
18780
|
-
return {
|
|
18781
|
-
content: [
|
|
18782
|
-
{
|
|
18783
|
-
type: "text",
|
|
18784
|
-
text: JSON.stringify(page, null, 2)
|
|
18785
|
-
}
|
|
18786
|
-
]
|
|
18787
|
-
};
|
|
18788
|
-
}
|
|
18789
|
-
);
|
|
18790
|
-
server.registerTool(
|
|
18791
|
-
"list_pages",
|
|
18792
|
-
{
|
|
18793
|
-
description: "List indexed pages with optional path prefix filtering and cursor-based pagination. Returns url, title, description, and routeFile for each page. Use nextCursor to fetch subsequent pages.",
|
|
18794
|
-
inputSchema: {
|
|
18795
|
-
pathPrefix: z.string().optional(),
|
|
18796
|
-
cursor: z.string().optional(),
|
|
18797
|
-
limit: z.number().int().positive().max(200).optional(),
|
|
18798
|
-
scope: z.string().optional()
|
|
18799
|
-
}
|
|
18800
|
-
},
|
|
18801
|
-
async (input) => {
|
|
18802
|
-
const result = await engine.listPages({
|
|
18803
|
-
pathPrefix: input.pathPrefix,
|
|
18804
|
-
cursor: input.cursor,
|
|
18805
|
-
limit: input.limit,
|
|
18806
|
-
scope: input.scope
|
|
18730
|
+
groupBy: input.groupBy
|
|
18807
18731
|
});
|
|
18808
|
-
|
|
18809
|
-
|
|
18810
|
-
|
|
18811
|
-
|
|
18812
|
-
|
|
18813
|
-
|
|
18814
|
-
|
|
18815
|
-
|
|
18816
|
-
|
|
18817
|
-
);
|
|
18818
|
-
server.registerTool(
|
|
18819
|
-
"get_site_structure",
|
|
18820
|
-
{
|
|
18821
|
-
description: "Returns the hierarchical page tree derived from URL paths. Use this to understand site navigation structure, find where pages belong, or scope further operations to a section. Nodes with isIndexed: false are implicit structural parents not directly in the index. Large sites (>2000 pages) return truncated: true.",
|
|
18822
|
-
inputSchema: {
|
|
18823
|
-
pathPrefix: z.string().optional(),
|
|
18824
|
-
scope: z.string().optional(),
|
|
18825
|
-
maxPages: z.number().int().positive().max(2e3).optional()
|
|
18732
|
+
if (result.results.length === 0) {
|
|
18733
|
+
return {
|
|
18734
|
+
content: [
|
|
18735
|
+
{
|
|
18736
|
+
type: "text",
|
|
18737
|
+
text: `No results found for "${input.query}". Try broader keywords or remove filters.`
|
|
18738
|
+
}
|
|
18739
|
+
]
|
|
18740
|
+
};
|
|
18826
18741
|
}
|
|
18827
|
-
},
|
|
18828
|
-
async (input) => {
|
|
18829
|
-
const result = await engine.getSiteStructure({
|
|
18830
|
-
pathPrefix: input.pathPrefix,
|
|
18831
|
-
scope: input.scope,
|
|
18832
|
-
maxPages: input.maxPages
|
|
18833
|
-
});
|
|
18834
18742
|
return {
|
|
18835
18743
|
content: [
|
|
18836
18744
|
{
|
|
@@ -18842,56 +18750,51 @@ function createServer(engine) {
|
|
|
18842
18750
|
}
|
|
18843
18751
|
);
|
|
18844
18752
|
server.registerTool(
|
|
18845
|
-
"
|
|
18753
|
+
"get_page",
|
|
18846
18754
|
{
|
|
18847
|
-
description: "
|
|
18755
|
+
description: "Retrieves the full markdown content and metadata for a specific page by its URL path. Use this after search when snippets lack the detail needed to answer a question. Returns reconstructed page markdown, frontmatter (title, routeFile, tags, link counts, indexedAt), and the source file path. Do NOT use this for discovery \u2014 use search first to find relevant pages.",
|
|
18848
18756
|
inputSchema: {
|
|
18849
|
-
|
|
18757
|
+
path: z.string().min(1).describe("URL path of the page (e.g. '/docs/auth'). Use a URL from search results."),
|
|
18850
18758
|
scope: z.string().optional()
|
|
18851
18759
|
}
|
|
18852
18760
|
},
|
|
18853
18761
|
async (input) => {
|
|
18854
|
-
|
|
18855
|
-
|
|
18856
|
-
|
|
18857
|
-
|
|
18858
|
-
|
|
18859
|
-
|
|
18762
|
+
try {
|
|
18763
|
+
const page = await engine.getPage(input.path, input.scope);
|
|
18764
|
+
return {
|
|
18765
|
+
content: [
|
|
18766
|
+
{
|
|
18767
|
+
type: "text",
|
|
18768
|
+
text: JSON.stringify(page, null, 2)
|
|
18769
|
+
}
|
|
18770
|
+
]
|
|
18771
|
+
};
|
|
18772
|
+
} catch {
|
|
18773
|
+
const suggestions = await engine.search({ q: input.path, topK: 3, scope: input.scope });
|
|
18774
|
+
const similar = suggestions.results.map((r) => r.url);
|
|
18860
18775
|
return {
|
|
18861
18776
|
content: [
|
|
18862
18777
|
{
|
|
18863
18778
|
type: "text",
|
|
18864
|
-
text:
|
|
18865
|
-
error: "No matching content found for the given query."
|
|
18866
|
-
})
|
|
18779
|
+
text: similar.length > 0 ? `Page '${input.path}' not found. Similar pages: ${similar.join(", ")}` : `Page '${input.path}' not found. Use search to find the correct URL.`
|
|
18867
18780
|
}
|
|
18868
18781
|
]
|
|
18869
18782
|
};
|
|
18870
18783
|
}
|
|
18871
|
-
const match = result.results[0];
|
|
18872
|
-
const { url, routeFile, sectionTitle, snippet } = match;
|
|
18873
|
-
return {
|
|
18874
|
-
content: [
|
|
18875
|
-
{
|
|
18876
|
-
type: "text",
|
|
18877
|
-
text: JSON.stringify({ url, routeFile, sectionTitle, snippet })
|
|
18878
|
-
}
|
|
18879
|
-
]
|
|
18880
|
-
};
|
|
18881
18784
|
}
|
|
18882
18785
|
);
|
|
18883
18786
|
server.registerTool(
|
|
18884
18787
|
"get_related_pages",
|
|
18885
18788
|
{
|
|
18886
|
-
description: "
|
|
18789
|
+
description: "Finds pages related to a specific page using link graph analysis, semantic similarity, and URL structure. Returns related pages with relationship type (outgoing_link, incoming_link, sibling, semantic) and relevance score. Do NOT use this for general search \u2014 use search instead. Use this only when you already have a specific page URL and need to discover connected content.",
|
|
18887
18790
|
inputSchema: {
|
|
18888
|
-
|
|
18889
|
-
|
|
18890
|
-
|
|
18791
|
+
path: z.string().min(1).describe("URL path of the source page (e.g. '/docs/auth'). Use a URL from search results."),
|
|
18792
|
+
topK: z.number().int().positive().max(25).optional().describe("Number of related pages to return (default: 10, max: 25)"),
|
|
18793
|
+
scope: z.string().optional()
|
|
18891
18794
|
}
|
|
18892
18795
|
},
|
|
18893
18796
|
async (input) => {
|
|
18894
|
-
const result = await engine.getRelatedPages(input.
|
|
18797
|
+
const result = await engine.getRelatedPages(input.path, {
|
|
18895
18798
|
topK: input.topK,
|
|
18896
18799
|
scope: input.scope
|
|
18897
18800
|
});
|