@youdotcom-oss/mcp 1.3.6 → 1.3.8
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/README.md +2 -0
- package/bin/stdio.js +22 -8
- package/package.json +2 -1
- package/src/search/search.schemas.ts +23 -4
- package/src/search/search.utils.ts +1 -1
- package/src/search/tests/search.utils.spec.ts +51 -5
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@ Visit [you.com/platform/api-keys](https://you.com/platform/api-keys) to get your
|
|
|
22
22
|
|
|
23
23
|
### 2. Choose your setup
|
|
24
24
|
|
|
25
|
+
You can discover this server in the [Anthropic MCP Registry](https://registry.modelcontextprotocol.io/) as `io.github.youdotcom-oss/mcp`, or configure it manually:
|
|
26
|
+
|
|
25
27
|
**Remote server (recommended)** - No installation, always up-to-date, just add the URL and API key
|
|
26
28
|
- Use `https://api.you.com/mcp` with HTTP transport
|
|
27
29
|
- Authentication via `Authorization: Bearer <your-key>` header
|
package/bin/stdio.js
CHANGED
|
@@ -12155,7 +12155,7 @@ class StdioServerTransport {
|
|
|
12155
12155
|
// package.json
|
|
12156
12156
|
var package_default = {
|
|
12157
12157
|
name: "@youdotcom-oss/mcp",
|
|
12158
|
-
version: "1.3.
|
|
12158
|
+
version: "1.3.8",
|
|
12159
12159
|
description: "You.com API Model Context Protocol Server",
|
|
12160
12160
|
license: "MIT",
|
|
12161
12161
|
engines: {
|
|
@@ -12214,6 +12214,7 @@ var package_default = {
|
|
|
12214
12214
|
"test:coverage:watch": "bun test --coverage --watch",
|
|
12215
12215
|
"test:watch": "bun test --watch"
|
|
12216
12216
|
},
|
|
12217
|
+
mcpName: "io.github.youdotcom-oss/mcp",
|
|
12217
12218
|
dependencies: {
|
|
12218
12219
|
zod: "^4.1.13",
|
|
12219
12220
|
"@hono/mcp": "^0.2.0",
|
|
@@ -20062,8 +20063,8 @@ var getMCpServer = () => new McpServer({
|
|
|
20062
20063
|
// src/search/search.schemas.ts
|
|
20063
20064
|
var SearchQuerySchema = object({
|
|
20064
20065
|
query: string2().min(1, "Query is required").describe("Search query (supports +, -, site:, filetype:, lang:)"),
|
|
20065
|
-
count: number2().int().min(1).max(
|
|
20066
|
-
freshness:
|
|
20066
|
+
count: number2().int().min(1).max(100).optional().describe("Max results per section"),
|
|
20067
|
+
freshness: string2().optional().describe("day/week/month/year or YYYY-MM-DDtoYYYY-MM-DD"),
|
|
20067
20068
|
offset: number2().int().min(0).max(9).optional().describe("Pagination offset"),
|
|
20068
20069
|
country: _enum([
|
|
20069
20070
|
"AR",
|
|
@@ -20108,7 +20109,9 @@ var SearchQuerySchema = object({
|
|
|
20108
20109
|
fileType: string2().optional().describe("File type"),
|
|
20109
20110
|
language: string2().optional().describe("ISO 639-1 language code"),
|
|
20110
20111
|
excludeTerms: string2().optional().describe("Terms to exclude (pipe-separated)"),
|
|
20111
|
-
exactTerms: string2().optional().describe("Exact terms (pipe-separated)")
|
|
20112
|
+
exactTerms: string2().optional().describe("Exact terms (pipe-separated)"),
|
|
20113
|
+
livecrawl: _enum(["web", "news", "all"]).optional().describe("Live-crawl sections for full content"),
|
|
20114
|
+
livecrawl_formats: _enum(["html", "markdown"]).optional().describe("Format for crawled content")
|
|
20112
20115
|
});
|
|
20113
20116
|
var WebResultSchema = object({
|
|
20114
20117
|
url: string2().describe("URL"),
|
|
@@ -20116,16 +20119,27 @@ var WebResultSchema = object({
|
|
|
20116
20119
|
description: string2().describe("Description"),
|
|
20117
20120
|
snippets: array(string2()).describe("Content snippets"),
|
|
20118
20121
|
page_age: string2().optional().describe("Publication timestamp"),
|
|
20119
|
-
authors: array(string2()).optional().describe("Authors")
|
|
20122
|
+
authors: array(string2()).optional().describe("Authors"),
|
|
20123
|
+
thumbnail_url: string2().optional().describe("Thumbnail image URL"),
|
|
20124
|
+
favicon_url: string2().optional().describe("Favicon URL"),
|
|
20125
|
+
contents: object({
|
|
20126
|
+
html: string2().optional().describe("Full HTML content"),
|
|
20127
|
+
markdown: string2().optional().describe("Full Markdown content")
|
|
20128
|
+
}).optional().describe("Live-crawled page content")
|
|
20120
20129
|
});
|
|
20121
20130
|
var NewsResultSchema = object({
|
|
20122
20131
|
title: string2().describe("Title"),
|
|
20123
20132
|
description: string2().describe("Description"),
|
|
20124
20133
|
page_age: string2().describe("Publication timestamp"),
|
|
20125
|
-
url: string2().describe("URL")
|
|
20134
|
+
url: string2().describe("URL"),
|
|
20135
|
+
thumbnail_url: string2().optional().describe("Thumbnail image URL"),
|
|
20136
|
+
contents: object({
|
|
20137
|
+
html: string2().optional().describe("Full HTML content"),
|
|
20138
|
+
markdown: string2().optional().describe("Full Markdown content")
|
|
20139
|
+
}).optional().describe("Live-crawled page content")
|
|
20126
20140
|
});
|
|
20127
20141
|
var MetadataSchema = object({
|
|
20128
|
-
|
|
20142
|
+
search_uuid: string2().optional().describe("Unique search request ID"),
|
|
20129
20143
|
query: string2().describe("Query"),
|
|
20130
20144
|
latency: number2().describe("Latency in seconds")
|
|
20131
20145
|
});
|
|
@@ -20164,7 +20178,7 @@ var fetchSearchResults = async ({
|
|
|
20164
20178
|
const searchParams = new URLSearchParams;
|
|
20165
20179
|
const searchQuery = [query];
|
|
20166
20180
|
site && searchQuery.push(`site:${site}`);
|
|
20167
|
-
fileType && searchQuery.push(`
|
|
20181
|
+
fileType && searchQuery.push(`filetype:${fileType}`);
|
|
20168
20182
|
language && searchQuery.push(`lang:${language}`);
|
|
20169
20183
|
if (exactTerms && excludeTerms) {
|
|
20170
20184
|
throw new Error("Cannot specify both exactTerms and excludeTerms - please use only one");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youdotcom-oss/mcp",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.8",
|
|
4
4
|
"description": "You.com API Model Context Protocol Server",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"test:coverage:watch": "bun test --coverage --watch",
|
|
60
60
|
"test:watch": "bun test --watch"
|
|
61
61
|
},
|
|
62
|
+
"mcpName": "io.github.youdotcom-oss/mcp",
|
|
62
63
|
"dependencies": {
|
|
63
64
|
"zod": "^4.1.13",
|
|
64
65
|
"@hono/mcp": "^0.2.0",
|
|
@@ -2,8 +2,8 @@ import * as z from 'zod';
|
|
|
2
2
|
|
|
3
3
|
export const SearchQuerySchema = z.object({
|
|
4
4
|
query: z.string().min(1, 'Query is required').describe('Search query (supports +, -, site:, filetype:, lang:)'),
|
|
5
|
-
count: z.number().int().min(1).max(
|
|
6
|
-
freshness: z.
|
|
5
|
+
count: z.number().int().min(1).max(100).optional().describe('Max results per section'),
|
|
6
|
+
freshness: z.string().optional().describe('day/week/month/year or YYYY-MM-DDtoYYYY-MM-DD'),
|
|
7
7
|
offset: z.number().int().min(0).max(9).optional().describe('Pagination offset'),
|
|
8
8
|
country: z
|
|
9
9
|
.enum([
|
|
@@ -52,6 +52,8 @@ export const SearchQuerySchema = z.object({
|
|
|
52
52
|
language: z.string().optional().describe('ISO 639-1 language code'),
|
|
53
53
|
excludeTerms: z.string().optional().describe('Terms to exclude (pipe-separated)'),
|
|
54
54
|
exactTerms: z.string().optional().describe('Exact terms (pipe-separated)'),
|
|
55
|
+
livecrawl: z.enum(['web', 'news', 'all']).optional().describe('Live-crawl sections for full content'),
|
|
56
|
+
livecrawl_formats: z.enum(['html', 'markdown']).optional().describe('Format for crawled content'),
|
|
55
57
|
});
|
|
56
58
|
|
|
57
59
|
export type SearchQuery = z.infer<typeof SearchQuerySchema>;
|
|
@@ -63,6 +65,15 @@ const WebResultSchema = z.object({
|
|
|
63
65
|
snippets: z.array(z.string()).describe('Content snippets'),
|
|
64
66
|
page_age: z.string().optional().describe('Publication timestamp'),
|
|
65
67
|
authors: z.array(z.string()).optional().describe('Authors'),
|
|
68
|
+
thumbnail_url: z.string().optional().describe('Thumbnail image URL'),
|
|
69
|
+
favicon_url: z.string().optional().describe('Favicon URL'),
|
|
70
|
+
contents: z
|
|
71
|
+
.object({
|
|
72
|
+
html: z.string().optional().describe('Full HTML content'),
|
|
73
|
+
markdown: z.string().optional().describe('Full Markdown content'),
|
|
74
|
+
})
|
|
75
|
+
.optional()
|
|
76
|
+
.describe('Live-crawled page content'),
|
|
66
77
|
});
|
|
67
78
|
|
|
68
79
|
const NewsResultSchema = z.object({
|
|
@@ -70,12 +81,20 @@ const NewsResultSchema = z.object({
|
|
|
70
81
|
description: z.string().describe('Description'),
|
|
71
82
|
page_age: z.string().describe('Publication timestamp'),
|
|
72
83
|
url: z.string().describe('URL'),
|
|
84
|
+
thumbnail_url: z.string().optional().describe('Thumbnail image URL'),
|
|
85
|
+
contents: z
|
|
86
|
+
.object({
|
|
87
|
+
html: z.string().optional().describe('Full HTML content'),
|
|
88
|
+
markdown: z.string().optional().describe('Full Markdown content'),
|
|
89
|
+
})
|
|
90
|
+
.optional()
|
|
91
|
+
.describe('Live-crawled page content'),
|
|
73
92
|
});
|
|
74
93
|
|
|
75
94
|
export type NewsResult = z.infer<typeof NewsResultSchema>;
|
|
76
95
|
|
|
77
96
|
const MetadataSchema = z.object({
|
|
78
|
-
|
|
97
|
+
search_uuid: z.string().optional().describe('Unique search request ID'),
|
|
79
98
|
query: z.string().describe('Query'),
|
|
80
99
|
latency: z.number().describe('Latency in seconds'),
|
|
81
100
|
});
|
|
@@ -91,7 +110,7 @@ export const SearchResponseSchema = z.object({
|
|
|
91
110
|
export type SearchResponse = z.infer<typeof SearchResponseSchema>;
|
|
92
111
|
|
|
93
112
|
// Minimal schema for structuredContent (reduces payload duplication)
|
|
94
|
-
// Excludes metadata (query,
|
|
113
|
+
// Excludes metadata (query, search_uuid, latency) as these are not actionable by LLM
|
|
95
114
|
export const SearchStructuredContentSchema = z.object({
|
|
96
115
|
resultCounts: z.object({
|
|
97
116
|
web: z.number().describe('Web results'),
|
|
@@ -18,7 +18,7 @@ export const fetchSearchResults = async ({
|
|
|
18
18
|
// Build Query Param
|
|
19
19
|
const searchQuery = [query];
|
|
20
20
|
site && searchQuery.push(`site:${site}`);
|
|
21
|
-
fileType && searchQuery.push(`
|
|
21
|
+
fileType && searchQuery.push(`filetype:${fileType}`);
|
|
22
22
|
language && searchQuery.push(`lang:${language}`);
|
|
23
23
|
if (exactTerms && excludeTerms) {
|
|
24
24
|
throw new Error('Cannot specify both exactTerms and excludeTerms - please use only one');
|
|
@@ -22,8 +22,8 @@ describe('fetchSearchResults', () => {
|
|
|
22
22
|
expect(typeof result.metadata?.query).toBe('string');
|
|
23
23
|
|
|
24
24
|
// Optional fields: only assert type if present
|
|
25
|
-
if (result.metadata?.
|
|
26
|
-
expect(typeof result.metadata.
|
|
25
|
+
if (result.metadata?.search_uuid !== undefined) {
|
|
26
|
+
expect(typeof result.metadata.search_uuid).toBe('string');
|
|
27
27
|
}
|
|
28
28
|
});
|
|
29
29
|
|
|
@@ -66,6 +66,52 @@ describe('fetchSearchResults', () => {
|
|
|
66
66
|
expect(newsResult).toHaveProperty('description');
|
|
67
67
|
expect(newsResult).toHaveProperty('page_age');
|
|
68
68
|
});
|
|
69
|
+
|
|
70
|
+
test('handles livecrawl parameters', async () => {
|
|
71
|
+
const result = await fetchSearchResults({
|
|
72
|
+
searchQuery: {
|
|
73
|
+
query: 'python tutorial',
|
|
74
|
+
count: 2,
|
|
75
|
+
livecrawl: 'web',
|
|
76
|
+
livecrawl_formats: 'markdown',
|
|
77
|
+
},
|
|
78
|
+
getUserAgent,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
expect(result.results.web?.length).toBeLessThanOrEqual(2);
|
|
82
|
+
// If livecrawl worked, results should have contents field
|
|
83
|
+
if (result.results.web?.[0]?.contents) {
|
|
84
|
+
expect(result.results.web[0].contents).toHaveProperty('markdown');
|
|
85
|
+
expect(typeof result.results.web[0].contents.markdown).toBe('string');
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('handles freshness date ranges', async () => {
|
|
90
|
+
const result = await fetchSearchResults({
|
|
91
|
+
searchQuery: {
|
|
92
|
+
query: 'AI news',
|
|
93
|
+
freshness: '2024-01-01to2024-12-31',
|
|
94
|
+
count: 3,
|
|
95
|
+
},
|
|
96
|
+
getUserAgent,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(result).toHaveProperty('results');
|
|
100
|
+
expect(result.metadata?.query).toContain('AI news');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('handles count greater than 20', async () => {
|
|
104
|
+
const result = await fetchSearchResults({
|
|
105
|
+
searchQuery: {
|
|
106
|
+
query: 'machine learning',
|
|
107
|
+
count: 50,
|
|
108
|
+
},
|
|
109
|
+
getUserAgent,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(result.results.web?.length).toBeGreaterThan(0);
|
|
113
|
+
expect(result.results.web?.length).toBeLessThanOrEqual(50);
|
|
114
|
+
});
|
|
69
115
|
});
|
|
70
116
|
|
|
71
117
|
describe('formatSearchResults', () => {
|
|
@@ -85,7 +131,7 @@ describe('formatSearchResults', () => {
|
|
|
85
131
|
news: [],
|
|
86
132
|
},
|
|
87
133
|
metadata: {
|
|
88
|
-
|
|
134
|
+
search_uuid: 'test-uuid',
|
|
89
135
|
query: 'test query',
|
|
90
136
|
latency: 0.1,
|
|
91
137
|
},
|
|
@@ -132,7 +178,7 @@ describe('formatSearchResults', () => {
|
|
|
132
178
|
],
|
|
133
179
|
},
|
|
134
180
|
metadata: {
|
|
135
|
-
|
|
181
|
+
search_uuid: 'test-uuid',
|
|
136
182
|
query: 'test query',
|
|
137
183
|
latency: 0.1,
|
|
138
184
|
},
|
|
@@ -182,7 +228,7 @@ describe('formatSearchResults', () => {
|
|
|
182
228
|
],
|
|
183
229
|
},
|
|
184
230
|
metadata: {
|
|
185
|
-
|
|
231
|
+
search_uuid: 'test-uuid',
|
|
186
232
|
query: 'test query',
|
|
187
233
|
latency: 0.1,
|
|
188
234
|
},
|