@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 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 two tools that work seamlessly with your AI agent through natural language:
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. **Research + Extract**: "Search for the best TypeScript tutorials, then extract the content from the top 3 results"
225
- 2. **Question + Deep Dive**: "What is WebAssembly? Then search for real-world examples and extract code samples"
226
- 3. **News + Analysis**: "Find recent articles about AI regulation, then summarize the key points"
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: () => mod[key],
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: (newValue) => all[name] = () => newValue
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
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 DEEP_SEARCH_API_URL = process.env.YDC_DEEP_SEARCH_API_URL || "https://api.you.com/v1/deep_search";
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/deep-search/deep-search.schemas.ts
21061
- var SearchEffortSchema = _enum2(["low", "medium", "high"]).describe("Search effort level");
21062
- var DeepSearchQuerySchema = object({
21063
- query: string2().min(1, "Query is required").describe("The research question or complex query requiring in-depth investigation and multi-step reasoning"),
21064
- search_effort: SearchEffortSchema.optional().default("medium").describe("Computation budget: low (<30s), medium (<60s, default), high (<300s)")
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 DeepSearchSourceSchema = object({
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 DeepSearchResponseSchema = object({
21072
- answer: string2().describe("Comprehensive response with inline citations, formatted in Markdown"),
21073
- results: array(DeepSearchSourceSchema).describe("List of web sources used to generate the answer")
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
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.0+702d06f8eea47248/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
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.8",
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.3.4",
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.8",
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.3.4",
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)
@@ -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
+ })