Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
},
"metadata": {
"description": "Official Perplexity AI plugin providing real-time web search, reasoning, and research capabilities",
"version": "1.0.0"
"version": "1.1.0"
},
"plugins": [
{
"name": "perplexity",
"source": "./",
"description": "Real-time web search, reasoning, and research through Perplexity's API",
"version": "1.0.0",
"version": "1.1.0",
"author": {
"name": "Perplexity AI",
"email": "api@perplexity.ai"
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,5 @@ Desktop.ini
*.pem
mcp-publisher
migrate-namespace.sh

*.tgz
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The official MCP server implementation for the Perplexity API Platform, providin
## Available Tools

### **perplexity_search**
Direct web search using the Perplexity Search API. Returns ranked search results with metadata, perfect for finding current information.
Direct web search using the Perplexity Search API. Returns ranked search results with metadata, perfect for finding current information. Supports recency filters (`search_recency_filter`) and domain restrictions (`search_domain_filter`).

### **perplexity_ask**
General-purpose conversational AI with real-time web search, backed by the Agent API `fast` preset. Great for quick questions and everyday searches.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@perplexity-ai/mcp-server",
"version": "1.0.0",
"version": "1.1.0",
"mcpName": "ai.perplexity/mcp-server",
"description": "Real-time web search, reasoning, and research through Perplexity's API",
"keywords": [
Expand All @@ -23,14 +23,25 @@
"access": "public"
},
"type": "module",
"main": "dist/index.js",
"main": "dist/server.js",
"types": "dist/server.d.ts",
"exports": {
".": {
"types": "./dist/server.d.ts",
"default": "./dist/server.js"
},
"./package.json": "./package.json"
},
"bin": {
"perplexity-mcp": "dist/index.js"
},
"files": [
"dist/*.js",
"dist/*.d.ts",
"!dist/*.test.js",
"!dist/*.test.d.ts",
"!dist/vitest.config.js",
"!dist/vitest.config.d.ts",
"README.md",
".claude-plugin"
],
Expand Down
4 changes: 2 additions & 2 deletions server.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
"name": "ai.perplexity/mcp-server",
"title": "Perplexity API Platform",
"description": "Real-time web search, reasoning, and research through Perplexity's API",
"version": "1.0.0",
"version": "1.1.0",
"packages": [
{
"registryType": "npm",
"identifier": "@perplexity-ai/mcp-server",
"version": "1.0.0",
"version": "1.1.0",
"transport": {
"type": "stdio"
}
Expand Down
20 changes: 16 additions & 4 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { AgentResponseSchema, SearchResponseSchema } from "./validation.js";

const PERPLEXITY_API_KEY = process.env.PERPLEXITY_API_KEY;
const PERPLEXITY_BASE_URL = process.env.PERPLEXITY_BASE_URL || "https://api.perplexity.ai";
const VERSION = "1.0.0";
const VERSION = "1.1.0";

// Agent API presets backing each tool: https://docs.perplexity.ai/docs/agent-api/presets
export const ASK_PRESET = "fast";
Expand Down Expand Up @@ -462,13 +462,16 @@ export async function performSearch(
maxResults: number = 10,
maxTokensPerPage: number = 1024,
country?: string,
filters?: Pick<AgentToolOptions, "search_recency_filter" | "search_domain_filter">,
serviceOrigin?: string
): Promise<string> {
const body: Record<string, unknown> = {
query: query,
max_results: maxResults,
max_tokens_per_page: maxTokensPerPage,
...(country && { country }),
...(filters?.search_recency_filter && { search_recency_filter: filters.search_recency_filter }),
...(filters?.search_domain_filter && { search_domain_filter: filters.search_domain_filter }),
};

const response = await makeApiRequest("search", body, serviceOrigin);
Expand Down Expand Up @@ -524,7 +527,7 @@ export function createPerplexityServer(serviceOrigin?: string) {
{
instructions:
"Perplexity AI server for web-grounded search, research, and reasoning, backed by the Perplexity Agent API. " +
"Use perplexity_search for finding URLs, facts, and recent news. " +
"Use perplexity_search for finding URLs, facts, and recent news. Supports recency filters and domain restrictions. " +
"Use perplexity_ask for quick AI-answered questions with citations. Supports recency filters, domain restrictions, and search context size control. " +
"Use perplexity_research for in-depth multi-source investigation (slow, can take minutes). " +
"Use perplexity_reason for complex analysis requiring step-by-step logic. Supports recency filters, domain restrictions, and search context size control. " +
Expand Down Expand Up @@ -699,6 +702,8 @@ export function createPerplexityServer(serviceOrigin?: string) {
.describe("Maximum tokens to extract per webpage (default: 1024)"),
country: z.string().optional()
.describe("ISO 3166-1 alpha-2 country code for regional results (e.g., 'US', 'GB')"),
search_recency_filter: searchRecencyFilterField,
search_domain_filter: searchDomainFilterField,
};

const searchOutputSchema = {
Expand All @@ -712,6 +717,7 @@ export function createPerplexityServer(serviceOrigin?: string) {
description: "Search the web and return a ranked list of results with titles, URLs, snippets, and dates. " +
"Best for: finding specific URLs, checking recent news, verifying facts, discovering sources. " +
"Returns formatted results (title, URL, snippet, date) with no AI synthesis. " +
"Supports recency filters and domain restrictions. " +
"For AI-generated answers with citations, use perplexity_ask instead.",
inputSchema: searchInputSchema as any,
outputSchema: searchOutputSchema as any,
Expand All @@ -723,17 +729,23 @@ export function createPerplexityServer(serviceOrigin?: string) {
},
},
async (args: any) => {
const { query, max_results, max_tokens_per_page, country } = args as {
const { query, max_results, max_tokens_per_page, country, search_recency_filter, search_domain_filter } = args as {
query: string;
max_results?: number;
max_tokens_per_page?: number;
country?: string;
search_recency_filter?: "hour" | "day" | "week" | "month" | "year";
search_domain_filter?: string[];
};
const maxResults = typeof max_results === "number" ? max_results : 10;
const maxTokensPerPage = typeof max_tokens_per_page === "number" ? max_tokens_per_page : 1024;
const countryCode = typeof country === "string" ? country : undefined;
const filters = {
...(search_recency_filter && { search_recency_filter }),
...(search_domain_filter && { search_domain_filter }),
};

const result = await performSearch(query, maxResults, maxTokensPerPage, countryCode, serviceOrigin);
const result = await performSearch(query, maxResults, maxTokensPerPage, countryCode, filters, serviceOrigin);
return {
content: [{ type: "text" as const, text: result }],
structuredContent: { results: result },
Expand Down
46 changes: 46 additions & 0 deletions src/transport.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import express from "express";
import cors from "cors";
import { Server } from "http";
import { readFileSync } from "node:fs";

function agentSseResponse(text: string): Response {
const completed = {
Expand Down Expand Up @@ -267,6 +268,19 @@ describe("Transport Integration Tests", () => {
});

describe("Backward Compatibility", () => {
it("should advertise the package.json version to clients", async () => {
const { client, server } = await connectInMemoryClient();
try {
const pkg = JSON.parse(
readFileSync(new URL("../package.json", import.meta.url), "utf8")
);
expect(client.getServerVersion()?.version).toBe(pkg.version);
} finally {
await client.close();
await server.close();
}
});

it("should keep the historical response shape including the citations block", async () => {
global.fetch = vi
.fn()
Expand Down Expand Up @@ -348,6 +362,38 @@ describe("Transport Integration Tests", () => {
}
});

it("should forward search filters to the search API request body", async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ results: [] }),
} as Response);

const { client, server } = await connectInMemoryClient();
try {
const result: any = await client.callTool({
name: "perplexity_search",
arguments: {
query: "test",
search_recency_filter: "week",
search_domain_filter: ["wikipedia.org", "-reddit.com"],
},
});

expect(result.isError).toBeFalsy();
const upstreamBody = JSON.parse(
(global.fetch as ReturnType<typeof vi.fn>).mock.calls[0][1].body as string
);
expect(upstreamBody).toMatchObject({
query: "test",
search_recency_filter: "week",
search_domain_filter: ["wikipedia.org", "-reddit.com"],
});
} finally {
await client.close();
await server.close();
}
});

it("should emit progress notifications when the client requests progress", async () => {
const events = [
{ type: "response.created", response: { id: "resp_progress" } },
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"module": "ESNext",
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
Expand Down