Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
a7db231
feat: switch to Streamable HTTP transport (Phase 1)
aaearon Feb 25, 2026
e874f18
feat: add token auth bridge for per-user OAuth sessions (Phase 2)
aaearon Feb 25, 2026
f5c0b55
feat: add token verifier and session manager (Phase 3)
aaearon Feb 25, 2026
e1d5948
feat: wire OAuth auth into FastMCP server (Phase 4)
aaearon Feb 25, 2026
77f3b4d
docs: update documentation for OAuth migration (Phase 5)
aaearon Feb 25, 2026
f4de4c0
refactor: hardcode well-known OIDC app ID, remove CYBERARK_OAUTH_APP_…
aaearon Feb 25, 2026
2df1dd1
feat: add MCP_TRANSPORT env var for explicit transport selection
aaearon Feb 25, 2026
9b90998
feat: add Dockerfile and docker-compose for streamable-http deployment
aaearon Feb 25, 2026
f0fdc9c
fix: install git in Docker builder stage for ark-sdk-python
aaearon Feb 25, 2026
31c3d36
fix: copy README.md into Docker builder for hatchling build
aaearon Feb 25, 2026
97d1958
feat: add MCP_SERVER_URL for public HTTPS OAuth metadata
aaearon Feb 25, 2026
37235e8
feat: serve /.well-known/oauth-authorization-server via OIDC discover…
aaearon Feb 25, 2026
0190a9b
fix: point authorization_servers to MCP server URL for metadata disco…
aaearon Feb 25, 2026
c368a27
feat: add Dynamic Client Registration proxy for claude.ai OAuth flow
aaearon Feb 25, 2026
7290e89
feat: switch OIDC app ID to custom mcpprivilegecloud app
aaearon Feb 25, 2026
3d1dd6d
fix: use app-specific CyberArk Identity OAuth endpoints
aaearon Feb 25, 2026
b8ccb77
feat: support confidential client DCR with CYBERARK_CLIENT_ID/SECRET
aaearon Feb 25, 2026
8ff7e03
docs: add claude.ai OAuth integration investigation report
aaearon Feb 26, 2026
e58eb10
fix: add ctx parameter to all 48 MCP tools for OAuth per-user sessions
aaearon Feb 26, 2026
3c86291
chore: sync OAuth tests and docs with configurable OIDC app ID
aaearon Feb 26, 2026
f583f3f
fix: resolve JWKS URI from OIDC discovery instead of hardcoding
aaearon Feb 26, 2026
71d75cd
fix: log token aud/iss/sub claims on verification failure for debugging
aaearon Feb 26, 2026
82d318b
fix: use CYBERARK_CLIENT_ID as expected JWT audience
aaearon Feb 26, 2026
d0194d7
fix: add CYBERARK_OAUTH_AUDIENCE for JWT audience validation
aaearon Feb 26, 2026
7267933
fix: log JWT subdomain/platform_domain/unique_name for URL debugging
aaearon Feb 26, 2026
ca109d0
fix: simplify OAuth env var config and fix DCR client_id resolution
aaearon Feb 26, 2026
6bf0560
fix: add CYBERARK_SUBDOMAIN to override PCloud URL resolution
aaearon Feb 26, 2026
77490a1
fix: add CYBERARK_SUBDOMAIN to docker-compose environment
aaearon Feb 26, 2026
b27b49f
feat: service account token bridge for OAuth mode
aaearon Feb 26, 2026
eeb322c
fix: audience priority — CYBERARK_OAUTH_AUDIENCE takes precedence
aaearon Feb 26, 2026
eb076cf
chore: technical debt cleanup — remove dead code, fix docs, clean pro…
aaearon Feb 27, 2026
f3f02be
chore: remove remaining dead code found by audit
aaearon Feb 27, 2026
afb972d
fix: handle empty MCP_SERVER_URL from docker-compose env vars
aaearon Feb 27, 2026
a108fb9
fix: OAuth metadata issuer mismatch causing auth loop
aaearon Feb 27, 2026
f05c916
fix: issuer trailing slash mismatch causing OAuth auth loop
aaearon Feb 27, 2026
fd86d5d
fix: include /mcp path in issuer_url and resource_server_url
aaearon Feb 27, 2026
ae0e306
fix: add jwks_uri to OAuth metadata (required per RFC 8414)
aaearon Feb 27, 2026
1f08a2d
fix: proxy authorize/token endpoints for same-origin OAuth metadata
aaearon Feb 27, 2026
154b99e
fix: accept multiple JWT audience values for token verification
aaearon Feb 27, 2026
025a1bf
docs: add reverse proxy trailing slash fix for OAuth
aaearon Feb 27, 2026
f13b44c
fix: strip trailing slashes to prevent OAuth header loss on 307 redirect
aaearon Feb 27, 2026
b952f7f
refactor: remove dead code and fix documentation inconsistencies
aaearon Feb 28, 2026
15910bb
chore: remove dead CYBERARK_SUBDOMAIN env var
aaearon Mar 6, 2026
556721b
refactor: extract _make_jwt helper to shared tests/helpers.py
aaearon Mar 6, 2026
8e201de
fix: prevent credential leakage in DCR and harden token proxy
aaearon Mar 6, 2026
0be1dd3
chore: run Docker as non-root, remove debug logging and dead code
aaearon Mar 6, 2026
0caf639
refactor: deduplicate test suite, removing 14 redundant tests
aaearon Mar 7, 2026
06b1cef
chore: remove CYBERARK_OAUTH_AUDIENCE env var
aaearon Mar 7, 2026
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
21 changes: 21 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.venv/
venv/
__pycache__/
*.pyc
.git/
.github/
.pytest_cache/
htmlcov/
.coverage
*.egg-info/
dist/
build/
.env
.env.*
!.env.example
*.log
.mypy_cache/
.ruff_cache/
.serena/
tests/
docs/
54 changes: 47 additions & 7 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,9 +1,49 @@
# CyberArk Privilege Cloud Configuration
# Copy this file to .env and fill in your actual values
# CyberArk Privilege Cloud MCP Server Configuration
# Copy this file to .env and fill in your actual values.
#
# WHY TWO SETS OF CREDENTIALS?
# The MCP server needs two separate credential pairs:
# 1. Service account (CYBERARK_CLIENT_ID/SECRET) - A CyberArk Identity user marked
# as "OAuth 2.0 confidential client". Used to obtain a PCloud platform token for
# all API calls. All users share this service account's PCloud permissions.
# 2. OIDC app (CYBERARK_OAUTH_CLIENT_ID/SECRET) - Auto-generated credentials from
# the OIDC app's Trust tab. Used for the OAuth authorization flow (DCR,
# /token proxy) so end-users authenticate with their own identity.
# See docs/CYBERARK_IDENTITY_SETUP.md for full setup instructions.

# Required: Service account credentials
CYBERARK_CLIENT_ID=your-service-account-username
CYBERARK_CLIENT_SECRET=your-service-account-password
# ============================================================
# OAuth Per-User Mode (Recommended)
# ============================================================
# Triggers OAuth mode; each user authenticates with their own identity.

# Optional: Log level (default: INFO)
CYBERARK_LOG_LEVEL=INFO
# Required: CyberArk Identity tenant URL
CYBERARK_IDENTITY_TENANT_URL=https://your-tenant.id.cyberark.cloud

# Service account credentials (must be marked "OAuth 2.0 confidential client" in CyberArk Identity).
# Used for: PCloud platform token (all API calls).
[email protected]
CYBERARK_CLIENT_SECRET=service-account-password
# ============================================================
# Required: OIDC App Credentials (from Trust tab in CyberArk Identity)
# ============================================================
# These are the OIDC app credentials used for DCR (Dynamic Client Registration)
# and JWT audience validation. Found on the Trust tab of your OIDC app in CyberArk Identity.
CYBERARK_OAUTH_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
CYBERARK_OAUTH_CLIENT_SECRET=oidc-app-client-secret

# ============================================================
# Transport & Server Settings (Optional)
# ============================================================

# Transport protocol: stdio (default), sse, or streamable-http
# MCP_TRANSPORT=streamable-http

# Server bind settings (HTTP transports only)
# MCP_HOST=127.0.0.1
# MCP_PORT=8000

# Public URL for OAuth metadata (defaults to http://{host}:{port})
# MCP_SERVER_URL=https://mcp.example.com

# Log level
# CYBERARK_LOG_LEVEL=INFO
38 changes: 4 additions & 34 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,14 @@ __pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# Virtual environments
venv/
env/
ENV/
env.bak/
venv.bak/
.venv/

# Environment variables and secrets
.env
Expand All @@ -37,14 +21,10 @@ venv.bak/
*.p12
*.pfx
secrets.json
config.json
credentials.json

# CyberArk specific secrets
cyberark_credentials.*
*_credentials.*
api_keys.*
tokens.*

# Testing
.coverage
Expand All @@ -61,26 +41,20 @@ htmlcov/

# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Logs
*.log
logs/
debug.log
error.log

# PoC files
poc_*

# MCP
*.mcp.json
mcp_server_config.json

# Claude Desktop configs with credentials
claude_desktop_config.json
*config*windows.json

# Temporary files
*.tmp
Expand All @@ -91,7 +65,3 @@ claude_desktop_config.json
# Backup files
*.bak
*.backup
*~

# serena
.serena/cache/*
76 changes: 48 additions & 28 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

**BEFORE CODING**:
1. **Always read this entire CLAUDE.md file first** - Contains critical patterns and constraints
2. **Check current test status** - All changes must maintain 210+ passing tests
2. **Check current test status** - All changes must maintain 269+ passing tests
3. **Follow existing patterns** - Simplified architecture patterns are established and documented
4. **Use official SDK** - All CyberArk operations MUST use ark-sdk-python (never direct HTTP)
5. **MANDATORY: Use context7 MCP tools for ALL API documentation** - Before working with any library or API, use context7 MCP server tools to get up-to-date documentation
Expand Down Expand Up @@ -96,9 +96,9 @@ Use context7 resolve-library-id and get-library-docs tools:

**Purpose**: MCP server for CyberArk Privilege Cloud integration, enabling AI assistants to securely manage privileged accounts.

**Current Status**: ✅ **MODERNIZED MCP ARCHITECTURE** - Production ready with FastMCP lifespan management, context injection, typed response models, and complete PCloud service coverage
**Last Updated**: January 31, 2026
**Recent Achievement**: Modernized MCP server architecture leveraging mcp SDK v1.26.0 features. Implemented lifespan management for proper server lifecycle, context injection for dependency access in tools, and Pydantic response models for typed returns. Added 50+ new tests bringing total to 213 passing tests with zero regression.
**Current Status**: ✅ **SERVICE ACCOUNT TOKEN BRIDGE COMPLETE** - OAuth mode verifies user identity via OIDC JWT, then uses a shared service account platform token for all PCloud API calls.
**Last Updated**: February 27, 2026
**Recent Achievement**: Technical debt cleanup — removed dead code (session_manager.py, token_auth.py, HTTP bypasses, unused server methods), simplified AppContext to use `is_oauth` flag, fixed token_verifier exception handling, updated all documentation. 276 passing tests with zero regression.

## Architecture

Expand All @@ -113,7 +113,7 @@ The server provides 53 MCP tools for comprehensive CyberArk operations, built on
- **Safe Management Tools (10 tools)**: Complete CRUD plus member management - `list_safes`, `get_safe_details`, `add_safe`, `update_safe`, `delete_safe`, `list_safe_members`, `get_safe_member_details`, `add_safe_member`, `update_safe_member`, `remove_safe_member`
- **Platform Management Tools (12 tools)**: Complete lifecycle plus statistics - `list_platforms`, `get_platform_details`, `import_platform_package`, `export_platform`, `duplicate_target_platform`, `activate_target_platform`, `deactivate_target_platform`, `delete_target_platform`, `get_platform_statistics`, `get_target_platform_statistics`
- **Applications Management Tools (8 tools)**: Complete application lifecycle - `list_applications`, `get_application_details`, `add_application`, `delete_application`, `list_application_auth_methods`, `get_application_auth_method_details`, `add_application_auth_method`, `delete_application_auth_method`, `get_applications_stats`
- **Session Monitoring Tools (5 tools)**: Privileged session monitoring and analytics - `list_sessions`, `list_sessions_by_filter`, `get_session_details`, `list_session_activities`, `count_sessions`, `get_session_statistics`
- **Session Monitoring Tools (6 tools)**: Privileged session monitoring and analytics - `list_sessions`, `list_sessions_by_filter`, `get_session_details`, `list_session_activities`, `count_sessions`, `get_session_statistics`

> **Complete PCloud Coverage**: All tools provide comprehensive coverage of CyberArk's 5 PCloud services with enterprise-grade CRUD operations, advanced analytics, member management, and privileged session monitoring. Built on official ark-sdk-python library with 210+ passing tests and zero regression.

Expand Down Expand Up @@ -274,7 +274,7 @@ The codebase underwent a systematic simplification process achieving **~27% code
- **Simplified Testing**: Cleaner test patterns with reduced mocking complexity

**Performance & Reliability**:
- **Zero Functional Regression**: All 210+ tests passing with complete functionality coverage
- **Zero Functional Regression**: All 261+ tests passing with complete functionality coverage
- **Preserved SDK Integration**: Official ark-sdk-python patterns maintained
- **Graceful Error Handling**: Centralized error management with consistent logging
- **Backward Compatibility**: No breaking changes to MCP tool interfaces
Expand Down Expand Up @@ -309,38 +309,52 @@ async def your_new_tool(
**🤖 MANDATORY PATTERN: Lifespan Management**
```python
# Server lifecycle is managed via app_lifespan context manager
# Access server through ctx.request_context.lifespan_context.server
# Dual mode: OAuth (is_oauth=True) or legacy (is_oauth=False)
@dataclass
class AppContext:
server: CyberArkMCPServer
server: Optional[CyberArkMCPServer] = None
is_oauth: bool = False

@asynccontextmanager
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
cyberark_server = CyberArkMCPServer.from_environment()
try:
if is_oauth_mode():
yield AppContext(server=cyberark_server, is_oauth=True)
else:
yield AppContext(server=cyberark_server)
finally:
# Cleanup resources
pass
```

**🤖 FILE STRUCTURE GUIDE**:
- `sdk_auth.py` - Authentication only, no business logic
- `server.py` - Business logic with @handle_sdk_errors decorator
- `mcp_server.py` - MCP tools with lifespan management and context injection
- `models.py` - Pydantic response models for typed returns
- `exceptions.py` - Custom exceptions only
- `sdk_auth.py` - Service account authentication (legacy mode)
- `token_verifier.py` - JWT verification via CyberArk Identity JWKS (MCP TokenVerifier protocol)
- `server.py` - Business logic with @handle_sdk_errors decorator + `from_token()` factory
- `mcp_server.py` - MCP tools with dual-mode lifespan, context injection, Streamable HTTP transport, RFC 8414 metadata route
- `exceptions.py` - Custom exceptions: CyberArkAPIError, SDK compatibility layer

### Testing Validation ✅ **VERIFIED**
- **210+ tests passing** - Zero functionality regression with comprehensive expansion
- **Test Coverage Maintained** - All expansion preserved existing test patterns
- **261+ tests passing** - Zero functionality regression across all phases
- **Test Coverage Maintained** - 16 token verifier + 14 OAuth integration tests added
- **Integration Tests Updated** - MCP tool parameter passing verified for all 53 tools
- **Performance Baseline** - No degradation in execution performance

## Configuration

**Required Environment Variables**:
- `CYBERARK_CLIENT_ID` - OAuth service account username
**Required Environment Variables** (OAuth per-user mode):
- `CYBERARK_IDENTITY_TENANT_URL` - CyberArk Identity tenant URL (e.g., `https://abc1234.id.cyberark.cloud`)
- `CYBERARK_CLIENT_ID` - Service account login name (for PCloud platform token access)
- `CYBERARK_CLIENT_SECRET` - Service account password
- `CYBERARK_OAUTH_CLIENT_ID` - OIDC app client ID from Trust tab (for DCR and /token proxy)
- `CYBERARK_OAUTH_CLIENT_SECRET` - OIDC app client secret from Trust tab (injected server-side by /token proxy, never exposed via DCR)

**Optional Environment Variables**:
- `CYBERARK_OIDC_APP_ID` - OIDC app name in URL paths (default: `mcpprivilegecloud`)
- `MCP_TRANSPORT` - Transport protocol: `stdio`, `sse`, or `streamable-http` (default: `stdio`)
- `MCP_HOST` - Server bind host (default: `127.0.0.1`)
- `MCP_PORT` - Server bind port (default: `8000`)
- `MCP_SERVER_URL` - Public URL for metadata (default: `http://{host}:{port}`)

**Legacy Environment Variables** (service account mode -- fallback):
- `CYBERARK_CLIENT_ID` - Service account login name
- `CYBERARK_CLIENT_SECRET` - Service account password

*See README.md for complete configuration details*
Expand Down Expand Up @@ -379,24 +393,30 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
### Entry Points

#### Standardized Execution Methods (Recommended)
- **`uvx mcp-privilege-cloud`** - Primary production execution method
- **`uvx mcp-privilege-cloud`** - Primary production execution (stdio by default)
- **`uv run mcp-privilege-cloud`** - Development execution with dependency management
- **`python -m mcp_privilege_cloud`** - Standard Python module execution
- **Transport**: Controlled by `MCP_TRANSPORT` env var (default: `stdio`). Set to `streamable-http` for HTTP on `MCP_HOST`:`MCP_PORT`.

#### Legacy Entry Points (Deprecated)
- **`run_server.py`** - Legacy multiplatform entry point (removed in SDK migration)
- **`python src/mcp_privilege_cloud/mcp_server.py`** - Legacy direct execution (deprecated)

## Testing Strategy

**Test Files**: 160+ total tests across 9 test files
**Test Files**: 261+ total tests across 15 test files
- `tests/test_core_functionality.py` - Authentication, server core, platform management (comprehensive error handling)
- `tests/test_account_operations.py` - Account lifecycle management with CRUD operations
- `tests/test_account_operations.py` - Account lifecycle management with CRUD operations
- `tests/test_applications_service.py` - Applications service testing with authentication methods
- `tests/test_mcp_integration.py` - MCP tool wrappers and integration testing
- `tests/test_integration_tools.py` - End-to-end integration tests for all tools
- `tests/test_enhanced_error_handling.py` - Enhanced error handling validation
- `tests/test_enhanced_error_messages.py` - Error message consistency testing
- `tests/test_transport.py` - Streamable HTTP transport configuration tests
- `tests/test_token_verifier.py` - CyberArkTokenVerifier JWT verification tests
- `tests/test_oauth_integration.py` - Full OAuth integration: dual-mode, execute_tool, lifespan
- `tests/test_oauth_metadata.py` - RFC 8414 `/.well-known/oauth-authorization-server` endpoint tests
- `tests/test_env_var_resolution.py` - Environment variable priority chain tests (DCR)
- Additional test files for comprehensive coverage of all 53 tools

**Key Commands**:
Expand Down Expand Up @@ -462,7 +482,7 @@ async def get_account_password(account_id: str) -> Dict[str, Any]:
2. **NEVER bypass patterns** - Always use @handle_sdk_errors decorator
3. **ALWAYS follow TDD** - Write failing test first, then implementation
4. **SDK-only operations** - Never create direct HTTP requests
5. **Preserve test coverage** - All 210+ tests must continue passing
5. **Preserve test coverage** - All 261+ tests must continue passing
6. **Use existing models** - Leverage ark-sdk-python model classes

**🔍 Mandatory Context7 Workflow**:
Expand All @@ -473,17 +493,17 @@ async def get_account_password(account_id: str) -> Dict[str, Any]:
- get-library-docs with the resolved ID
2. Write failing test using current patterns
3. Implement using up-to-date SDK methods
4. Verify all 210+ tests still pass
4. Verify all 261+ tests still pass
```

## References

- **README.md** - Complete setup and configuration documentation
- **ARCHITECTURE.md** - System architecture and component details
- **docs/ARCHITECTURE.md** - System architecture and component details
- **DEVELOPMENT.md** - Development workflows and procedures
- **INSTRUCTIONS.md** - Development workflow and coding standards
- **docs/API_REFERENCE.md** - Complete tool specifications and examples
- **docs/TESTING.md** - Comprehensive testing guidelines and procedures
- **docs/CYBERARK_IDENTITY_SETUP.md** - CyberArk Identity OAuth app configuration guide

---

Expand Down
36 changes: 36 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FROM python:3.12-slim AS builder

RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir uv

WORKDIR /app

# Copy dependency files first for layer caching
COPY pyproject.toml uv.lock ./

# Install dependencies (without the project itself)
RUN uv sync --frozen --no-install-project

# Copy source and files needed by hatchling build
COPY src/ src/
COPY README.md ./

# Install the project
RUN uv sync --frozen

FROM python:3.12-slim

RUN useradd --create-home --no-log-init appuser

WORKDIR /app

# Copy the entire virtual environment and project from builder
COPY --from=builder /app /app

ENV PATH="/app/.venv/bin:$PATH"

USER appuser

EXPOSE 8000

ENTRYPOINT ["mcp-privilege-cloud"]
Loading