@youdotcom-oss/mcp 2.0.7 → 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.26.0/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.26.0/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.26.0/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.26.0/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",
@@ -21256,23 +21341,18 @@ var fetchSearchResults = async ({
21256
21341
  } else if (errorCode === 402) {
21257
21342
  let errorMessage = "Free tier limit exceeded. Please upgrade to continue.";
21258
21343
  let upgradeUrl = "https://you.com/platform";
21259
- try {
21260
- const json2 = await response.json();
21261
- const parseResult = ApiErrorResponseSchema.safeParse(json2);
21262
- if (parseResult.success) {
21263
- const errorBody = parseResult.data;
21264
- if (errorBody.message) {
21265
- errorMessage = errorBody.message;
21266
- }
21267
- if (errorBody.upgrade_url) {
21268
- upgradeUrl = errorBody.upgrade_url;
21269
- }
21270
- if (errorBody.reset_at) {
21271
- const resetDate = new Date(errorBody.reset_at).toLocaleDateString();
21272
- errorMessage += ` Limit resets on ${resetDate}.`;
21273
- }
21274
- }
21275
- } catch {}
21344
+ const json2 = await response.json();
21345
+ const errorBody = ApiErrorResponseSchema.parse(json2);
21346
+ if (errorBody.message) {
21347
+ errorMessage = errorBody.message;
21348
+ }
21349
+ if (errorBody.upgrade_url) {
21350
+ upgradeUrl = errorBody.upgrade_url;
21351
+ }
21352
+ if (errorBody.reset_at) {
21353
+ const resetDate = new Date(errorBody.reset_at).toLocaleDateString();
21354
+ errorMessage += ` Limit resets on ${resetDate}.`;
21355
+ }
21276
21356
  throw new Error(`${errorMessage} Upgrade at: ${upgradeUrl}`);
21277
21357
  }
21278
21358
  throw new Error(`Failed to perform search. Error code: ${errorCode}`);
@@ -25320,7 +25400,7 @@ function object2(shape, params) {
25320
25400
  };
25321
25401
  return new ZodMiniObject(def);
25322
25402
  }
25323
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
25324
25404
  function isZ4Schema(s) {
25325
25405
  const schema = s;
25326
25406
  return !!schema._zod;
@@ -25464,7 +25544,7 @@ function getLiteralValue(schema) {
25464
25544
  return;
25465
25545
  }
25466
25546
 
25467
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
25468
25548
  function isTerminal(status) {
25469
25549
  return status === "completed" || status === "failed" || status === "cancelled";
25470
25550
  }
@@ -26705,7 +26785,7 @@ var zodToJsonSchema = (schema, options) => {
26705
26785
  }
26706
26786
  return combined;
26707
26787
  };
26708
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
26709
26789
  function mapMiniTarget(t) {
26710
26790
  if (!t)
26711
26791
  return "draft-7";
@@ -26747,7 +26827,7 @@ function parseWithCompat(schema, data) {
26747
26827
  return result.data;
26748
26828
  }
26749
26829
 
26750
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
26751
26831
  var DEFAULT_REQUEST_TIMEOUT_MSEC = 60000;
26752
26832
 
26753
26833
  class Protocol {
@@ -27582,7 +27662,7 @@ function mergeCapabilities(base, additional) {
27582
27662
  return result;
27583
27663
  }
27584
27664
 
27585
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
27586
27666
  var import_ajv = __toESM(require_ajv(), 1);
27587
27667
  var import_ajv_formats = __toESM(require_dist(), 1);
27588
27668
  function createDefaultAjvInstance() {
@@ -27622,7 +27702,7 @@ class AjvJsonSchemaValidator {
27622
27702
  }
27623
27703
  }
27624
27704
 
27625
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
27626
27706
  class ExperimentalServerTasks {
27627
27707
  constructor(_server) {
27628
27708
  this._server = _server;
@@ -27630,6 +27710,62 @@ class ExperimentalServerTasks {
27630
27710
  requestStream(request, resultSchema, options) {
27631
27711
  return this._server.requestStream(request, resultSchema, options);
27632
27712
  }
27713
+ createMessageStream(params, options) {
27714
+ const clientCapabilities = this._server.getClientCapabilities();
27715
+ if ((params.tools || params.toolChoice) && !clientCapabilities?.sampling?.tools) {
27716
+ throw new Error("Client does not support sampling tools capability.");
27717
+ }
27718
+ if (params.messages.length > 0) {
27719
+ const lastMessage = params.messages[params.messages.length - 1];
27720
+ const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
27721
+ const hasToolResults = lastContent.some((c) => c.type === "tool_result");
27722
+ const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : undefined;
27723
+ const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
27724
+ const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
27725
+ if (hasToolResults) {
27726
+ if (lastContent.some((c) => c.type !== "tool_result")) {
27727
+ throw new Error("The last message must contain only tool_result content if any is present");
27728
+ }
27729
+ if (!hasPreviousToolUse) {
27730
+ throw new Error("tool_result blocks are not matching any tool_use from the previous message");
27731
+ }
27732
+ }
27733
+ if (hasPreviousToolUse) {
27734
+ const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
27735
+ const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
27736
+ if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
27737
+ throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
27738
+ }
27739
+ }
27740
+ }
27741
+ return this.requestStream({
27742
+ method: "sampling/createMessage",
27743
+ params
27744
+ }, CreateMessageResultSchema, options);
27745
+ }
27746
+ elicitInputStream(params, options) {
27747
+ const clientCapabilities = this._server.getClientCapabilities();
27748
+ const mode = params.mode ?? "form";
27749
+ switch (mode) {
27750
+ case "url": {
27751
+ if (!clientCapabilities?.elicitation?.url) {
27752
+ throw new Error("Client does not support url elicitation.");
27753
+ }
27754
+ break;
27755
+ }
27756
+ case "form": {
27757
+ if (!clientCapabilities?.elicitation?.form) {
27758
+ throw new Error("Client does not support form elicitation.");
27759
+ }
27760
+ break;
27761
+ }
27762
+ }
27763
+ const normalizedParams = mode === "form" && params.mode === undefined ? { ...params, mode: "form" } : params;
27764
+ return this.requestStream({
27765
+ method: "elicitation/create",
27766
+ params: normalizedParams
27767
+ }, ElicitResultSchema, options);
27768
+ }
27633
27769
  async getTask(taskId, options) {
27634
27770
  return this._server.getTask({ taskId }, options);
27635
27771
  }
@@ -27644,7 +27780,7 @@ class ExperimentalServerTasks {
27644
27780
  }
27645
27781
  }
27646
27782
 
27647
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
27648
27784
  function assertToolsCallTaskCapability(requests, method, entityName) {
27649
27785
  if (!requests) {
27650
27786
  throw new Error(`${entityName} does not support task creation (required for ${method})`);
@@ -27679,7 +27815,7 @@ function assertClientRequestTaskCapability(requests, method, entityName) {
27679
27815
  }
27680
27816
  }
27681
27817
 
27682
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
27683
27819
  class Server extends Protocol {
27684
27820
  constructor(_serverInfo, options) {
27685
27821
  super(options);
@@ -28012,7 +28148,7 @@ class Server extends Protocol {
28012
28148
  }
28013
28149
  }
28014
28150
 
28015
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
28016
28152
  var COMPLETABLE_SYMBOL = Symbol.for("mcp.completable");
28017
28153
  function isCompletable(schema) {
28018
28154
  return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
@@ -28026,7 +28162,7 @@ var McpZodTypeKind;
28026
28162
  McpZodTypeKind2["Completable"] = "McpCompletable";
28027
28163
  })(McpZodTypeKind || (McpZodTypeKind = {}));
28028
28164
 
28029
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
28030
28166
  var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
28031
28167
  function validateToolName(name) {
28032
28168
  const warnings = [];
@@ -28084,7 +28220,7 @@ function validateAndWarnToolName(name) {
28084
28220
  return result.isValid;
28085
28221
  }
28086
28222
 
28087
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
28088
28224
  class ExperimentalMcpServerTasks {
28089
28225
  constructor(_mcpServer) {
28090
28226
  this._mcpServer = _mcpServer;
@@ -28099,7 +28235,7 @@ class ExperimentalMcpServerTasks {
28099
28235
  }
28100
28236
  }
28101
28237
 
28102
- // ../../node_modules/.bun/@modelcontextprotocol+sdk@1.26.0/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
28103
28239
  class McpServer {
28104
28240
  constructor(serverInfo, options) {
28105
28241
  this._registeredResources = {};
@@ -28821,7 +28957,7 @@ var EMPTY_COMPLETION_RESULT = {
28821
28957
  // package.json
28822
28958
  var package_default = {
28823
28959
  name: "@youdotcom-oss/mcp",
28824
- version: "2.0.7",
28960
+ version: "2.1.0",
28825
28961
  description: "You.com API Model Context Protocol Server - For programmatic API access, use @youdotcom-oss/api",
28826
28962
  license: "MIT",
28827
28963
  engines: {
@@ -28877,7 +29013,7 @@ var package_default = {
28877
29013
  },
28878
29014
  mcpName: "io.github.youdotcom-oss/mcp",
28879
29015
  dependencies: {
28880
- "@youdotcom-oss/api": "0.3.3",
29016
+ "@youdotcom-oss/api": "0.4.0",
28881
29017
  zod: "^4.3.6",
28882
29018
  "@hono/mcp": "^0.2.3",
28883
29019
  "@modelcontextprotocol/sdk": "^1.25.3",
@@ -28900,6 +29036,92 @@ var getMCpServer = () => new McpServer({
28900
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.`
28901
29037
  });
28902
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
+
28903
29125
  // src/search/search.schema.ts
28904
29126
  var SearchStructuredContentSchema = object({
28905
29127
  resultCounts: object({
@@ -29103,6 +29325,7 @@ try {
29103
29325
  const getUserAgent = useGetClientVersion(mcp);
29104
29326
  registerSearchTool({ mcp, YDC_API_KEY, getUserAgent });
29105
29327
  registerContentsTool({ mcp, YDC_API_KEY, getUserAgent });
29328
+ registerResearchTool({ mcp, YDC_API_KEY, getUserAgent });
29106
29329
  const transport = new StdioServerTransport;
29107
29330
  await mcp.connect(transport);
29108
29331
  } catch (error48) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youdotcom-oss/mcp",
3
- "version": "2.0.7",
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.3",
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,12 +3,13 @@ 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
9
10
 
10
11
  beforeAll(async () => {
11
- await $`bun run build` // 1256
12
+ await $`bun run build`
12
13
  const transport = new StdioClientTransport({
13
14
  command: 'npx',
14
15
  args: [Bun.resolveSync('../../bin/stdio', import.meta.dir)],
@@ -23,7 +24,7 @@ beforeAll(async () => {
23
24
  })
24
25
 
25
26
  await client.connect(transport)
26
- })
27
+ }, 30_000)
27
28
 
28
29
  afterAll(async () => {
29
30
  await client.close()
@@ -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
+ })