Skip to content

Conversation

@mjunaidca
Copy link
Collaborator

Summary

  • Integrates PanaversityFS MCP server with Panaversity SSO (Better Auth)
  • Implements dual authentication: JWT/JWKS for user sessions, API keys for M2M
  • Uses MCP SDK's auth contextvar for FR-021 compliant audit logging
  • Adds CORS middleware for browser-based MCP clients (Inspector)

Key Changes

Authentication (auth.py)

  • JWKSTokenVerifier: RS256 JWT validation via JWKS endpoint
  • APIKeyVerifier: M2M API key verification via SSO API
  • CombinedTokenVerifier: Routes by prefix (sk_live_, pana_ → API key, else → JWT)

Audit Logging (audit.py)

  • Uses get_access_token() from MCP SDK auth contextvar
  • Automatically extracts client_id from verified token
  • Falls back to "dev-mode-agent" when auth disabled

Configuration (config.py)

  • PANAVERSITY_AUTH_SERVER_URL: Enables auth when set
  • Auto-computes JWKS and API key verify URLs
  • Configurable cache TTLs for tokens

Server (server.py)

  • CORS middleware allows all origins for MCP clients
  • Proper lifespan management for sessions

Test plan

  • All 317 tests pass
  • Unit tests for auth components (JWKSTokenVerifier, APIKeyVerifier, DualAuthValidator)
  • Integration tests updated with mock_context fixture
  • Manual test with real SSO server and API key

Auth Flow

Client → Authorization: Bearer <token>
       ↓
CombinedTokenVerifier.verify_token()
       ↓
   prefix check
   ├── sk_live_*, pana_* → APIKeyVerifier → AuthContext
   └── other → JWKSTokenVerifier → AccessToken
       ↓
AuthContextMiddleware → stores in contextvar
       ↓
get_access_token() → audit.py → client_id for logging

🤖 Generated with Claude Code

mjunaidca and others added 8 commits December 4, 2025 11:10
…ersityFS hardening (Phase 1-2)

Phase 1 (Setup):
- Add sqlalchemy, asyncpg, aiosqlite, alembic, prometheus-client, hypothesis
- Initialize Alembic migrations
- Add database_url to config

Phase 2 (Foundational):
- Database: FileJournal and AuditLog SQLAlchemy models with CHECK constraints
- Connection: Async session factory with SQLite/PostgreSQL support
- Migration: Initial schema with indexes and constraints
- Path validation: CONTENT_PATH_PATTERN, ASSET_PATH_PATTERN, overlay validation
- Metrics: Prometheus counters, histograms, gauges with @Instrument decorators
- Errors: SchemaViolationError, HashRequiredError

Tests: 74 passing (42 path_utils, 20 metrics, 12 journal)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Phase 3 - User Story 1: Streaming Archives (T018-T023)
- Refactor bulk.py with StreamingArchiveBuffer for 64MB memory cap
- Add @instrument_archive decorator for metrics
- Add archive_memory gauge tracking
- Create integration tests for streaming ZIP generation
- Create performance tests for SC-001/R4 validation

Phase 4 - User Story 2: Conflict Detection (T024-T033)
- Rename file_hash to expected_hash in WriteContentInput (FR-003)
- Integrate FileJournal queries into write_content() (FR-002)
- Add HashRequiredError for updates without expected_hash (FR-004)
- Add atomic journal+storage transactions with rollback (FR-002)
- Add mode: "created"|"updated" to write response (FR-005)
- Apply @instrument_write decorator
- Create integration tests for conflict detection
- Create property tests for journal-storage consistency (R2)

Test suite: 214 tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
…ing (Phase 5-6)

Delta Build System (FR-025/026/027):
- Add plan_build tool with manifest-hash-based delta detection
- Add ManifestSnapshot table for historical state tracking
- Return only changed files (added/modified/deleted) for CI/CD efficiency
- Add delta_build tool for timestamp-based change detection

Validation Tools:
- Add validate_book tool for structural integrity checks
- Add schema enforcement for content (FR-007) and assets (FR-008)
- Integrate validation into read/write/delete operations

Archive Improvements:
- Rewrite get_book_archive to use disk-based streaming
- Produce single ZIP file for Docusaurus compatibility
- Maintain <64MB memory constraint via tempfile approach

Presigned URL Support:
- Add presign_read/presign_write to storage layer
- Update upload_asset to return presigned URLs for large files

Documentation:
- Add comprehensive developer guide (7 chapters)
- Update MCP-TOOLS.md with new tool documentation

Testing:
- Add 20 delta build tests covering all scenarios
- Add property tests for R1 (schema), R5 (overlay), R6 (audit), R7 (agent)
- Add unit tests for audit chain, overlay content, validation
- All 313 tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Following MCP SDK best practices:

Database Lifecycle (app.py):
- Add app_lifespan context manager with AppContext dataclass
- Initialize database on startup via init_db()
- Dispose database engine on shutdown via reset_engine()
- Pass lifespan to FastMCP constructor

Session Manager Lifecycle (server.py):
- Add starlette_lifespan for MCP session manager
- Wrap streamable_http_app in Starlette with lifespan
- Ensures proper session cleanup on server shutdown

This fixes potential connection leaks when the server shuts down,
as the database engine was never being disposed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Problem:
- Previous design used singleton engine with connection pool
- Connections stayed open for server lifetime
- Not suitable for serverless (Cloud Run) or cloud DBs (Neon, Supabase)

Solution:
- Use NullPool - no local connection pooling
- Each request creates fresh connection, disposed immediately after
- Cloud database providers (Neon PgBouncer, Supabase) handle pooling
- Removed unnecessary lifespan management from app.py

Pattern for cloud databases:
```python
engine = create_async_engine(url, poolclass=NullPool)
async with session:
    # ... use session
# Connection released back to cloud pooler
await engine.dispose()
```

Benefits:
- Works with serverless (no warm connections needed)
- Compatible with Neon, Supabase, PlanetScale connection poolers
- No connection leaks on server restart
- Simpler lifecycle - no startup/shutdown hooks needed

Test fixtures updated to use StaticPool for in-memory SQLite.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Implements dual authentication for PanaversityFS MCP server:
- JWT/JWKS verification for user sessions (RS256)
- API key verification for M2M (GitHub Actions, agents)
- CombinedTokenVerifier routes by prefix (sk_live_, pana_)
- FR-021 audit logging via MCP SDK auth contextvar
- CORS middleware for browser-based MCP clients

All 317 tests pass with updated mock_context fixtures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copilot AI review requested due to automatic review settings December 4, 2025 18:28
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR integrates PanaversityFS MCP server with Panaversity SSO (Better Auth) and introduces FR-021 compliant audit logging by extracting real agent identifiers from authenticated contexts.

Key Changes:

  • Replaces JWT-only authentication with dual-mode system supporting both RS256 JWT (JWKS) and M2M API keys
  • Implements CombinedTokenVerifier that routes authentication by token prefix
  • Updates audit logging to extract client_id from MCP SDK's auth contextvar instead of hardcoded "system"
  • Adds CORS middleware for browser-based MCP clients (Inspector)

Reviewed changes

Copilot reviewed 85 out of 86 changed files in this pull request and generated no comments.

Show a summary per file
File Description
specs/039-panaversity-fs-hardening/* New specification documents for production hardening with authentication integration
panaversity-fs/tests/unit/test_auth.py Refactored authentication tests for new dual-auth architecture
panaversity-fs/tests/unit/test_search_tools.py Updated search tool tests to accept mock_context parameter
panaversity-fs/tests/unit/test_* Multiple test files updated to pass mock_context parameter to tool functions
panaversity-fs/src/panaversity_fs/tools/search.py Updated search tools to accept Context parameter, removed hardcoded "system" agent_id

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

mjunaidca and others added 6 commits December 5, 2025 00:29
- Auto-convert postgresql:// and postgres:// to postgresql+asyncpg://
- Convert sslmode=require to ssl=require for asyncpg compatibility
- Use TIMESTAMP(timezone=True) for all datetime columns
- Add migration for timezone-aware timestamps
- Fix .env.example: PANAVERSITY_DATABASE_URL prefix, Bearer auth docs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Add apiKey config option to MCPHttpClient constructor
- Pass Authorization: Bearer header in all HTTP requests
- Add PANAVERSITY_API_KEY environment variable support
- Update .env.example with API key configuration
- Log auth status on plugin startup

Enables Docusaurus to fetch content from authenticated PanaversityFS
MCP server using API keys issued by Panaversity SSO.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Add Authorization: Bearer header for API key authentication
- Wrap tool arguments in 'params' object (MCP SDK Pydantic requirement)
- Required secrets: PANAVERSITY_SERVER_URL, PANAVERSITY_API_KEY

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
OpenDAL adds bucket name automatically. Including it in endpoint causes
double bucket path (bucket/bucket/path) and 404 errors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
- Add AbortController-based timeout to callTool method
- Default 2 minute timeout (book fetch can be slow)
- Configurable via timeoutMs option
- Better error messages for timeouts

Fixes MCP timeout errors when fetching large book content.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@mjunaidca mjunaidca merged commit f43cd75 into main Dec 5, 2025
1 check passed
@mjunaidca mjunaidca deleted the 039-panaversity-fs-hardening branch December 5, 2025 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants