Skip to content
Open
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
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,12 @@ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | ie

The following environment variable is required in your claude_desktop_config.json. You can obtain an API key from [Perplexity](https://perplexity.ai)

- `PERPLEXITY_API_KEY`: Your Perplexity AI API key
- `PERPLEXITY_API_KEY`: Your Perplexity AI API key (required when using Perplexity provider)

Optional environment variables:

- `SEARCH_PROVIDER`: Select the search backend — `perplexity` (default) or `tavily`
- `TAVILY_API_KEY`: Your Tavily API key (required when `SEARCH_PROVIDER=tavily`). Get one at [Tavily](https://app.tavily.com)
- `PERPLEXITY_MODEL`: The Perplexity model to use (defaults to "sonar" if not specified)

Available models:
Expand All @@ -94,7 +96,9 @@ Add this tool as a mcp server by editing the Cursor/Claude config file.
"perplexity-mcp": {
"env": {
"PERPLEXITY_API_KEY": "XXXXXXXXXXXXXXXXXXXX",
"PERPLEXITY_MODEL": "sonar"
"PERPLEXITY_MODEL": "sonar",
"SEARCH_PROVIDER": "perplexity",
"TAVILY_API_KEY": "tvly-XXXXXXXXXXXXXXXXXXXX"
},
"command": "uvx",
"args": [
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies = [
"aiohttp>=3.8.0",
"pydantic>=2.0.0",
"mcp>=1.0.2",
"tavily-python>=0.5",
]

[project.scripts]
Expand Down
107 changes: 76 additions & 31 deletions src/perplexity_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from datetime import datetime
import os

from tavily import AsyncTavilyClient

from mcp.server.models import InitializationOptions
import mcp.types as types
from mcp.server import NotificationOptions, Server
Expand All @@ -25,7 +27,7 @@ async def handle_list_prompts() -> list[types.Prompt]:
return [
types.Prompt(
name="perplexity_search_web",
description="Search the web using Perplexity AI and filter results by recency",
description="Search the web with recency filtering",
arguments=[
types.PromptArgument(
name="query",
Expand Down Expand Up @@ -80,7 +82,7 @@ async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="perplexity_search_web",
description="Search the web using Perplexity AI with recency filtering",
description="Search the web with recency filtering",
inputSchema={
"type": "object",
"properties": {
Expand All @@ -104,7 +106,11 @@ async def call_tool(
if name == "perplexity_search_web":
query = arguments["query"]
recency = arguments.get("recency", "month")
result = await call_perplexity(query, recency)
provider = os.getenv("SEARCH_PROVIDER", "perplexity")
if provider == "tavily":
result = await call_tavily(query, recency)
else:
result = await call_perplexity(query, recency)
return [types.TextContent(type="text", text=str(result))]
raise ValueError(f"Tool not found: {name}")

Expand Down Expand Up @@ -156,10 +162,37 @@ async def call_perplexity(query: str, recency: str) -> str:
return content


async def call_tavily(query: str, recency: str) -> str:
try:
client = AsyncTavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
response = await client.search(
query=query,
max_results=5,
search_depth="advanced",
time_range=recency,
)
except Exception as e:
return f"Tavily search error: {e}"
results = response.get("results", [])
if not results:
return "No results found."
formatted = []
for i, r in enumerate(results, 1):
title = r.get("title", "No title")
url = r.get("url", "")
content = r.get("content", "")
formatted.append(f"[{i}] {title}\n {url}\n {content}")
return "\n\n".join(formatted)


async def main_async():
API_KEY = os.getenv("PERPLEXITY_API_KEY")
if not API_KEY:
raise ValueError("PERPLEXITY_API_KEY environment variable is required")
provider = os.getenv("SEARCH_PROVIDER", "perplexity")
if provider == "tavily":
if not os.getenv("TAVILY_API_KEY"):
raise ValueError("TAVILY_API_KEY environment variable is required when SEARCH_PROVIDER=tavily")
else:
if not os.getenv("PERPLEXITY_API_KEY"):
raise ValueError("PERPLEXITY_API_KEY environment variable is required")

async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
Expand All @@ -180,32 +213,44 @@ def main():
"""CLI entry point for perplexity-mcp"""
logging.basicConfig(level=logging.INFO)

API_KEY = os.getenv("PERPLEXITY_API_KEY")
if not API_KEY:
print(
"Error: PERPLEXITY_API_KEY environment variable is required",
file=sys.stderr,
)
sys.exit(1)
provider = os.getenv("SEARCH_PROVIDER", "perplexity")
logging.info(f"Search provider: {provider}")

# Log which model is being used (helpful for debug)
model = os.getenv("PERPLEXITY_MODEL", "sonar")
logging.info(f"Using Perplexity AI model: {model}")

# List available models
available_models = {
"sonar-deep-research": "128k context - Enhanced research capabilities",
"sonar-reasoning-pro": "128k context - Advanced reasoning with professional focus",
"sonar-reasoning": "128k context - Enhanced reasoning capabilities",
"sonar-pro": "200k context - Professional grade model",
"sonar": "128k context - Default model",
"r1-1776": "128k context - Alternative architecture"
}

logging.info("Available Perplexity models (set with PERPLEXITY_MODEL environment variable):")
for model_name, description in available_models.items():
marker = "→" if model_name == model else " "
logging.info(f" {marker} {model_name}: {description}")
if provider == "tavily":
if not os.getenv("TAVILY_API_KEY"):
print(
"Error: TAVILY_API_KEY environment variable is required when SEARCH_PROVIDER=tavily",
file=sys.stderr,
)
sys.exit(1)
logging.info("Using Tavily search API")
else:
API_KEY = os.getenv("PERPLEXITY_API_KEY")
if not API_KEY:
print(
"Error: PERPLEXITY_API_KEY environment variable is required",
file=sys.stderr,
)
sys.exit(1)

# Log which model is being used (helpful for debug)
model = os.getenv("PERPLEXITY_MODEL", "sonar")
logging.info(f"Using Perplexity AI model: {model}")

# List available models
available_models = {
"sonar-deep-research": "128k context - Enhanced research capabilities",
"sonar-reasoning-pro": "128k context - Advanced reasoning with professional focus",
"sonar-reasoning": "128k context - Enhanced reasoning capabilities",
"sonar-pro": "200k context - Professional grade model",
"sonar": "128k context - Default model",
"r1-1776": "128k context - Alternative architecture"
}

logging.info("Available Perplexity models (set with PERPLEXITY_MODEL environment variable):")
for model_name, description in available_models.items():
marker = "→" if model_name == model else " "
logging.info(f" {marker} {model_name}: {description}")

asyncio.run(main_async())

Expand Down