Skip to main content
Glama

USPTO Final Petition Decisions MCP Server

by john-walkoe
windows_setup.ps145 kB
# Windows Deployment Script for USPTO Final Petition Decisions MCP # SECURE VERSION - API keys stored in DPAPI encrypted storage, NOT in config files Write-Host "=== USPTO Final Petition Decisions MCP - Windows Setup ===" -ForegroundColor Green Write-Host "" # Get project directory $ProjectDir = Get-Location # Import validation helpers $ValidationModule = Join-Path $PSScriptRoot "Validation-Helpers.psm1" if (Test-Path $ValidationModule) { Import-Module $ValidationModule -Force Write-Host "[OK] Loaded API key validation helpers" -ForegroundColor Green } else { Write-Host "[ERROR] Validation helpers not found: $ValidationModule" -ForegroundColor Red Write-Host " Please ensure Validation-Helpers.psm1 is in the deploy directory" -ForegroundColor Yellow exit 1 } # Audit logging function function Write-AuditLog { param([string]$Event) $auditFile = Join-Path $env:USERPROFILE ".uspto_mcp_audit.log" $timestamp = Get-Date -Format "o" Add-Content -Path $auditFile -Value "[$timestamp] AUDIT: $Event" # Secure the audit log (best-effort, requires admin privileges) if (Test-Path $auditFile) { try { $acl = Get-Acl $auditFile $acl.SetAccessRuleProtection($true, $false) $rule = New-Object System.Security.AccessControl.FileSystemAccessRule( [System.Security.Principal.WindowsIdentity]::GetCurrent().Name, "FullControl", "Allow" ) $acl.SetAccessRule($rule) Set-Acl $auditFile $acl -ErrorAction Stop } catch { # Silently ignore if we don't have SeSecurityPrivilege (requires admin) # Audit log will still work, just without restricted ACL } } } # SECURE: Unified secure storage functions using ENVIRONMENT VARIABLES function Set-UnifiedUsptoKey { <# .SYNOPSIS Securely stores USPTO API key using DPAPI encryption .DESCRIPTION Stores the USPTO API key in encrypted storage using environment variables to pass the key to Python (NOT visible in process list) #> param([string]$ApiKey) try { Set-Location $ProjectDir # ✅ SECURE: Use environment variable (NOT embedded in command) $env:TEMP_USPTO_API_KEY = $ApiKey $result = uv run python -c " import os import sys from pathlib import Path sys.path.insert(0, str(Path('src'))) try: from fpd_mcp.shared_secure_storage import store_uspto_api_key # Read from environment variable (NOT from command line) api_key = os.environ.get('TEMP_USPTO_API_KEY', '') if not api_key: print('ERROR: No API key provided') sys.exit(1) success = store_uspto_api_key(api_key) print('SUCCESS' if success else 'FAILED') except Exception as e: print(f'ERROR: {str(e)}') sys.exit(1) " 2>&1 | Out-String # ✅ CRITICAL: Clear environment variable immediately Remove-Item Env:TEMP_USPTO_API_KEY -ErrorAction SilentlyContinue # Parse result - get last non-empty line (uv may output diagnostic messages to stderr) $lines = $result -split "`n" | Where-Object { $_.Trim() -ne "" } $lastLine = if ($lines.Count -gt 0) { ([string]$lines[-1]).Trim() } else { "" } if ($lastLine -eq "SUCCESS") { Write-Host "[OK] USPTO API key stored in DPAPI encrypted storage" -ForegroundColor Green Write-Host " Location: ~/.uspto_api_key (DPAPI encrypted)" -ForegroundColor Yellow # ✅ SECURE: Mark file as hidden $keyFile = Join-Path $env:USERPROFILE ".uspto_api_key" if (Test-Path $keyFile) { attrib +H "$keyFile" Write-Host " File marked as hidden" -ForegroundColor Yellow } # Log audit event Write-AuditLog "USPTO API key stored via windows_setup.ps1" return $true } else { Write-Host "[ERROR] Failed to store USPTO API key: $result" -ForegroundColor Red return $false } } catch { # Clear environment variable on error too Remove-Item Env:TEMP_USPTO_API_KEY -ErrorAction SilentlyContinue Write-Host "[ERROR] Failed to store USPTO API key: $_" -ForegroundColor Red return $false } } function Set-UnifiedMistralKey { <# .SYNOPSIS Securely stores Mistral API key using DPAPI encryption .DESCRIPTION Stores the Mistral API key in encrypted storage using environment variables to pass the key to Python (NOT visible in process list) #> param([string]$ApiKey) try { Set-Location $ProjectDir # ✅ SECURE: Use environment variable (NOT embedded in command) $env:TEMP_MISTRAL_API_KEY = $ApiKey $result = uv run python -c " import os import sys from pathlib import Path sys.path.insert(0, str(Path('src'))) try: from fpd_mcp.shared_secure_storage import store_mistral_api_key # Read from environment variable (NOT from command line) api_key = os.environ.get('TEMP_MISTRAL_API_KEY', '') if not api_key: print('ERROR: No API key provided') sys.exit(1) success = store_mistral_api_key(api_key) print('SUCCESS' if success else 'FAILED') except Exception as e: print(f'ERROR: {str(e)}') sys.exit(1) " 2>&1 | Out-String # ✅ CRITICAL: Clear environment variable immediately Remove-Item Env:TEMP_MISTRAL_API_KEY -ErrorAction SilentlyContinue # Parse result - get last non-empty line (uv may output diagnostic messages to stderr) $lines = $result -split "`n" | Where-Object { $_.Trim() -ne "" } $lastLine = if ($lines.Count -gt 0) { ([string]$lines[-1]).Trim() } else { "" } if ($lastLine -eq "SUCCESS") { Write-Host "[OK] Mistral API key stored in DPAPI encrypted storage" -ForegroundColor Green Write-Host " Location: ~/.mistral_api_key (DPAPI encrypted)" -ForegroundColor Yellow # ✅ SECURE: Mark file as hidden $keyFile = Join-Path $env:USERPROFILE ".mistral_api_key" if (Test-Path $keyFile) { attrib +H "$keyFile" Write-Host " File marked as hidden" -ForegroundColor Yellow } # Log audit event Write-AuditLog "Mistral API key stored via windows_setup.ps1" return $true } else { Write-Host "[ERROR] Failed to store Mistral API key: $result" -ForegroundColor Red return $false } } catch { # Clear environment variable on error too Remove-Item Env:TEMP_MISTRAL_API_KEY -ErrorAction SilentlyContinue Write-Host "[ERROR] Failed to store Mistral API key: $_" -ForegroundColor Red return $false } } function Test-UnifiedKeys { <# .SYNOPSIS Tests if API keys exist in secure storage .DESCRIPTION Checks for the presence of USPTO and Mistral API keys in encrypted storage #> try { Set-Location $ProjectDir $result = uv run python -c " import sys from pathlib import Path sys.path.insert(0, str(Path('src'))) try: from fpd_mcp.shared_secure_storage import UnifiedSecureStorage storage = UnifiedSecureStorage() uspto_key = storage.get_uspto_key() mistral_key = storage.get_mistral_key() print(f'USPTO:{'YES' if uspto_key and len(uspto_key) >= 10 else 'NO'}') print(f'MISTRAL:{'YES' if mistral_key and len(mistral_key) >= 10 else 'NO'}') except Exception as e: print(f'ERROR: {str(e)}') sys.exit(1) " 2>&1 | Out-String $lines = $result -split "`n" | Where-Object { $_.Trim() -ne "" } $usptoFound = $false $mistralFound = $false foreach ($line in $lines) { if ($line -match "USPTO:(YES|NO)") { $usptoFound = ($matches[1] -eq "YES") } if ($line -match "MISTRAL:(YES|NO)") { $mistralFound = ($matches[1] -eq "YES") } } return @{ "USPTO" = $usptoFound "MISTRAL" = $mistralFound } } catch { return @{ "USPTO" = $false "MISTRAL" = $false } } } # Check if uv is installed, install if not Write-Host "[INFO] Python NOT required - uv will manage Python automatically" -ForegroundColor Cyan Write-Host "" try { $uvVersion = uv --version 2>$null Write-Host "[OK] uv found: $uvVersion" -ForegroundColor Green } catch { Write-Host "[INFO] uv not found. Installing uv..." -ForegroundColor Yellow # Try winget first (preferred method) try { winget install --id=astral-sh.uv -e Write-Host "[OK] uv installed via winget" -ForegroundColor Green } catch { Write-Host "[INFO] winget failed, trying PowerShell install method..." -ForegroundColor Yellow try { powershell -c "irm https://astral.sh/uv/install.ps1 | iex" Write-Host "[OK] uv installed via PowerShell script" -ForegroundColor Green } catch { Write-Host "[ERROR] Failed to install uv. Please install manually:" -ForegroundColor Red Write-Host " winget install --id=astral-sh.uv -e" -ForegroundColor Yellow Write-Host " OR visit: https://docs.astral.sh/uv/getting-started/installation/" -ForegroundColor Yellow exit 1 } } # Refresh PATH for current session $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User") # Add uv's typical installation paths if not already in PATH $uvPaths = @( "$env:USERPROFILE\.cargo\bin", # cargo install location "$env:LOCALAPPDATA\Programs\uv\bin", # winget install location "$env:APPDATA\uv\bin" # alternative location ) foreach ($uvPath in $uvPaths) { if (Test-Path $uvPath) { if ($env:PATH -notlike "*$uvPath*") { $env:PATH = "$uvPath;$env:PATH" Write-Host "[INFO] Added $uvPath to PATH" -ForegroundColor Yellow } } } # Verify uv is now accessible try { $uvVersion = uv --version 2>$null Write-Host "[OK] uv is now accessible: $uvVersion" -ForegroundColor Green } catch { Write-Host "[ERROR] uv installed but not accessible. Please restart PowerShell and run script again." -ForegroundColor Red Write-Host "[INFO] Or manually add uv to PATH and continue." -ForegroundColor Yellow exit 1 } } # Install dependencies with uv Write-Host "[INFO] Installing dependencies with uv..." -ForegroundColor Yellow # Force uv to use prebuilt wheels (avoid Rust compilation) Write-Host "[INFO] Installing dependencies with prebuilt wheels (Python 3.12)..." -ForegroundColor Yellow try { # Use Python 3.12 which has guaranteed prebuilt wheels for all dependencies # Python 3.14 is too new and doesn't have wheels yet uv sync --python 3.12 Write-Host "[OK] Dependencies installed" -ForegroundColor Green } catch { Write-Host "[ERROR] Failed to install dependencies" -ForegroundColor Red exit 1 } # Fix: Ensure pyvenv.cfg exists (required for secure storage on older uv versions) $pyvenvCfgPath = ".venv\pyvenv.cfg" if (-not (Test-Path $pyvenvCfgPath)) { Write-Host "[INFO] Creating missing pyvenv.cfg file (older uv version)..." -ForegroundColor Yellow try { # Get the Python path that uv is using $uvPythonInfo = uv python list --only-managed 2>$null | Select-String "cpython-3\.1[2-4].*-windows" | Select-Object -First 1 if ($uvPythonInfo) { $pythonVersion = ($uvPythonInfo.Line -split '\s+')[1] # Extract version like "3.12.8" $pythonPath = ($uvPythonInfo.Line -split '\s+')[2] # Extract path # Create pyvenv.cfg content $pyvenvContent = @" home = $pythonPath implementation = CPython uv = 0.9.11 version_info = $pythonVersion include-system-site-packages = false prompt = uspto-fpd-mcp "@ Set-Content -Path $pyvenvCfgPath -Value $pyvenvContent -Encoding UTF8 Write-Host "[OK] Created pyvenv.cfg file" -ForegroundColor Green } else { # Fallback: Create minimal pyvenv.cfg Write-Host "[WARN] Could not detect uv Python path, creating minimal pyvenv.cfg" -ForegroundColor Yellow $fallbackContent = @" implementation = CPython version_info = 3.12.8 include-system-site-packages = false prompt = uspto-fpd-mcp "@ Set-Content -Path $pyvenvCfgPath -Value $fallbackContent -Encoding UTF8 Write-Host "[OK] Created minimal pyvenv.cfg file" -ForegroundColor Green } } catch { Write-Host "[WARN] Could not create pyvenv.cfg, but continuing..." -ForegroundColor Yellow } } else { Write-Host "[OK] pyvenv.cfg already exists (newer uv version)" -ForegroundColor Green } # Verify installation Write-Host "[INFO] Verifying installation..." -ForegroundColor Yellow try { $commandCheck = Get-Command fpd-mcp -ErrorAction SilentlyContinue if ($commandCheck) { Write-Host "[OK] Command available: $($commandCheck.Source)" -ForegroundColor Green } else { Write-Host "[WARN] Warning: Command verification failed - check PATH" -ForegroundColor Yellow Write-Host "[INFO] You can run the server with: uv run fpd-mcp" -ForegroundColor Yellow } } catch { Write-Host "[WARN] Warning: Command verification failed - check PATH" -ForegroundColor Yellow Write-Host "[INFO] You can run the server with: uv run fpd-mcp" -ForegroundColor Yellow } # API Key Configuration with Unified Storage Write-Host "" Write-Host "==========================================" -ForegroundColor Cyan Write-Host "SECURE API KEY CONFIGURATION" -ForegroundColor Cyan Write-Host "==========================================" -ForegroundColor Cyan Write-Host "" Write-Host "API keys will be stored in DPAPI encrypted storage" -ForegroundColor Yellow Write-Host "Location: ~/.uspto_api_key and ~/.mistral_api_key" -ForegroundColor Yellow Write-Host "Encryption: Windows Data Protection API (user + machine specific)" -ForegroundColor Yellow Write-Host "" # Step 1: Check for existing keys first Write-Host "[INFO] Checking for existing API keys in secure storage..." -ForegroundColor Yellow $existingKeys = Test-UnifiedKeys # Flags for tracking configuration path $usingPreexistingDPAPI = $false $newKeyAsEnv = $false $finalConfigMethod = "none" # Track final config method: "dpapi", "traditional", or "none" # Test secure storage system ONLY if no keys exist yet (avoids deleting existing keys) if (-not $existingKeys.USPTO -and -not $existingKeys.MISTRAL) { Write-Host "[INFO] Testing secure storage system..." -ForegroundColor Yellow try { $pythonExe = ".venv/Scripts/python.exe" $testCode = @" import sys from pathlib import Path sys.path.insert(0, str(Path('src'))) try: from fpd_mcp.shared_secure_storage import UnifiedSecureStorage storage = UnifiedSecureStorage() test_result = storage.store_uspto_key('testkey12345678901234567890') storage.uspto_key_path.unlink(missing_ok=True) # Clean up test - safe because no real keys exist yet print('SUCCESS' if test_result else 'FAILED') except Exception as e: print(f'ERROR: {e}') "@ $storageResult = & $pythonExe -c $testCode 2>$null | Out-String if ($storageResult -match "SUCCESS") { Write-Host "[OK] Secure storage system working" -ForegroundColor Green } else { Write-Host "[WARN] Secure storage test failed - API key storage may not work properly" -ForegroundColor Yellow Write-Host "[INFO] Error details: $storageResult" -ForegroundColor Yellow } } catch { Write-Host "[WARN] Could not test secure storage system" -ForegroundColor Yellow } } else { Write-Host "[OK] Secure storage system verified (existing keys found)" -ForegroundColor Green } # Step 2: Ask user to use existing or update if ($existingKeys.USPTO -and $existingKeys.MISTRAL) { Write-Host "[OK] Both USPTO and Mistral API keys found in encrypted storage" -ForegroundColor Green Write-Host "[INFO] Configuration: [1] Use existing keys [2] Update keys" -ForegroundColor Cyan $keyChoice = Read-Host "Enter choice (1 or 2, default is 1)" if ($keyChoice -eq "2") { $updateKeys = $true } else { $updateKeys = $false $usingPreexistingDPAPI = $true # Flag: Using existing DPAPI keys Write-Host "[OK] Using existing encrypted API keys" -ForegroundColor Green } } elseif ($existingKeys.USPTO) { Write-Host "[OK] USPTO API key found in encrypted storage" -ForegroundColor Green Write-Host "[INFO] Mistral API key not found" -ForegroundColor Yellow Write-Host "[INFO] Configuration: [1] Add Mistral key [2] Use USPTO key only [3] Update both keys" -ForegroundColor Cyan $keyChoice = Read-Host "Enter choice (1, 2, or 3, default is 1)" if ($keyChoice -eq "2") { $updateKeys = $false $usingPreexistingDPAPI = $true # Flag: Using existing USPTO DPAPI key Write-Host "[OK] Using existing USPTO key, skipping Mistral" -ForegroundColor Green } elseif ($keyChoice -eq "3") { $updateKeys = $true } else { $updateKeys = "mistral_only" # Note: Don't set $usingPreexistingDPAPI here because adding new Mistral key } } elseif ($existingKeys.MISTRAL) { Write-Host "[WARN] Only Mistral API key found, USPTO key missing (required)" -ForegroundColor Yellow $updateKeys = $true } else { Write-Host "[INFO] No API keys found in encrypted storage" -ForegroundColor Yellow $updateKeys = $true } # Step 3: Collect and store keys if needed if ($updateKeys -eq $true -or $updateKeys -eq "mistral_only") { # Show API key format requirements Show-ApiKeyRequirements # Collect USPTO API key if needed (skip for mistral_only case) $usptoApiKey = "" if ($updateKeys -eq $true) { if ($existingKeys.USPTO) { Write-Host "" Write-Host "[INFO] USPTO API key already exists in encrypted storage" -ForegroundColor Yellow Write-Host "" $updateUspto = Read-Host "Do you want to update the USPTO API key? (y/N)" if ($updateUspto -eq "y" -or $updateUspto -eq "Y") { Write-Host "" Write-Host "Enter your USPTO API key (required - get from https://data.uspto.gov/myodp/):" -ForegroundColor Cyan Write-Host "" $usptoApiKey = Read-UsptoApiKeyWithValidation if (-not $usptoApiKey) { Write-Host "[ERROR] Cannot proceed without valid USPTO API key" -ForegroundColor Red exit 1 } } else { Write-Host "[OK] Keeping existing USPTO API key" -ForegroundColor Green } } else { Write-Host "" Write-Host "Enter your USPTO API key (required - get from https://data.uspto.gov/myodp/):" -ForegroundColor Cyan Write-Host "" $usptoApiKey = Read-UsptoApiKeyWithValidation if (-not $usptoApiKey) { Write-Host "[ERROR] Cannot proceed without valid USPTO API key" -ForegroundColor Red exit 1 } } } # Collect Mistral API key with validation (uses helper function with retry logic) $mistralApiKey = "" if ($updateKeys -eq "mistral_only") { # Special case: Only adding Mistral key, USPTO already exists Write-Host "" Write-Host "[INFO] Mistral API key is OPTIONAL (for OCR on scanned petition documents)" -ForegroundColor Yellow Write-Host " Without it, you can still use free PyPDF2 extraction for text-based PDFs" -ForegroundColor Yellow Write-Host "" $mistralApiKey = Read-MistralApiKeyWithValidation if ($null -eq $mistralApiKey) { Write-Host "[ERROR] Mistral API key validation failed" -ForegroundColor Red exit 1 } } elseif ($existingKeys.MISTRAL) { Write-Host "" Write-Host "[INFO] Mistral API key already exists in encrypted storage" -ForegroundColor Yellow Write-Host "[INFO] Mistral API key is OPTIONAL (for OCR on scanned petition documents)" -ForegroundColor Yellow Write-Host "" $updateMistral = Read-Host "Do you want to update the Mistral API key? (y/N)" if ($updateMistral -eq "y" -or $updateMistral -eq "Y") { Write-Host "" $mistralApiKey = Read-MistralApiKeyWithValidation if ($null -eq $mistralApiKey) { Write-Host "[ERROR] Mistral API key validation failed" -ForegroundColor Red exit 1 } } else { Write-Host "[OK] Keeping existing Mistral API key" -ForegroundColor Green } } else { Write-Host "" Write-Host "[INFO] Mistral API key is OPTIONAL (for OCR on scanned petition documents)" -ForegroundColor Yellow Write-Host " Without it, you can still use free PyPDF2 extraction for text-based PDFs" -ForegroundColor Yellow Write-Host "" $mistralApiKey = Read-MistralApiKeyWithValidation if ($null -eq $mistralApiKey) { Write-Host "[ERROR] Mistral API key validation failed" -ForegroundColor Red exit 1 } } # Store keys in unified encrypted storage Write-Host "" Write-Host "[INFO] Storing API keys in DPAPI encrypted storage..." -ForegroundColor Yellow Write-Host "" if (-not [string]::IsNullOrWhiteSpace($usptoApiKey)) { if (Set-UnifiedUsptoKey -ApiKey $usptoApiKey) { Write-Host "[OK] USPTO API key stored in encrypted storage" -ForegroundColor Green $newKeyAsEnv = $true # Flag: New key entered and stored } else { Write-Host "[ERROR] Failed to store USPTO API key" -ForegroundColor Red exit 1 } } if (-not [string]::IsNullOrWhiteSpace($mistralApiKey)) { if (Set-UnifiedMistralKey -ApiKey $mistralApiKey) { Write-Host "[OK] Mistral API key stored in encrypted storage" -ForegroundColor Green $newKeyAsEnv = $true # Flag: New key entered and stored } else { Write-Host "[WARN] Failed to store Mistral API key" -ForegroundColor Yellow } } # Clear sensitive variables from memory $usptoApiKey = $null $mistralApiKey = $null [System.GC]::Collect() if ($newKeyAsEnv) { Write-Host "" Write-Host "[OK] Encrypted storage benefits:" -ForegroundColor Cyan Write-Host " - Single-key-per-file architecture" -ForegroundColor White Write-Host " - DPAPI encryption (user + machine specific)" -ForegroundColor White Write-Host " - Shared across all USPTO MCPs (FPD/PFW/PTAB/Citations)" -ForegroundColor White Write-Host " - Files: ~/.uspto_api_key, ~/.mistral_api_key" -ForegroundColor White Write-Host " - Hidden file attributes applied" -ForegroundColor White } } # Get current directory and convert backslashes to forward slashes $CurrentDir = (Get-Location).Path -replace "\\","/" # PFW MCP Detection Write-Host "" Write-Host "USPTO MCP Ecosystem Integration" -ForegroundColor Cyan Write-Host "" Write-Host "The USPTO Patent File Wrapper (PFW) MCP provides a centralized proxy server" -ForegroundColor Yellow Write-Host "that offers enhanced features when used with FPD MCP:" -ForegroundColor Yellow Write-Host " - Persistent document links (7-day encrypted URLs)" -ForegroundColor White Write-Host " - Unified rate limiting across all USPTO MCPs" -ForegroundColor White Write-Host " - Cross-MCP document sharing and caching" -ForegroundColor White Write-Host "" $hasPfwMcp = Read-Host "Do you have the USPTO PFW MCP already installed? (y/N)" if ($hasPfwMcp -eq "y" -or $hasPfwMcp -eq "Y") { Write-Host "[OK] FPD will use PFW's centralized proxy for enhanced features" -ForegroundColor Green Write-Host " No local proxy configuration needed" -ForegroundColor Yellow $useCentralizedProxy = $true } else { Write-Host "[INFO] FPD will run in standalone mode with local proxy (always-on)" -ForegroundColor Yellow Write-Host " Install USPTO PFW MCP later for enhanced features:" -ForegroundColor Cyan Write-Host " https://github.com/johnwalkoe/patent_filewrapper_mcp" -ForegroundColor Cyan $useCentralizedProxy = $false } # Get or generate shared INTERNAL_AUTH_SECRET using unified storage Write-Host "" Write-Host "[INFO] Configuring shared INTERNAL_AUTH_SECRET..." -ForegroundColor Yellow try { Set-Location $ProjectDir # Use uv run python (not direct python.exe) to avoid stderr diagnostic messages $result = uv run python -c @' import sys from pathlib import Path sys.path.insert(0, str(Path('src'))) from fpd_mcp.shared_secure_storage import ensure_internal_auth_secret # Get or create shared secret secret = ensure_internal_auth_secret() if secret: print(secret) else: sys.exit(1) '@ 2>&1 | Out-String $lines = $result -split "`n" | Where-Object { $_.Trim() -ne "" } # Filter to find the secret (base64 pattern, 40+ chars, ignoring diagnostic/error messages) $internalSecret = "" foreach ($line in $lines) { $trimmed = ([string]$line).Trim() # Match base64 pattern: alphanumeric+/= characters, ends with =, length 40+ if ($trimmed -match '^[A-Za-z0-9+/]+=*$' -and $trimmed.Length -ge 40) { $internalSecret = $trimmed break } } if (-not [string]::IsNullOrWhiteSpace($internalSecret)) { # Check if this was a newly generated secret or existing one if ($result -match "Generating new internal auth secret") { Write-Host "[OK] Generated new INTERNAL_AUTH_SECRET (first USPTO MCP installation)" -ForegroundColor Green Write-Host " Location: ~/.uspto_internal_auth_secret (DPAPI encrypted)" -ForegroundColor Yellow Write-Host " This secret will be SHARED across all USPTO MCPs (FPD/PFW/PTAB/Citations)" -ForegroundColor Yellow } else { Write-Host "[OK] Using existing INTERNAL_AUTH_SECRET from unified storage" -ForegroundColor Green Write-Host " Location: ~/.uspto_internal_auth_secret (DPAPI encrypted)" -ForegroundColor Yellow Write-Host " Shared with other installed USPTO MCPs" -ForegroundColor Yellow } Write-Host " This secret authenticates internal MCP communication" -ForegroundColor Yellow } else { Write-Host "[ERROR] Failed to get or generate INTERNAL_AUTH_SECRET" -ForegroundColor Red exit 1 } } catch { Write-Host "[ERROR] Failed to configure INTERNAL_AUTH_SECRET: $_" -ForegroundColor Red exit 1 } # Step 4: Ask about Claude Desktop configuration Write-Host "" Write-Host "Claude Desktop Configuration" -ForegroundColor Cyan Write-Host "" $configureClaudeDesktop = Read-Host "Would you like to configure Claude Desktop integration? (Y/n)" if ($configureClaudeDesktop -eq "" -or $configureClaudeDesktop -eq "Y" -or $configureClaudeDesktop -eq "y") { # Step 5 & 6: Determine configuration method based on flags $useSecureStorage = $false $configureUsptoApiKey = "" $configureMistralApiKey = "" if ($usingPreexistingDPAPI -and -not $newKeyAsEnv) { # Step 5: User is using existing DPAPI keys → Auto-configure as DPAPI Write-Host "" Write-Host "[OK] Using DPAPI encrypted storage (secure)" -ForegroundColor Green Write-Host " API keys will be loaded automatically from encrypted storage" -ForegroundColor Yellow Write-Host " No API keys will be stored in Claude Desktop config file" -ForegroundColor Yellow Write-Host "" $useSecureStorage = $true $finalConfigMethod = "dpapi" } elseif ($newKeyAsEnv) { # Step 6: User just entered a new key → Give choice between Secure and Traditional Write-Host "" Write-Host "Claude Desktop Configuration Method:" -ForegroundColor Cyan Write-Host " [1] Secure Python DPAPI (recommended) - API keys loaded from encrypted storage" -ForegroundColor White Write-Host " [2] Traditional - API keys stored in Claude Desktop config file" -ForegroundColor White Write-Host "" $configChoice = Read-Host "Enter choice (1 or 2, default is 1)" if ($configChoice -eq "2") { # Step 8: Traditional configuration Write-Host "[INFO] Using traditional method (API keys in config file)" -ForegroundColor Yellow $useSecureStorage = $false $finalConfigMethod = "traditional" # Retrieve the keys from DPAPI storage for config file try { Set-Location $ProjectDir $pythonCode = @' import sys from pathlib import Path sys.path.insert(0, str(Path('src'))) from fpd_mcp.shared_secure_storage import UnifiedSecureStorage storage = UnifiedSecureStorage() uspto_key = storage.get_uspto_key() mistral_key = storage.get_mistral_key() if uspto_key: print(f'USPTO:{uspto_key}') if mistral_key: print(f'MISTRAL:{mistral_key}') '@ $result = uv run python -c $pythonCode 2>$null | Out-String $lines = $result -split "`n" foreach ($line in $lines) { if ($line.StartsWith("USPTO:")) { $configureUsptoApiKey = $line.Substring(6).Trim() } elseif ($line.StartsWith("MISTRAL:")) { $configureMistralApiKey = $line.Substring(8).Trim() } } } catch { $configureUsptoApiKey = "" $configureMistralApiKey = "" } } else { # Step 7: Secure DPAPI configuration Write-Host "[OK] Using DPAPI encrypted storage (secure)" -ForegroundColor Green Write-Host " API keys will be loaded automatically from encrypted storage" -ForegroundColor Yellow Write-Host " No API keys will be stored in Claude Desktop config file" -ForegroundColor Yellow Write-Host "" $useSecureStorage = $true $finalConfigMethod = "dpapi" } } else { # No keys configured → Default to DPAPI (no keys to store) Write-Host "" Write-Host "[OK] Using DPAPI encrypted storage (secure)" -ForegroundColor Green Write-Host " No API keys configured" -ForegroundColor Yellow Write-Host "" $useSecureStorage = $true $finalConfigMethod = "dpapi" } # Function to generate env section based on configuration choice function Get-EnvSection { param($centralized = $false, $indent = " ") $envItems = @() if ($useSecureStorage) { # Secure storage - no API keys in config if ($centralized) { $envItems += "$indent`"CENTRALIZED_PROXY_PORT`": `"8080`"" $envItems += "$indent`"FPD_PROXY_PORT`": `"8081`"" $envItems += "$indent`"INTERNAL_AUTH_SECRET`": `"$internalSecret`"" } else { $envItems += "$indent`"CENTRALIZED_PROXY_PORT`": `"none`"" $envItems += "$indent`"FPD_PROXY_PORT`": `"8081`"" } } else { # Traditional - API keys in config if ($configureUsptoApiKey) { $envItems += "$indent`"USPTO_API_KEY`": `"$configureUsptoApiKey`"" } if ($configureMistralApiKey) { $envItems += "$indent`"MISTRAL_API_KEY`": `"$configureMistralApiKey`"" } if ($centralized) { $envItems += "$indent`"CENTRALIZED_PROXY_PORT`": `"8080`"" $envItems += "$indent`"FPD_PROXY_PORT`": `"8081`"" $envItems += "$indent`"INTERNAL_AUTH_SECRET`": `"$internalSecret`"" } else { $envItems += "$indent`"CENTRALIZED_PROXY_PORT`": `"none`"" $envItems += "$indent`"FPD_PROXY_PORT`": `"8081`"" } } return $envItems -join ",`n" } # Function to generate server JSON entry function Get-ServerJson { param($centralized = $false, $indent = " ") $envSection = Get-EnvSection -centralized $centralized -indent " " return @" $indent"uspto_fpd": { $indent "command": "$CurrentDir/.venv/Scripts/python.exe", $indent "args": [ $indent "-m", $indent "fpd_mcp.main" $indent ], $indent "cwd": "$CurrentDir", $indent "env": { $envSection $indent } $indent} "@ } # Claude Desktop config location $ClaudeConfigDir = "$env:APPDATA\Claude" $ClaudeConfigFile = "$ClaudeConfigDir\claude_desktop_config.json" Write-Host "[INFO] Claude Desktop config location: $ClaudeConfigFile" -ForegroundColor Yellow if (Test-Path $ClaudeConfigFile) { Write-Host "[INFO] Existing Claude Desktop config found" -ForegroundColor Yellow Write-Host "[INFO] Merging USPTO FPD configuration with existing config..." -ForegroundColor Yellow try { # Read existing config as raw text $existingJsonText = Get-Content $ClaudeConfigFile -Raw # Backup the original file $backupFile = "$ClaudeConfigFile.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')" Copy-Item $ClaudeConfigFile $backupFile Write-Host "[INFO] Backup created: $backupFile" -ForegroundColor Yellow # Try to parse JSON, with better error handling for malformed JSON try { $existingConfig = $existingJsonText | ConvertFrom-Json } catch { Write-Host "[ERROR] Existing Claude Desktop config has JSON syntax errors" -ForegroundColor Red Write-Host "[ERROR] Common issue: Missing comma after closing braces '}' between MCP server sections" -ForegroundColor Red Write-Host "[INFO] Please fix the JSON syntax and run the setup script again" -ForegroundColor Yellow Write-Host "[INFO] Your backup is saved at: $backupFile" -ForegroundColor Yellow Write-Host "" Write-Host "Quick fix: Look for lines like this pattern and add missing commas:" -ForegroundColor Yellow Write-Host " }" -ForegroundColor White Write-Host " `"next_server`": {" -ForegroundColor White Write-Host "" Write-Host "Should be:" -ForegroundColor Yellow Write-Host " }," -ForegroundColor Green Write-Host " `"next_server`": {" -ForegroundColor White Write-Host "" exit 1 } # Check if mcpServers exists, create if not if (-not $existingConfig.mcpServers) { # Empty config - create from scratch (NO API KEYS) if ($useCentralizedProxy) { # PFW centralized proxy $envSection = Get-EnvSection -centralized $true $jsonConfig = @" { "mcpServers": { "uspto_fpd": { "command": "$CurrentDir/.venv/Scripts/python.exe", "args": [ "-m", "fpd_mcp.main" ], "cwd": "$CurrentDir", "env": { $envSection } } } } "@ } else { # Standalone mode $envSection = Get-EnvSection -centralized $false $jsonConfig = @" { "mcpServers": { "uspto_fpd": { "command": "$CurrentDir/.venv/Scripts/python.exe", "args": [ "-m", "fpd_mcp.main" ], "cwd": "$CurrentDir", "env": { $envSection } } } } "@ } } else { # Has existing servers - need to merge manually # Build the uspto_fpd section $usptoFpdJson = Get-ServerJson -centralized $useCentralizedProxy # Get all existing server names $existingServers = $existingConfig.mcpServers.PSObject.Properties.Name # Build the mcpServers object with all servers $serverEntries = @() foreach ($serverName in $existingServers) { if ($serverName -ne "uspto_fpd") { # Convert to JSON without compression for readability $serverJson = $existingConfig.mcpServers.$serverName | ConvertTo-Json -Depth 10 # Split into lines and format properly $jsonLines = $serverJson -split "`n" # First line: "serverName": { $formattedEntry = " `"$serverName`": $($jsonLines[0])" # Remaining lines: indent by 4 spaces for ($i = 1; $i -lt $jsonLines.Length; $i++) { $formattedEntry += "`n $($jsonLines[$i])" } # Add the formatted server entry $serverEntries += $formattedEntry } } # Add uspto_fpd $serverEntries += $usptoFpdJson.TrimEnd() $allServers = $serverEntries -join ",`n" $jsonConfig = @" { "mcpServers": { $allServers } } "@ } # Write with UTF8 without BOM $utf8NoBom = New-Object System.Text.UTF8Encoding $false [System.IO.File]::WriteAllText($ClaudeConfigFile, $jsonConfig, $utf8NoBom) Write-Host "[OK] Successfully merged USPTO FPD configuration!" -ForegroundColor Green Write-Host "[OK] Your existing MCP servers have been preserved" -ForegroundColor Green if ($useSecureStorage) { Write-Host "[INFO] API keys are NOT in config file (loaded from encrypted storage)" -ForegroundColor Yellow } else { Write-Host "[INFO] API keys are stored in config file (traditional method)" -ForegroundColor Yellow } Write-Host "[INFO] Configuration backup saved at: $backupFile" -ForegroundColor Yellow # Log audit event $auditMsg = if ($useSecureStorage) { "Claude Desktop configured via windows_setup.ps1 (secure DPAPI)" } else { "Claude Desktop configured via windows_setup.ps1 (traditional with API keys in config)" } Write-AuditLog $auditMsg } catch { Write-Host "[ERROR] Failed to merge configuration: $_" -ForegroundColor Red Write-Host "[ERROR] Details: $($_.Exception.Message)" -ForegroundColor Red Write-Host "" Write-Host "Please manually add this configuration to: $ClaudeConfigFile" -ForegroundColor Yellow Write-Host "" Write-Host "Add this to your mcpServers section:" -ForegroundColor White # Manual JSON string for display $manualJson = Get-ServerJson -centralized $useCentralizedProxy -indent "" Write-Host $manualJson -ForegroundColor Cyan Write-Host "" if (Test-Path $backupFile) { Write-Host "Your backup is saved at: $backupFile" -ForegroundColor Yellow } exit 1 } } else { # Create new config file Write-Host "[INFO] Creating new Claude Desktop config..." -ForegroundColor Yellow # Create directory if it doesn't exist if (-not (Test-Path $ClaudeConfigDir)) { New-Item -ItemType Directory -Path $ClaudeConfigDir -Force | Out-Null } # Create config (NO API KEYS) $serverJson = Get-ServerJson -centralized $useCentralizedProxy $jsonConfig = @" { "mcpServers": { $serverJson } } "@ # Write with UTF8 without BOM $utf8NoBom = New-Object System.Text.UTF8Encoding $false [System.IO.File]::WriteAllText($ClaudeConfigFile, $jsonConfig, $utf8NoBom) Write-Host "[OK] Created new Claude Desktop config" -ForegroundColor Green if ($useSecureStorage) { Write-Host "[INFO] ✅ API keys are NOT in config file (loaded from encrypted storage)" -ForegroundColor Yellow } else { Write-Host "[INFO] API keys are stored in config file (traditional method)" -ForegroundColor Yellow } # Log audit event $auditMsg = if ($useSecureStorage) { "Claude Desktop configured via windows_setup.ps1 (secure DPAPI)" } else { "Claude Desktop configured via windows_setup.ps1 (traditional with API keys in config)" } Write-AuditLog $auditMsg } Write-Host "[OK] Claude Desktop configuration complete!" -ForegroundColor Green } # Final summary Write-Host "" Write-Host "==========================================" -ForegroundColor Green Write-Host "Windows setup complete!" -ForegroundColor Green Write-Host "==========================================" -ForegroundColor Green Write-Host "" Write-Host "Please restart Claude Desktop to load the MCP server" -ForegroundColor Yellow Write-Host "" Write-Host "Configuration Summary:" -ForegroundColor Cyan Write-Host "" # Check final key status $finalKeys = Test-UnifiedKeys if ($finalKeys.USPTO) { Write-Host " [OK] USPTO API Key: Stored in DPAPI encrypted storage" -ForegroundColor Green Write-Host " Location: ~/.uspto_api_key (DPAPI encrypted + hidden)" -ForegroundColor Yellow } else { Write-Host " [WARN] USPTO API Key: Not found in encrypted storage" -ForegroundColor Yellow } if ($finalKeys.MISTRAL) { Write-Host " [OK] Mistral API Key: Stored in DPAPI encrypted storage" -ForegroundColor Green Write-Host " Location: ~/.mistral_api_key (DPAPI encrypted + hidden)" -ForegroundColor Yellow } else { Write-Host " [INFO] Mistral API Key: Not set (PyPDF2 fallback for text PDFs)" -ForegroundColor Yellow } Write-Host "" Write-Host " [OK] Storage Architecture: Single-key-per-file (shared across USPTO MCPs)" -ForegroundColor Green Write-Host " [OK] Proxy Port: 8081" -ForegroundColor Green Write-Host " [OK] Installation Directory: $CurrentDir" -ForegroundColor Green Write-Host "" Write-Host "Security Features:" -ForegroundColor Cyan if ($finalConfigMethod -eq "dpapi") { Write-Host " [*] Configuration Method: DPAPI Encrypted Storage (Secure)" -ForegroundColor White Write-Host " [*] API keys encrypted with Windows DPAPI (user and machine specific)" -ForegroundColor White Write-Host " [*] API keys NOT in Claude Desktop config file" -ForegroundColor White Write-Host " [*] API keys NOT visible in process list (environment variables used)" -ForegroundColor White } elseif ($finalConfigMethod -eq "traditional") { Write-Host " [*] Configuration Method: Traditional (API keys in config file)" -ForegroundColor White Write-Host " [*] API keys stored in Claude Desktop config file" -ForegroundColor White Write-Host " [*] API keys also backed up in DPAPI encrypted storage" -ForegroundColor White } else { Write-Host " [*] Configuration Method: Not configured" -ForegroundColor White } Write-Host " [*] API key format validation (prevents typos)" -ForegroundColor White Write-Host " [*] Hidden file attributes applied to key files" -ForegroundColor White Write-Host " [*] Audit logging enabled (~/.uspto_mcp_audit.log)" -ForegroundColor White Write-Host "" Write-Host "Available Tools (7):" -ForegroundColor Cyan Write-Host " - fpd_search_petitions_minimal (ultra-fast discovery)" -ForegroundColor White Write-Host " - fpd_search_petitions_balanced (detailed analysis)" -ForegroundColor White Write-Host " - fpd_search_by_art_unit (art unit quality)" -ForegroundColor White Write-Host " - fpd_search_by_application (petition history)" -ForegroundColor White Write-Host " - fpd_get_petition_details (full details)" -ForegroundColor White Write-Host " - fpd_get_document_download (PDF downloads)" -ForegroundColor White Write-Host " - fpd_get_tool_reflections (workflow guidance)" -ForegroundColor White Write-Host "" Write-Host "Proxy Server:" -ForegroundColor Cyan Write-Host " Start with: uv run fpd-proxy" -ForegroundColor Yellow Write-Host " Port: 8081 (avoids conflict with PFW on 8080)" -ForegroundColor White Write-Host "" Write-Host "Key Management:" -ForegroundColor Cyan Write-Host " Manage keys: ./deploy/manage_api_keys.ps1" -ForegroundColor Yellow Write-Host " Cross-MCP: Keys shared with PFW, PTAB, and Citations MCPs" -ForegroundColor White Write-Host "" Write-Host "Test with: fpd_search_petitions_minimal" -ForegroundColor Yellow Write-Host "Learn workflows: fpd_get_tool_reflections" -ForegroundColor Yellow Write-Host ""

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/john-walkoe/uspto_fpd_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server