@youdotcom-oss/mcp 2.0.8 → 2.1.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/README.md +16 -4
- package/bin/stdio.js +204 -32
- package/package.json +2 -2
- package/src/http.ts +2 -0
- package/src/research/register-research-tool.ts +69 -0
- package/src/research/research.schemas.ts +19 -0
- package/src/research/research.utils.ts +30 -0
- package/src/stdio.ts +2 -0
- package/src/tests/tool.spec.ts +126 -0
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ The You.com MCP Server gives your AI agents **real-time access to the latest web
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Web and news search**: Comprehensive search using You.com's unified Search API with advanced search operators
|
|
8
|
+
- **Research**: Comprehensive answers with cited sources, configurable effort (lite to exhaustive)
|
|
8
9
|
- **Content extraction**: Extract and retrieve full content from web pages in markdown or HTML format
|
|
9
10
|
- **Multiple transport protocols**: STDIO and Streamable HTTP support
|
|
10
11
|
- **Bearer Token Authentication**: Secure API access in HTTP mode
|
|
@@ -180,13 +181,18 @@ For setup, follow the MCP installation [guide](https://zed.dev/docs/ai/mcp#as-cu
|
|
|
180
181
|
|
|
181
182
|
## Available tools
|
|
182
183
|
|
|
183
|
-
This MCP server provides
|
|
184
|
+
This MCP server provides three tools that work seamlessly with your AI agent through natural language:
|
|
184
185
|
|
|
185
186
|
### you-search
|
|
186
187
|
Comprehensive web and news search with advanced filtering capabilities. Perfect for finding current information, research articles, documentation, and news stories.
|
|
187
188
|
|
|
188
189
|
**When to use**: When you need to search the web for information, filter by specific sites/file types, or get the latest news on a topic.
|
|
189
190
|
|
|
191
|
+
### you-research
|
|
192
|
+
Research with comprehensive answers and cited sources. Configurable effort levels (lite, standard, deep, exhaustive) let you trade speed for thoroughness.
|
|
193
|
+
|
|
194
|
+
**When to use**: When you need in-depth answers to complex questions, research reports with citations, or thorough analysis that goes beyond simple search results.
|
|
195
|
+
|
|
190
196
|
### you-contents
|
|
191
197
|
Extract full page content from URLs in markdown, HTML, or structured metadata formats. Useful for documentation analysis, content processing, SEO data extraction, and batch URL processing.
|
|
192
198
|
|
|
@@ -208,6 +214,12 @@ Here are common scenarios showing when and how to use each tool with natural lan
|
|
|
208
214
|
- "Get the latest news about renewable energy from the past week"
|
|
209
215
|
- "Find PDF files about machine learning algorithms"
|
|
210
216
|
|
|
217
|
+
**Use you-research when:**
|
|
218
|
+
- "Give me a comprehensive analysis of the current state of AI regulation"
|
|
219
|
+
- "Research the pros and cons of WebAssembly vs JavaScript for performance-critical applications"
|
|
220
|
+
- "What are the latest breakthroughs in quantum computing? Include sources."
|
|
221
|
+
- "Provide a detailed comparison of React, Vue, and Svelte with citations"
|
|
222
|
+
|
|
211
223
|
### Content extraction & analysis
|
|
212
224
|
|
|
213
225
|
**Use you-contents when:**
|
|
@@ -221,9 +233,9 @@ Here are common scenarios showing when and how to use each tool with natural lan
|
|
|
221
233
|
### Combined workflows
|
|
222
234
|
|
|
223
235
|
Your AI agent can combine multiple tools in a single conversation:
|
|
224
|
-
1. **
|
|
225
|
-
2. **
|
|
226
|
-
3. **News +
|
|
236
|
+
1. **Search + Extract**: "Search for the best TypeScript tutorials, then extract the content from the top 3 results"
|
|
237
|
+
2. **Research + Deep Dive**: "Research WebAssembly performance benefits, then extract code samples from the cited sources"
|
|
238
|
+
3. **News + Research**: "Find recent articles about AI regulation, then research the policy implications"
|
|
227
239
|
|
|
228
240
|
### Pro tips
|
|
229
241
|
|
package/bin/stdio.js
CHANGED
|
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
7
12
|
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
8
20
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
21
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
22
|
for (let key of __getOwnPropNames(mod))
|
|
11
23
|
if (!__hasOwnProp.call(to, key))
|
|
12
24
|
__defProp(to, key, {
|
|
13
|
-
get: (
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
14
26
|
enumerable: true
|
|
15
27
|
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
16
30
|
return to;
|
|
17
31
|
};
|
|
18
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __returnValue = (v) => v;
|
|
34
|
+
function __exportSetter(name, newValue) {
|
|
35
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
36
|
+
}
|
|
19
37
|
var __export = (target, all) => {
|
|
20
38
|
for (var name in all)
|
|
21
39
|
__defProp(target, name, {
|
|
22
40
|
get: all[name],
|
|
23
41
|
enumerable: true,
|
|
24
42
|
configurable: true,
|
|
25
|
-
set: (
|
|
43
|
+
set: __exportSetter.bind(all, name)
|
|
26
44
|
});
|
|
27
45
|
};
|
|
28
46
|
|
|
@@ -6498,7 +6516,7 @@ var require_dist = __commonJS((exports, module) => {
|
|
|
6498
6516
|
exports.default = formatsPlugin;
|
|
6499
6517
|
});
|
|
6500
6518
|
|
|
6501
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
6519
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
6502
6520
|
import process3 from "node:process";
|
|
6503
6521
|
|
|
6504
6522
|
// ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
@@ -20034,7 +20052,7 @@ function date4(params) {
|
|
|
20034
20052
|
// ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
20035
20053
|
config(en_default());
|
|
20036
20054
|
|
|
20037
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
20055
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
|
|
20038
20056
|
var LATEST_PROTOCOL_VERSION = "2025-11-25";
|
|
20039
20057
|
var SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05", "2024-10-07"];
|
|
20040
20058
|
var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
|
|
@@ -20876,7 +20894,7 @@ class UrlElicitationRequiredError extends McpError {
|
|
|
20876
20894
|
}
|
|
20877
20895
|
}
|
|
20878
20896
|
|
|
20879
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
20897
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
|
|
20880
20898
|
class ReadBuffer {
|
|
20881
20899
|
append(chunk) {
|
|
20882
20900
|
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
|
|
@@ -20906,7 +20924,7 @@ function serializeMessage(message) {
|
|
|
20906
20924
|
`;
|
|
20907
20925
|
}
|
|
20908
20926
|
|
|
20909
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
20927
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
20910
20928
|
class StdioServerTransport {
|
|
20911
20929
|
constructor(_stdin = process3.stdin, _stdout = process3.stdout) {
|
|
20912
20930
|
this._stdin = _stdin;
|
|
@@ -20990,7 +21008,7 @@ var ContentsItemSchema = object({
|
|
|
20990
21008
|
var ContentsApiResponseSchema = array(ContentsItemSchema);
|
|
20991
21009
|
// ../api/src/shared/api.constants.ts
|
|
20992
21010
|
var SEARCH_API_URL = process.env.YDC_SEARCH_API_URL || "https://api.you.com/v1/agents/search";
|
|
20993
|
-
var
|
|
21011
|
+
var RESEARCH_API_URL = process.env.YDC_RESEARCH_API_URL || "https://api.you.com/v1/research";
|
|
20994
21012
|
var CONTENTS_API_URL = process.env.YDC_CONTENTS_API_URL || "https://ydc-index.io/v1/contents";
|
|
20995
21013
|
|
|
20996
21014
|
// ../api/src/shared/check-response-for-errors.ts
|
|
@@ -21057,21 +21075,89 @@ var fetchContents = async ({
|
|
|
21057
21075
|
const parsedResults = ContentsApiResponseSchema.parse(results);
|
|
21058
21076
|
return parsedResults;
|
|
21059
21077
|
};
|
|
21060
|
-
// ../api/src/
|
|
21061
|
-
var
|
|
21062
|
-
var
|
|
21063
|
-
|
|
21064
|
-
|
|
21078
|
+
// ../api/src/research/research.schemas.ts
|
|
21079
|
+
var ResearchEffortSchema = _enum2(["lite", "standard", "deep", "exhaustive"]).describe("Controls how much time and effort the Research API spends on your question. Higher effort levels run more searches and dig deeper into sources, at the cost of a longer response time.");
|
|
21080
|
+
var ResearchQuerySchema = object({
|
|
21081
|
+
input: string2().min(1, "Input is required").describe("The research question or complex query requiring in-depth investigation and multi-step reasoning. Maximum length: 40,000 characters."),
|
|
21082
|
+
research_effort: ResearchEffortSchema.optional().default("standard").describe("Controls how much time and effort the Research API spends on your question. lite: fast answers, standard: balanced (default), deep: thorough, exhaustive: most comprehensive.")
|
|
21065
21083
|
});
|
|
21066
|
-
var
|
|
21084
|
+
var ResearchSourceSchema = object({
|
|
21067
21085
|
url: string2().describe("Source webpage URL"),
|
|
21068
|
-
title: string2().describe("Source webpage title"),
|
|
21086
|
+
title: string2().optional().describe("Source webpage title"),
|
|
21069
21087
|
snippets: array(string2()).describe("Relevant excerpts from the source page used in generating the answer")
|
|
21070
21088
|
});
|
|
21071
|
-
var
|
|
21072
|
-
|
|
21073
|
-
|
|
21089
|
+
var ResearchOutputSchema = object({
|
|
21090
|
+
content: string2().describe("Comprehensive response with inline citations, formatted in Markdown"),
|
|
21091
|
+
content_type: _enum2(["text"]).describe("The format of the content field"),
|
|
21092
|
+
sources: array(ResearchSourceSchema).describe("List of web sources used to generate the answer")
|
|
21074
21093
|
});
|
|
21094
|
+
var ResearchResponseSchema = object({
|
|
21095
|
+
output: ResearchOutputSchema.describe("The research output containing the answer and sources")
|
|
21096
|
+
});
|
|
21097
|
+
// ../api/src/research/research.utils.ts
|
|
21098
|
+
var callResearch = async ({
|
|
21099
|
+
researchQuery,
|
|
21100
|
+
YDC_API_KEY = process.env.YDC_API_KEY,
|
|
21101
|
+
getUserAgent
|
|
21102
|
+
}) => {
|
|
21103
|
+
if (!YDC_API_KEY) {
|
|
21104
|
+
throw new Error("YDC_API_KEY is required for Research API");
|
|
21105
|
+
}
|
|
21106
|
+
const response = await fetch(RESEARCH_API_URL, {
|
|
21107
|
+
method: "POST",
|
|
21108
|
+
headers: new Headers({
|
|
21109
|
+
"X-API-Key": YDC_API_KEY,
|
|
21110
|
+
"Content-Type": "application/json",
|
|
21111
|
+
"User-Agent": getUserAgent()
|
|
21112
|
+
}),
|
|
21113
|
+
body: JSON.stringify(researchQuery)
|
|
21114
|
+
});
|
|
21115
|
+
if (!response.ok) {
|
|
21116
|
+
const errorCode = response.status;
|
|
21117
|
+
if (errorCode === 429) {
|
|
21118
|
+
throw new Error("Rate limited by You.com API. Please try again later.");
|
|
21119
|
+
} else if (errorCode === 403) {
|
|
21120
|
+
throw new Error("Forbidden. Please check your You.com API key.");
|
|
21121
|
+
} else if (errorCode === 402) {
|
|
21122
|
+
throw new Error("Free tier limit exceeded. Please upgrade at: https://you.com/platform");
|
|
21123
|
+
}
|
|
21124
|
+
throw new Error(`Research API request failed. Error code: ${errorCode}`);
|
|
21125
|
+
}
|
|
21126
|
+
const data = await response.json();
|
|
21127
|
+
checkResponseForErrors(data);
|
|
21128
|
+
return ResearchResponseSchema.parse(data);
|
|
21129
|
+
};
|
|
21130
|
+
var formatResearchResponse = (response) => {
|
|
21131
|
+
const parts = [];
|
|
21132
|
+
parts.push(`# Answer
|
|
21133
|
+
`);
|
|
21134
|
+
parts.push(response.output.content);
|
|
21135
|
+
parts.push(`
|
|
21136
|
+
`);
|
|
21137
|
+
if (response.output.sources.length > 0) {
|
|
21138
|
+
parts.push(`
|
|
21139
|
+
## Sources
|
|
21140
|
+
`);
|
|
21141
|
+
for (const [index, source] of response.output.sources.entries()) {
|
|
21142
|
+
parts.push(`
|
|
21143
|
+
### ${index + 1}. ${source.title ?? source.url}
|
|
21144
|
+
`);
|
|
21145
|
+
parts.push(`**URL:** ${source.url}
|
|
21146
|
+
`);
|
|
21147
|
+
if (source.snippets.length > 0) {
|
|
21148
|
+
parts.push(`
|
|
21149
|
+
**Key Excerpts:**
|
|
21150
|
+
`);
|
|
21151
|
+
for (const snippet of source.snippets) {
|
|
21152
|
+
parts.push(`> ${snippet}
|
|
21153
|
+
`);
|
|
21154
|
+
}
|
|
21155
|
+
}
|
|
21156
|
+
}
|
|
21157
|
+
}
|
|
21158
|
+
return parts.join(`
|
|
21159
|
+
`);
|
|
21160
|
+
};
|
|
21075
21161
|
// ../api/src/search/search.schemas.ts
|
|
21076
21162
|
var LanguageSchema = _enum2([
|
|
21077
21163
|
"AR",
|
|
@@ -21157,7 +21243,6 @@ var SearchQuerySchema = object({
|
|
|
21157
21243
|
"CN",
|
|
21158
21244
|
"PL",
|
|
21159
21245
|
"PT",
|
|
21160
|
-
"PT-BR",
|
|
21161
21246
|
"PH",
|
|
21162
21247
|
"RU",
|
|
21163
21248
|
"SA",
|
|
@@ -25315,7 +25400,7 @@ function object2(shape, params) {
|
|
|
25315
25400
|
};
|
|
25316
25401
|
return new ZodMiniObject(def);
|
|
25317
25402
|
}
|
|
25318
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
25403
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
25319
25404
|
function isZ4Schema(s) {
|
|
25320
25405
|
const schema = s;
|
|
25321
25406
|
return !!schema._zod;
|
|
@@ -25459,7 +25544,7 @@ function getLiteralValue(schema) {
|
|
|
25459
25544
|
return;
|
|
25460
25545
|
}
|
|
25461
25546
|
|
|
25462
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
25547
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
|
|
25463
25548
|
function isTerminal(status) {
|
|
25464
25549
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
25465
25550
|
}
|
|
@@ -26700,7 +26785,7 @@ var zodToJsonSchema = (schema, options) => {
|
|
|
26700
26785
|
}
|
|
26701
26786
|
return combined;
|
|
26702
26787
|
};
|
|
26703
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
26788
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
|
|
26704
26789
|
function mapMiniTarget(t) {
|
|
26705
26790
|
if (!t)
|
|
26706
26791
|
return "draft-7";
|
|
@@ -26742,7 +26827,7 @@ function parseWithCompat(schema, data) {
|
|
|
26742
26827
|
return result.data;
|
|
26743
26828
|
}
|
|
26744
26829
|
|
|
26745
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
26830
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
|
|
26746
26831
|
var DEFAULT_REQUEST_TIMEOUT_MSEC = 60000;
|
|
26747
26832
|
|
|
26748
26833
|
class Protocol {
|
|
@@ -27577,7 +27662,7 @@ function mergeCapabilities(base, additional) {
|
|
|
27577
27662
|
return result;
|
|
27578
27663
|
}
|
|
27579
27664
|
|
|
27580
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
27665
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
|
|
27581
27666
|
var import_ajv = __toESM(require_ajv(), 1);
|
|
27582
27667
|
var import_ajv_formats = __toESM(require_dist(), 1);
|
|
27583
27668
|
function createDefaultAjvInstance() {
|
|
@@ -27617,7 +27702,7 @@ class AjvJsonSchemaValidator {
|
|
|
27617
27702
|
}
|
|
27618
27703
|
}
|
|
27619
27704
|
|
|
27620
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
27705
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
|
|
27621
27706
|
class ExperimentalServerTasks {
|
|
27622
27707
|
constructor(_server) {
|
|
27623
27708
|
this._server = _server;
|
|
@@ -27695,7 +27780,7 @@ class ExperimentalServerTasks {
|
|
|
27695
27780
|
}
|
|
27696
27781
|
}
|
|
27697
27782
|
|
|
27698
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
27783
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
|
|
27699
27784
|
function assertToolsCallTaskCapability(requests, method, entityName) {
|
|
27700
27785
|
if (!requests) {
|
|
27701
27786
|
throw new Error(`${entityName} does not support task creation (required for ${method})`);
|
|
@@ -27730,7 +27815,7 @@ function assertClientRequestTaskCapability(requests, method, entityName) {
|
|
|
27730
27815
|
}
|
|
27731
27816
|
}
|
|
27732
27817
|
|
|
27733
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
27818
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
|
|
27734
27819
|
class Server extends Protocol {
|
|
27735
27820
|
constructor(_serverInfo, options) {
|
|
27736
27821
|
super(options);
|
|
@@ -28063,7 +28148,7 @@ class Server extends Protocol {
|
|
|
28063
28148
|
}
|
|
28064
28149
|
}
|
|
28065
28150
|
|
|
28066
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
28151
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
|
|
28067
28152
|
var COMPLETABLE_SYMBOL = Symbol.for("mcp.completable");
|
|
28068
28153
|
function isCompletable(schema) {
|
|
28069
28154
|
return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
|
|
@@ -28077,7 +28162,7 @@ var McpZodTypeKind;
|
|
|
28077
28162
|
McpZodTypeKind2["Completable"] = "McpCompletable";
|
|
28078
28163
|
})(McpZodTypeKind || (McpZodTypeKind = {}));
|
|
28079
28164
|
|
|
28080
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
28165
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
|
|
28081
28166
|
var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
|
|
28082
28167
|
function validateToolName(name) {
|
|
28083
28168
|
const warnings = [];
|
|
@@ -28135,7 +28220,7 @@ function validateAndWarnToolName(name) {
|
|
|
28135
28220
|
return result.isValid;
|
|
28136
28221
|
}
|
|
28137
28222
|
|
|
28138
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
28223
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
|
|
28139
28224
|
class ExperimentalMcpServerTasks {
|
|
28140
28225
|
constructor(_mcpServer) {
|
|
28141
28226
|
this._mcpServer = _mcpServer;
|
|
@@ -28150,7 +28235,7 @@ class ExperimentalMcpServerTasks {
|
|
|
28150
28235
|
}
|
|
28151
28236
|
}
|
|
28152
28237
|
|
|
28153
|
-
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.
|
|
28238
|
+
// ../../node_modules/.bun/@modelcontextprotocol+sdk@1.27.1+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
|
|
28154
28239
|
class McpServer {
|
|
28155
28240
|
constructor(serverInfo, options) {
|
|
28156
28241
|
this._registeredResources = {};
|
|
@@ -28872,7 +28957,7 @@ var EMPTY_COMPLETION_RESULT = {
|
|
|
28872
28957
|
// package.json
|
|
28873
28958
|
var package_default = {
|
|
28874
28959
|
name: "@youdotcom-oss/mcp",
|
|
28875
|
-
version: "2.0
|
|
28960
|
+
version: "2.1.0",
|
|
28876
28961
|
description: "You.com API Model Context Protocol Server - For programmatic API access, use @youdotcom-oss/api",
|
|
28877
28962
|
license: "MIT",
|
|
28878
28963
|
engines: {
|
|
@@ -28928,7 +29013,7 @@ var package_default = {
|
|
|
28928
29013
|
},
|
|
28929
29014
|
mcpName: "io.github.youdotcom-oss/mcp",
|
|
28930
29015
|
dependencies: {
|
|
28931
|
-
"@youdotcom-oss/api": "0.
|
|
29016
|
+
"@youdotcom-oss/api": "0.4.0",
|
|
28932
29017
|
zod: "^4.3.6",
|
|
28933
29018
|
"@hono/mcp": "^0.2.3",
|
|
28934
29019
|
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
@@ -28951,6 +29036,92 @@ var getMCpServer = () => new McpServer({
|
|
|
28951
29036
|
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.`
|
|
28952
29037
|
});
|
|
28953
29038
|
|
|
29039
|
+
// src/research/research.schemas.ts
|
|
29040
|
+
var ResearchStructuredContentSchema = object({
|
|
29041
|
+
content_type: string2().describe("Format of the content field"),
|
|
29042
|
+
sourceCount: number2().describe("Number of sources used"),
|
|
29043
|
+
sources: array(object({
|
|
29044
|
+
url: string2().describe("Source URL"),
|
|
29045
|
+
title: string2().optional().describe("Source title"),
|
|
29046
|
+
snippetCount: number2().describe("Number of excerpts from this source")
|
|
29047
|
+
})).describe("Sources used in the research answer")
|
|
29048
|
+
});
|
|
29049
|
+
|
|
29050
|
+
// src/research/research.utils.ts
|
|
29051
|
+
var formatResearchResults = (response) => {
|
|
29052
|
+
const text = formatResearchResponse(response);
|
|
29053
|
+
return {
|
|
29054
|
+
content: [
|
|
29055
|
+
{
|
|
29056
|
+
type: "text",
|
|
29057
|
+
text
|
|
29058
|
+
}
|
|
29059
|
+
],
|
|
29060
|
+
structuredContent: {
|
|
29061
|
+
content_type: response.output.content_type,
|
|
29062
|
+
sourceCount: response.output.sources.length,
|
|
29063
|
+
sources: response.output.sources.map((source) => ({
|
|
29064
|
+
url: source.url,
|
|
29065
|
+
title: source.title,
|
|
29066
|
+
snippetCount: source.snippets.length
|
|
29067
|
+
}))
|
|
29068
|
+
}
|
|
29069
|
+
};
|
|
29070
|
+
};
|
|
29071
|
+
|
|
29072
|
+
// src/research/register-research-tool.ts
|
|
29073
|
+
var registerResearchTool = ({
|
|
29074
|
+
mcp,
|
|
29075
|
+
YDC_API_KEY,
|
|
29076
|
+
getUserAgent
|
|
29077
|
+
}) => {
|
|
29078
|
+
mcp.registerTool("you-research", {
|
|
29079
|
+
title: "Research",
|
|
29080
|
+
description: "Research a topic with comprehensive answers and cited sources. Configurable effort levels (lite, standard, deep, exhaustive).",
|
|
29081
|
+
inputSchema: ResearchQuerySchema.shape,
|
|
29082
|
+
outputSchema: ResearchStructuredContentSchema.shape
|
|
29083
|
+
}, async (researchQuery) => {
|
|
29084
|
+
const logger = getLogger(mcp);
|
|
29085
|
+
try {
|
|
29086
|
+
const response = await callResearch({
|
|
29087
|
+
researchQuery,
|
|
29088
|
+
YDC_API_KEY,
|
|
29089
|
+
getUserAgent
|
|
29090
|
+
});
|
|
29091
|
+
const sourceCount = response.output.sources.length;
|
|
29092
|
+
await logger({
|
|
29093
|
+
level: "info",
|
|
29094
|
+
data: `Research successful for input: "${researchQuery.input.substring(0, 100)}" - ${sourceCount} source(s)`
|
|
29095
|
+
});
|
|
29096
|
+
const { content, structuredContent } = formatResearchResults(response);
|
|
29097
|
+
return { content, structuredContent };
|
|
29098
|
+
} catch (err) {
|
|
29099
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
29100
|
+
const reportLink = generateErrorReportLink({
|
|
29101
|
+
errorMessage,
|
|
29102
|
+
tool: "you-research",
|
|
29103
|
+
clientInfo: getUserAgent()
|
|
29104
|
+
});
|
|
29105
|
+
await logger({
|
|
29106
|
+
level: "error",
|
|
29107
|
+
data: `Research API call failed: ${errorMessage}
|
|
29108
|
+
|
|
29109
|
+
Report this issue: ${reportLink}`
|
|
29110
|
+
});
|
|
29111
|
+
return {
|
|
29112
|
+
content: [
|
|
29113
|
+
{
|
|
29114
|
+
type: "text",
|
|
29115
|
+
text: `Error: ${errorMessage}`
|
|
29116
|
+
}
|
|
29117
|
+
],
|
|
29118
|
+
structuredContent: undefined,
|
|
29119
|
+
isError: true
|
|
29120
|
+
};
|
|
29121
|
+
}
|
|
29122
|
+
});
|
|
29123
|
+
};
|
|
29124
|
+
|
|
28954
29125
|
// src/search/search.schema.ts
|
|
28955
29126
|
var SearchStructuredContentSchema = object({
|
|
28956
29127
|
resultCounts: object({
|
|
@@ -29154,6 +29325,7 @@ try {
|
|
|
29154
29325
|
const getUserAgent = useGetClientVersion(mcp);
|
|
29155
29326
|
registerSearchTool({ mcp, YDC_API_KEY, getUserAgent });
|
|
29156
29327
|
registerContentsTool({ mcp, YDC_API_KEY, getUserAgent });
|
|
29328
|
+
registerResearchTool({ mcp, YDC_API_KEY, getUserAgent });
|
|
29157
29329
|
const transport = new StdioServerTransport;
|
|
29158
29330
|
await mcp.connect(transport);
|
|
29159
29331
|
} catch (error48) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youdotcom-oss/mcp",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "You.com API Model Context Protocol Server - For programmatic API access, use @youdotcom-oss/api",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"mcpName": "io.github.youdotcom-oss/mcp",
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@youdotcom-oss/api": "0.
|
|
59
|
+
"@youdotcom-oss/api": "0.4.0",
|
|
60
60
|
"zod": "^4.3.6",
|
|
61
61
|
"@hono/mcp": "^0.2.3",
|
|
62
62
|
"@modelcontextprotocol/sdk": "^1.25.3",
|
package/src/http.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { trimTrailingSlash } from 'hono/trailing-slash'
|
|
|
4
4
|
import packageJson from '../package.json' with { type: 'json' }
|
|
5
5
|
import { registerContentsTool } from './contents/register-contents-tool.ts'
|
|
6
6
|
import { getMCpServer } from './get-mcp-server.ts'
|
|
7
|
+
import { registerResearchTool } from './research/register-research-tool.ts'
|
|
7
8
|
import { registerSearchTool } from './search/register-search-tool.ts'
|
|
8
9
|
import { useGetClientVersion } from './shared/use-client-version.ts'
|
|
9
10
|
|
|
@@ -40,6 +41,7 @@ const handleMcpRequest = async (c: Context) => {
|
|
|
40
41
|
getUserAgent,
|
|
41
42
|
})
|
|
42
43
|
registerContentsTool({ mcp, YDC_API_KEY, getUserAgent })
|
|
44
|
+
registerResearchTool({ mcp, YDC_API_KEY, getUserAgent })
|
|
43
45
|
|
|
44
46
|
const transport = new StreamableHTTPTransport()
|
|
45
47
|
await mcp.connect(transport)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
+
import { callResearch, generateErrorReportLink, ResearchQuerySchema } from '@youdotcom-oss/api'
|
|
3
|
+
import { getLogger } from '../shared/get-logger.ts'
|
|
4
|
+
import { ResearchStructuredContentSchema } from './research.schemas.ts'
|
|
5
|
+
import { formatResearchResults } from './research.utils.ts'
|
|
6
|
+
|
|
7
|
+
export const registerResearchTool = ({
|
|
8
|
+
mcp,
|
|
9
|
+
YDC_API_KEY,
|
|
10
|
+
getUserAgent,
|
|
11
|
+
}: {
|
|
12
|
+
mcp: McpServer
|
|
13
|
+
YDC_API_KEY?: string
|
|
14
|
+
getUserAgent: () => string
|
|
15
|
+
}) => {
|
|
16
|
+
mcp.registerTool(
|
|
17
|
+
'you-research',
|
|
18
|
+
{
|
|
19
|
+
title: 'Research',
|
|
20
|
+
description:
|
|
21
|
+
'Research a topic with comprehensive answers and cited sources. Configurable effort levels (lite, standard, deep, exhaustive).',
|
|
22
|
+
inputSchema: ResearchQuerySchema.shape,
|
|
23
|
+
outputSchema: ResearchStructuredContentSchema.shape,
|
|
24
|
+
},
|
|
25
|
+
async (researchQuery) => {
|
|
26
|
+
const logger = getLogger(mcp)
|
|
27
|
+
try {
|
|
28
|
+
const response = await callResearch({
|
|
29
|
+
researchQuery,
|
|
30
|
+
YDC_API_KEY,
|
|
31
|
+
getUserAgent,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const sourceCount = response.output.sources.length
|
|
35
|
+
|
|
36
|
+
await logger({
|
|
37
|
+
level: 'info',
|
|
38
|
+
data: `Research successful for input: "${researchQuery.input.substring(0, 100)}" - ${sourceCount} source(s)`,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const { content, structuredContent } = formatResearchResults(response)
|
|
42
|
+
return { content, structuredContent }
|
|
43
|
+
} catch (err: unknown) {
|
|
44
|
+
const errorMessage = err instanceof Error ? err.message : String(err)
|
|
45
|
+
const reportLink = generateErrorReportLink({
|
|
46
|
+
errorMessage,
|
|
47
|
+
tool: 'you-research',
|
|
48
|
+
clientInfo: getUserAgent(),
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
await logger({
|
|
52
|
+
level: 'error',
|
|
53
|
+
data: `Research API call failed: ${errorMessage}\n\nReport this issue: ${reportLink}`,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
content: [
|
|
58
|
+
{
|
|
59
|
+
type: 'text' as const,
|
|
60
|
+
text: `Error: ${errorMessage}`,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
structuredContent: undefined,
|
|
64
|
+
isError: true,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as z from 'zod'
|
|
2
|
+
|
|
3
|
+
// Minimal schema for structuredContent (reduces payload duplication)
|
|
4
|
+
// Full research content is in the text content field
|
|
5
|
+
export const ResearchStructuredContentSchema = z.object({
|
|
6
|
+
content_type: z.string().describe('Format of the content field'),
|
|
7
|
+
sourceCount: z.number().describe('Number of sources used'),
|
|
8
|
+
sources: z
|
|
9
|
+
.array(
|
|
10
|
+
z.object({
|
|
11
|
+
url: z.string().describe('Source URL'),
|
|
12
|
+
title: z.string().optional().describe('Source title'),
|
|
13
|
+
snippetCount: z.number().describe('Number of excerpts from this source'),
|
|
14
|
+
}),
|
|
15
|
+
)
|
|
16
|
+
.describe('Sources used in the research answer'),
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export type ResearchStructuredContent = z.infer<typeof ResearchStructuredContentSchema>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ResearchResponse } from '@youdotcom-oss/api'
|
|
2
|
+
import { formatResearchResponse } from '@youdotcom-oss/api'
|
|
3
|
+
import type { ResearchStructuredContent } from './research.schemas.ts'
|
|
4
|
+
|
|
5
|
+
export const formatResearchResults = (
|
|
6
|
+
response: ResearchResponse,
|
|
7
|
+
): {
|
|
8
|
+
content: Array<{ type: 'text'; text: string }>
|
|
9
|
+
structuredContent: ResearchStructuredContent
|
|
10
|
+
} => {
|
|
11
|
+
const text = formatResearchResponse(response)
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
content: [
|
|
15
|
+
{
|
|
16
|
+
type: 'text',
|
|
17
|
+
text,
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
structuredContent: {
|
|
21
|
+
content_type: response.output.content_type,
|
|
22
|
+
sourceCount: response.output.sources.length,
|
|
23
|
+
sources: response.output.sources.map((source) => ({
|
|
24
|
+
url: source.url,
|
|
25
|
+
title: source.title,
|
|
26
|
+
snippetCount: source.snippets.length,
|
|
27
|
+
})),
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/stdio.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
3
3
|
import { registerContentsTool } from './contents/register-contents-tool.ts'
|
|
4
4
|
import { getMCpServer } from './get-mcp-server.ts'
|
|
5
|
+
import { registerResearchTool } from './research/register-research-tool.ts'
|
|
5
6
|
import { registerSearchTool } from './search/register-search-tool.ts'
|
|
6
7
|
import { useGetClientVersion } from './shared/use-client-version.ts'
|
|
7
8
|
|
|
@@ -13,6 +14,7 @@ try {
|
|
|
13
14
|
|
|
14
15
|
registerSearchTool({ mcp, YDC_API_KEY, getUserAgent })
|
|
15
16
|
registerContentsTool({ mcp, YDC_API_KEY, getUserAgent })
|
|
17
|
+
registerResearchTool({ mcp, YDC_API_KEY, getUserAgent })
|
|
16
18
|
|
|
17
19
|
const transport = new StdioServerTransport()
|
|
18
20
|
await mcp.connect(transport)
|
package/src/tests/tool.spec.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
|
|
3
3
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
|
4
4
|
import { $ } from 'bun'
|
|
5
5
|
import type { ContentsStructuredContent } from '../contents/contents.schemas.ts'
|
|
6
|
+
import type { ResearchStructuredContent } from '../research/research.schemas.ts'
|
|
6
7
|
import type { SearchStructuredContent } from '../search/search.schema.ts'
|
|
7
8
|
|
|
8
9
|
let client: Client
|
|
@@ -521,3 +522,128 @@ describe('registerContentsTool', () => {
|
|
|
521
522
|
{ retry: 2 },
|
|
522
523
|
)
|
|
523
524
|
})
|
|
525
|
+
|
|
526
|
+
describe('registerResearchTool', () => {
|
|
527
|
+
test('tool is registered and available', async () => {
|
|
528
|
+
const tools = await client.listTools()
|
|
529
|
+
|
|
530
|
+
const researchTool = tools.tools.find((t) => t.name === 'you-research')
|
|
531
|
+
|
|
532
|
+
expect(researchTool).toBeDefined()
|
|
533
|
+
expect(researchTool?.title).toBe('Research')
|
|
534
|
+
expect(researchTool?.description).toContain('Research a topic')
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
test(
|
|
538
|
+
'performs basic research successfully',
|
|
539
|
+
async () => {
|
|
540
|
+
const result = await client.callTool({
|
|
541
|
+
name: 'you-research',
|
|
542
|
+
arguments: {
|
|
543
|
+
input: 'What is TypeScript?',
|
|
544
|
+
research_effort: 'lite',
|
|
545
|
+
},
|
|
546
|
+
})
|
|
547
|
+
|
|
548
|
+
expect(result).toHaveProperty('content')
|
|
549
|
+
expect(result).toHaveProperty('structuredContent')
|
|
550
|
+
|
|
551
|
+
const content = result.content as { type: string; text: string }[]
|
|
552
|
+
expect(Array.isArray(content)).toBe(true)
|
|
553
|
+
expect(content[0]).toHaveProperty('type', 'text')
|
|
554
|
+
expect(content[0]).toHaveProperty('text')
|
|
555
|
+
|
|
556
|
+
const text = content[0]?.text
|
|
557
|
+
expect(typeof text).toBe('string')
|
|
558
|
+
expect(text?.length).toBeGreaterThan(0)
|
|
559
|
+
},
|
|
560
|
+
{ retry: 2 },
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
test(
|
|
564
|
+
'returns structured content with source information',
|
|
565
|
+
async () => {
|
|
566
|
+
const result = await client.callTool({
|
|
567
|
+
name: 'you-research',
|
|
568
|
+
arguments: {
|
|
569
|
+
input: 'What are the benefits of REST APIs?',
|
|
570
|
+
research_effort: 'lite',
|
|
571
|
+
},
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
const structuredContent = result.structuredContent as ResearchStructuredContent
|
|
575
|
+
expect(structuredContent).toHaveProperty('content_type', 'text')
|
|
576
|
+
expect(structuredContent).toHaveProperty('sourceCount')
|
|
577
|
+
expect(typeof structuredContent.sourceCount).toBe('number')
|
|
578
|
+
expect(structuredContent.sourceCount).toBeGreaterThan(0)
|
|
579
|
+
|
|
580
|
+
expect(structuredContent).toHaveProperty('sources')
|
|
581
|
+
expect(Array.isArray(structuredContent.sources)).toBe(true)
|
|
582
|
+
expect(structuredContent.sources.length).toBeGreaterThan(0)
|
|
583
|
+
|
|
584
|
+
const source = structuredContent.sources[0]
|
|
585
|
+
expect(source).toBeDefined()
|
|
586
|
+
expect(source).toHaveProperty('url')
|
|
587
|
+
expect(typeof source?.url).toBe('string')
|
|
588
|
+
expect(source).toHaveProperty('snippetCount')
|
|
589
|
+
expect(typeof source?.snippetCount).toBe('number')
|
|
590
|
+
},
|
|
591
|
+
{ retry: 2 },
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
test(
|
|
595
|
+
'text content contains markdown with answer and sources',
|
|
596
|
+
async () => {
|
|
597
|
+
const result = await client.callTool({
|
|
598
|
+
name: 'you-research',
|
|
599
|
+
arguments: {
|
|
600
|
+
input: 'What is JWT authentication?',
|
|
601
|
+
research_effort: 'lite',
|
|
602
|
+
},
|
|
603
|
+
})
|
|
604
|
+
|
|
605
|
+
const content = result.content as { type: string; text: string }[]
|
|
606
|
+
const text = content[0]?.text
|
|
607
|
+
expect(text).toContain('# Answer')
|
|
608
|
+
expect(text).toContain('## Sources')
|
|
609
|
+
},
|
|
610
|
+
{ retry: 2 },
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
test(
|
|
614
|
+
'handles research_effort parameter',
|
|
615
|
+
async () => {
|
|
616
|
+
const transport = new StdioClientTransport({
|
|
617
|
+
command: 'npx',
|
|
618
|
+
args: [Bun.resolveSync('../../bin/stdio', import.meta.dir)],
|
|
619
|
+
env: {
|
|
620
|
+
YDC_API_KEY: process.env.YDC_API_KEY ?? '',
|
|
621
|
+
},
|
|
622
|
+
})
|
|
623
|
+
|
|
624
|
+
const dedicatedClient = new Client({
|
|
625
|
+
name: 'test-client-research',
|
|
626
|
+
version: '0.0.1',
|
|
627
|
+
})
|
|
628
|
+
|
|
629
|
+
await dedicatedClient.connect(transport)
|
|
630
|
+
|
|
631
|
+
try {
|
|
632
|
+
const result = await dedicatedClient.callTool({
|
|
633
|
+
name: 'you-research',
|
|
634
|
+
arguments: {
|
|
635
|
+
input: 'Explain microservices architecture',
|
|
636
|
+
research_effort: 'standard',
|
|
637
|
+
},
|
|
638
|
+
})
|
|
639
|
+
|
|
640
|
+
const content = result.content as { type: string; text: string }[]
|
|
641
|
+
expect(content[0]).toHaveProperty('text')
|
|
642
|
+
expect(content[0]?.text?.length).toBeGreaterThan(0)
|
|
643
|
+
} finally {
|
|
644
|
+
await dedicatedClient.close()
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
{ retry: 2, timeout: 120_000 },
|
|
648
|
+
)
|
|
649
|
+
})
|