@perplexity-ai/mcp-server 0.6.2 → 0.7.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.
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Official Perplexity AI plugin providing real-time web search, reasoning, and research capabilities",
9
- "version": "0.6.2"
9
+ "version": "0.7.0"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "perplexity",
14
14
  "source": "./",
15
15
  "description": "Real-time web search, reasoning, and research through Perplexity's API",
16
- "version": "0.6.2",
16
+ "version": "0.7.0",
17
17
  "author": {
18
18
  "name": "Perplexity AI",
19
19
  "email": "api@perplexity.ai"
package/dist/server.js CHANGED
@@ -43,7 +43,7 @@ export function validateMessages(messages, toolName) {
43
43
  export function stripThinkingTokens(content) {
44
44
  return content.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
45
45
  }
46
- export async function performChatCompletion(messages, model = "sonar-pro", stripThinking = false, serviceOrigin) {
46
+ export async function performChatCompletion(messages, model = "sonar-pro", stripThinking = false, serviceOrigin, options) {
47
47
  if (!PERPLEXITY_API_KEY) {
48
48
  throw new Error("PERPLEXITY_API_KEY environment variable is required");
49
49
  }
@@ -53,6 +53,10 @@ export async function performChatCompletion(messages, model = "sonar-pro", strip
53
53
  const body = {
54
54
  model: model,
55
55
  messages: messages,
56
+ ...(options?.search_recency_filter && { search_recency_filter: options.search_recency_filter }),
57
+ ...(options?.search_domain_filter && { search_domain_filter: options.search_domain_filter }),
58
+ ...(options?.search_context_size && { web_search_options: { search_context_size: options.search_context_size } }),
59
+ ...(options?.reasoning_effort && { reasoning_effort: options.reasoning_effort }),
56
60
  };
57
61
  const controller = new AbortController();
58
62
  const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
@@ -200,36 +204,76 @@ export async function performSearch(query, maxResults = 10, maxTokensPerPage = 1
200
204
  export function createPerplexityServer(serviceOrigin) {
201
205
  const server = new McpServer({
202
206
  name: "io.github.perplexityai/mcp-server",
203
- version: "0.6.2",
207
+ version: "0.7.0",
208
+ }, {
209
+ instructions: "Perplexity AI server for web-grounded search, research, and reasoning. " +
210
+ "Use perplexity_search for finding URLs, facts, and recent news. " +
211
+ "Use perplexity_ask for quick AI-answered questions with citations. Supports recency filters, domain restrictions, and search context size control. " +
212
+ "Use perplexity_research for in-depth multi-source investigation (slow, 30s+). Supports reasoning_effort parameter to control depth. " +
213
+ "Use perplexity_reason for complex analysis requiring step-by-step logic. Supports recency filters, domain restrictions, and search context size control. " +
214
+ "All tools are read-only and access live web data.",
204
215
  });
205
216
  const messageSchema = z.object({
206
- role: z.string().describe("Role of the message (e.g., system, user, assistant)"),
217
+ role: z.enum(["system", "user", "assistant"]).describe("Role of the message sender"),
207
218
  content: z.string().describe("The content of the message"),
208
219
  });
209
220
  const messagesField = z.array(messageSchema).describe("Array of conversation messages");
210
221
  const stripThinkingField = z.boolean().optional()
211
222
  .describe("If true, removes <think>...</think> tags and their content from the response to save context tokens. Default is false.");
223
+ const searchRecencyFilterField = z.enum(["hour", "day", "week", "month", "year"]).optional()
224
+ .describe("Filter search results by recency. Use 'hour' for very recent news, 'day' for today's updates, 'week' for this week, etc.");
225
+ const searchDomainFilterField = z.array(z.string()).optional()
226
+ .describe("Restrict search results to specific domains (e.g., ['wikipedia.org', 'arxiv.org']). Use '-' prefix for exclusion (e.g., ['-reddit.com']).");
227
+ const searchContextSizeField = z.enum(["low", "medium", "high"]).optional()
228
+ .describe("Controls how much web context is retrieved. 'low' (default) is fastest, 'high' provides more comprehensive results.");
229
+ const reasoningEffortField = z.enum(["minimal", "low", "medium", "high"]).optional()
230
+ .describe("Controls depth of deep research reasoning. Higher values produce more thorough analysis.");
212
231
  const responseOutputSchema = {
213
- response: z.string().describe("The response from Perplexity"),
232
+ response: z.string().describe("AI-generated text response with numbered citation references"),
214
233
  };
215
234
  // Input schemas
216
- const messagesOnlyInputSchema = { messages: messagesField };
217
- const messagesWithStripThinkingInputSchema = { messages: messagesField, strip_thinking: stripThinkingField };
235
+ const messagesOnlyInputSchema = {
236
+ messages: messagesField,
237
+ search_recency_filter: searchRecencyFilterField,
238
+ search_domain_filter: searchDomainFilterField,
239
+ search_context_size: searchContextSizeField,
240
+ };
241
+ const messagesWithStripThinkingInputSchema = {
242
+ messages: messagesField,
243
+ strip_thinking: stripThinkingField,
244
+ search_recency_filter: searchRecencyFilterField,
245
+ search_domain_filter: searchDomainFilterField,
246
+ search_context_size: searchContextSizeField,
247
+ };
248
+ const researchInputSchema = {
249
+ messages: messagesField,
250
+ strip_thinking: stripThinkingField,
251
+ reasoning_effort: reasoningEffortField,
252
+ };
218
253
  server.registerTool("perplexity_ask", {
219
254
  title: "Ask Perplexity",
220
- description: "Engages in a conversation using the Sonar API. " +
221
- "Accepts an array of messages (each with a role and content) " +
222
- "and returns a chat completion response from the Perplexity model.",
255
+ description: "Answer a question using web-grounded AI (Sonar Pro model). " +
256
+ "Best for: quick factual questions, summaries, explanations, and general Q&A. " +
257
+ "Returns a text response with numbered citations. Fastest and cheapest option. " +
258
+ "Supports filtering by recency (hour/day/week/month/year), domain restrictions, and search context size. " +
259
+ "For in-depth multi-source research, use perplexity_research instead. " +
260
+ "For step-by-step reasoning and analysis, use perplexity_reason instead.",
223
261
  inputSchema: messagesOnlyInputSchema,
224
262
  outputSchema: responseOutputSchema,
225
263
  annotations: {
226
264
  readOnlyHint: true,
227
265
  openWorldHint: true,
266
+ idempotentHint: true,
228
267
  },
229
268
  }, async (args) => {
230
- const { messages } = args;
269
+ const { messages, search_recency_filter, search_domain_filter, search_context_size } = args;
231
270
  validateMessages(messages, "perplexity_ask");
232
- const result = await performChatCompletion(messages, "sonar-pro", false, serviceOrigin);
271
+ const options = {
272
+ ...(search_recency_filter && { search_recency_filter }),
273
+ ...(search_domain_filter && { search_domain_filter }),
274
+ ...(search_context_size && { search_context_size }),
275
+ };
276
+ const result = await performChatCompletion(messages, "sonar-pro", false, serviceOrigin, Object.keys(options).length > 0 ? options : undefined);
233
277
  return {
234
278
  content: [{ type: "text", text: result }],
235
279
  structuredContent: { response: result },
@@ -237,20 +281,27 @@ export function createPerplexityServer(serviceOrigin) {
237
281
  });
238
282
  server.registerTool("perplexity_research", {
239
283
  title: "Deep Research",
240
- description: "Performs deep research using the Perplexity API. " +
241
- "Accepts an array of messages (each with a role and content) " +
242
- "and returns a comprehensive research response with citations.",
243
- inputSchema: messagesWithStripThinkingInputSchema,
284
+ description: "Conduct deep, multi-source research on a topic (Sonar Deep Research model). " +
285
+ "Best for: literature reviews, comprehensive overviews, investigative queries needing " +
286
+ "many sources. Returns a detailed response with numbered citations. " +
287
+ "Significantly slower than other tools (30+ seconds). " +
288
+ "For quick factual questions, use perplexity_ask instead. " +
289
+ "For logical analysis and reasoning, use perplexity_reason instead.",
290
+ inputSchema: researchInputSchema,
244
291
  outputSchema: responseOutputSchema,
245
292
  annotations: {
246
293
  readOnlyHint: true,
247
294
  openWorldHint: true,
295
+ idempotentHint: true,
248
296
  },
249
297
  }, async (args) => {
250
- const { messages, strip_thinking } = args;
298
+ const { messages, strip_thinking, reasoning_effort } = args;
251
299
  validateMessages(messages, "perplexity_research");
252
300
  const stripThinking = typeof strip_thinking === "boolean" ? strip_thinking : false;
253
- const result = await performChatCompletion(messages, "sonar-deep-research", stripThinking, serviceOrigin);
301
+ const options = {
302
+ ...(reasoning_effort && { reasoning_effort }),
303
+ };
304
+ const result = await performChatCompletion(messages, "sonar-deep-research", stripThinking, serviceOrigin, Object.keys(options).length > 0 ? options : undefined);
254
305
  return {
255
306
  content: [{ type: "text", text: result }],
256
307
  structuredContent: { response: result },
@@ -258,20 +309,29 @@ export function createPerplexityServer(serviceOrigin) {
258
309
  });
259
310
  server.registerTool("perplexity_reason", {
260
311
  title: "Advanced Reasoning",
261
- description: "Performs reasoning tasks using the Perplexity API. " +
262
- "Accepts an array of messages (each with a role and content) " +
263
- "and returns a well-reasoned response using the sonar-reasoning-pro model.",
312
+ description: "Analyze a question using step-by-step reasoning with web grounding (Sonar Reasoning Pro model). " +
313
+ "Best for: math, logic, comparisons, complex arguments, and tasks requiring chain-of-thought. " +
314
+ "Returns a reasoned response with numbered citations. " +
315
+ "Supports filtering by recency (hour/day/week/month/year), domain restrictions, and search context size. " +
316
+ "For quick factual questions, use perplexity_ask instead. " +
317
+ "For comprehensive multi-source research, use perplexity_research instead.",
264
318
  inputSchema: messagesWithStripThinkingInputSchema,
265
319
  outputSchema: responseOutputSchema,
266
320
  annotations: {
267
321
  readOnlyHint: true,
268
322
  openWorldHint: true,
323
+ idempotentHint: true,
269
324
  },
270
325
  }, async (args) => {
271
- const { messages, strip_thinking } = args;
326
+ const { messages, strip_thinking, search_recency_filter, search_domain_filter, search_context_size } = args;
272
327
  validateMessages(messages, "perplexity_reason");
273
328
  const stripThinking = typeof strip_thinking === "boolean" ? strip_thinking : false;
274
- const result = await performChatCompletion(messages, "sonar-reasoning-pro", stripThinking, serviceOrigin);
329
+ const options = {
330
+ ...(search_recency_filter && { search_recency_filter }),
331
+ ...(search_domain_filter && { search_domain_filter }),
332
+ ...(search_context_size && { search_context_size }),
333
+ };
334
+ const result = await performChatCompletion(messages, "sonar-reasoning-pro", stripThinking, serviceOrigin, Object.keys(options).length > 0 ? options : undefined);
275
335
  return {
276
336
  content: [{ type: "text", text: result }],
277
337
  structuredContent: { response: result },
@@ -287,18 +347,20 @@ export function createPerplexityServer(serviceOrigin) {
287
347
  .describe("ISO 3166-1 alpha-2 country code for regional results (e.g., 'US', 'GB')"),
288
348
  };
289
349
  const searchOutputSchema = {
290
- results: z.string().describe("Formatted search results"),
350
+ results: z.string().describe("Formatted search results, each with title, URL, snippet, and date"),
291
351
  };
292
352
  server.registerTool("perplexity_search", {
293
353
  title: "Search the Web",
294
- description: "Performs web search using the Perplexity Search API. " +
295
- "Returns ranked search results with titles, URLs, snippets, and metadata. " +
296
- "Perfect for finding up-to-date facts, news, or specific information.",
354
+ description: "Search the web and return a ranked list of results with titles, URLs, snippets, and dates. " +
355
+ "Best for: finding specific URLs, checking recent news, verifying facts, discovering sources. " +
356
+ "Returns formatted results (title, URL, snippet, date) no AI synthesis. " +
357
+ "For AI-generated answers with citations, use perplexity_ask instead.",
297
358
  inputSchema: searchInputSchema,
298
359
  outputSchema: searchOutputSchema,
299
360
  annotations: {
300
361
  readOnlyHint: true,
301
362
  openWorldHint: true,
363
+ idempotentHint: true,
302
364
  },
303
365
  }, async (args) => {
304
366
  const { query, max_results, max_tokens_per_page, country } = args;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@perplexity-ai/mcp-server",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "mcpName": "io.github.perplexityai/mcp-server",
5
5
  "description": "Real-time web search, reasoning, and research through Perplexity's API",
6
6
  "keywords": [