@youdotcom-oss/mcp 3.2.2 → 3.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.
- package/bin/stdio.js +70 -165
- package/package.json +4 -3
- package/server.json +3 -3
- package/src/contents/contents.utils.ts +8 -34
- package/src/contents/register-contents-tool.ts +15 -13
- package/src/contents/tests/contents.utils.spec.ts +9 -33
- package/src/main.ts +0 -3
- package/src/research/register-research-tool.ts +5 -6
- package/src/research/research.utils.ts +6 -24
- package/src/research/tests/research.utils.spec.ts +5 -68
- package/src/search/register-search-tool.ts +13 -14
- package/src/search/search.utils.ts +7 -50
- package/src/search/tests/register-search-tool.spec.ts +119 -0
- package/src/search/tests/search.utils.spec.ts +104 -68
- package/src/shared/format-search-results-text.ts +20 -0
- package/src/shared/tests/format-search-results-text.spec.ts +95 -0
- package/src/contents/contents.schemas.ts +0 -30
- package/src/research/research.schemas.ts +0 -19
- package/src/search/search.schemas.ts +0 -38
package/bin/stdio.js
CHANGED
|
@@ -12572,7 +12572,7 @@ var ResearchQuerySchema = object({
|
|
|
12572
12572
|
var ResearchSourceSchema = object({
|
|
12573
12573
|
url: string2().describe("Source webpage URL"),
|
|
12574
12574
|
title: string2().optional().describe("Source webpage title"),
|
|
12575
|
-
snippets: array(string2()).describe("Relevant excerpts from the source page used in generating the answer")
|
|
12575
|
+
snippets: array(string2()).optional().describe("Relevant excerpts from the source page used in generating the answer")
|
|
12576
12576
|
});
|
|
12577
12577
|
var ResearchOutputSchema = object({
|
|
12578
12578
|
content: string2().describe("Comprehensive response with inline citations, formatted in Markdown"),
|
|
@@ -12634,7 +12634,7 @@ var formatResearchResponse = (response) => {
|
|
|
12634
12634
|
`);
|
|
12635
12635
|
parts.push(`**URL:** ${source.url}
|
|
12636
12636
|
`);
|
|
12637
|
-
if (source.snippets
|
|
12637
|
+
if (source.snippets?.length) {
|
|
12638
12638
|
parts.push(`
|
|
12639
12639
|
**Key Excerpts:**
|
|
12640
12640
|
`);
|
|
@@ -12747,8 +12747,17 @@ var SearchQuerySchema = object({
|
|
|
12747
12747
|
]).optional().describe("Country code"),
|
|
12748
12748
|
safesearch: _enum(["off", "moderate", "strict"]).optional().describe("Filter level"),
|
|
12749
12749
|
livecrawl: _enum(["web", "news", "all"]).optional().describe("Live-crawl sections for full content"),
|
|
12750
|
-
livecrawl_formats: _enum(["html", "markdown"]).optional().describe("
|
|
12750
|
+
livecrawl_formats: array(_enum(["html", "markdown"])).optional().describe("Formats for crawled content"),
|
|
12751
|
+
language: LanguageSchema.optional().describe("Language code (BCP 47 format)"),
|
|
12752
|
+
include_domains: array(string2()).max(500).optional().describe("Domains to include in results (up to 500)"),
|
|
12753
|
+
exclude_domains: array(string2()).max(500).optional().describe("Domains to exclude from results (up to 500)"),
|
|
12754
|
+
crawl_timeout: number2().int().min(1).max(60).optional().describe("Crawl timeout in seconds (1-60)")
|
|
12751
12755
|
});
|
|
12756
|
+
var validateSearchQuery = (searchQuery) => {
|
|
12757
|
+
if (searchQuery.include_domains && searchQuery.exclude_domains) {
|
|
12758
|
+
throw new Error("Cannot combine include_domains and exclude_domains");
|
|
12759
|
+
}
|
|
12760
|
+
};
|
|
12752
12761
|
var WebResultSchema = object({
|
|
12753
12762
|
url: string2().describe("URL"),
|
|
12754
12763
|
title: string2().describe("Title"),
|
|
@@ -12807,23 +12816,18 @@ var fetchSearchResults = async ({
|
|
|
12807
12816
|
getUserAgent,
|
|
12808
12817
|
customHeaders
|
|
12809
12818
|
}) => {
|
|
12810
|
-
|
|
12811
|
-
const searchParams = new URLSearchParams;
|
|
12812
|
-
for (const [name, value] of Object.entries(searchQuery)) {
|
|
12813
|
-
if (value !== undefined && value !== null) {
|
|
12814
|
-
searchParams.append(name, `${value}`);
|
|
12815
|
-
}
|
|
12816
|
-
}
|
|
12817
|
-
url.search = searchParams.toString();
|
|
12819
|
+
validateSearchQuery(searchQuery);
|
|
12818
12820
|
const options = {
|
|
12819
|
-
method: "
|
|
12821
|
+
method: "POST",
|
|
12820
12822
|
headers: new Headers({
|
|
12821
12823
|
...customHeaders,
|
|
12822
12824
|
"X-API-Key": YDC_API_KEY || "",
|
|
12825
|
+
"Content-Type": "application/json",
|
|
12823
12826
|
"User-Agent": getUserAgent()
|
|
12824
|
-
})
|
|
12827
|
+
}),
|
|
12828
|
+
body: JSON.stringify(searchQuery)
|
|
12825
12829
|
};
|
|
12826
|
-
const response = await fetch(
|
|
12830
|
+
const response = await fetch(SEARCH_API_URL, options);
|
|
12827
12831
|
if (!response.ok) {
|
|
12828
12832
|
const errorCode = response.status;
|
|
12829
12833
|
if (errorCode === 429) {
|
|
@@ -12885,29 +12889,12 @@ var getLogger = (sendNotification) => async (params) => {
|
|
|
12885
12889
|
await sendNotification({ method: "notifications/message", params });
|
|
12886
12890
|
};
|
|
12887
12891
|
|
|
12888
|
-
// src/contents/contents.schemas.ts
|
|
12889
|
-
var ContentsStructuredContentSchema = object({
|
|
12890
|
-
count: number2().describe("URLs processed"),
|
|
12891
|
-
formats: array(string2()).describe("Content formats requested"),
|
|
12892
|
-
items: array(object({
|
|
12893
|
-
url: string2().describe("URL"),
|
|
12894
|
-
title: string2().optional().describe("Title"),
|
|
12895
|
-
markdown: string2().optional().describe("Markdown content"),
|
|
12896
|
-
html: string2().optional().describe("HTML content"),
|
|
12897
|
-
metadata: object({
|
|
12898
|
-
favicon_url: string2().describe("Favicon URL"),
|
|
12899
|
-
site_name: string2().optional().nullable().describe("Site name")
|
|
12900
|
-
}).optional().nullable().describe("Page metadata")
|
|
12901
|
-
})).describe("Extracted items")
|
|
12902
|
-
});
|
|
12903
|
-
|
|
12904
12892
|
// src/contents/contents.utils.ts
|
|
12905
12893
|
var formatContentsResponse = (response, formats) => {
|
|
12906
12894
|
const textParts = [`Successfully extracted content from ${response.length} URL(s):
|
|
12907
12895
|
`];
|
|
12908
12896
|
textParts.push(`Formats: ${formats.join(", ")}
|
|
12909
12897
|
`);
|
|
12910
|
-
const items = [];
|
|
12911
12898
|
for (const item of response) {
|
|
12912
12899
|
textParts.push(`
|
|
12913
12900
|
## ${item.title || "Untitled"}`);
|
|
@@ -12953,28 +12940,14 @@ var formatContentsResponse = (response, formats) => {
|
|
|
12953
12940
|
textParts.push(`
|
|
12954
12941
|
---
|
|
12955
12942
|
`);
|
|
12956
|
-
items.push({
|
|
12957
|
-
url: item.url,
|
|
12958
|
-
title: item.title ?? undefined,
|
|
12959
|
-
markdown: item.markdown ?? undefined,
|
|
12960
|
-
html: item.html ?? undefined,
|
|
12961
|
-
metadata: item.metadata ?? undefined
|
|
12962
|
-
});
|
|
12963
12943
|
}
|
|
12964
|
-
return
|
|
12965
|
-
|
|
12966
|
-
|
|
12967
|
-
|
|
12968
|
-
text: textParts.join(`
|
|
12944
|
+
return [
|
|
12945
|
+
{
|
|
12946
|
+
type: "text",
|
|
12947
|
+
text: textParts.join(`
|
|
12969
12948
|
`)
|
|
12970
|
-
}
|
|
12971
|
-
],
|
|
12972
|
-
structuredContent: {
|
|
12973
|
-
count: response.length,
|
|
12974
|
-
formats,
|
|
12975
|
-
items
|
|
12976
12949
|
}
|
|
12977
|
-
|
|
12950
|
+
];
|
|
12978
12951
|
};
|
|
12979
12952
|
|
|
12980
12953
|
// src/contents/register-contents-tool.ts
|
|
@@ -12986,8 +12959,10 @@ var registerContentsTool = ({
|
|
|
12986
12959
|
mcp.registerTool("you-contents", {
|
|
12987
12960
|
title: "Extract Web Page Contents",
|
|
12988
12961
|
description: "Extract page content in markdown or HTML",
|
|
12989
|
-
inputSchema: ContentsQuerySchema
|
|
12990
|
-
outputSchema:
|
|
12962
|
+
inputSchema: ContentsQuerySchema,
|
|
12963
|
+
outputSchema: object({
|
|
12964
|
+
output: ContentsApiResponseSchema
|
|
12965
|
+
})
|
|
12991
12966
|
}, async (contentsQuery, { sendNotification }) => {
|
|
12992
12967
|
const logger = getLogger(sendNotification);
|
|
12993
12968
|
try {
|
|
@@ -13003,14 +12978,16 @@ var registerContentsTool = ({
|
|
|
13003
12978
|
YDC_API_KEY,
|
|
13004
12979
|
getUserAgent
|
|
13005
12980
|
});
|
|
13006
|
-
const
|
|
12981
|
+
const content = formatContentsResponse(response, requestFormats);
|
|
13007
12982
|
await logger({
|
|
13008
12983
|
level: "info",
|
|
13009
12984
|
data: `Contents API call successful: extracted ${response.length} page(s)`
|
|
13010
12985
|
});
|
|
13011
12986
|
return {
|
|
13012
12987
|
content,
|
|
13013
|
-
structuredContent
|
|
12988
|
+
structuredContent: {
|
|
12989
|
+
output: response
|
|
12990
|
+
}
|
|
13014
12991
|
};
|
|
13015
12992
|
} catch (err) {
|
|
13016
12993
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -20460,7 +20437,7 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
20460
20437
|
// package.json
|
|
20461
20438
|
var package_default = {
|
|
20462
20439
|
name: "@youdotcom-oss/mcp",
|
|
20463
|
-
version: "3.
|
|
20440
|
+
version: "3.3.0",
|
|
20464
20441
|
description: "You.com MCP server — web search, AI research, and content extraction via You.com APIs",
|
|
20465
20442
|
license: "MIT",
|
|
20466
20443
|
engines: {
|
|
@@ -20513,11 +20490,12 @@ var package_default = {
|
|
|
20513
20490
|
mcpName: "io.github.youdotcom-oss/mcp",
|
|
20514
20491
|
dependencies: {
|
|
20515
20492
|
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
20516
|
-
"@youdotcom-oss/api": "0.5.
|
|
20493
|
+
"@youdotcom-oss/api": "0.5.2",
|
|
20517
20494
|
zod: "^4.3.6"
|
|
20518
20495
|
},
|
|
20519
20496
|
devDependencies: {
|
|
20520
|
-
"@modelcontextprotocol/inspector": "0.21.
|
|
20497
|
+
"@modelcontextprotocol/inspector": "0.21.2",
|
|
20498
|
+
"@types/bun": "latest"
|
|
20521
20499
|
}
|
|
20522
20500
|
};
|
|
20523
20501
|
|
|
@@ -20533,37 +20511,15 @@ var getMcpServer = () => new McpServer({
|
|
|
20533
20511
|
instructions: `Use this server to search the web, get AI-powered answers with web context, and extract content from web pages using You.com. The you-contents tool extracts page content and returns it in markdown or HTML format. Use HTML format for layout preservation, interactive content, and visual fidelity; use markdown for text extraction and simpler consumption.`
|
|
20534
20512
|
});
|
|
20535
20513
|
|
|
20536
|
-
// src/research/research.schemas.ts
|
|
20537
|
-
var ResearchStructuredContentSchema = object({
|
|
20538
|
-
contentType: string2().describe("Format of the content field"),
|
|
20539
|
-
sourceCount: number2().describe("Number of sources used"),
|
|
20540
|
-
sources: array(object({
|
|
20541
|
-
url: string2().describe("Source URL"),
|
|
20542
|
-
title: string2().optional().describe("Source title"),
|
|
20543
|
-
snippetCount: number2().describe("Number of excerpts from this source")
|
|
20544
|
-
})).describe("Sources used in the research answer")
|
|
20545
|
-
});
|
|
20546
|
-
|
|
20547
20514
|
// src/research/research.utils.ts
|
|
20548
20515
|
var formatResearchResults = (response) => {
|
|
20549
20516
|
const text = formatResearchResponse(response);
|
|
20550
|
-
return
|
|
20551
|
-
|
|
20552
|
-
|
|
20553
|
-
|
|
20554
|
-
text
|
|
20555
|
-
}
|
|
20556
|
-
],
|
|
20557
|
-
structuredContent: {
|
|
20558
|
-
contentType: response.output.content_type,
|
|
20559
|
-
sourceCount: response.output.sources.length,
|
|
20560
|
-
sources: response.output.sources.map((source) => ({
|
|
20561
|
-
url: source.url,
|
|
20562
|
-
title: source.title,
|
|
20563
|
-
snippetCount: source.snippets?.length ?? 0
|
|
20564
|
-
}))
|
|
20517
|
+
return [
|
|
20518
|
+
{
|
|
20519
|
+
type: "text",
|
|
20520
|
+
text
|
|
20565
20521
|
}
|
|
20566
|
-
|
|
20522
|
+
];
|
|
20567
20523
|
};
|
|
20568
20524
|
|
|
20569
20525
|
// src/research/register-research-tool.ts
|
|
@@ -20575,8 +20531,8 @@ var registerResearchTool = ({
|
|
|
20575
20531
|
mcp.registerTool("you-research", {
|
|
20576
20532
|
title: "Research",
|
|
20577
20533
|
description: "Research a topic with comprehensive answers and cited sources. Configurable effort levels (lite, standard, deep, exhaustive).",
|
|
20578
|
-
inputSchema: ResearchQuerySchema
|
|
20579
|
-
outputSchema:
|
|
20534
|
+
inputSchema: ResearchQuerySchema,
|
|
20535
|
+
outputSchema: ResearchResponseSchema
|
|
20580
20536
|
}, async (researchQuery, { sendNotification }) => {
|
|
20581
20537
|
const logger = getLogger(sendNotification);
|
|
20582
20538
|
try {
|
|
@@ -20590,8 +20546,8 @@ var registerResearchTool = ({
|
|
|
20590
20546
|
level: "info",
|
|
20591
20547
|
data: `Research for "${researchQuery.input.substring(0, 100)}" complete: ${sourceCount} source(s)`
|
|
20592
20548
|
});
|
|
20593
|
-
const
|
|
20594
|
-
return { content, structuredContent };
|
|
20549
|
+
const content = formatResearchResults(response);
|
|
20550
|
+
return { content, structuredContent: response };
|
|
20595
20551
|
} catch (err) {
|
|
20596
20552
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
20597
20553
|
const reportLink = generateErrorReportLink({
|
|
@@ -20619,28 +20575,8 @@ Report this issue: ${reportLink}`
|
|
|
20619
20575
|
});
|
|
20620
20576
|
};
|
|
20621
20577
|
|
|
20622
|
-
// src/search/search.schemas.ts
|
|
20623
|
-
var SearchStructuredContentSchema = object({
|
|
20624
|
-
resultCounts: object({
|
|
20625
|
-
web: number2().describe("Web results"),
|
|
20626
|
-
news: number2().describe("News results"),
|
|
20627
|
-
total: number2().describe("Total results")
|
|
20628
|
-
}),
|
|
20629
|
-
results: object({
|
|
20630
|
-
web: array(object({
|
|
20631
|
-
url: string2().describe("URL"),
|
|
20632
|
-
title: string2().describe("Title"),
|
|
20633
|
-
page_age: string2().optional().describe("Publication timestamp")
|
|
20634
|
-
})).optional().describe("Web results"),
|
|
20635
|
-
news: array(object({
|
|
20636
|
-
url: string2().describe("URL"),
|
|
20637
|
-
title: string2().describe("Title"),
|
|
20638
|
-
page_age: string2().describe("Publication timestamp")
|
|
20639
|
-
})).optional().describe("News results")
|
|
20640
|
-
}).optional().describe("Search results")
|
|
20641
|
-
});
|
|
20642
|
-
|
|
20643
20578
|
// src/shared/format-search-results-text.ts
|
|
20579
|
+
var formatCharCount = (count) => count.toLocaleString();
|
|
20644
20580
|
var formatSearchResultsText = (results) => {
|
|
20645
20581
|
return results.map((result) => {
|
|
20646
20582
|
const parts = [`Title: ${result.title}`];
|
|
@@ -20658,6 +20594,18 @@ var formatSearchResultsText = (results) => {
|
|
|
20658
20594
|
} else if (result.snippet) {
|
|
20659
20595
|
parts.push(`Snippet: ${result.snippet}`);
|
|
20660
20596
|
}
|
|
20597
|
+
if (result.contents) {
|
|
20598
|
+
const formats = [];
|
|
20599
|
+
if (result.contents.markdown) {
|
|
20600
|
+
formats.push(`${formatCharCount(result.contents.markdown.length)} chars (markdown)`);
|
|
20601
|
+
}
|
|
20602
|
+
if (result.contents.html) {
|
|
20603
|
+
formats.push(`${formatCharCount(result.contents.html.length)} chars (html)`);
|
|
20604
|
+
}
|
|
20605
|
+
if (formats.length > 0) {
|
|
20606
|
+
parts.push(`Page content available: ${formats.join(", ")}`);
|
|
20607
|
+
}
|
|
20608
|
+
}
|
|
20661
20609
|
return parts.join(`
|
|
20662
20610
|
`);
|
|
20663
20611
|
}).join(`
|
|
@@ -20675,14 +20623,7 @@ var formatSearchResults = (response) => {
|
|
|
20675
20623
|
${webResults}`;
|
|
20676
20624
|
}
|
|
20677
20625
|
if (response.results.news?.length) {
|
|
20678
|
-
const newsResults = response.results.news
|
|
20679
|
-
URL: ${article.url}
|
|
20680
|
-
Description: ${article.description}
|
|
20681
|
-
Published: ${article.page_age}`).join(`
|
|
20682
|
-
|
|
20683
|
-
---
|
|
20684
|
-
|
|
20685
|
-
`);
|
|
20626
|
+
const newsResults = formatSearchResultsText(response.results.news);
|
|
20686
20627
|
if (formattedResults) {
|
|
20687
20628
|
formattedResults += `
|
|
20688
20629
|
|
|
@@ -20694,44 +20635,14 @@ ${"=".repeat(50)}
|
|
|
20694
20635
|
|
|
20695
20636
|
${newsResults}`;
|
|
20696
20637
|
}
|
|
20697
|
-
|
|
20698
|
-
|
|
20699
|
-
|
|
20700
|
-
|
|
20701
|
-
url: result.url,
|
|
20702
|
-
title: result.title
|
|
20703
|
-
};
|
|
20704
|
-
if (result.page_age)
|
|
20705
|
-
item.page_age = result.page_age;
|
|
20706
|
-
return item;
|
|
20707
|
-
});
|
|
20708
|
-
}
|
|
20709
|
-
if (response.results.news?.length) {
|
|
20710
|
-
structuredResults.news = response.results.news.map((article) => ({
|
|
20711
|
-
url: article.url,
|
|
20712
|
-
title: article.title,
|
|
20713
|
-
page_age: article.page_age
|
|
20714
|
-
}));
|
|
20715
|
-
}
|
|
20716
|
-
return {
|
|
20717
|
-
content: [
|
|
20718
|
-
{
|
|
20719
|
-
type: "text",
|
|
20720
|
-
text: `Search Results for "${response.metadata.query}":
|
|
20638
|
+
return [
|
|
20639
|
+
{
|
|
20640
|
+
type: "text",
|
|
20641
|
+
text: `Search Results for "${response.metadata.query}":
|
|
20721
20642
|
|
|
20722
20643
|
${formattedResults}`
|
|
20723
|
-
|
|
20724
|
-
|
|
20725
|
-
structuredContent: {
|
|
20726
|
-
resultCounts: {
|
|
20727
|
-
web: response.results.web?.length || 0,
|
|
20728
|
-
news: response.results.news?.length || 0,
|
|
20729
|
-
total: (response.results.web?.length || 0) + (response.results.news?.length || 0)
|
|
20730
|
-
},
|
|
20731
|
-
results: Object.keys(structuredResults).length > 0 ? structuredResults : undefined
|
|
20732
|
-
},
|
|
20733
|
-
fullResponse: response
|
|
20734
|
-
};
|
|
20644
|
+
}
|
|
20645
|
+
];
|
|
20735
20646
|
};
|
|
20736
20647
|
|
|
20737
20648
|
// src/search/register-search-tool.ts
|
|
@@ -20742,9 +20653,9 @@ var registerSearchTool = ({
|
|
|
20742
20653
|
}) => {
|
|
20743
20654
|
mcp.registerTool("you-search", {
|
|
20744
20655
|
title: "Web Search",
|
|
20745
|
-
description: "Web and news search via You.com",
|
|
20746
|
-
inputSchema: SearchQuerySchema
|
|
20747
|
-
outputSchema:
|
|
20656
|
+
description: "Web and news search via You.com. Supports domain filtering, language selection, livecrawl for full page content, and date freshness controls.",
|
|
20657
|
+
inputSchema: SearchQuerySchema,
|
|
20658
|
+
outputSchema: SearchResponseSchema
|
|
20748
20659
|
}, async (searchQuery, { sendNotification }) => {
|
|
20749
20660
|
const logger = getLogger(sendNotification);
|
|
20750
20661
|
try {
|
|
@@ -20762,21 +20673,15 @@ var registerSearchTool = ({
|
|
|
20762
20673
|
});
|
|
20763
20674
|
return {
|
|
20764
20675
|
content: [{ type: "text", text: "No results found." }],
|
|
20765
|
-
structuredContent:
|
|
20766
|
-
resultCounts: {
|
|
20767
|
-
web: 0,
|
|
20768
|
-
news: 0,
|
|
20769
|
-
total: 0
|
|
20770
|
-
}
|
|
20771
|
-
}
|
|
20676
|
+
structuredContent: response
|
|
20772
20677
|
};
|
|
20773
20678
|
}
|
|
20774
20679
|
await logger({
|
|
20775
20680
|
level: "info",
|
|
20776
20681
|
data: `Search successful for query: "${searchQuery.query}" - ${webCount} web results, ${newsCount} news results (${webCount + newsCount} total)`
|
|
20777
20682
|
});
|
|
20778
|
-
const
|
|
20779
|
-
return { content, structuredContent };
|
|
20683
|
+
const content = formatSearchResults(response);
|
|
20684
|
+
return { content, structuredContent: response };
|
|
20780
20685
|
} catch (err) {
|
|
20781
20686
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
20782
20687
|
const reportLink = generateErrorReportLink({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youdotcom-oss/mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "You.com MCP server — web search, AI research, and content extraction via You.com APIs",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -53,10 +53,11 @@
|
|
|
53
53
|
"mcpName": "io.github.youdotcom-oss/mcp",
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
56
|
-
"@youdotcom-oss/api": "0.5.
|
|
56
|
+
"@youdotcom-oss/api": "0.5.2",
|
|
57
57
|
"zod": "^4.3.6"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@modelcontextprotocol/inspector": "0.21.
|
|
60
|
+
"@modelcontextprotocol/inspector": "0.21.2",
|
|
61
|
+
"@types/bun": "latest"
|
|
61
62
|
}
|
|
62
63
|
}
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.youdotcom-oss/mcp",
|
|
4
4
|
"title": "You.com Web Access & AI",
|
|
5
5
|
"description": "Web search, AI agent, and content extraction via You.com APIs",
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.2.3",
|
|
7
7
|
"remotes": [
|
|
8
8
|
{
|
|
9
9
|
"type": "streamable-http",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
{
|
|
23
23
|
"registryType": "npm",
|
|
24
24
|
"identifier": "@youdotcom-oss/mcp",
|
|
25
|
-
"version": "3.
|
|
25
|
+
"version": "3.2.3",
|
|
26
26
|
"transport": {
|
|
27
27
|
"type": "stdio"
|
|
28
28
|
},
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"description": "Remote MCP server URL (defaults to https://api.you.com/mcp)",
|
|
40
40
|
"isRequired": false,
|
|
41
41
|
"isSecret": false,
|
|
42
|
-
"format": "
|
|
42
|
+
"format": "string"
|
|
43
43
|
}
|
|
44
44
|
]
|
|
45
45
|
}
|
|
@@ -1,33 +1,23 @@
|
|
|
1
1
|
import type { ContentsApiResponse } from '@youdotcom-oss/api'
|
|
2
|
-
import type { ContentsStructuredContent } from './contents.schemas.ts'
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Format contents API response for MCP output
|
|
6
|
-
* Returns full content in both text and structured formats
|
|
7
5
|
* @param response - Validated API response
|
|
8
6
|
* @param formats - Formats used for extraction
|
|
9
|
-
* @returns
|
|
7
|
+
* @returns Text content blocks for the MCP response
|
|
10
8
|
*/
|
|
11
9
|
export const formatContentsResponse = (
|
|
12
10
|
response: ContentsApiResponse,
|
|
13
11
|
formats: string[],
|
|
14
|
-
): {
|
|
15
|
-
content: Array<{ type: 'text'; text: string }>
|
|
16
|
-
structuredContent: ContentsStructuredContent
|
|
17
|
-
} => {
|
|
18
|
-
// Build text content with full extracted content
|
|
12
|
+
): Array<{ type: 'text'; text: string }> => {
|
|
19
13
|
const textParts: string[] = [`Successfully extracted content from ${response.length} URL(s):\n`]
|
|
20
14
|
textParts.push(`Formats: ${formats.join(', ')}\n`)
|
|
21
15
|
|
|
22
|
-
const items: ContentsStructuredContent['items'] = []
|
|
23
|
-
|
|
24
16
|
for (const item of response) {
|
|
25
|
-
// Add header for this item
|
|
26
17
|
textParts.push(`\n## ${item.title || 'Untitled'}`)
|
|
27
18
|
textParts.push(`URL: ${item.url}\n`)
|
|
28
19
|
textParts.push('---\n')
|
|
29
20
|
|
|
30
|
-
// Add content based on requested formats
|
|
31
21
|
if (formats.includes('markdown') && item.markdown) {
|
|
32
22
|
textParts.push('\n### Markdown Content\n')
|
|
33
23
|
textParts.push(item.markdown)
|
|
@@ -35,7 +25,7 @@ export const formatContentsResponse = (
|
|
|
35
25
|
}
|
|
36
26
|
|
|
37
27
|
if (formats.includes('html') && item.html) {
|
|
38
|
-
// Text output is a brief preview only — full HTML is in structuredContent.
|
|
28
|
+
// Text output is a brief preview only — full HTML is in structuredContent.output[].html
|
|
39
29
|
textParts.push('\n### HTML Content\n')
|
|
40
30
|
textParts.push(`Length: ${item.html.length} characters\n`)
|
|
41
31
|
textParts.push(item.html.substring(0, 500))
|
|
@@ -58,28 +48,12 @@ export const formatContentsResponse = (
|
|
|
58
48
|
}
|
|
59
49
|
|
|
60
50
|
textParts.push('\n---\n')
|
|
61
|
-
|
|
62
|
-
// Add to structured content
|
|
63
|
-
items.push({
|
|
64
|
-
url: item.url,
|
|
65
|
-
title: item.title ?? undefined,
|
|
66
|
-
markdown: item.markdown ?? undefined,
|
|
67
|
-
html: item.html ?? undefined,
|
|
68
|
-
metadata: item.metadata ?? undefined,
|
|
69
|
-
})
|
|
70
51
|
}
|
|
71
52
|
|
|
72
|
-
return
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
text: textParts.join('\n'),
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
structuredContent: {
|
|
80
|
-
count: response.length,
|
|
81
|
-
formats,
|
|
82
|
-
items,
|
|
53
|
+
return [
|
|
54
|
+
{
|
|
55
|
+
type: 'text',
|
|
56
|
+
text: textParts.join('\n'),
|
|
83
57
|
},
|
|
84
|
-
|
|
58
|
+
]
|
|
85
59
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ContentsApiResponseSchema,
|
|
4
|
+
ContentsQuerySchema,
|
|
5
|
+
fetchContents,
|
|
6
|
+
generateErrorReportLink,
|
|
7
|
+
} from '@youdotcom-oss/api'
|
|
8
|
+
import * as z from 'zod'
|
|
3
9
|
import { getLogger } from '../shared/get-logger.ts'
|
|
4
|
-
import { ContentsStructuredContentSchema } from './contents.schemas.ts'
|
|
5
10
|
import { formatContentsResponse } from './contents.utils.ts'
|
|
6
11
|
|
|
7
12
|
/**
|
|
@@ -17,43 +22,39 @@ export const registerContentsTool = ({
|
|
|
17
22
|
YDC_API_KEY?: string
|
|
18
23
|
getUserAgent: () => string
|
|
19
24
|
}) => {
|
|
20
|
-
// Register the tool
|
|
21
25
|
mcp.registerTool(
|
|
22
26
|
'you-contents',
|
|
23
27
|
{
|
|
24
28
|
title: 'Extract Web Page Contents',
|
|
25
29
|
description: 'Extract page content in markdown or HTML',
|
|
26
|
-
inputSchema: ContentsQuerySchema
|
|
27
|
-
outputSchema:
|
|
30
|
+
inputSchema: ContentsQuerySchema,
|
|
31
|
+
outputSchema: z.object({
|
|
32
|
+
output: ContentsApiResponseSchema,
|
|
33
|
+
}),
|
|
28
34
|
},
|
|
29
35
|
async (contentsQuery, { sendNotification }) => {
|
|
30
36
|
const logger = getLogger(sendNotification)
|
|
31
37
|
|
|
32
38
|
try {
|
|
33
|
-
// Validate and parse input
|
|
34
39
|
const { urls, formats, format, crawl_timeout } = contentsQuery
|
|
35
40
|
|
|
36
41
|
// Handle backward compatibility: prefer formats array, fallback to format string, default to ['markdown']
|
|
37
42
|
const requestFormats = formats || (format ? [format] : ['markdown'])
|
|
38
43
|
|
|
39
|
-
// Log the request
|
|
40
44
|
const timeoutInfo = crawl_timeout ? ` with timeout: ${crawl_timeout}s` : ''
|
|
41
45
|
await logger({
|
|
42
46
|
level: 'info',
|
|
43
47
|
data: `Contents API call initiated for ${urls.length} URL(s) with formats: ${requestFormats.join(', ')}${timeoutInfo}`,
|
|
44
48
|
})
|
|
45
49
|
|
|
46
|
-
// Fetch contents from API
|
|
47
50
|
const response = await fetchContents({
|
|
48
51
|
contentsQuery,
|
|
49
52
|
YDC_API_KEY,
|
|
50
53
|
getUserAgent,
|
|
51
54
|
})
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
const { content, structuredContent } = formatContentsResponse(response, requestFormats)
|
|
56
|
+
const content = formatContentsResponse(response, requestFormats)
|
|
55
57
|
|
|
56
|
-
// Log success
|
|
57
58
|
await logger({
|
|
58
59
|
level: 'info',
|
|
59
60
|
data: `Contents API call successful: extracted ${response.length} page(s)`,
|
|
@@ -61,10 +62,11 @@ export const registerContentsTool = ({
|
|
|
61
62
|
|
|
62
63
|
return {
|
|
63
64
|
content,
|
|
64
|
-
structuredContent
|
|
65
|
+
structuredContent: {
|
|
66
|
+
output: response,
|
|
67
|
+
},
|
|
65
68
|
}
|
|
66
69
|
} catch (err: unknown) {
|
|
67
|
-
// Handle and log errors
|
|
68
70
|
const errorMessage = err instanceof Error ? err.message : String(err)
|
|
69
71
|
const reportLink = generateErrorReportLink({
|
|
70
72
|
errorMessage,
|