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
46 changes: 43 additions & 3 deletions BACKLOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,52 @@
# Code Review Backlog

This document captures findings from a comprehensive code review of the PerplexityAgent
codebase. Each finding is also tracked as a GitHub Issue. Issues are grouped by priority.
This document captures findings from code reviews of the PerplexityAgent codebase.
Each finding is also tracked as a GitHub Issue. Issues are grouped by priority.

## Active Review Findings (2026-10-06)

**Summary:** 201 tests pass (90.02% coverage, gate: 85%), ruff check clean, mypy strict clean, 0 security vulnerabilities.
Tracked under GitHub issues #131–#135.

### P2: Update SECURITY.md to reflect MCP 2026-07-28 stateless capability model
- **Issue:** [#131](https://github.com/CryptoJones/PerplexityAgent/issues/131)
- **File:** `SECURITY.md` (lines 81–83)
- **Description:** Commit `daa358a` upgraded to the MCP `2026-07-28` stateless specification, eliminating transport-level sessions in favor of shared namespaces (`_STORE_NAMESPACE = "shared"`) where possession of capability tokens (`retrieve_key` or `response_id`) authorizes retrieval. `SECURITY.md` still describes legacy per-session scoping where requests from other sessions are rejected.
- **Action:** Update `SECURITY.md` to reflect the stateless capability model accurately.
- **Status:** Resolved

### P3: Reformat codebase with ruff format and enforce in CI
- **Issue:** [#132](https://github.com/CryptoJones/PerplexityAgent/issues/132)
- **Files:** 11 files across `src/perplexity_agent/` and `tests/`
- **Description:** Running `uv run ruff format --check` identifies 11 files with minor formatting drift (multi-line call/dict wrapping).
- **Action:** Run `uv run ruff format` to normalize all files, and consider adding `uv run ruff format --check` to `.github/workflows/ci.yml`.

### P3: Deprecate or remove unused Store.response_owner in memory.py
- **Issue:** [#133](https://github.com/CryptoJones/PerplexityAgent/issues/133)
- **File:** `src/perplexity_agent/memory.py` (lines 235–241)
- **Description:** `Store.response_owner` was originally invoked to check session ownership before returning stored responses. With the transition to stateless capability handles in `server.py`, it is no longer called in `src/`.
- **Action:** Deprecate or remove `response_owner` and note the transition.

### P3: Update stale FastMCP comment in pyproject.toml
- **Issue:** [#134](https://github.com/CryptoJones/PerplexityAgent/issues/134)
- **File:** `pyproject.toml` (lines 74–77)
- **Description:** A comment under `[tool.mypy.overrides]` still refers to `FastMCP` instead of `MCPServer`.
- **Action:** Update the comment to reference `MCPServer`.

### P3: Add .env.bak* to .gitignore
- **Issue:** [#135](https://github.com/CryptoJones/PerplexityAgent/issues/135)
- **File:** `.gitignore`
- **Description:** Backup `.env` files (e.g. `.env.bak.1782075379`) are currently untracked, risking accidental secret leakage if committed.
- **Action:** Add `.env.bak*` to `.gitignore`.

---

## Historical Review Findings (Resolved)

**Status: resolved.** All findings and testing gaps are implemented with regression
coverage; GitHub issues #86–#99 track the corresponding changes.

## Summary
### Summary

- **200 tests pass**, **89.94% coverage** (gate: 85%), **ruff clean**, **mypy strict clean**
- **0 critical vulnerabilities** found
Expand Down
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.4.0] - 2026-10-06

### Changed

- **Adopt MCP revision `2026-07-28` (the stateless revision) via the `mcp` 2.x
Expand All @@ -23,6 +25,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
is now an unguessable random capability token (was a content hash), and holding
a `retrieve_key` / `response_id` is the authorization to fetch it.

### Documentation

- Update `SECURITY.md` to reflect the MCP `2026-07-28` stateless capability model,
clarifying that unguessable capability tokens (`retrieve_key`) and `response_id`
handles authorize access across shared namespaces in place of transport sessions (#131).

### Fixed

- **The stdio transport crashed on any JSON-RPC request larger than 64 KiB.** The
Expand Down Expand Up @@ -225,7 +233,8 @@ validate citations) over stdio, with an optional bearer-token HTTP transport.
Strict pydantic input validation, token-bucket rate limiting, redacting JSON
audit log, and CI (ruff, pytest, pip-audit, gitleaks, CodeQL).

[Unreleased]: https://codeberg.org/CryptoJones/PerplexityAgent/compare/v0.3.1...HEAD
[Unreleased]: https://codeberg.org/CryptoJones/PerplexityAgent/compare/v0.4.0...HEAD
[0.4.0]: https://codeberg.org/CryptoJones/PerplexityAgent/compare/v0.3.1...v0.4.0
[0.3.1]: https://codeberg.org/CryptoJones/PerplexityAgent/compare/v0.3.0...v0.3.1
[0.3.0]: https://codeberg.org/CryptoJones/PerplexityAgent/compare/v0.2.0...v0.3.0
[0.2.0]: https://codeberg.org/CryptoJones/PerplexityAgent/compare/v0.1.0...v0.2.0
Expand Down
21 changes: 13 additions & 8 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ operating-system account the server runs under.

| NSA recommendation | How PerplexityAgent implements it |
| --- | --- |
| **Choose supported MCP projects** | Built on the official `mcp` Python SDK (FastMCP). Dependencies are pinned and fully locked in `uv.lock`. |
| **Choose supported MCP projects** | Built on the official `mcp` Python SDK (`MCPServer`). Dependencies are pinned and fully locked in `uv.lock`. |
| **Design for boundaries / least privilege** | Default **stdio** transport runs locally with no network exposure. No shell execution, no filesystem writes (except an optional, explicitly-configured audit-log path). The API key lives only in the client layer and is **never** returned by a tool. Egress goes to `api.perplexity.ai`; the optional TUI and explicit `fetch_url` tool add an SSRF-guarded public-page fetcher (see below). |
| **Validate parameters** | Every tool input is validated against a strict `pydantic` model (`schemas.py`) with bounded strings/arrays (including Agent input, model chains, domains, tools, and image payloads), numeric ranges (`max_results` 1–20, `max_steps` 1–10), and constrained enums. Unknown fields are rejected (`extra="forbid"`), preventing parameter smuggling. |
| **Constrain & sandbox tool execution** | Per-request timeouts, a hard response-size cap, and capped retries with jittered backoff (`client.py`). Run the process under seccomp/AppArmor/SELinux or in a container for OS-level isolation (see below). |
Expand Down Expand Up @@ -78,13 +78,18 @@ space retains only the most-recent tabs (default 50, configurable). Conversation
history is unbounded by default — it is never auto-deleted — but an operator can
cap it with `PERPLEXITY_MAX_HISTORY_PER_SPACE`.

Stored Agent response snapshots share this owner-only SQLite database. Each row is
scoped to the originating MCP client session; a known response ID owned by another
session is rejected before continuation or retrieval. Retention is bounded per
session (default 100 snapshots). Automatic function chaining is opt-in and can
invoke only server-operator-registered handlers. Function JSON
schemas, arguments, outputs, and continuation rounds are all bounded; arbitrary
Python supplied by an MCP caller is never evaluated.
Stored Agent response snapshots share this owner-only SQLite database. Under the
MCP revision 2026-07-28 stateless capability model, transport-level sessions are
removed and cross-call state is addressed by server-minted capability handles passed
as ordinary tool arguments. Stored response snapshots are indexed under a shared
namespace (`_STORE_NAMESPACE = "shared"`), where possession of the unguessable
`response_id` provides authorization for continuation or retrieval. Similarly,
offloaded tool results are stashed behind unguessable 96-bit random capability tokens
(`retrieve_key`) in a bounded in-memory store. Retention is bounded (default 100
snapshots in the SQLite store, default 128 entries for offload cache). Automatic
function chaining is opt-in and can invoke only server-operator-registered handlers.
Function JSON schemas, arguments, outputs, and continuation rounds are all bounded;
arbitrary Python supplied by an MCP caller is never evaluated.

## Secret handling

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "perplexity-agent"
version = "0.3.1"
version = "0.4.0"
description = "A simple, security-hardened MCP server exposing the Perplexity Search + Sonar APIs to AI agents (Claude Code, Hermes)."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
2 changes: 1 addition & 1 deletion src/perplexity_agent/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""PerplexityAgent — a security-hardened MCP server for the Perplexity API."""

__version__ = "0.3.1"
__version__ = "0.4.0"
2 changes: 1 addition & 1 deletion uv.lock

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

Loading