{"id":50792876,"url":"https://github.com/cryptojones/perplexityagent","last_synced_at":"2026-06-12T12:02:25.661Z","repository":{"id":362681641,"uuid":"1258100140","full_name":"CryptoJones/PerplexityAgent","owner":"CryptoJones","description":"Security-hardened MCP server exposing the Perplexity Search + Sonar APIs (search, ask, deep_research) to AI agents. Hardened per NSA MCP guidance.","archived":false,"fork":false,"pushed_at":"2026-06-05T12:25:31.000Z","size":546,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-05T13:18:36.274Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CryptoJones.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-03T09:20:29.000Z","updated_at":"2026-06-05T12:23:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/CryptoJones/PerplexityAgent","commit_stats":null,"previous_names":["cryptojones/perplexityagent"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/CryptoJones/PerplexityAgent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CryptoJones%2FPerplexityAgent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CryptoJones%2FPerplexityAgent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CryptoJones%2FPerplexityAgent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CryptoJones%2FPerplexityAgent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CryptoJones","download_url":"https://codeload.github.com/CryptoJones/PerplexityAgent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CryptoJones%2FPerplexityAgent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34243053,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-12T12:01:46.553Z","updated_at":"2026-06-12T12:02:25.598Z","avatar_url":"https://github.com/CryptoJones.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PerplexityAgent\n\n[![Release](https://img.shields.io/gitea/v/release/CryptoJones/PerplexityAgent?gitea_url=https%3A%2F%2Fcodeberg.org\u0026label=release)](https://codeberg.org/CryptoJones/PerplexityAgent/releases)\n[![CI](https://github.com/CryptoJones/PerplexityAgent/actions/workflows/ci.yml/badge.svg)](https://github.com/CryptoJones/PerplexityAgent/actions/workflows/ci.yml)\n[![CodeQL](https://github.com/CryptoJones/PerplexityAgent/actions/workflows/codeql.yml/badge.svg)](https://github.com/CryptoJones/PerplexityAgent/actions/workflows/codeql.yml)\n[![Python ≥ 3.11](https://img.shields.io/badge/python-%E2%89%A5%203.11-blue)](pyproject.toml)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n\nA simple, **security-hardened** [Model Context Protocol](https://modelcontextprotocol.io)\n(MCP) server that gives AI agents — [Claude Code](https://claude.com/claude-code),\nHermes, or any MCP client — access to the [Perplexity](https://docs.perplexity.ai)\nSearch and Sonar APIs.\n\nIt follows a retrieval-first reference architecture (search and synthesis kept\nseparate, citations validated against retrieval metadata) and applies the defensive\ncontrols from the NSA's *Model Context Protocol (MCP): Security Design Considerations*\n(May 2026). See [`SECURITY.md`](SECURITY.md) for the full control mapping.\n\n- **Canonical repo:** https://codeberg.org/CryptoJones/PerplexityAgent\n- **Mirror:** https://github.com/CryptoJones/PerplexityAgent\n\n## Tools\n\n| Tool | Description |\n| --- | --- |\n| `perplexity_search` | Ranked web results from the Perplexity Search API. |\n| `sonar_ask` | A grounded answer from Sonar / Sonar Pro (OpenAI-compatible chat). |\n| `deep_research` | Multi-step pipeline: decompose → search each sub-question → dedupe → synthesize (JSON schema) → **validate citations** → return a cited report with a `validation_report`. |\n\n## Requirements\n\n- Python ≥ 3.11\n- [`uv`](https://docs.astral.sh/uv/)\n- A Perplexity API key (https://www.perplexity.ai/settings/api)\n\n## Setup\n\n```bash\ngit clone https://codeberg.org/CryptoJones/PerplexityAgent.git\ncd PerplexityAgent\nuv sync                      # install (add --extra dev for tests)\ncp .env.example .env         # then edit .env and set PERPLEXITY_API_KEY\n```\n\nThe API key is read **server-side only** (from the environment or `.env`) and is\nnever returned in any tool output. The server refuses to start without it.\n\n## Running\n\n### stdio (default — recommended)\n\nRuns as a local subprocess of the agent with no network exposure:\n\n```bash\nuv run perplexity-agent\n```\n\n### Register with Claude Code\n\n```bash\nclaude mcp add perplexity -- uv --directory /abs/path/to/PerplexityAgent run perplexity-agent\n```\n\nor in your MCP client config (`mcpServers`):\n\n```json\n{\n  \"mcpServers\": {\n    \"perplexity\": {\n      \"command\": \"uv\",\n      \"args\": [\"--directory\", \"/abs/path/to/PerplexityAgent\", \"run\", \"perplexity-agent\"],\n      \"env\": { \"PERPLEXITY_API_KEY\": \"pplx-...\" }\n    }\n  }\n}\n```\n\n### Hermes\n\nHermes consumes MCP servers over stdio the same way — point it at the\n`uv ... run perplexity-agent` command with `PERPLEXITY_API_KEY` in the environment.\n\n### Optional hardened HTTP transport\n\nOff by default. It **refuses to start without a bearer token** and binds to\nlocalhost. Only enable it if you understand the added attack surface (see\n`SECURITY.md`):\n\n```bash\nPERPLEXITY_HTTP_AUTH_TOKEN=\"$(openssl rand -hex 32)\" uv run perplexity-agent --transport http\n```\n\nClients must send `Authorization: Bearer \u003ctoken\u003e`. Terminate TLS in front of it\n(reverse proxy) and keep it behind a filtering egress proxy.\n\n## Calling the tools\n\nOnce the server is registered, the agent calls these tools automatically. The\nsignatures, sample arguments, and return shapes are below.\n\n### `perplexity_search`\n\nRanked web results from the Search API.\n\n| Param | Type | Default | Bounds |\n| --- | --- | --- | --- |\n| `query` | string | — (required) | 1–4096 chars |\n| `max_results` | int | `5` | 1–20 |\n| `max_tokens_per_page` | int | `1024` | 128–4096 |\n\n```json\n{ \"query\": \"latest CRISPR base-editing clinical trials\", \"max_results\": 8 }\n```\n\nReturns the raw Search API payload, e.g.:\n\n```json\n{\n  \"results\": [\n    { \"title\": \"…\", \"url\": \"https://…\", \"snippet\": \"…\" }\n  ]\n}\n```\n\n### `sonar_ask`\n\nA grounded answer from Sonar (OpenAI-compatible chat completion).\n\n| Param | Type | Default | Notes |\n| --- | --- | --- | --- |\n| `question` | string | — (required) | 1–4096 chars |\n| `model` | string | `\"sonar\"` | `\"sonar\"` or `\"sonar-pro\"` only |\n| `system_prompt` | string | `null` | optional, ≤ 4096 chars |\n\n```json\n{\n  \"question\": \"What changed in the EU AI Act's 2026 enforcement timeline?\",\n  \"model\": \"sonar-pro\",\n  \"system_prompt\": \"Answer concisely and cite sources.\"\n}\n```\n\nReturns the chat-completion payload; the answer is at\n`choices[0].message.content`, with citations in the response metadata.\n\n### `deep_research`\n\nThe full pipeline: decompose → search each sub-question → dedupe → synthesize\n(JSON schema) → validate citations.\n\n| Param | Type | Default | Bounds |\n| --- | --- | --- | --- |\n| `question` | string | — (required) | 1–4096 chars |\n| `num_subquestions` | int | `4` | 1–8 |\n| `model` | string | `\"sonar-pro\"` | `\"sonar\"` or `\"sonar-pro\"` |\n| `max_results_per_subquestion` | int | `5` | 1–10 |\n| `use_model_decomposition` | bool | `false` | ask Sonar to derive the sub-questions (one extra call; falls back to the deterministic angles on any failure) |\n\n```json\n{ \"question\": \"Is small modular nuclear cost-competitive with grid-scale solar?\", \"num_subquestions\": 5 }\n```\n\nReturns a structured, **citation-validated** report:\n\n```json\n{\n  \"question\": \"…\",\n  \"subquestions\": [\"…\", \"…\"],\n  \"sources\": [{ \"title\": \"…\", \"url\": \"https://…\", \"snippet\": \"…\" }],\n  \"report\": {\n    \"answer\": \"…\",\n    \"key_findings\": [\"…\"],\n    \"open_questions\": [\"…\"],\n    \"claims\": [\n      { \"claim\": \"…\", \"supporting_urls\": [\"https://…\"], \"confidence\": \"high\" }\n    ]\n  },\n  \"validation_report\": {\n    \"total_claims\": 6,\n    \"all_claims_supported\": true,\n    \"all_urls_known\": true,\n    \"passed\": true,\n    \"flagged\": []\n  },\n  \"security_flags\": { \"possible_prompt_injection_patterns\": [] },\n  \"usage\": { \"prompt_tokens\": 1234, \"completion_tokens\": 567 }\n}\n```\n\nA claim whose URL was never seen in retrieval is downgraded to `low` confidence\nand listed in `validation_report.flagged` — `passed` is `false` if any claim is\nunsupported or cites an unknown URL.\n\n### From a Claude Code / agent prompt\n\nYou don't construct the JSON yourself — just ask, and the model picks the tool:\n\n```\n\u003e Use deep_research to assess whether small modular reactors are cost-competitive\n  with grid-scale solar, then summarize only the high-confidence claims.\n```\n\n### From a raw MCP client (stdio, for testing)\n\nAny MCP client works. Using the Python SDK that ships with this project:\n\n```python\nimport asyncio\nfrom mcp import ClientSession, StdioServerParameters\nfrom mcp.client.stdio import stdio_client\n\nasync def main():\n    params = StdioServerParameters(\n        command=\"uv\",\n        args=[\"--directory\", \"/abs/path/to/PerplexityAgent\", \"run\", \"perplexity-agent\"],\n        env={\"PERPLEXITY_API_KEY\": \"pplx-...\"},\n    )\n    async with stdio_client(params) as (read, write):\n        async with ClientSession(read, write) as session:\n            await session.initialize()\n            print([t.name for t in (await session.list_tools()).tools])\n            result = await session.call_tool(\n                \"perplexity_search\", {\"query\": \"what is MCP\", \"max_results\": 3}\n            )\n            print(result.content)\n\nasyncio.run(main())\n```\n\nYou can also explore the tools interactively with the\n[MCP Inspector](https://github.com/modelcontextprotocol/inspector):\n\n```bash\nnpx @modelcontextprotocol/inspector uv run perplexity-agent\n```\n\n### Over the HTTP transport\n\nWith the server started via `--transport http`, point any streamable-HTTP MCP\nclient at `http://127.0.0.1:8080/mcp` and send the bearer token:\n\n```\nAuthorization: Bearer \u003cPERPLEXITY_HTTP_AUTH_TOKEN\u003e\n```\n\n## Comet-style TUI (`perplexity-agent tui`)\n\nAn optional interactive terminal app that brings the spirit of Perplexity's\n[Comet](https://www.perplexity.ai/comet/) browser to the terminal, backed by the\nsame Search / Sonar / deep-research client. A terminal can't render web pages, drive\na real browser (clicking, booking, buying), or do voice — those are out of scope by\nphysics. Everything else maps onto terminal-feasible equivalents:\n\n| Comet feature | In the TUI |\n| --- | --- |\n| Assistant sidebar | A persistent chat pane (Sonar) that answers with your open \"tabs\" as context |\n| Answer-first search | `/search` — ranked results **plus** a grounded, cited answer |\n| Open / summarize a page | `/open \u003curl\u003e` — SSRF-guarded fetch → readable text → one-click summary |\n| Ask about / translate a page | `/ask \u003cq\u003e`, `/translate \u003clang\u003e` on the current page |\n| Chat with your tabs / synthesis | `/summary` across all open tabs; bare chat is tab-aware |\n| AI tab grouping | `/group` clusters open tabs into named groups |\n| Deep research | `/research \u003cq\u003e` runs the full validated, cited pipeline |\n| Memory \u0026 Spaces | Local SQLite store (owner-only 0600; tabs dedupe per space+URL, newest 50 kept); `/space [name]` switches workspaces |\n| Background / scheduled tasks | `/task search\\|fetch \u003cseconds\u003e \u003ctarget\u003e` monitors and alerts on change; `/untask \u003cid\u003e` stops it |\n| Agentic task planning | Research-only planning (decompose a goal); **no real web actions** |\n\nInstall the extra and launch it (needs `PERPLEXITY_API_KEY`, same as the server):\n\n```bash\nuv sync --extra tui\nuv run perplexity-agent tui\n```\n\nThe page fetcher (`/open`) is the only egress path other than the Perplexity API and\nis reachable **only from the TUI**, never via the MCP tools. It is SSRF-hardened\n(scheme allowlist, private/loopback/link-local IPs rejected on every redirect hop,\nconnections pinned to the validated IP, a streaming size cap, and a text-only\ncontent-type allowlist) and flags fetched text for indirect prompt injection before\nit reaches Sonar. See [`SECURITY.md`](SECURITY.md). The MCP tool surface is unchanged.\n\n## Configuration\n\nAll optional knobs are environment variables (see [`.env.example`](.env.example)):\ntimeouts, response-size cap, retry count, rate limits, an optional JSON audit-log\npath, and (for the TUI) the fetch User-Agent, `PERPLEXITY_FETCH_ALLOW_PRIVATE`, and\n`PERPLEXITY_STORE_PATH`.\n\n## Development\n\n```bash\nuv sync --extra dev --extra tui   # add --extra tui to exercise the TUI tests\nuv run pytest          # unit tests (no live API needed; httpx is mocked)\nuv run ruff check .    # lint\nuv run mypy src        # type check (strict)\nuv run pip-audit       # dependency vulnerability scan\n```\n\n## License\n\nMIT — see [`LICENSE`](LICENSE).\n\nProudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcryptojones%2Fperplexityagent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcryptojones%2Fperplexityagent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcryptojones%2Fperplexityagent/lists"}