From 2bb955e86215d78f93f4eaa6b6a65f1ed3bd329e Mon Sep 17 00:00:00 2001 From: Alberto Valverde Date: Fri, 19 Dec 2025 10:40:40 +0100 Subject: [PATCH 001/121] docs(005-servant-oauth-extraction): add specification and design docs Add comprehensive planning documentation for removing MCP imports from Servant.OAuth2.IDP modules to prepare for package extraction: - spec.md: Feature requirements (FR-001 through FR-008) - plan.md: Implementation phases and file-by-file changes - research.md: Dependency analysis and extraction strategy - data-model.md: Type definitions for OAuthEnv and OAuthTrace - module-dependencies.md: Import graph and breaking changes - quickstart.md: Developer guide for implementation Also updates .beads/issues.jsonl with tracking and CLAUDE.md with technology choices for the feature. --- CLAUDE.md | 2 + .../contracts/module-dependencies.md | 112 +++++ .../data-model.md | 381 ++++++++++++++++++ specs/005-servant-oauth-extraction/plan.md | 266 ++++++++++++ .../quickstart.md | 187 +++++++++ .../005-servant-oauth-extraction/research.md | 269 +++++++++++++ specs/005-servant-oauth-extraction/spec.md | 146 +++++++ 7 files changed, 1363 insertions(+) create mode 100644 specs/005-servant-oauth-extraction/contracts/module-dependencies.md create mode 100644 specs/005-servant-oauth-extraction/data-model.md create mode 100644 specs/005-servant-oauth-extraction/plan.md create mode 100644 specs/005-servant-oauth-extraction/quickstart.md create mode 100644 specs/005-servant-oauth-extraction/research.md create mode 100644 specs/005-servant-oauth-extraction/spec.md diff --git a/CLAUDE.md b/CLAUDE.md index b069af8..8f661b8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -316,6 +316,8 @@ curl -i http://localhost:8080/mcp - Haskell GHC2021 (GHC 9.4+ via base ^>=4.18.2.1) + servant-server 0.19-0.20, servant-auth-server 0.4, jose 0.10-0.11, cryptonite 0.30, stm 2.5, mtl 2.3, aeson 2.1-2.2, generic-lens (to add) (004-oauth-auth-typeclasses) - In-memory (TVar-based) for default implementation; typeclass enables PostgreSQL/Redis backends (004-oauth-auth-typeclasses) - Haskell GHC2021 (GHC 9.4+ via base ^>=4.18.2.1) + servant-server 0.19-0.20, servant-auth-server 0.4, jose 0.10-0.11, cryptonite 0.30, stm 2.5, mtl 2.3, aeson 2.1-2.2, generic-lens (to add), network-uri (to add) (004-oauth-auth-typeclasses) +- Haskell GHC2021 (GHC 9.4+ via base ^>=4.18.2.1) + servant-server 0.19-0.20, servant-auth-server 0.4, aeson 2.1-2.2, monad-time, network-uri, cryptonite 0.30, generic-lens (005-servant-oauth-extraction) +- N/A (refactoring only, no data changes) (005-servant-oauth-extraction) ## Recent Changes - 004-oauth-auth-typeclasses: Refactored OAuth to typeclass-based architecture diff --git a/specs/005-servant-oauth-extraction/contracts/module-dependencies.md b/specs/005-servant-oauth-extraction/contracts/module-dependencies.md new file mode 100644 index 0000000..b94d229 --- /dev/null +++ b/specs/005-servant-oauth-extraction/contracts/module-dependencies.md @@ -0,0 +1,112 @@ +# Module Dependencies Contract + +**Branch**: `005-servant-oauth-extraction` +**Date**: 2025-12-18 + +## Invariant + +After this refactoring, the following invariant MUST hold: + +``` +∀ module M ∈ Servant.OAuth2.IDP.* : + ∀ import I ∈ imports(M) : + ¬(I starts with "MCP.") +``` + +In plain English: **No module under `Servant.OAuth2.IDP.*` may import any module from the `MCP.*` namespace.** + +## Allowed Dependencies (Servant.OAuth2.IDP.*) + +### External Packages + +| Package | Modules | Purpose | +|---------|---------|---------| +| `base` | `Control.*, Data.*, GHC.*` | Standard library | +| `monad-time` | `Control.Monad.Time` | MonadTime typeclass | +| `aeson` | `Data.Aeson` | JSON serialization | +| `text` | `Data.Text` | Text type | +| `bytestring` | `Data.ByteString` | ByteString type | +| `time` | `Data.Time` | Time types | +| `cryptonite` | `Crypto.Hash` | SHA256 for PKCE | +| `memory` | `Data.ByteArray` | ByteArray operations | +| `base64-bytestring` | `Data.ByteString.Base64.URL` | Base64URL encoding | +| `servant` | `Servant.*` | Servant types | +| `servant-server` | `Servant.Server.*` | Servant server | +| `servant-auth-server` | `Servant.Auth.Server.*` | JWT support | +| `http-types` | `Network.HTTP.Types.*` | HTTP types | +| `generic-lens` | `Data.Generics.*` | Generic lens | +| `containers` | `Data.Map, Data.Set` | Container types | +| `stm` | `Control.Concurrent.STM` | STM operations | +| `mtl` | `Control.Monad.*` | Monad transformers | +| `transformers` | `Control.Monad.Trans.*` | Transformers | + +### Internal (Servant.OAuth2.IDP.*) + +Modules may import other `Servant.OAuth2.IDP.*` modules. + +Dependency graph (no cycles): +``` +Types.hs <- (no internal deps) +Config.hs <- Types +Trace.hs <- Types +Metadata.hs <- Types +PKCE.hs <- Types +Store.hs <- Types +Store/InMemory.hs <- Store, Types +Boundary.hs <- Types +Auth/Backend.hs <- Types +Auth/Demo.hs <- Auth/Backend, Types +Handlers/*.hs <- Store, Config, Trace, Types, PKCE, Metadata, Auth/* +Server.hs <- Handlers, API, Store, Config, Trace +API.hs <- Types, Metadata +``` + +## Forbidden Dependencies (Servant.OAuth2.IDP.*) + +| Namespace | Reason | +|-----------|--------| +| `MCP.*` | Package extraction goal | +| `Plow.*` | MCP-specific logging | + +## MCP.* Module Dependencies + +MCP modules MAY import from Servant: + +```haskell +-- Allowed +import Servant.OAuth2.IDP.Config (OAuthEnv (..)) +import Servant.OAuth2.IDP.Trace (OAuthTrace (..)) +import Servant.OAuth2.IDP.Metadata (OAuthMetadata (..), ProtectedResourceMetadata (..)) +import Servant.OAuth2.IDP.PKCE (validateCodeVerifier, generateCodeChallenge) +``` + +## Verification Script + +```bash +#!/bin/bash +# verify-no-mcp-imports.sh + +echo "Checking for MCP imports in Servant.OAuth2.IDP modules..." +VIOLATIONS=$(rg "^import MCP\." src/Servant/) + +if [ -n "$VIOLATIONS" ]; then + echo "ERROR: Found MCP imports in Servant modules:" + echo "$VIOLATIONS" + exit 1 +else + echo "OK: No MCP imports found in Servant modules" + exit 0 +fi +``` + +## CI Integration + +Add to CI pipeline: +```yaml +- name: Verify module dependencies + run: | + if rg "^import MCP\." src/Servant/; then + echo "ERROR: MCP imports found in Servant modules" + exit 1 + fi +``` diff --git a/specs/005-servant-oauth-extraction/data-model.md b/specs/005-servant-oauth-extraction/data-model.md new file mode 100644 index 0000000..a39b3c4 --- /dev/null +++ b/specs/005-servant-oauth-extraction/data-model.md @@ -0,0 +1,381 @@ +# Data Model: Remove MCP Imports from Servant.OAuth2.IDP + +**Branch**: `005-servant-oauth-extraction` +**Date**: 2025-12-18 + +## Overview + +This refactoring introduces 4 new types and moves 4 existing types to new modules. No data storage changes - this is a compile-time module boundary reorganization. + +--- + +## New Types + +### OAuthEnv + +**Module**: `Servant.OAuth2.IDP.Config` + +**Purpose**: Protocol-agnostic OAuth 2.1 server configuration. Contains all settings needed by OAuth handlers without MCP-specific concerns (demo mode, providers). + +```haskell +data OAuthEnv = OAuthEnv + { oauthBaseUrl :: Text + -- ^ Base URL for OAuth endpoints (e.g., "https://api.example.com") + -- Used to construct authorization_endpoint, token_endpoint in metadata + + , oauthAuthCodeExpiry :: NominalDiffTime + -- ^ Authorization code lifetime (typically 10 minutes per OAuth spec) + -- Codes older than this are rejected during token exchange + + , oauthAccessTokenExpiry :: NominalDiffTime + -- ^ Access token lifetime (typically 1 hour) + -- Used when generating JWT 'exp' claim + + , oauthLoginSessionExpiry :: NominalDiffTime + -- ^ Pending authorization session lifetime + -- Time user has to complete login after authorization request + + , oauthAuthCodePrefix :: Text + -- ^ Prefix for generated authorization codes (e.g., "code_") + -- Helps identify token types in logs/debugging + + , oauthRefreshTokenPrefix :: Text + -- ^ Prefix for generated refresh tokens (e.g., "rt_") + + , oauthClientIdPrefix :: Text + -- ^ Prefix for dynamically registered client IDs (e.g., "client_") + + , oauthSupportedScopes :: [Scope] + -- ^ Scopes this server understands (returned in metadata) + + , oauthSupportedResponseTypes :: [ResponseType] + -- ^ Response types supported (typically [ResponseTypeCode]) + + , oauthSupportedGrantTypes :: [GrantType] + -- ^ Grant types supported (typically [GrantTypeAuthorizationCode, GrantTypeRefreshToken]) + + , oauthSupportedAuthMethods :: [ClientAuthMethod] + -- ^ Token endpoint auth methods (typically [ClientSecretPost, ClientSecretBasic, None]) + + , oauthSupportedCodeChallengeMethods :: [CodeChallengeMethod] + -- ^ PKCE methods supported (typically [S256], Plain discouraged) + } + deriving (Generic) +``` + +**Validation Rules**: +- `oauthBaseUrl` must be non-empty +- `oauthAuthCodeExpiry` must be positive +- `oauthAccessTokenExpiry` must be positive +- `oauthLoginSessionExpiry` must be positive +- At least one entry in each supported* list + +**Smart Constructor**: +```haskell +mkOAuthEnv :: Text -> OAuthEnv +-- Creates OAuthEnv with sensible defaults: +-- - authCodeExpiry: 600 seconds (10 minutes) +-- - accessTokenExpiry: 3600 seconds (1 hour) +-- - loginSessionExpiry: 600 seconds (10 minutes) +-- - prefixes: "code_", "rt_", "client_" +-- - supported*: standard OAuth 2.1 values +``` + +--- + +### OAuthTrace + +**Module**: `Servant.OAuth2.IDP.Trace` + +**Purpose**: Structured trace events for OAuth flows. Enables observability without coupling to MCP trace infrastructure. + +```haskell +data OAuthTrace + = TraceClientRegistration + { trClientId :: ClientId + , trClientName :: ClientName + } + -- ^ Client successfully registered via /register endpoint + + | TraceAuthorizationRequest + { trClientId :: ClientId + , trScopes :: [Scope] + , trHasState :: Bool + } + -- ^ Authorization request received at /authorize + + | TraceLoginPageServed + { trSessionId :: SessionId + } + -- ^ Login page HTML returned to user agent + + | TraceLoginAttempt + { trUsername :: Text + , trSuccess :: Bool + } + -- ^ User submitted login form + + | TracePKCEValidation + { trIsValid :: Bool + } + -- ^ PKCE code_verifier validated against code_challenge + + | TraceAuthorizationGranted + { trClientId :: ClientId + , trUserId :: UserId + } + -- ^ User approved authorization, code generated + + | TraceAuthorizationDenied + { trClientId :: ClientId + , trReason :: Text + } + -- ^ User denied authorization or validation failed + + | TraceTokenExchange + { trGrantType :: GrantType + , trSuccess :: Bool + } + -- ^ Token endpoint request processed + + | TraceTokenRefresh + { trSuccess :: Bool + } + -- ^ Refresh token exchange attempted + + | TraceSessionExpired + { trSessionId :: SessionId + } + -- ^ Pending authorization session expired + + | TraceValidationError + { trErrorType :: Text + , trDetail :: Text + } + -- ^ Semantic validation failed (e.g., redirect_uri mismatch) + + deriving (Show, Eq) +``` + +**Rendering Function**: +```haskell +renderOAuthTrace :: OAuthTrace -> Text +-- Human-readable rendering for log output +``` + +--- + +## Moved Types + +### OAuthMetadata + +**From**: `MCP.Server.Auth` +**To**: `Servant.OAuth2.IDP.Metadata` + +**Purpose**: RFC 8414 OAuth Authorization Server Metadata discovery response. + +```haskell +data OAuthMetadata = OAuthMetadata + { issuer :: Text + , authorizationEndpoint :: Text + , tokenEndpoint :: Text + , registrationEndpoint :: Maybe Text + , userInfoEndpoint :: Maybe Text + , jwksUri :: Maybe Text + , scopesSupported :: Maybe [Scope] + , responseTypesSupported :: [ResponseType] + , grantTypesSupported :: Maybe [GrantType] + , tokenEndpointAuthMethodsSupported :: Maybe [ClientAuthMethod] + , codeChallengeMethodsSupported :: Maybe [CodeChallengeMethod] + } + deriving (Show, Generic) +``` + +**JSON Instances**: Custom `FromJSON`/`ToJSON` with snake_case keys per RFC 8414. + +--- + +### ProtectedResourceMetadata + +**From**: `MCP.Server.Auth` +**To**: `Servant.OAuth2.IDP.Metadata` + +**Purpose**: RFC 9728 OAuth Protected Resource Metadata. + +```haskell +data ProtectedResourceMetadata = ProtectedResourceMetadata + { resource :: Text + -- ^ Protected resource identifier (MUST be absolute URI with https) + + , authorizationServers :: [Text] + -- ^ List of authorization server issuer identifiers + + , scopesSupported :: Maybe [Scope] + -- ^ Scope values the resource server understands + + , bearerMethodsSupported :: Maybe [Text] + -- ^ Token presentation methods (default: ["header"]) + + , resourceName :: Maybe Text + -- ^ Human-readable name for display + + , resourceDocumentation :: Maybe Text + -- ^ URL of developer documentation + } + deriving (Show, Generic) +``` + +**JSON Instances**: Custom `FromJSON`/`ToJSON` with snake_case keys per RFC 9728. + +--- + +## Moved Functions + +### validateCodeVerifier + +**From**: `MCP.Server.Auth` +**To**: `Servant.OAuth2.IDP.PKCE` + +```haskell +validateCodeVerifier :: CodeVerifier -> CodeChallenge -> Bool +validateCodeVerifier (CodeVerifier verifier) (CodeChallenge challenge) = + generateCodeChallenge verifier == challenge +``` + +**Semantics**: Returns `True` iff `SHA256(base64url(verifier)) == challenge`. + +**Property**: `forall v. validateCodeVerifier v (mkChallenge v) == True` + where `mkChallenge = CodeChallenge . generateCodeChallenge . unCodeVerifier` + +--- + +### generateCodeChallenge + +**From**: `MCP.Server.Auth` +**To**: `Servant.OAuth2.IDP.PKCE` + +```haskell +generateCodeChallenge :: Text -> Text +generateCodeChallenge verifier = + let verifierBytes = TE.encodeUtf8 verifier + challengeHash = hashWith SHA256 verifierBytes + challengeBytes = convert challengeHash :: ByteString + in TE.decodeUtf8 $ B64URL.encodeUnpadded challengeBytes +``` + +**Semantics**: S256 code challenge per RFC 7636 Section 4.2. + +**Property**: Deterministic - same input always produces same output. + +--- + +## Type Relationships + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Servant.OAuth2.IDP.* │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │ +│ │ Config.hs │ │ Trace.hs │ │ Metadata.hs │ │ +│ │ │ │ │ │ │ │ +│ │ OAuthEnv │ │ OAuthTrace │ │ OAuthMetadata │ │ +│ │ │ │ │ │ ProtectedRes... │ │ +│ └─────────────┘ └─────────────┘ └─────────────────┘ │ +│ │ +│ ┌─────────────┐ │ +│ │ PKCE.hs │ Uses: │ +│ │ │ - Servant.OAuth2.IDP.Types │ +│ │ validate... │ (CodeVerifier, CodeChallenge, │ +│ │ generate... │ Scope, ClientId, etc.) │ +│ └─────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ + │ + │ contramap (adapter) + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ MCP.* │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────┐ ┌─────────────────────────────┐ │ +│ │ MCP.Server.HTTP │ │ MCP.Trace.HTTP │ │ +│ │ .AppEnv │ │ │ │ +│ │ │ │ HTTPOAuth (adapter to │ │ +│ │ AppEnv contains: │ │ Servant.OAuth2.IDP.Trace) │ │ +│ │ - envOAuthEnv :: │ │ │ │ +│ │ OAuthEnv │ │ │ │ +│ └─────────────────────┘ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────┐ │ +│ │ MCP.Server.Auth │ │ +│ │ │ │ +│ │ OAuthConfig (demo │ │ +│ │ mode, providers) │ │ +│ │ generateCodeVerifier│ │ +│ │ (IO action) │ │ +│ └─────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Migration Notes + +### For Handlers + +Before: +```haskell +import MCP.Server.Auth (OAuthConfig (..), validateCodeVerifier) +import MCP.Server.HTTP.AppEnv (HTTPServerConfig (..)) + +handleToken :: (HasType HTTPServerConfig env, ...) => ... +handleToken = do + config <- view (typed @HTTPServerConfig) + let oauthCfg = fromMaybe defaultConfig (httpOAuthConfig config) + let valid = validateCodeVerifier verifier challenge +``` + +After: +```haskell +import Servant.OAuth2.IDP.Config (OAuthEnv (..)) +import Servant.OAuth2.IDP.PKCE (validateCodeVerifier) + +handleToken :: (HasType OAuthEnv env, ...) => ... +handleToken = do + oauthEnv <- view (typed @OAuthEnv) + let valid = validateCodeVerifier verifier challenge +``` + +### For MCP Integration + +```haskell +-- In MCP.Server.HTTP.AppEnv + +import Servant.OAuth2.IDP.Config (OAuthEnv (..)) +import Servant.OAuth2.IDP.Trace (OAuthTrace) + +-- Build OAuthEnv from HTTPServerConfig +mkOAuthEnv :: HTTPServerConfig -> OAuthEnv +mkOAuthEnv HTTPServerConfig{..} = + let oauthCfg = fromMaybe defaultOAuthConfig httpOAuthConfig + in OAuthEnv + { oauthBaseUrl = httpBaseUrl + , oauthAuthCodeExpiry = fromIntegral (authCodeExpirySeconds oauthCfg) + , oauthAccessTokenExpiry = fromIntegral (accessTokenExpirySeconds oauthCfg) + , oauthLoginSessionExpiry = fromIntegral (loginSessionExpirySeconds oauthCfg) + , oauthAuthCodePrefix = authCodePrefix oauthCfg + , oauthRefreshTokenPrefix = refreshTokenPrefix oauthCfg + , oauthClientIdPrefix = clientIdPrefix oauthCfg + , oauthSupportedScopes = supportedScopes oauthCfg + , oauthSupportedResponseTypes = supportedResponseTypes oauthCfg + , oauthSupportedGrantTypes = supportedGrantTypes oauthCfg + , oauthSupportedAuthMethods = supportedAuthMethods oauthCfg + , oauthSupportedCodeChallengeMethods = supportedCodeChallengeMethods oauthCfg + } + +-- Adapt trace types +mkOAuthTracer :: IOTracer HTTPTrace -> IOTracer OAuthTrace +mkOAuthTracer = contramap HTTPOAuth +``` diff --git a/specs/005-servant-oauth-extraction/plan.md b/specs/005-servant-oauth-extraction/plan.md new file mode 100644 index 0000000..5d440b6 --- /dev/null +++ b/specs/005-servant-oauth-extraction/plan.md @@ -0,0 +1,266 @@ +# Implementation Plan: Remove MCP Imports from Servant.OAuth2.IDP + +**Branch**: `005-servant-oauth-extraction` | **Date**: 2025-12-18 | **Spec**: [spec.md](./spec.md) +**Input**: Feature specification from `/specs/005-servant-oauth-extraction/spec.md` + +## Summary + +Prepare `Servant.OAuth2.IDP.*` modules for extraction to a separate package by removing all `MCP.*` dependencies. This is a pure refactoring: move types to new Servant modules, use explicit record parameters, clean break with no backwards-compatibility re-exports. + +## Technical Context + +**Language/Version**: Haskell GHC2021 (GHC 9.4+ via base ^>=4.18.2.1) +**Primary Dependencies**: servant-server 0.19-0.20, servant-auth-server 0.4, aeson 2.1-2.2, monad-time, network-uri, cryptonite 0.30, generic-lens +**Storage**: N/A (refactoring only, no data changes) +**Testing**: cabal test (HSpec test suite) +**Target Platform**: Linux server +**Project Type**: Single Haskell library +**Performance Goals**: N/A (no runtime changes) +**Constraints**: Must maintain API compatibility for OAuth functionality +**Scale/Scope**: ~15 files modified, 4 new files created + +## Constitution Check + +*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* + +Reference: `.specify/memory/constitution.md` + +| Principle | Status | Evidence/Notes | +|-----------|--------|----------------| +| I. Type-Driven Design | :white_check_mark: | `OAuthEnv` record designed with explicit typed fields; `OAuthTrace` ADT with typed constructors; moved types preserve existing validation (CodeChallenge, CodeVerifier newtypes) | +| II. Deep Module Architecture | :white_check_mark: | New modules expose minimal public interface; `OAuthEnv` hides configuration complexity behind single record; PKCE internals hidden behind `validateCodeVerifier` | +| III. Denotational Semantics | :white_check_mark: | PKCE validation has clear mathematical semantics (S256 hash comparison); no new abstractions requiring law documentation | +| IV. Total Functions | :white_check_mark: | `validateCodeVerifier :: CodeVerifier -> CodeChallenge -> Bool` - total function returning Bool; `generateCodeChallenge :: Text -> Text` - total | +| V. Pure Core, Impure Shell | :white_check_mark: | All new modules are pure (`Trace`, `Config`, `Metadata`, `PKCE`); impure operations remain at boundary in handlers | +| VI. Property-Based Testing | :white_check_mark: | Existing tests preserved; PKCE round-trip testable: `validateCodeVerifier (generateCodeVerifier) (generateCodeChallenge verifier) == True` | + +## Project Structure + +### Documentation (this feature) + +```text +specs/005-servant-oauth-extraction/ +├── plan.md # This file +├── research.md # Phase 0 output +├── data-model.md # Phase 1 output +├── quickstart.md # Phase 1 output +└── contracts/ # Phase 1 output (N/A for refactoring) +``` + +### Source Code (repository root) + +```text +src/ +├── Servant/OAuth2/IDP/ +│ ├── API.hs # MODIFY: Import Metadata from new location +│ ├── Auth/ +│ │ ├── Backend.hs # UNCHANGED +│ │ └── Demo.hs # UNCHANGED +│ ├── Boundary.hs # UNCHANGED +│ ├── Config.hs # NEW: OAuthEnv record +│ ├── Handlers/ +│ │ ├── Authorization.hs # MODIFY: Use OAuthEnv, OAuthTrace +│ │ ├── Helpers.hs # MODIFY: Use OAuthEnv, OAuthTrace +│ │ ├── HTML.hs # UNCHANGED +│ │ ├── Login.hs # MODIFY: Use OAuthEnv, OAuthTrace +│ │ ├── Metadata.hs # MODIFY: Use OAuthEnv, import from Servant +│ │ ├── Registration.hs # MODIFY: Use OAuthEnv, OAuthTrace +│ │ └── Token.hs # MODIFY: Use OAuthEnv, OAuthTrace, PKCE +│ ├── Handlers.hs # UNCHANGED (re-exports) +│ ├── LoginFlowError.hs # UNCHANGED +│ ├── Metadata.hs # NEW: OAuthMetadata, ProtectedResourceMetadata +│ ├── PKCE.hs # NEW: validateCodeVerifier, generateCodeChallenge +│ ├── Server.hs # MODIFY: Use OAuthEnv, OAuthTrace +│ ├── Store/ +│ │ └── InMemory.hs # MODIFY: MonadTime import +│ ├── Store.hs # MODIFY: MonadTime import +│ ├── Test/ +│ │ └── Internal.hs # MODIFY: MonadTime import, fix doc comment typo +│ ├── Trace.hs # NEW: OAuthTrace ADT +│ └── Types.hs # UNCHANGED +└── MCP/ + ├── Server/ + │ ├── Auth.hs # MODIFY: Remove moved types + │ ├── HTTP/ + │ │ └── AppEnv.hs # MODIFY: Add OAuthEnv, tracer adapter + │ └── Time.hs # UNCHANGED + └── Trace/ + ├── HTTP.hs # UNCHANGED (still uses MCP.Trace.OAuth) + └── OAuth.hs # UNCHANGED (stays as MCP-specific trace embedding) +``` + +**Structure Decision**: Single library structure. New modules added under `Servant.OAuth2.IDP.*` namespace to maintain package extraction path. + +## Complexity Tracking + +> No violations requiring justification. All changes follow Constitution principles. + +| Violation | Why Needed | Simpler Alternative Rejected Because | +|-----------|------------|-------------------------------------| +| N/A | N/A | N/A | + +--- + +## Phase 0: Research + +### R-001: MonadTime Package Availability + +**Question**: Is `Control.Monad.Time` from the `monad-time` package already a direct dependency? + +**Finding**: Yes. The `MCP.Server.Time` module is a thin re-export: +```haskell +module MCP.Server.Time (MonadTime (..)) where +import Control.Monad.Time (MonadTime (..)) +``` + +**Decision**: Direct import from `Control.Monad.Time` is safe - no new dependency needed. + +### R-002: Test Module Documentation Typo + +**Question**: What is `MCP.Server.OAuth.Test.Internal` that Test.Internal references? + +**Finding**: The file `src/Servant/OAuth2/IDP/Test/Internal.hs` declares `module Servant.OAuth2.IDP.Test.Internal` (line 37), but the doc comment header says `Module : MCP.Server.OAuth.Test.Internal` (line 7). The references at lines 22, etc. are in documentation examples, not actual imports. + +**Decision**: Fix the documentation typo. The module exists and is correctly named; only doc comments need updating. + +### R-003: OAuthTrace Design + +**Question**: Should `OAuthTrace` duplicate `MCP.Trace.OAuth.OAuthTrace` or import it? + +**Finding**: `MCP.Trace.OAuth.OAuthTrace` already exists and is MCP-independent (comment in file: "This module is intentionally MCP-independent to enable future package separation"). + +**Decision**: Move the type from `MCP.Trace.OAuth` to `Servant.OAuth2.IDP.Trace`, then have MCP re-export or adapt. This achieves true separation. However, examining the architecture more closely: + +- `MCP.Trace.HTTP` imports `MCP.Trace.OAuth` and embeds it via `HTTPOAuth OAuthTrace` +- Changing this creates a larger refactoring scope + +**Revised Decision**: Create `Servant.OAuth2.IDP.Trace` with a new trace type. Handlers will emit via the Servant trace type. MCP adapts at boundary via `contramap`. This is cleaner separation. + +### R-004: OAuthEnv vs HTTPServerConfig + OAuthConfig + +**Question**: What fields from `HTTPServerConfig` and `OAuthConfig` are actually used in Servant handlers? + +**Finding** (from grep of handler files): +- `httpBaseUrl` - for constructing redirect URLs +- `httpOAuthConfig` - for: + - `authCodeExpirySeconds` + - `accessTokenExpirySeconds` + - `authCodePrefix` + - `refreshTokenPrefix` + - `clientIdPrefix` + - `supportedScopes` + - `supportedResponseTypes` + - `supportedGrantTypes` + - `supportedAuthMethods` + - `supportedCodeChallengeMethods` + - `loginSessionExpirySeconds` + - `autoApproveAuth` (demo mode) + - `demoUserIdTemplate` (demo mode) + - `demoEmailDomain` (demo mode) + +**Decision**: `OAuthEnv` should contain: +1. Core fields: baseUrl, expiry times, prefixes, supported params +2. NOT demo mode fields - those are MCP-specific + +The handlers should be parameterized to work without demo mode fields. Demo mode logic stays in MCP. + +--- + +## Phase 1: Design + +### Data Model + +See [data-model.md](./data-model.md). + +### Key Types + +#### OAuthEnv (new) + +```haskell +data OAuthEnv = OAuthEnv + { oauthBaseUrl :: Text + , oauthAuthCodeExpiry :: NominalDiffTime + , oauthAccessTokenExpiry :: NominalDiffTime + , oauthLoginSessionExpiry :: NominalDiffTime + , oauthAuthCodePrefix :: Text + , oauthRefreshTokenPrefix :: Text + , oauthClientIdPrefix :: Text + , oauthSupportedScopes :: [Scope] + , oauthSupportedResponseTypes :: [ResponseType] + , oauthSupportedGrantTypes :: [GrantType] + , oauthSupportedAuthMethods :: [ClientAuthMethod] + , oauthSupportedCodeChallengeMethods :: [CodeChallengeMethod] + } + deriving (Generic) +``` + +#### OAuthTrace (new) + +```haskell +data OAuthTrace + = TraceClientRegistration ClientId ClientName + | TraceAuthorizationRequest ClientId [Scope] Bool -- hasState + | TraceLoginPageServed SessionId + | TraceLoginAttempt Text Bool -- username, success + | TracePKCEValidation Bool -- isValid + | TraceAuthorizationGranted ClientId UserId + | TraceAuthorizationDenied ClientId Text -- reason + | TraceTokenExchange GrantType Bool -- success + | TraceTokenRefresh Bool -- success + | TraceSessionExpired SessionId + | TraceValidationError Text Text -- errorType, detail + deriving (Show, Eq) +``` + +### Migration Path + +1. **Phase A** (Additive): Create new Servant modules (`Trace`, `Config`, `Metadata`, `PKCE`) +2. **Phase B** (Update Servant): Change imports in Servant handlers to use new modules +3. **Phase C** (Update MCP): Add `OAuthEnv` to `AppEnv`, create adapter functions +4. **Phase D** (Clean Break): Remove moved types from `MCP.Server.Auth` + +### Contracts + +No external API contracts change. Internal module boundaries shift. + +--- + +## Implementation Phases (for /speckit.tasks) + +### Phase A: Create New Servant Modules + +1. Create `src/Servant/OAuth2/IDP/Trace.hs` with `OAuthTrace` ADT +2. Create `src/Servant/OAuth2/IDP/Config.hs` with `OAuthEnv` record +3. Create `src/Servant/OAuth2/IDP/Metadata.hs` with moved types +4. Create `src/Servant/OAuth2/IDP/PKCE.hs` with moved functions +5. Update `mcp-haskell.cabal` with new modules + +### Phase B: Update Servant Imports + +1. Update `Store.hs` and `Store/InMemory.hs` - MonadTime import +2. Update `API.hs` - Metadata import +3. Update `Handlers/Metadata.hs` - use Servant Metadata +4. Update `Handlers/Token.hs` - use PKCE from Servant +5. Update `Handlers/Helpers.hs` - OAuthEnv and trace +6. Update `Handlers/Registration.hs` - OAuthEnv and trace +7. Update `Handlers/Authorization.hs` - OAuthEnv and trace +8. Update `Handlers/Login.hs` - OAuthEnv and trace +9. Update `Server.hs` - OAuthEnv and trace +10. Update `Test/Internal.hs` - fix doc comment typo, MonadTime import + +### Phase C: Update MCP + +1. Add `OAuthEnv` field to `AppEnv` +2. Create `mkOAuthEnv :: HTTPServerConfig -> OAuthEnv` +3. Create `mkOAuthTracer :: IOTracer HTTPTrace -> IOTracer OAuthTrace` +4. Update handler call sites to pass OAuthEnv + +### Phase D: Clean Break + +1. Remove `OAuthMetadata` from `MCP.Server.Auth` exports +2. Remove `ProtectedResourceMetadata` from `MCP.Server.Auth` exports +3. Remove `validateCodeVerifier` from `MCP.Server.Auth` exports +4. Remove `generateCodeChallenge` from `MCP.Server.Auth` exports +5. Verify: `rg "^import MCP\." src/Servant/` returns empty +6. Verify: `cabal build` succeeds +7. Verify: `cabal test` passes diff --git a/specs/005-servant-oauth-extraction/quickstart.md b/specs/005-servant-oauth-extraction/quickstart.md new file mode 100644 index 0000000..8f06261 --- /dev/null +++ b/specs/005-servant-oauth-extraction/quickstart.md @@ -0,0 +1,187 @@ +# Quickstart: Remove MCP Imports from Servant.OAuth2.IDP + +**Branch**: `005-servant-oauth-extraction` +**Date**: 2025-12-18 + +## Overview + +This refactoring removes all `MCP.*` imports from `Servant.OAuth2.IDP.*` modules, preparing them for extraction to a separate package. + +## Changes Summary + +| Change Type | Count | Description | +|-------------|-------|-------------| +| New Modules | 4 | Trace, Config, Metadata, PKCE | +| Modified Modules | 15 | Import updates, signature changes | +| Removed Exports | 4 | From MCP.Server.Auth | + +## New Module Imports + +After this refactoring, use these imports: + +```haskell +-- OAuth configuration +import Servant.OAuth2.IDP.Config (OAuthEnv (..)) + +-- OAuth trace events +import Servant.OAuth2.IDP.Trace (OAuthTrace (..)) + +-- OAuth metadata types (RFC 8414, RFC 9728) +import Servant.OAuth2.IDP.Metadata (OAuthMetadata (..), ProtectedResourceMetadata (..)) + +-- PKCE validation +import Servant.OAuth2.IDP.PKCE (validateCodeVerifier, generateCodeChallenge) + +-- MonadTime (direct from monad-time, not via MCP) +import Control.Monad.Time (MonadTime (..)) +``` + +## Handler Signature Changes + +### Before + +```haskell +import MCP.Server.Auth (OAuthConfig (..)) +import MCP.Server.HTTP.AppEnv (HTTPServerConfig (..)) +import MCP.Trace.HTTP (HTTPTrace (..)) + +handleAuthorization :: + ( OAuthStateStore m + , MonadReader env m + , HasType HTTPServerConfig env + , HasType (IOTracer HTTPTrace) env + ) => AuthorizationRequest -> m AuthorizationResponse +``` + +### After + +```haskell +import Servant.OAuth2.IDP.Config (OAuthEnv (..)) +import Servant.OAuth2.IDP.Trace (OAuthTrace (..)) + +handleAuthorization :: + ( OAuthStateStore m + , MonadReader env m + , HasType OAuthEnv env + , HasType (IOTracer OAuthTrace) env + ) => AuthorizationRequest -> m AuthorizationResponse +``` + +## Configuration Access Changes + +### Before + +```haskell +config <- view (typed @HTTPServerConfig) +let baseUrl = httpBaseUrl config +let oauthCfg = fromMaybe defaultOAuthConfig (httpOAuthConfig config) +let prefix = authCodePrefix oauthCfg +let expiry = fromIntegral (authCodeExpirySeconds oauthCfg) +``` + +### After + +```haskell +oauthEnv <- view (typed @OAuthEnv) +let baseUrl = oauthBaseUrl oauthEnv +let prefix = oauthAuthCodePrefix oauthEnv +let expiry = oauthAuthCodeExpiry oauthEnv -- Already NominalDiffTime +``` + +## Trace Emission Changes + +### Before + +```haskell +import MCP.Trace.HTTP (HTTPTrace (..)) +import MCP.Trace.OAuth qualified as OAuthTrace + +tracer <- view (typed @(IOTracer HTTPTrace)) +liftIO $ emit tracer (HTTPOAuth $ OAuthTrace.OAuthClientRegistration clientId name) +``` + +### After + +```haskell +import Servant.OAuth2.IDP.Trace (OAuthTrace (..)) + +tracer <- view (typed @(IOTracer OAuthTrace)) +liftIO $ emit tracer (TraceClientRegistration clientId name) +``` + +## MCP Integration + +For MCP applications, add `OAuthEnv` to your `AppEnv`: + +```haskell +import Servant.OAuth2.IDP.Config (OAuthEnv (..)) +import Servant.OAuth2.IDP.Trace (OAuthTrace) + +data AppEnv = AppEnv + { ... + , envOAuthEnv :: OAuthEnv -- NEW + , envOAuthTracer :: IOTracer OAuthTrace -- NEW (or adapt existing) + } + +-- Build OAuthEnv from existing config +mkOAuthEnv :: HTTPServerConfig -> OAuthEnv +mkOAuthEnv cfg = OAuthEnv + { oauthBaseUrl = httpBaseUrl cfg + , oauthAuthCodeExpiry = fromIntegral (authCodeExpirySeconds oauthCfg) + , ... -- See data-model.md for full mapping + } + where + oauthCfg = fromMaybe defaultOAuthConfig (httpOAuthConfig cfg) +``` + +## Verification + +After implementation, verify the refactoring succeeded: + +```bash +# No MCP imports in Servant modules +rg "^import MCP\." src/Servant/ +# Should return empty + +# Build succeeds +cabal build + +# Tests pass +cabal test +``` + +## Breaking Changes + +### Removed from MCP.Server.Auth + +These exports are removed - import from Servant instead: + +| Removed | New Location | +|---------|--------------| +| `OAuthMetadata` | `Servant.OAuth2.IDP.Metadata` | +| `ProtectedResourceMetadata` | `Servant.OAuth2.IDP.Metadata` | +| `validateCodeVerifier` | `Servant.OAuth2.IDP.PKCE` | +| `generateCodeChallenge` | `Servant.OAuth2.IDP.PKCE` | + +### Still in MCP.Server.Auth + +These remain unchanged: + +- `OAuthConfig` - MCP-specific configuration +- `OAuthProvider` - Provider configuration +- `OAuthGrantType` - Grant type enum +- `TokenInfo` - Token introspection +- `extractBearerToken` - Utility function +- `PKCEChallenge` - PKCE data +- `generateCodeVerifier` - IO action (needs cryptonite) + +## Timeline + +This is a pure refactoring with no runtime behavior changes. Implementation phases: + +1. **Phase A**: Create new Servant modules (additive, safe) +2. **Phase B**: Update Servant imports (internal changes) +3. **Phase C**: Update MCP integration (add OAuthEnv to AppEnv) +4. **Phase D**: Clean break (remove old exports) + +Each phase should build and test successfully before proceeding. diff --git a/specs/005-servant-oauth-extraction/research.md b/specs/005-servant-oauth-extraction/research.md new file mode 100644 index 0000000..68252ab --- /dev/null +++ b/specs/005-servant-oauth-extraction/research.md @@ -0,0 +1,269 @@ +# Research: Remove MCP Imports from Servant.OAuth2.IDP + +**Branch**: `005-servant-oauth-extraction` +**Date**: 2025-12-18 + +## R-001: MonadTime Package Availability + +**Question**: Is `Control.Monad.Time` from the `monad-time` package already a direct dependency? + +**Investigation**: +```haskell +-- src/MCP/Server/Time.hs +module MCP.Server.Time (MonadTime (..)) where +import Control.Monad.Time (MonadTime (..)) +``` + +The MCP.Server.Time module is a thin re-export layer. The `monad-time` package is already a transitive dependency. + +**Decision**: Direct import from `Control.Monad.Time` is safe - no new package dependency needed. + +**Rationale**: Eliminates unnecessary indirection. The Servant modules can directly depend on `monad-time` which is already in the dependency tree. + +**Alternatives Considered**: +- Keep MCP.Server.Time re-export: Rejected - creates unnecessary coupling +- Define own MonadTime typeclass: Rejected - reinventing the wheel + +--- + +## R-002: Test Module Documentation Typo + +**Question**: What is `MCP.Server.OAuth.Test.Internal` that Test.Internal references? + +**Investigation**: + +The file `src/Servant/OAuth2/IDP/Test/Internal.hs` has: +- Line 7: Doc comment says `Module : MCP.Server.OAuth.Test.Internal` (TYPO) +- Line 37: Actual declaration is `module Servant.OAuth2.IDP.Test.Internal` +- Line 22: Doc example shows `import MCP.Server.OAuth.Test.Internal` (TYPO in example) + +The references to `MCP.Server.OAuth.Test.Internal` are in documentation comments and examples, not actual import statements. The module exists as `Servant.OAuth2.IDP.Test.Internal`. + +**Decision**: Fix the documentation typo. The module is correctly named `Servant.OAuth2.IDP.Test.Internal`; only the doc header and usage example need updating. + +**Rationale**: This is a minor documentation fix, not dead code removal. + +**Alternatives Considered**: +- Leave typo: Rejected - causes confusion and doesn't match actual module name + +--- + +## R-003: OAuthTrace Design + +**Question**: Should `Servant.OAuth2.IDP.Trace.OAuthTrace` duplicate `MCP.Trace.OAuth.OAuthTrace` or move it? + +**Investigation**: +```haskell +-- src/MCP/Trace/OAuth.hs +{- | OAuth 2.0 flow events. + +This type is semantically independent of MCP for future package separation. +-} +data OAuthTrace = ... +``` + +The existing `MCP.Trace.OAuth.OAuthTrace` was designed to be MCP-independent. However: + +1. `MCP.Trace.HTTP` imports and embeds it: `HTTPOAuth OAuthTrace` +2. Moving it would require updating `MCP.Trace.HTTP` to import from Servant +3. This creates a dependency in the wrong direction (MCP depending on Servant) + +**Decision**: Create a new `Servant.OAuth2.IDP.Trace` module with its own `OAuthTrace` type. The Servant handlers emit Servant trace events. MCP adapts at the boundary using `contramap` to convert to `MCP.Trace.HTTP.HTTPTrace`. + +**Rationale**: +- Clean separation: Servant modules have no knowledge of MCP trace infrastructure +- Flexibility: Different trace granularity for different contexts +- Contramap pattern: Standard approach for trace adaptation + +**Alternatives Considered**: +- Move `MCP.Trace.OAuth.OAuthTrace` to Servant: Rejected - creates wrong dependency direction +- Re-export from MCP: Rejected - doesn't achieve package separation goal +- Share via third package: Rejected - overkill for this use case + +--- + +## R-004: OAuthEnv vs HTTPServerConfig + OAuthConfig + +**Question**: What configuration fields are actually needed by Servant handlers? + +**Investigation** (grep of handler files): + +From `HTTPServerConfig`: +- `httpBaseUrl :: Text` - constructing OAuth redirect URLs and metadata + +From `OAuthConfig` (nested in `HTTPServerConfig.httpOAuthConfig`): +- `authCodeExpirySeconds :: Int` - authorization code lifetime +- `accessTokenExpirySeconds :: Int` - access token lifetime +- `loginSessionExpirySeconds :: Int` - pending authorization lifetime +- `authCodePrefix :: Text` - prefix for generated auth codes +- `refreshTokenPrefix :: Text` - prefix for generated refresh tokens +- `clientIdPrefix :: Text` - prefix for generated client IDs +- `supportedScopes :: [Scope]` - metadata response +- `supportedResponseTypes :: [ResponseType]` - metadata response +- `supportedGrantTypes :: [GrantType]` - metadata response +- `supportedAuthMethods :: [ClientAuthMethod]` - metadata response +- `supportedCodeChallengeMethods :: [CodeChallengeMethod]` - metadata response + +Demo-mode fields (MCP-specific, NOT needed in handlers): +- `autoApproveAuth :: Bool` +- `demoUserIdTemplate :: Maybe Text` +- `demoEmailDomain :: Text` +- `demoUserName :: Text` + +**Decision**: Create `OAuthEnv` with protocol-agnostic fields only. Demo mode logic stays in MCP-specific code paths. + +**Rationale**: +- `OAuthEnv` represents pure OAuth 2.1 configuration +- Demo mode is an MCP implementation detail, not protocol-required +- Handlers parameterized by `OAuthEnv` are reusable in non-demo contexts + +**Type Design**: +```haskell +data OAuthEnv = OAuthEnv + { oauthBaseUrl :: Text + , oauthAuthCodeExpiry :: NominalDiffTime + , oauthAccessTokenExpiry :: NominalDiffTime + , oauthLoginSessionExpiry :: NominalDiffTime + , oauthAuthCodePrefix :: Text + , oauthRefreshTokenPrefix :: Text + , oauthClientIdPrefix :: Text + , oauthSupportedScopes :: [Scope] + , oauthSupportedResponseTypes :: [ResponseType] + , oauthSupportedGrantTypes :: [GrantType] + , oauthSupportedAuthMethods :: [ClientAuthMethod] + , oauthSupportedCodeChallengeMethods :: [CodeChallengeMethod] + } +``` + +**Alternatives Considered**: +- Include demo fields: Rejected - couples Servant to MCP demo functionality +- Use typeclass for config: Rejected - explicit record simpler, no laws needed +- Pass individual fields: Rejected - too many parameters, hard to maintain + +--- + +## R-005: Handler Signature Refactoring + +**Question**: How should handler type signatures change to use `OAuthEnv` instead of `HTTPServerConfig`? + +**Investigation**: + +Current pattern: +```haskell +handleToken :: + ( OAuthStateStore m + , AuthBackend m + , MonadReader env m + , HasType HTTPServerConfig env + , HasType (IOTracer HTTPTrace) env + , ... + ) => ... +``` + +Fields accessed: +```haskell +config <- view (typed @HTTPServerConfig) +let baseUrl = httpBaseUrl config +let oauthCfg = fromMaybe defaultOAuthConfig (httpOAuthConfig config) +let prefix = authCodePrefix oauthCfg +``` + +**Decision**: Replace `HasType HTTPServerConfig env` with `HasType OAuthEnv env`. Update field access to use `OAuthEnv` fields directly. + +New pattern: +```haskell +handleToken :: + ( OAuthStateStore m + , AuthBackend m + , MonadReader env m + , HasType OAuthEnv env + , HasType (IOTracer OAuthTrace) env + , ... + ) => ... +``` + +Field access: +```haskell +oauthEnv <- view (typed @OAuthEnv) +let baseUrl = oauthBaseUrl oauthEnv +let prefix = oauthAuthCodePrefix oauthEnv +``` + +**Rationale**: Direct field access without `Maybe` unwrapping. All configuration is required at construction time. + +--- + +## R-006: MCP.Server.Auth Module Split + +**Question**: What stays in `MCP.Server.Auth` after moving types to Servant? + +**Investigation**: + +Current exports: +```haskell +module MCP.Server.Auth ( + -- Re-exported + module Servant.OAuth2.IDP.Auth.Backend, + + -- OAuth Configuration (MCP-specific) + OAuthConfig (..), + OAuthProvider (..), + OAuthGrantType (..), + + -- Token Validation + TokenInfo (..), + extractBearerToken, + + -- PKCE Support + PKCEChallenge (..), + generateCodeVerifier, -- IO action (stays in MCP) + generateCodeChallenge, -- MOVE to Servant + validateCodeVerifier, -- MOVE to Servant + + -- Metadata Discovery + OAuthMetadata (..), -- MOVE to Servant + + -- Protected Resource Metadata + ProtectedResourceMetadata (..), -- MOVE to Servant + ProtectedResourceAuth, + ProtectedResourceAuthConfig (..), +) +``` + +**Decision**: + +Types staying in `MCP.Server.Auth`: +- `OAuthConfig` - MCP-specific with demo mode fields +- `OAuthProvider` - MCP provider configuration +- `OAuthGrantType` - MCP grant type enum +- `TokenInfo` - token introspection response +- `extractBearerToken` - utility function +- `PKCEChallenge` - PKCE data container +- `generateCodeVerifier` - IO action using cryptonite +- `ProtectedResourceAuth` - type-level auth tag +- `ProtectedResourceAuthConfig` - config for above + +Types moving to `Servant.OAuth2.IDP.*`: +- `OAuthMetadata` -> `Servant.OAuth2.IDP.Metadata` +- `ProtectedResourceMetadata` -> `Servant.OAuth2.IDP.Metadata` +- `validateCodeVerifier` -> `Servant.OAuth2.IDP.PKCE` +- `generateCodeChallenge` -> `Servant.OAuth2.IDP.PKCE` + +**Rationale**: +- IO actions stay in MCP (generateCodeVerifier uses cryptonite random) +- Pure functions move to Servant (validateCodeVerifier, generateCodeChallenge) +- Metadata types move to Servant (used in OAuth discovery endpoints) + +--- + +## Summary of Decisions + +| Item | Decision | +|------|----------| +| MonadTime | Direct import from `Control.Monad.Time` | +| Doc typo | Fix `MCP.Server.OAuth.Test.Internal` references in doc comments | +| OAuthTrace | New type in `Servant.OAuth2.IDP.Trace`, MCP adapts via contramap | +| OAuthEnv | New record with protocol-agnostic fields only | +| Demo mode | Stays in MCP, not part of OAuthEnv | +| Handler signatures | Replace `HasType HTTPServerConfig` with `HasType OAuthEnv` | +| MCP.Server.Auth | Keep IO actions, move pure functions and metadata types | diff --git a/specs/005-servant-oauth-extraction/spec.md b/specs/005-servant-oauth-extraction/spec.md new file mode 100644 index 0000000..3335b0c --- /dev/null +++ b/specs/005-servant-oauth-extraction/spec.md @@ -0,0 +1,146 @@ +# Feature Specification: Remove MCP Imports from Servant.OAuth2.IDP + +**Branch**: `005-servant-oauth-extraction` +**Status**: In Planning +**Date**: 2025-12-18 + +## Summary + +Prepare the `Servant.OAuth2.IDP.*` modules for extraction to a separate package by removing all `MCP.*` dependencies. This is a refactoring task that moves types and functions from MCP modules to new Servant modules, using explicit parameters and record types rather than new typeclasses. + +## Goals + +1. **Package Independence**: `Servant.OAuth2.IDP.*` modules should have zero imports from `MCP.*` namespace +2. **Clean Break**: No backwards compatibility re-exports - MCP modules that moved types simply remove them +3. **Minimal API Surface**: Use explicit record parameters rather than introducing new typeclasses + +## Non-Goals + +- Creating a separate Hackage package (that's a future step) +- Adding new functionality to OAuth +- Changing OAuth behavior + +## Requirements + +### Functional Requirements + +#### FR-001: Move MonadTime Import +**Priority**: P0 (Critical) +**Description**: Replace all `import MCP.Server.Time (MonadTime)` with direct `import Control.Monad.Time (MonadTime)` +**Files Affected**: +- `src/Servant/OAuth2/IDP/Store.hs` +- `src/Servant/OAuth2/IDP/Store/InMemory.hs` +- `src/Servant/OAuth2/IDP/Handlers/Authorization.hs` +- `src/Servant/OAuth2/IDP/Handlers/Login.hs` +- `src/Servant/OAuth2/IDP/Test/Internal.hs` + +#### FR-002: Create Servant.OAuth2.IDP.Metadata Module +**Priority**: P0 (Critical) +**Description**: Move `OAuthMetadata` and `ProtectedResourceMetadata` types from `MCP.Server.Auth` to new `Servant.OAuth2.IDP.Metadata` module +**Types to Move**: +- `OAuthMetadata` (RFC 8414 discovery response) +- `ProtectedResourceMetadata` (RFC 9728) +- All associated JSON instances + +#### FR-003: Create Servant.OAuth2.IDP.PKCE Module +**Priority**: P0 (Critical) +**Description**: Move PKCE validation functions from `MCP.Server.Auth` to new `Servant.OAuth2.IDP.PKCE` module +**Functions to Move**: +- `validateCodeVerifier :: CodeVerifier -> CodeChallenge -> Bool` +- `generateCodeChallenge :: Text -> Text` + +#### FR-004: Create Servant.OAuth2.IDP.Config Module +**Priority**: P1 (High) +**Description**: Define `OAuthEnv` record containing protocol-agnostic OAuth configuration +**Fields**: +- `baseUrl :: URI` +- `authCodeExpiry :: NominalDiffTime` +- `accessTokenExpiry :: NominalDiffTime` +- `loginSessionExpiry :: NominalDiffTime` +- Token prefixes (authCode, refreshToken, clientId) +- Supported scopes, response types, grant types, auth methods, code challenge methods + +#### FR-005: Create Servant.OAuth2.IDP.Trace Module +**Priority**: P1 (High) +**Description**: Define `OAuthTrace` ADT for OAuth-specific trace events +**Constructors**: +- `TraceClientRegistration ClientId Text` +- `TraceAuthorizationRequest ClientId [Scope] Bool` +- `TraceLoginPageServed SessionId` +- `TraceLoginAttempt Text Bool` +- `TracePKCEValidation Bool` +- `TraceAuthorizationGranted ClientId Text` +- `TraceAuthorizationDenied ClientId Text` +- `TraceTokenExchange GrantType Bool` +- `TraceTokenRefresh Bool` +- `TraceSessionExpired SessionId` +- `TraceValidationError Text Text` + +#### FR-006: Update Handler Signatures +**Priority**: P1 (High) +**Description**: Update all handler modules to use Servant types instead of MCP types +**Changes**: +- Replace `HasType HTTPServerConfig env` with `HasType OAuthEnv env` +- Replace `HasType (IOTracer HTTPTrace) env` with `HasType (IOTracer OAuthTrace) env` + +#### FR-007: Update MCP.Server.HTTP.AppEnv +**Priority**: P1 (High) +**Description**: Embed `OAuthEnv` in `AppEnv` and create tracer adapter +**Changes**: +- Add `envOAuthEnv :: OAuthEnv` field +- Create `mkOAuthEnv :: HTTPServerConfig -> OAuthEnv` function +- Create `mkOAuthTracer :: IOTracer HTTPTrace -> IOTracer OAuthTrace` via contramap + +#### FR-008: Remove Types from MCP.Server.Auth +**Priority**: P2 (Medium) +**Description**: Clean break - remove moved types from MCP.Server.Auth exports +**Types to Remove**: +- `OAuthMetadata` +- `ProtectedResourceMetadata` +- `validateCodeVerifier` +- `generateCodeChallenge` + +## Acceptance Criteria + +1. `rg "^import MCP\." src/Servant/` returns empty (no MCP imports in Servant modules) +2. `cabal build` succeeds with no errors +3. `cabal test` passes (all existing tests pass) +4. No functionality changes - this is a pure refactoring + +## Dependencies + +- `monad-time` package (already a dependency via MCP.Server.Time) +- `network-uri` package for URI type in OAuthEnv + +## Files to Create + +| File | Purpose | +|------|---------| +| `src/Servant/OAuth2/IDP/Trace.hs` | OAuthTrace ADT | +| `src/Servant/OAuth2/IDP/Config.hs` | OAuthEnv record | +| `src/Servant/OAuth2/IDP/Metadata.hs` | OAuthMetadata, ProtectedResourceMetadata | +| `src/Servant/OAuth2/IDP/PKCE.hs` | PKCE validation functions | + +## Files to Modify + +| File | Changes | +|------|---------| +| `src/Servant/OAuth2/IDP/Store.hs` | MonadTime import | +| `src/Servant/OAuth2/IDP/Store/InMemory.hs` | MonadTime import | +| `src/Servant/OAuth2/IDP/API.hs` | Metadata import | +| `src/Servant/OAuth2/IDP/Server.hs` | Config/Trace types | +| `src/Servant/OAuth2/IDP/Handlers/Helpers.hs` | Config record, trace events | +| `src/Servant/OAuth2/IDP/Handlers/Metadata.hs` | Config record, Metadata import | +| `src/Servant/OAuth2/IDP/Handlers/Registration.hs` | Config/Trace types | +| `src/Servant/OAuth2/IDP/Handlers/Authorization.hs` | Config/Trace types | +| `src/Servant/OAuth2/IDP/Handlers/Login.hs` | Config/Trace types | +| `src/Servant/OAuth2/IDP/Handlers/Token.hs` | Config/Trace types, PKCE import | +| `src/Servant/OAuth2/IDP/Test/Internal.hs` | MonadTime import, fix doc comment typo | +| `src/MCP/Server/Auth.hs` | Remove moved types (clean break) | +| `src/MCP/Server/HTTP/AppEnv.hs` | Add OAuthEnv, tracer adapter | +| `mcp-haskell.cabal` | Add new modules | + +## Risks + +1. **Documentation Typo**: The Test.Internal module has stale doc comments referencing `MCP.Server.OAuth.Test.Internal` instead of `Servant.OAuth2.IDP.Test.Internal` - minor fix needed +2. **API Breakage**: Clean break approach may break downstream code importing from MCP.Server.Auth - acceptable since this is pre-release From 3b3174e7a6a1dbd2ee47827643132d176c95a126 Mon Sep 17 00:00:00 2001 From: Alberto Valverde Date: Fri, 19 Dec 2025 11:45:47 +0100 Subject: [PATCH 002/121] docs(005-servant-oauth-extraction): reorganize implementation plan with phased tasks Restructure epic mcp-5wk with clearer phase organization: - Phase A: Create new Servant modules (Trace, Config, Metadata, PKCE) - Phase B: Update Servant handlers to use new modules - Phase C: Update MCP integration layer (AppEnv, adapters) - Phase D: Clean break (remove old code, verify no MCP imports) Close superseded tasks (mcp-5wk.1-22) and create reorganized tasks (mcp-5wk.23-48) with explicit dependencies and parallel execution opportunities where possible. --- .beads/issues.jsonl | 70 +++++--- .specify/memory/constitution.md | 13 +- specs/005-servant-oauth-extraction/plan.md | 57 +++--- specs/005-servant-oauth-extraction/spec.md | 196 +++++++++++++++++---- 4 files changed, 254 insertions(+), 82 deletions(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index f9c4940..823f216 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -49,28 +49,54 @@ {"id":"mcp-51r.9","title":"Migrate handleMetadata and handleProtectedResourceMetadata to use OAuth.Types","description":"Migrate metadata handlers in src/MCP/Server/HTTP.hs:676,684 to ensure consistent type usage.\n\nHANDLERS:\n- handleProtectedResourceMetadata :: HTTPServerConfig -\u003e Handler ProtectedResourceMetadata\n- handleMetadata :: HTTPServerConfig -\u003e Handler OAuthMetadata\n\nThese handlers primarily return configuration data. Check if any internal types need migration:\n- Scopes in metadata responses\n- Grant types in metadata responses\n- Response types in metadata responses\n\nEXISTING PROOF-OF-CONCEPT:\nhandleMetadataAppM (line 745) already uses typeclass constraints pattern.\nMay serve as reference for migration pattern.\n\nCOMPLEXITY: Low (mostly response type fields, not input parsing)","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-11T22:13:55.363764843+01:00","updated_at":"2025-12-11T23:20:29.540097127+01:00","closed_at":"2025-12-11T23:20:29.540097127+01:00","labels":["handler:metadata","phase:migration"],"dependencies":[{"issue_id":"mcp-51r.9","depends_on_id":"mcp-51r","type":"parent-child","created_at":"2025-12-11T22:13:55.365620245+01:00","created_by":"daemon"},{"issue_id":"mcp-51r.9","depends_on_id":"mcp-51r.1","type":"blocks","created_at":"2025-12-11T22:14:28.362939968+01:00","created_by":"daemon"}]} {"id":"mcp-55k","title":"T006: Create ServerTrace skeleton in src/MCP/Trace/Server.hs","description":"[P] Create MCP.Trace.Server module with ServerTrace type (composite placeholder only) and renderServerTrace stub. Ref: data-model.md ServerTrace section.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-10T11:33:35.697634413+01:00","updated_at":"2025-12-10T12:17:35.551473295+01:00","closed_at":"2025-12-10T12:17:35.551473295+01:00"} {"id":"mcp-5wk","title":"Epic: Remove MCP Imports from Servant.OAuth2.IDP","description":"Prepare Servant.OAuth2.IDP.* modules for extraction to separate package by removing all MCP.* dependencies. Pure refactoring: move types to new Servant modules, use explicit record parameters, clean break with no backwards-compatibility re-exports. See /home/claude/workspace/specs/005-servant-oauth-extraction for full specification.","status":"open","priority":1,"issue_type":"epic","created_at":"2025-12-18T20:08:49.968197161+01:00","updated_at":"2025-12-18T20:08:49.968197161+01:00","labels":["feature:005-servant-oauth-extraction"]} -{"id":"mcp-5wk.1","title":"Create Servant.OAuth2.IDP.Trace module with OAuthTrace ADT","description":"WHERE: src/Servant/OAuth2/IDP/Trace.hs (NEW FILE)\nWHAT: Create OAuthTrace ADT for OAuth-specific trace events. Constructors: TraceClientRegistration, TraceAuthorizationRequest, TraceLoginPageServed, TraceLoginAttempt, TracePKCEValidation, TraceAuthorizationGranted, TraceAuthorizationDenied, TraceTokenExchange, TraceTokenRefresh, TraceSessionExpired, TraceValidationError.\nWHY: Enables Servant OAuth handlers to emit traces without depending on MCP.Trace.* modules.\nHOW: Define ADT with Show, Eq deriving. Import types from Servant.OAuth2.IDP.Types (ClientId, SessionId, etc.). Add renderOAuthTrace :: OAuthTrace -\u003e Text function.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-18T20:09:06.617950127+01:00","updated_at":"2025-12-18T20:09:06.617950127+01:00","labels":["parallel:true","phase:a-new-modules"],"dependencies":[{"issue_id":"mcp-5wk.1","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:06.61901111+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.10","title":"Update Handlers/Helpers.hs - use OAuthEnv for config access","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Helpers.hs\nWHAT: Replace MCP imports with Servant imports. Use OAuthEnv for authCodePrefix, refreshTokenPrefix.\nWHY: Helper functions access config for token generation prefixes. Must use OAuthEnv instead.\nHOW: 1) Replace MCP.Server.Auth import with Servant.OAuth2.IDP.Config. 2) Replace MCP.Server.HTTP.AppEnv import removal. 3) Update functions using config: change httpOAuthConfig access pattern to direct OAuthEnv field access (oauthAuthCodePrefix, oauthRefreshTokenPrefix).","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:21.083199513+01:00","updated_at":"2025-12-18T20:10:21.083199513+01:00","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.10","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:21.085386043+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.10","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:30.754720779+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.11","title":"Update Handlers/Registration.hs - use OAuthEnv and OAuthTrace","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Registration.hs\nWHAT: Replace MCP imports. Use OAuthEnv for clientIdPrefix. Use OAuthTrace for trace emission.\nWHY: Registration handler emits TraceClientRegistration trace and accesses clientIdPrefix config.\nHOW: 1) Replace MCP.Server.Auth import with Servant.OAuth2.IDP.Config for OAuthEnv. 2) Replace MCP.Trace imports with Servant.OAuth2.IDP.Trace. 3) Update handler signature: HasType OAuthEnv env, HasType (IOTracer OAuthTrace) env. 4) Update config access: oauthClientIdPrefix. 5) Update trace: TraceClientRegistration.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:31.973620747+01:00","updated_at":"2025-12-18T20:10:31.973620747+01:00","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.11","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:31.97593652+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.11","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:30.772457534+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.11","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:30.789525983+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.12","title":"Update Handlers/Authorization.hs - use OAuthEnv, OAuthTrace, and Control.Monad.Time","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Authorization.hs\nWHAT: Replace all MCP imports. Use OAuthEnv, OAuthTrace, and direct MonadTime import.\nWHY: Authorization handler is central to OAuth flow. Emits multiple traces, accesses config, uses MonadTime.\nHOW: 1) Replace MCP.Server.Time with Control.Monad.Time. 2) Replace MCP.Server.Auth/HTTP.AppEnv with Servant.OAuth2.IDP.Config. 3) Replace MCP.Trace imports with Servant.OAuth2.IDP.Trace. 4) Update handler signature constraints. 5) Update config access patterns. 6) Update trace emissions: TraceAuthorizationRequest, TraceAuthorizationGranted, TraceAuthorizationDenied.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:39.266519488+01:00","updated_at":"2025-12-18T20:10:39.266519488+01:00","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.12","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:39.269269354+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.12","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:30.807054721+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.12","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:30.826439007+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.13","title":"Update Handlers/Login.hs - use OAuthEnv, OAuthTrace, and Control.Monad.Time","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Login.hs\nWHAT: Replace all MCP imports. Use OAuthEnv, OAuthTrace, and direct MonadTime import.\nWHY: Login handler emits TraceLoginPageServed, TraceLoginAttempt traces and accesses session expiry config.\nHOW: 1) Replace MCP.Server.Time with Control.Monad.Time. 2) Replace MCP.Server.Auth/HTTP.AppEnv with Servant.OAuth2.IDP.Config. 3) Replace MCP.Trace imports with Servant.OAuth2.IDP.Trace. 4) Update handler signature constraints. 5) Update config access: oauthLoginSessionExpiry. 6) Update trace emissions: TraceLoginPageServed, TraceLoginAttempt.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:48.406904308+01:00","updated_at":"2025-12-18T20:10:48.406904308+01:00","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.13","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:48.408977515+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.13","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:30.842116284+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.13","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:30.858199875+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.14","title":"Update Server.hs - use OAuthEnv and OAuthTrace type constraints","description":"WHERE: src/Servant/OAuth2/IDP/Server.hs\nWHAT: Replace MCP imports. Update server composition to use OAuthEnv and OAuthTrace constraints.\nWHY: Server.hs composes handlers and exports the server. Type constraints must match handler signatures.\nHOW: 1) Replace MCP.Server.HTTP.AppEnv import with Servant.OAuth2.IDP.Config. 2) Replace MCP.Trace.HTTP import with Servant.OAuth2.IDP.Trace. 3) Update oauthServer type signature: HasType OAuthEnv env, HasType (IOTracer OAuthTrace) env.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:55.943727027+01:00","updated_at":"2025-12-18T20:10:55.943727027+01:00","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.14","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:55.944951898+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.14","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:30.875240833+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.14","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:30.891783627+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.15","title":"Update Test/Internal.hs - fix doc comment typo and MonadTime import","description":"WHERE: src/Servant/OAuth2/IDP/Test/Internal.hs\nWHAT: 1) Fix doc comment at line 7 from 'Module : MCP.Server.OAuth.Test.Internal' to 'Module : Servant.OAuth2.IDP.Test.Internal'. 2) Fix usage example at line 22 to use correct module name. 3) Replace MCP.Server.Time import with Control.Monad.Time.\nWHY: Doc comments have stale module name causing confusion. MonadTime import should be direct.\nHOW: 1) Line 7: change 'MCP.Server.OAuth.Test.Internal' to 'Servant.OAuth2.IDP.Test.Internal'. 2) Line 22: same change in example. 3) Replace import MCP.Server.Time with import Control.Monad.Time.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-18T20:11:04.923136551+01:00","updated_at":"2025-12-18T20:11:04.923136551+01:00","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.15","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:04.925017881+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.16","title":"Add OAuthEnv field to AppEnv and create mkOAuthEnv adapter","description":"WHERE: src/MCP/Server/HTTP/AppEnv.hs\nWHAT: 1) Add envOAuthEnv :: OAuthEnv field to AppEnv record. 2) Create mkOAuthEnv :: HTTPServerConfig -\u003e OAuthEnv function to build OAuthEnv from existing config.\nWHY: MCP handlers use OAuthEnv-constrained Servant handlers. AppEnv must provide OAuthEnv via HasType.\nHOW: 1) Import Servant.OAuth2.IDP.Config (OAuthEnv (..)). 2) Add field to AppEnv record. 3) Implement mkOAuthEnv: extract httpBaseUrl, unwrap httpOAuthConfig with defaults, map fields (authCodeExpirySeconds -\u003e fromIntegral -\u003e oauthAuthCodeExpiry, etc.). 4) Generic instance auto-generates HasType OAuthEnv AppEnv.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:11:15.689690037+01:00","updated_at":"2025-12-18T20:11:15.689690037+01:00","labels":["phase:c-update-mcp"],"dependencies":[{"issue_id":"mcp-5wk.16","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:15.691412599+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.16","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:47.80398682+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.17","title":"Create mkOAuthTracer adapter function for trace type conversion","description":"WHERE: src/MCP/Server/HTTP/AppEnv.hs\nWHAT: Create mkOAuthTracer :: IOTracer HTTPTrace -\u003e IOTracer OAuthTrace using contramap.\nWHY: Servant handlers emit OAuthTrace events but MCP uses HTTPTrace. Need adapter to convert at boundary.\nHOW: 1) Import Servant.OAuth2.IDP.Trace (OAuthTrace). 2) Import Data.Functor.Contravariant (contramap). 3) Define mkOAuthTracer = contramap HTTPOAuth - wraps each OAuthTrace in HTTPOAuth constructor before emission.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:11:24.964321457+01:00","updated_at":"2025-12-18T20:11:24.964321457+01:00","labels":["phase:c-update-mcp"],"dependencies":[{"issue_id":"mcp-5wk.17","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:24.965812438+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.17","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:47.824087887+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.18","title":"Update MCP HTTP server to construct AppEnv with OAuthEnv","description":"WHERE: src/MCP/Server/HTTP.hs (or wherever AppEnv is constructed)\nWHAT: Update AppEnv construction to include envOAuthEnv field using mkOAuthEnv.\nWHY: AppEnv must be complete with all fields for HTTP server to start.\nHOW: Find where AppEnv is constructed (likely in HTTP.hs or main). Add envOAuthEnv = mkOAuthEnv config to the record construction. Ensure mkOAuthEnv is imported from AppEnv module.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:11:32.723421047+01:00","updated_at":"2025-12-18T20:11:32.723421047+01:00","labels":["phase:c-update-mcp"],"dependencies":[{"issue_id":"mcp-5wk.18","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:32.724887672+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.19","title":"Remove moved types from MCP.Server.Auth exports","description":"WHERE: src/MCP/Server/Auth.hs\nWHAT: Remove exports and implementations of types/functions that moved to Servant: OAuthMetadata, ProtectedResourceMetadata, validateCodeVerifier, generateCodeChallenge.\nWHY: Clean break - no backwards-compatibility re-exports. MCP.Server.Auth should only contain MCP-specific types.\nHOW: 1) Remove from export list: OAuthMetadata (..), ProtectedResourceMetadata (..), validateCodeVerifier, generateCodeChallenge. 2) Remove type definitions (data OAuthMetadata, data ProtectedResourceMetadata). 3) Remove JSON instances for those types. 4) Remove function implementations (validateCodeVerifier, generateCodeChallenge). 5) Remove now-unused imports (cryptonite, base64-bytestring if only used by removed functions).","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-18T20:11:42.987274484+01:00","updated_at":"2025-12-18T20:11:42.987274484+01:00","labels":["phase:d-clean-break"],"dependencies":[{"issue_id":"mcp-5wk.19","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:42.988403041+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.2","title":"Create Servant.OAuth2.IDP.Config module with OAuthEnv record","description":"WHERE: src/Servant/OAuth2/IDP/Config.hs (NEW FILE)\nWHAT: Define OAuthEnv record with protocol-agnostic OAuth configuration. Fields: oauthBaseUrl :: Text, oauthAuthCodeExpiry :: NominalDiffTime, oauthAccessTokenExpiry :: NominalDiffTime, oauthLoginSessionExpiry :: NominalDiffTime, oauthAuthCodePrefix :: Text, oauthRefreshTokenPrefix :: Text, oauthClientIdPrefix :: Text, oauthSupportedScopes :: [Scope], oauthSupportedResponseTypes :: [ResponseType], oauthSupportedGrantTypes :: [GrantType], oauthSupportedAuthMethods :: [ClientAuthMethod], oauthSupportedCodeChallengeMethods :: [CodeChallengeMethod].\nWHY: Replaces HasType HTTPServerConfig constraint in handlers with HasType OAuthEnv - no MCP dependency.\nHOW: Import types from Servant.OAuth2.IDP.Types. Add Generic deriving for generic-lens compatibility. Optionally add mkOAuthEnv smart constructor with sensible defaults.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-18T20:09:15.224752162+01:00","updated_at":"2025-12-18T20:09:15.224752162+01:00","labels":["parallel:true","phase:a-new-modules"],"dependencies":[{"issue_id":"mcp-5wk.2","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:15.225941125+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.20","title":"Verify no MCP imports in Servant modules","description":"WHERE: src/Servant/**/*.hs\nWHAT: Run verification command to ensure no MCP imports remain in Servant namespace.\nWHY: Primary acceptance criterion - Servant modules must be MCP-independent.\nHOW: Run: rg '^import MCP\\.' src/Servant/ - should return empty. If any matches found, fix the remaining imports in those files.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:11:51.04522668+01:00","updated_at":"2025-12-18T20:11:51.04522668+01:00","labels":["phase:d-clean-break","verification"],"dependencies":[{"issue_id":"mcp-5wk.20","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:51.046965163+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.20","depends_on_id":"mcp-5wk.19","type":"blocks","created_at":"2025-12-18T20:12:47.841317393+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.21","title":"Verify cabal build succeeds","description":"WHERE: Project root\nWHAT: Run cabal build and ensure it succeeds without errors.\nWHY: Build must pass after refactoring - no missing modules, no type errors.\nHOW: Run: cabal build. Fix any compilation errors. Common issues: missing imports, type mismatches in handler signatures, missing module in cabal file.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:12:01.155334066+01:00","updated_at":"2025-12-18T20:12:01.155334066+01:00","labels":["phase:d-clean-break","verification"],"dependencies":[{"issue_id":"mcp-5wk.21","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:12:01.157258941+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.21","depends_on_id":"mcp-5wk.20","type":"blocks","created_at":"2025-12-18T20:12:47.860694392+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.22","title":"Verify cabal test passes","description":"WHERE: Project root\nWHAT: Run cabal test and ensure all existing tests pass.\nWHY: Pure refactoring should not change behavior. All existing tests must still pass.\nHOW: Run: cabal test. Fix any test failures. Failures indicate behavior changed unexpectedly.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:12:07.62322988+01:00","updated_at":"2025-12-18T20:12:07.62322988+01:00","labels":["phase:d-clean-break","verification"],"dependencies":[{"issue_id":"mcp-5wk.22","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:12:07.625325676+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.22","depends_on_id":"mcp-5wk.21","type":"blocks","created_at":"2025-12-18T20:12:47.882234471+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.3","title":"Create Servant.OAuth2.IDP.Metadata module with OAuthMetadata and ProtectedResourceMetadata","description":"WHERE: src/Servant/OAuth2/IDP/Metadata.hs (NEW FILE)\nWHAT: Move OAuthMetadata (RFC 8414) and ProtectedResourceMetadata (RFC 9728) types from MCP.Server.Auth to this new module. Include all JSON instances.\nWHY: API.hs and Handlers/Metadata.hs need these types but should not import from MCP.\nHOW: Copy type definitions and FromJSON/ToJSON instances from MCP.Server.Auth. Use snake_case JSON keys per RFC 8414/9728. Import Scope, ResponseType, GrantType, etc. from Servant.OAuth2.IDP.Types.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-18T20:09:23.673044465+01:00","updated_at":"2025-12-18T20:09:23.673044465+01:00","labels":["parallel:true","phase:a-new-modules"],"dependencies":[{"issue_id":"mcp-5wk.3","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:23.674656056+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.4","title":"Create Servant.OAuth2.IDP.PKCE module with validation functions","description":"WHERE: src/Servant/OAuth2/IDP/PKCE.hs (NEW FILE)\nWHAT: Move PKCE validation functions from MCP.Server.Auth: validateCodeVerifier :: CodeVerifier -\u003e CodeChallenge -\u003e Bool, generateCodeChallenge :: Text -\u003e Text.\nWHY: Token.hs handler needs PKCE validation but should not import from MCP.Server.Auth.\nHOW: Copy functions from MCP.Server.Auth. Use cryptonite for SHA256, base64-bytestring for B64URL encoding. Import CodeVerifier, CodeChallenge from Servant.OAuth2.IDP.Types. Pure functions only - no IO.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-18T20:09:30.961000538+01:00","updated_at":"2025-12-18T20:09:30.961000538+01:00","labels":["parallel:true","phase:a-new-modules"],"dependencies":[{"issue_id":"mcp-5wk.4","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:30.962835327+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.5","title":"Update mcp-haskell.cabal with new Servant.OAuth2.IDP modules","description":"WHERE: mcp-haskell.cabal\nWHAT: Add new modules to exposed-modules list: Servant.OAuth2.IDP.Trace, Servant.OAuth2.IDP.Config, Servant.OAuth2.IDP.Metadata, Servant.OAuth2.IDP.PKCE.\nWHY: New modules must be registered in cabal file for build to succeed.\nHOW: Add to exposed-modules section in alphabetical order with existing Servant.OAuth2.IDP.* modules.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-18T20:09:37.877486488+01:00","updated_at":"2025-12-18T20:09:37.877486488+01:00","labels":["phase:a-new-modules"],"dependencies":[{"issue_id":"mcp-5wk.5","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:37.879613797+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.6","title":"Update Store.hs and Store/InMemory.hs - replace MCP.Server.Time with Control.Monad.Time","description":"WHERE: src/Servant/OAuth2/IDP/Store.hs, src/Servant/OAuth2/IDP/Store/InMemory.hs\nWHAT: Replace 'import MCP.Server.Time (MonadTime (..))' with 'import Control.Monad.Time (MonadTime (..))'.\nWHY: MCP.Server.Time is just a thin re-export. Direct import from monad-time package removes MCP dependency.\nHOW: Simple find-replace in both files. monad-time is already a transitive dependency.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:09:45.709194204+01:00","updated_at":"2025-12-18T20:09:45.709194204+01:00","labels":["parallel:true","phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.6","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:45.710537517+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.7","title":"Update API.hs - import Metadata from Servant.OAuth2.IDP.Metadata","description":"WHERE: src/Servant/OAuth2/IDP/API.hs\nWHAT: Replace 'import MCP.Server.Auth (OAuthMetadata, ProtectedResourceMetadata)' with 'import Servant.OAuth2.IDP.Metadata (OAuthMetadata, ProtectedResourceMetadata)'.\nWHY: API.hs defines the Servant API type which includes metadata endpoints. Types must come from Servant namespace.\nHOW: Change import statement. Types and JSON instances are identical.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:09:52.950806611+01:00","updated_at":"2025-12-18T20:09:52.950806611+01:00","labels":["parallel:true","phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.7","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:52.952842344+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.8","title":"Update Handlers/Metadata.hs - use Servant Metadata types and OAuthEnv","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Metadata.hs\nWHAT: Replace MCP imports with Servant imports. Change HasType HTTPServerConfig env to HasType OAuthEnv env.\nWHY: Metadata handler constructs OAuthMetadata response using config values. Must use OAuthEnv instead.\nHOW: 1) Replace MCP.Server.Auth imports with Servant.OAuth2.IDP.Metadata. 2) Replace MCP.Server.HTTP.AppEnv import with Servant.OAuth2.IDP.Config. 3) Update handler signature: HasType OAuthEnv env. 4) Update field access: use oauthBaseUrl, oauthSupportedScopes, etc. from OAuthEnv.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:02.563440081+01:00","updated_at":"2025-12-18T20:10:02.563440081+01:00","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.8","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:02.565078596+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.8","depends_on_id":"mcp-5wk.3","type":"blocks","created_at":"2025-12-18T20:12:21.327900887+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.8","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:21.367469395+01:00","created_by":"daemon"}]} -{"id":"mcp-5wk.9","title":"Update Handlers/Token.hs - use PKCE from Servant.OAuth2.IDP.PKCE and OAuthEnv/OAuthTrace","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Token.hs\nWHAT: Replace MCP imports. Use validateCodeVerifier from Servant.OAuth2.IDP.PKCE. Use OAuthEnv and OAuthTrace.\nWHY: Token handler validates PKCE and emits traces. Must not depend on MCP modules.\nHOW: 1) Replace MCP.Server.Auth imports with Servant.OAuth2.IDP.PKCE for validateCodeVerifier. 2) Import OAuthEnv from Servant.OAuth2.IDP.Config. 3) Import OAuthTrace from Servant.OAuth2.IDP.Trace. 4) Replace HasType HTTPServerConfig with HasType OAuthEnv. 5) Replace HasType (IOTracer HTTPTrace) with HasType (IOTracer OAuthTrace). 6) Update trace emissions to use OAuthTrace constructors.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:11.105498164+01:00","updated_at":"2025-12-18T20:10:11.105498164+01:00","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.9","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:11.106689977+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.9","depends_on_id":"mcp-5wk.4","type":"blocks","created_at":"2025-12-18T20:12:21.348386054+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.9","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:21.385221409+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.9","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:21.406470244+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.1","title":"Create Servant.OAuth2.IDP.Trace module with OAuthTrace ADT","description":"WHERE: src/Servant/OAuth2/IDP/Trace.hs (NEW FILE)\nWHAT: Create OAuthTrace ADT for OAuth-specific trace events. Constructors: TraceClientRegistration, TraceAuthorizationRequest, TraceLoginPageServed, TraceLoginAttempt, TracePKCEValidation, TraceAuthorizationGranted, TraceAuthorizationDenied, TraceTokenExchange, TraceTokenRefresh, TraceSessionExpired, TraceValidationError.\nWHY: Enables Servant OAuth handlers to emit traces without depending on MCP.Trace.* modules.\nHOW: Define ADT with Show, Eq deriving. Import types from Servant.OAuth2.IDP.Types (ClientId, SessionId, etc.). Add renderOAuthTrace :: OAuthTrace -\u003e Text function.","status":"closed","priority":0,"issue_type":"task","created_at":"2025-12-18T20:09:06.617950127+01:00","updated_at":"2025-12-19T11:38:33.811259046+01:00","closed_at":"2025-12-19T11:38:33.811259046+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["parallel:true","phase:a-new-modules"],"dependencies":[{"issue_id":"mcp-5wk.1","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:06.61901111+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.10","title":"Update Handlers/Helpers.hs - use OAuthEnv for config access","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Helpers.hs\nWHAT: Replace MCP imports with Servant imports. Use OAuthEnv for authCodePrefix, refreshTokenPrefix.\nWHY: Helper functions access config for token generation prefixes. Must use OAuthEnv instead.\nHOW: 1) Replace MCP.Server.Auth import with Servant.OAuth2.IDP.Config. 2) Replace MCP.Server.HTTP.AppEnv import removal. 3) Update functions using config: change httpOAuthConfig access pattern to direct OAuthEnv field access (oauthAuthCodePrefix, oauthRefreshTokenPrefix).","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:21.083199513+01:00","updated_at":"2025-12-19T11:38:33.973130785+01:00","closed_at":"2025-12-19T11:38:33.973130785+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.10","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:21.085386043+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.10","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:30.754720779+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.11","title":"Update Handlers/Registration.hs - use OAuthEnv and OAuthTrace","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Registration.hs\nWHAT: Replace MCP imports. Use OAuthEnv for clientIdPrefix. Use OAuthTrace for trace emission.\nWHY: Registration handler emits TraceClientRegistration trace and accesses clientIdPrefix config.\nHOW: 1) Replace MCP.Server.Auth import with Servant.OAuth2.IDP.Config for OAuthEnv. 2) Replace MCP.Trace imports with Servant.OAuth2.IDP.Trace. 3) Update handler signature: HasType OAuthEnv env, HasType (IOTracer OAuthTrace) env. 4) Update config access: oauthClientIdPrefix. 5) Update trace: TraceClientRegistration.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:31.973620747+01:00","updated_at":"2025-12-19T11:38:33.98920847+01:00","closed_at":"2025-12-19T11:38:33.98920847+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.11","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:31.97593652+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.11","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:30.772457534+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.11","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:30.789525983+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.12","title":"Update Handlers/Authorization.hs - use OAuthEnv, OAuthTrace, and Control.Monad.Time","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Authorization.hs\nWHAT: Replace all MCP imports. Use OAuthEnv, OAuthTrace, and direct MonadTime import.\nWHY: Authorization handler is central to OAuth flow. Emits multiple traces, accesses config, uses MonadTime.\nHOW: 1) Replace MCP.Server.Time with Control.Monad.Time. 2) Replace MCP.Server.Auth/HTTP.AppEnv with Servant.OAuth2.IDP.Config. 3) Replace MCP.Trace imports with Servant.OAuth2.IDP.Trace. 4) Update handler signature constraints. 5) Update config access patterns. 6) Update trace emissions: TraceAuthorizationRequest, TraceAuthorizationGranted, TraceAuthorizationDenied.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:39.266519488+01:00","updated_at":"2025-12-19T11:38:34.00876219+01:00","closed_at":"2025-12-19T11:38:34.00876219+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.12","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:39.269269354+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.12","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:30.807054721+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.12","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:30.826439007+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.13","title":"Update Handlers/Login.hs - use OAuthEnv, OAuthTrace, and Control.Monad.Time","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Login.hs\nWHAT: Replace all MCP imports. Use OAuthEnv, OAuthTrace, and direct MonadTime import.\nWHY: Login handler emits TraceLoginPageServed, TraceLoginAttempt traces and accesses session expiry config.\nHOW: 1) Replace MCP.Server.Time with Control.Monad.Time. 2) Replace MCP.Server.Auth/HTTP.AppEnv with Servant.OAuth2.IDP.Config. 3) Replace MCP.Trace imports with Servant.OAuth2.IDP.Trace. 4) Update handler signature constraints. 5) Update config access: oauthLoginSessionExpiry. 6) Update trace emissions: TraceLoginPageServed, TraceLoginAttempt.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:48.406904308+01:00","updated_at":"2025-12-19T11:38:34.02661953+01:00","closed_at":"2025-12-19T11:38:34.02661953+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.13","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:48.408977515+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.13","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:30.842116284+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.13","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:30.858199875+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.14","title":"Update Server.hs - use OAuthEnv and OAuthTrace type constraints","description":"WHERE: src/Servant/OAuth2/IDP/Server.hs\nWHAT: Replace MCP imports. Update server composition to use OAuthEnv and OAuthTrace constraints.\nWHY: Server.hs composes handlers and exports the server. Type constraints must match handler signatures.\nHOW: 1) Replace MCP.Server.HTTP.AppEnv import with Servant.OAuth2.IDP.Config. 2) Replace MCP.Trace.HTTP import with Servant.OAuth2.IDP.Trace. 3) Update oauthServer type signature: HasType OAuthEnv env, HasType (IOTracer OAuthTrace) env.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:55.943727027+01:00","updated_at":"2025-12-19T11:38:34.044337629+01:00","closed_at":"2025-12-19T11:38:34.044337629+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.14","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:55.944951898+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.14","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:30.875240833+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.14","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:30.891783627+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.15","title":"Update Test/Internal.hs - fix doc comment typo and MonadTime import","description":"WHERE: src/Servant/OAuth2/IDP/Test/Internal.hs\nWHAT: 1) Fix doc comment at line 7 from 'Module : MCP.Server.OAuth.Test.Internal' to 'Module : Servant.OAuth2.IDP.Test.Internal'. 2) Fix usage example at line 22 to use correct module name. 3) Replace MCP.Server.Time import with Control.Monad.Time.\nWHY: Doc comments have stale module name causing confusion. MonadTime import should be direct.\nHOW: 1) Line 7: change 'MCP.Server.OAuth.Test.Internal' to 'Servant.OAuth2.IDP.Test.Internal'. 2) Line 22: same change in example. 3) Replace import MCP.Server.Time with import Control.Monad.Time.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-18T20:11:04.923136551+01:00","updated_at":"2025-12-19T11:38:34.061941719+01:00","closed_at":"2025-12-19T11:38:34.061941719+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.15","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:04.925017881+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.16","title":"Add OAuthEnv field to AppEnv and create mkOAuthEnv adapter","description":"WHERE: src/MCP/Server/HTTP/AppEnv.hs\nWHAT: 1) Add envOAuthEnv :: OAuthEnv field to AppEnv record. 2) Create mkOAuthEnv :: HTTPServerConfig -\u003e OAuthEnv function to build OAuthEnv from existing config.\nWHY: MCP handlers use OAuthEnv-constrained Servant handlers. AppEnv must provide OAuthEnv via HasType.\nHOW: 1) Import Servant.OAuth2.IDP.Config (OAuthEnv (..)). 2) Add field to AppEnv record. 3) Implement mkOAuthEnv: extract httpBaseUrl, unwrap httpOAuthConfig with defaults, map fields (authCodeExpirySeconds -\u003e fromIntegral -\u003e oauthAuthCodeExpiry, etc.). 4) Generic instance auto-generates HasType OAuthEnv AppEnv.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:11:15.689690037+01:00","updated_at":"2025-12-19T11:38:34.078603951+01:00","closed_at":"2025-12-19T11:38:34.078603951+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:c-update-mcp"],"dependencies":[{"issue_id":"mcp-5wk.16","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:15.691412599+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.16","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:47.80398682+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.17","title":"Create mkOAuthTracer adapter function for trace type conversion","description":"WHERE: src/MCP/Server/HTTP/AppEnv.hs\nWHAT: Create mkOAuthTracer :: IOTracer HTTPTrace -\u003e IOTracer OAuthTrace using contramap.\nWHY: Servant handlers emit OAuthTrace events but MCP uses HTTPTrace. Need adapter to convert at boundary.\nHOW: 1) Import Servant.OAuth2.IDP.Trace (OAuthTrace). 2) Import Data.Functor.Contravariant (contramap). 3) Define mkOAuthTracer = contramap HTTPOAuth - wraps each OAuthTrace in HTTPOAuth constructor before emission.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:11:24.964321457+01:00","updated_at":"2025-12-19T11:38:34.094821532+01:00","closed_at":"2025-12-19T11:38:34.094821532+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:c-update-mcp"],"dependencies":[{"issue_id":"mcp-5wk.17","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:24.965812438+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.17","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:47.824087887+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.18","title":"Update MCP HTTP server to construct AppEnv with OAuthEnv","description":"WHERE: src/MCP/Server/HTTP.hs (or wherever AppEnv is constructed)\nWHAT: Update AppEnv construction to include envOAuthEnv field using mkOAuthEnv.\nWHY: AppEnv must be complete with all fields for HTTP server to start.\nHOW: Find where AppEnv is constructed (likely in HTTP.hs or main). Add envOAuthEnv = mkOAuthEnv config to the record construction. Ensure mkOAuthEnv is imported from AppEnv module.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:11:32.723421047+01:00","updated_at":"2025-12-19T11:38:34.110907949+01:00","closed_at":"2025-12-19T11:38:34.110907949+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:c-update-mcp"],"dependencies":[{"issue_id":"mcp-5wk.18","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:32.724887672+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.19","title":"Remove moved types from MCP.Server.Auth exports","description":"WHERE: src/MCP/Server/Auth.hs\nWHAT: Remove exports and implementations of types/functions that moved to Servant: OAuthMetadata, ProtectedResourceMetadata, validateCodeVerifier, generateCodeChallenge.\nWHY: Clean break - no backwards-compatibility re-exports. MCP.Server.Auth should only contain MCP-specific types.\nHOW: 1) Remove from export list: OAuthMetadata (..), ProtectedResourceMetadata (..), validateCodeVerifier, generateCodeChallenge. 2) Remove type definitions (data OAuthMetadata, data ProtectedResourceMetadata). 3) Remove JSON instances for those types. 4) Remove function implementations (validateCodeVerifier, generateCodeChallenge). 5) Remove now-unused imports (cryptonite, base64-bytestring if only used by removed functions).","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-18T20:11:42.987274484+01:00","updated_at":"2025-12-19T11:38:34.127589496+01:00","closed_at":"2025-12-19T11:38:34.127589496+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:d-clean-break"],"dependencies":[{"issue_id":"mcp-5wk.19","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:42.988403041+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.2","title":"Create Servant.OAuth2.IDP.Config module with OAuthEnv record","description":"WHERE: src/Servant/OAuth2/IDP/Config.hs (NEW FILE)\nWHAT: Define OAuthEnv record with protocol-agnostic OAuth configuration. Fields: oauthBaseUrl :: Text, oauthAuthCodeExpiry :: NominalDiffTime, oauthAccessTokenExpiry :: NominalDiffTime, oauthLoginSessionExpiry :: NominalDiffTime, oauthAuthCodePrefix :: Text, oauthRefreshTokenPrefix :: Text, oauthClientIdPrefix :: Text, oauthSupportedScopes :: [Scope], oauthSupportedResponseTypes :: [ResponseType], oauthSupportedGrantTypes :: [GrantType], oauthSupportedAuthMethods :: [ClientAuthMethod], oauthSupportedCodeChallengeMethods :: [CodeChallengeMethod].\nWHY: Replaces HasType HTTPServerConfig constraint in handlers with HasType OAuthEnv - no MCP dependency.\nHOW: Import types from Servant.OAuth2.IDP.Types. Add Generic deriving for generic-lens compatibility. Optionally add mkOAuthEnv smart constructor with sensible defaults.","status":"closed","priority":0,"issue_type":"task","created_at":"2025-12-18T20:09:15.224752162+01:00","updated_at":"2025-12-19T11:38:33.839461906+01:00","closed_at":"2025-12-19T11:38:33.839461906+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["parallel:true","phase:a-new-modules"],"dependencies":[{"issue_id":"mcp-5wk.2","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:15.225941125+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.20","title":"Verify no MCP imports in Servant modules","description":"WHERE: src/Servant/**/*.hs\nWHAT: Run verification command to ensure no MCP imports remain in Servant namespace.\nWHY: Primary acceptance criterion - Servant modules must be MCP-independent.\nHOW: Run: rg '^import MCP\\.' src/Servant/ - should return empty. If any matches found, fix the remaining imports in those files.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:11:51.04522668+01:00","updated_at":"2025-12-19T11:38:34.143743788+01:00","closed_at":"2025-12-19T11:38:34.143743788+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:d-clean-break","verification"],"dependencies":[{"issue_id":"mcp-5wk.20","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:11:51.046965163+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.20","depends_on_id":"mcp-5wk.19","type":"blocks","created_at":"2025-12-18T20:12:47.841317393+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.21","title":"Verify cabal build succeeds","description":"WHERE: Project root\nWHAT: Run cabal build and ensure it succeeds without errors.\nWHY: Build must pass after refactoring - no missing modules, no type errors.\nHOW: Run: cabal build. Fix any compilation errors. Common issues: missing imports, type mismatches in handler signatures, missing module in cabal file.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:12:01.155334066+01:00","updated_at":"2025-12-19T11:38:34.160974716+01:00","closed_at":"2025-12-19T11:38:34.160974716+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:d-clean-break","verification"],"dependencies":[{"issue_id":"mcp-5wk.21","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:12:01.157258941+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.21","depends_on_id":"mcp-5wk.20","type":"blocks","created_at":"2025-12-18T20:12:47.860694392+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.22","title":"Verify cabal test passes","description":"WHERE: Project root\nWHAT: Run cabal test and ensure all existing tests pass.\nWHY: Pure refactoring should not change behavior. All existing tests must still pass.\nHOW: Run: cabal test. Fix any test failures. Failures indicate behavior changed unexpectedly.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:12:07.62322988+01:00","updated_at":"2025-12-19T11:38:34.177201756+01:00","closed_at":"2025-12-19T11:38:34.177201756+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:d-clean-break","verification"],"dependencies":[{"issue_id":"mcp-5wk.22","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:12:07.625325676+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.22","depends_on_id":"mcp-5wk.21","type":"blocks","created_at":"2025-12-18T20:12:47.882234471+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.23","title":"Create Servant.OAuth2.IDP.Errors module with consolidated error types","description":"FR-004b (P0 Critical): Create src/Servant/OAuth2/IDP/Errors.hs consolidating all error types. Move from Types: ValidationError, AuthorizationError. Move from LoginFlowError.hs: LoginFlowError. Add new types: TokenParameter (TokenParamCode|TokenParamCodeVerifier|TokenParamRefreshToken), OAuthErrorCode ADT with RFC 6749 error codes (ErrInvalidRequest|ErrInvalidClient|ErrInvalidGrant|...) with ToJSON to snake_case. Add new ValidationError constructors: UnsupportedCodeChallengeMethod, MissingTokenParameter, InvalidTokenParameterFormat, EmptyRedirectUris. Update OAuthErrorResponse.oauthErrorCode to use OAuthErrorCode ADT.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-19T11:33:25.964687196+01:00","updated_at":"2025-12-19T11:33:25.964687196+01:00","labels":["fr:004b","phase:a"],"dependencies":[{"issue_id":"mcp-5wk.23","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:33:25.966857657+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.24","title":"Create Servant.OAuth2.IDP.Trace module with OAuthTrace ADT","description":"FR-005 (P1 High): Create src/Servant/OAuth2/IDP/Trace.hs with OAuthTrace ADT using domain newtypes. Add supporting types: OperationResult (Success|Failure), DenialReason (UserDenied|InvalidRequest|UnauthorizedClient|ServerError Text). Constructors use domain types from Types: TraceClientRegistration ClientId RedirectUri, TraceAuthorizationRequest ClientId [Scope] OperationResult, TraceLoginPageServed SessionId, TraceLoginAttempt Username OperationResult, TracePKCEValidation OperationResult, TraceAuthorizationGranted ClientId Username, TraceAuthorizationDenied ClientId DenialReason, TraceTokenExchange OAuthGrantType OperationResult, TraceTokenRefresh OperationResult, TraceSessionExpired SessionId, TraceValidationError ValidationError.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:33:37.271559034+01:00","updated_at":"2025-12-19T11:33:37.271559034+01:00","labels":["fr:005","phase:a"],"dependencies":[{"issue_id":"mcp-5wk.24","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:33:37.273083078+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.24","depends_on_id":"mcp-5wk.23","type":"blocks","created_at":"2025-12-19T11:37:23.40768616+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.24","depends_on_id":"mcp-5wk.28","type":"blocks","created_at":"2025-12-19T11:37:23.424327176+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.25","title":"Create Servant.OAuth2.IDP.Config module with OAuthEnv record","description":"FR-004 (P1 High): Create src/Servant/OAuth2/IDP/Config.hs with OAuthEnv record for protocol-agnostic OAuth configuration. Fields: oauthBaseUrl :: URI, oauthAuthCodeExpiry :: NominalDiffTime, oauthAccessTokenExpiry :: NominalDiffTime, oauthLoginSessionExpiry :: NominalDiffTime, oauthAuthCodePrefix :: Text, oauthRefreshTokenPrefix :: Text, oauthClientIdPrefix :: Text, oauthSupportedScopes :: [Scope], oauthSupportedResponseTypes :: NonEmpty ResponseType, oauthSupportedGrantTypes :: NonEmpty OAuthGrantType, oauthSupportedAuthMethods :: NonEmpty TokenAuthMethod, oauthSupportedCodeChallengeMethods :: NonEmpty CodeChallengeMethod. Uses Network.URI and Data.List.NonEmpty.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:33:48.221479369+01:00","updated_at":"2025-12-19T11:33:48.221479369+01:00","labels":["fr:004","phase:a"],"dependencies":[{"issue_id":"mcp-5wk.25","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:33:48.222635855+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.25","depends_on_id":"mcp-5wk.28","type":"blocks","created_at":"2025-12-19T11:37:23.440577023+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.26","title":"Create Servant.OAuth2.IDP.Metadata module with OAuth discovery types","description":"FR-002 (P0 Critical): Create src/Servant/OAuth2/IDP/Metadata.hs with OAuth metadata types moved from MCP.Server.Auth. Types: OAuthMetadata (RFC 8414 discovery response), ProtectedResourceMetadata (RFC 9728). Include all associated ToJSON/FromJSON instances. Export smart constructors if applicable.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-19T11:33:58.032852335+01:00","updated_at":"2025-12-19T11:33:58.032852335+01:00","labels":["fr:002","phase:a"],"dependencies":[{"issue_id":"mcp-5wk.26","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:33:58.034112012+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.27","title":"Create Servant.OAuth2.IDP.PKCE module with PKCE functions","description":"FR-003 (P0 Critical): Create src/Servant/OAuth2/IDP/PKCE.hs with PKCE functions moved from MCP.Server.Auth. Functions: generateCodeVerifier :: IO CodeVerifier (using cryptonite random, returns domain newtype), validateCodeVerifier :: CodeVerifier -\u003e CodeChallenge -\u003e Bool, generateCodeChallenge :: CodeVerifier -\u003e CodeChallenge. All functions use domain newtypes from Types module. Domain-based module boundaries (IO and pure together).","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-19T11:34:07.520540743+01:00","updated_at":"2025-12-19T11:34:07.520540743+01:00","labels":["fr:003","phase:a"],"dependencies":[{"issue_id":"mcp-5wk.27","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:34:07.52154028+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.28","title":"Move OAuthGrantType to Servant.OAuth2.IDP.Types","description":"FR-002b (P0 Critical): Move OAuthGrantType enum from MCP.Server.Auth to src/Servant/OAuth2/IDP/Types.hs. Include constructors (AuthorizationCode, RefreshToken, etc.) and all ToJSON/FromJSON instances. Core OAuth 2.1 protocol type per RFC 6749.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-19T11:34:15.899761516+01:00","updated_at":"2025-12-19T11:34:15.899761516+01:00","labels":["fr:002b","phase:a"],"dependencies":[{"issue_id":"mcp-5wk.28","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:34:15.900850867+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.29","title":"Add new domain types for type precision (LoginAction, TokenValidity)","description":"FR-004c (P0 Critical): Add new domain types to Servant.OAuth2.IDP.Types for type precision. Add: LoginAction ADT (ActionApprove|ActionDeny) with FromHttpApiData/ToHttpApiData instances. Add: newtype TokenValidity = TokenValidity NominalDiffTime with custom ToJSON outputting integer seconds for OAuth wire format. Update LoginForm.formAction :: Text to LoginForm.formAction :: LoginAction. Update TokenResponse.expires_in :: Maybe Int to TokenResponse.expires_in :: Maybe TokenValidity.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-19T11:34:27.019706897+01:00","updated_at":"2025-12-19T11:34:27.019706897+01:00","labels":["fr:004c","phase:a"],"dependencies":[{"issue_id":"mcp-5wk.29","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:34:27.020779754+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.3","title":"Create Servant.OAuth2.IDP.Metadata module with OAuthMetadata and ProtectedResourceMetadata","description":"WHERE: src/Servant/OAuth2/IDP/Metadata.hs (NEW FILE)\nWHAT: Move OAuthMetadata (RFC 8414) and ProtectedResourceMetadata (RFC 9728) types from MCP.Server.Auth to this new module. Include all JSON instances.\nWHY: API.hs and Handlers/Metadata.hs need these types but should not import from MCP.\nHOW: Copy type definitions and FromJSON/ToJSON instances from MCP.Server.Auth. Use snake_case JSON keys per RFC 8414/9728. Import Scope, ResponseType, GrantType, etc. from Servant.OAuth2.IDP.Types.","status":"closed","priority":0,"issue_type":"task","created_at":"2025-12-18T20:09:23.673044465+01:00","updated_at":"2025-12-19T11:38:33.856330319+01:00","closed_at":"2025-12-19T11:38:33.856330319+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["parallel:true","phase:a-new-modules"],"dependencies":[{"issue_id":"mcp-5wk.3","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:23.674656056+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.30","title":"Update mcp-haskell.cabal with new modules","description":"Update mcp-haskell.cabal: Add new exposed-modules: Servant.OAuth2.IDP.Trace, Servant.OAuth2.IDP.Config, Servant.OAuth2.IDP.Metadata, Servant.OAuth2.IDP.PKCE, Servant.OAuth2.IDP.Errors. Remove: Servant.OAuth2.IDP.LoginFlowError (consolidated into Errors). Verify build-depends includes monad-time and network-uri.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:34:35.383950277+01:00","updated_at":"2025-12-19T11:34:35.383950277+01:00","labels":["phase:a"],"dependencies":[{"issue_id":"mcp-5wk.30","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:34:35.385922156+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.30","depends_on_id":"mcp-5wk.23","type":"blocks","created_at":"2025-12-19T11:37:41.17185769+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.30","depends_on_id":"mcp-5wk.24","type":"blocks","created_at":"2025-12-19T11:37:41.199412159+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.30","depends_on_id":"mcp-5wk.25","type":"blocks","created_at":"2025-12-19T11:37:41.22496231+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.30","depends_on_id":"mcp-5wk.26","type":"blocks","created_at":"2025-12-19T11:37:41.248850073+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.30","depends_on_id":"mcp-5wk.27","type":"blocks","created_at":"2025-12-19T11:37:41.27595482+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.30","depends_on_id":"mcp-5wk.28","type":"blocks","created_at":"2025-12-19T11:37:41.302101874+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.30","depends_on_id":"mcp-5wk.29","type":"blocks","created_at":"2025-12-19T11:37:41.327137792+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.31","title":"Update Store.hs and Store/InMemory.hs with MonadTime import","description":"FR-001 (P0 Critical): Update src/Servant/OAuth2/IDP/Store.hs and src/Servant/OAuth2/IDP/Store/InMemory.hs. Replace 'import MCP.Server.Time (MonadTime)' with 'import Control.Monad.Time (MonadTime)'. Also update OAuthState record to use domain type keys (Map AuthCodeId instead of Map Text) per FR-004c.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-19T11:34:45.216657604+01:00","updated_at":"2025-12-19T11:34:45.216657604+01:00","labels":["fr:001","phase:b"],"dependencies":[{"issue_id":"mcp-5wk.31","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:34:45.218541173+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.31","depends_on_id":"mcp-5wk.30","type":"blocks","created_at":"2025-12-19T11:37:32.542828618+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.32","title":"Update API.hs with Metadata import from Servant module","description":"Update src/Servant/OAuth2/IDP/API.hs: Replace any MCP.Server.Auth imports for OAuthMetadata/ProtectedResourceMetadata with imports from Servant.OAuth2.IDP.Metadata.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:34:53.966647185+01:00","updated_at":"2025-12-19T11:34:53.966647185+01:00","labels":["fr:002","phase:b"],"dependencies":[{"issue_id":"mcp-5wk.32","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:34:53.9684344+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.32","depends_on_id":"mcp-5wk.30","type":"blocks","created_at":"2025-12-19T11:37:32.567511659+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.33","title":"Update Handlers/Metadata.hs with Servant imports","description":"Update src/Servant/OAuth2/IDP/Handlers/Metadata.hs: Import OAuthMetadata/ProtectedResourceMetadata from Servant.OAuth2.IDP.Metadata. Replace HasType HTTPServerConfig env with HasType OAuthEnv env per FR-006.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:35:03.01484359+01:00","updated_at":"2025-12-19T11:35:03.01484359+01:00","labels":["fr:002","fr:006","phase:b"],"dependencies":[{"issue_id":"mcp-5wk.33","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:35:03.016412103+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.33","depends_on_id":"mcp-5wk.30","type":"blocks","created_at":"2025-12-19T11:37:32.592123832+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.34","title":"Update Handlers/Token.hs with PKCE and Errors imports","description":"Update src/Servant/OAuth2/IDP/Handlers/Token.hs: Import PKCE functions from Servant.OAuth2.IDP.PKCE. Import error types from Servant.OAuth2.IDP.Errors. Replace HasType HTTPServerConfig env with HasType OAuthEnv env. Replace HasType (IOTracer HTTPTrace) env with HasType (IOTracer OAuthTrace) env per FR-006.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:35:10.05096651+01:00","updated_at":"2025-12-19T11:35:10.05096651+01:00","labels":["fr:003","fr:004b","fr:006","phase:b"],"dependencies":[{"issue_id":"mcp-5wk.34","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:35:10.053030279+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.34","depends_on_id":"mcp-5wk.30","type":"blocks","created_at":"2025-12-19T11:37:32.615548944+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.35","title":"Update Handlers/Helpers.hs with OAuthEnv and trace","description":"Update src/Servant/OAuth2/IDP/Handlers/Helpers.hs: Import from Servant.OAuth2.IDP.Config and Servant.OAuth2.IDP.Trace. Update generator signatures per FR-004c: generateAuthCode :: OAuthEnv -\u003e IO AuthCodeId, generateJWTAccessToken returns AccessTokenId, generateRefreshTokenWithConfig :: OAuthEnv -\u003e IO RefreshTokenId. Replace HasType HTTPServerConfig with HasType OAuthEnv. Import errors from Servant.OAuth2.IDP.Errors.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:35:20.104987881+01:00","updated_at":"2025-12-19T11:35:20.104987881+01:00","labels":["fr:004c","fr:006","phase:b"],"dependencies":[{"issue_id":"mcp-5wk.35","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:35:20.107212258+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.35","depends_on_id":"mcp-5wk.30","type":"blocks","created_at":"2025-12-19T11:37:32.641328834+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.36","title":"Update Handlers/Registration.hs with OAuthEnv and trace","description":"Update src/Servant/OAuth2/IDP/Handlers/Registration.hs: Import from Servant.OAuth2.IDP.Config and Servant.OAuth2.IDP.Trace. Replace HasType HTTPServerConfig with HasType OAuthEnv. Replace HasType (IOTracer HTTPTrace) with HasType (IOTracer OAuthTrace). Import errors from Servant.OAuth2.IDP.Errors.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:35:29.772771346+01:00","updated_at":"2025-12-19T11:35:29.772771346+01:00","labels":["fr:006","phase:b"],"dependencies":[{"issue_id":"mcp-5wk.36","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:35:29.775217393+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.36","depends_on_id":"mcp-5wk.30","type":"blocks","created_at":"2025-12-19T11:37:32.665788116+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.37","title":"Update Handlers/Authorization.hs with OAuthEnv, trace, MonadTime","description":"Update src/Servant/OAuth2/IDP/Handlers/Authorization.hs: Replace 'import MCP.Server.Time (MonadTime)' with 'import Control.Monad.Time (MonadTime)'. Import from Servant.OAuth2.IDP.Config and Servant.OAuth2.IDP.Trace. Replace HasType HTTPServerConfig with HasType OAuthEnv. Replace HasType (IOTracer HTTPTrace) with HasType (IOTracer OAuthTrace). Import errors from Servant.OAuth2.IDP.Errors.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-19T11:35:37.757172312+01:00","updated_at":"2025-12-19T11:35:37.757172312+01:00","labels":["fr:001","fr:006","phase:b"],"dependencies":[{"issue_id":"mcp-5wk.37","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:35:37.758290557+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.37","depends_on_id":"mcp-5wk.30","type":"blocks","created_at":"2025-12-19T11:37:32.689885379+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.38","title":"Update Handlers/Login.hs with OAuthEnv, trace, MonadTime, LoginAction","description":"Update src/Servant/OAuth2/IDP/Handlers/Login.hs: Replace 'import MCP.Server.Time (MonadTime)' with 'import Control.Monad.Time (MonadTime)'. Import from Servant.OAuth2.IDP.Config and Servant.OAuth2.IDP.Trace. Replace HasType HTTPServerConfig with HasType OAuthEnv. Replace HasType (IOTracer HTTPTrace) with HasType (IOTracer OAuthTrace). Import errors from Servant.OAuth2.IDP.Errors. Update to pattern match on LoginAction ADT instead of string comparison per FR-004c.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-19T11:35:46.576219204+01:00","updated_at":"2025-12-19T11:35:46.576219204+01:00","labels":["fr:001","fr:004c","fr:006","phase:b"],"dependencies":[{"issue_id":"mcp-5wk.38","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:35:46.578313123+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.38","depends_on_id":"mcp-5wk.30","type":"blocks","created_at":"2025-12-19T11:37:32.714939374+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.39","title":"Update Server.hs with OAuthEnv and OAuthTrace","description":"Update src/Servant/OAuth2/IDP/Server.hs: Import from Servant.OAuth2.IDP.Config and Servant.OAuth2.IDP.Trace. Replace HasType HTTPServerConfig with HasType OAuthEnv. Replace HasType (IOTracer HTTPTrace) with HasType (IOTracer OAuthTrace). Import errors from Servant.OAuth2.IDP.Errors.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:35:53.588452178+01:00","updated_at":"2025-12-19T11:35:53.588452178+01:00","labels":["fr:006","phase:b"],"dependencies":[{"issue_id":"mcp-5wk.39","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:35:53.589606613+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.39","depends_on_id":"mcp-5wk.30","type":"blocks","created_at":"2025-12-19T11:37:32.745659943+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.4","title":"Create Servant.OAuth2.IDP.PKCE module with validation functions","description":"WHERE: src/Servant/OAuth2/IDP/PKCE.hs (NEW FILE)\nWHAT: Move PKCE validation functions from MCP.Server.Auth: validateCodeVerifier :: CodeVerifier -\u003e CodeChallenge -\u003e Bool, generateCodeChallenge :: Text -\u003e Text.\nWHY: Token.hs handler needs PKCE validation but should not import from MCP.Server.Auth.\nHOW: Copy functions from MCP.Server.Auth. Use cryptonite for SHA256, base64-bytestring for B64URL encoding. Import CodeVerifier, CodeChallenge from Servant.OAuth2.IDP.Types. Pure functions only - no IO.","status":"closed","priority":0,"issue_type":"task","created_at":"2025-12-18T20:09:30.961000538+01:00","updated_at":"2025-12-19T11:38:33.873939929+01:00","closed_at":"2025-12-19T11:38:33.873939929+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["parallel:true","phase:a-new-modules"],"dependencies":[{"issue_id":"mcp-5wk.4","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:30.962835327+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.40","title":"Update Test/Internal.hs with MonadTime import and fix doc typo","description":"Update src/Servant/OAuth2/IDP/Test/Internal.hs: Replace 'import MCP.Server.Time (MonadTime)' with 'import Control.Monad.Time (MonadTime)'. Fix documentation typo: change 'Module : MCP.Server.OAuth.Test.Internal' to 'Module : Servant.OAuth2.IDP.Test.Internal' in doc comments.","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-19T11:36:01.954451031+01:00","updated_at":"2025-12-19T11:36:01.954451031+01:00","labels":["fr:001","phase:b"],"dependencies":[{"issue_id":"mcp-5wk.40","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:36:01.956076459+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.40","depends_on_id":"mcp-5wk.30","type":"blocks","created_at":"2025-12-19T11:37:32.772268791+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.41","title":"Audit Types.hs exports for smart constructor hygiene","description":"FR-004c (P0 Critical): Audit src/Servant/OAuth2/IDP/Types.hs exports. Ensure smart constructor hygiene: export pattern 'FooType, mkFooType, unFooType' NOT 'FooType(..)'. Verify all newtypes (AuthCodeId, ClientId, SessionId, AccessTokenId, RefreshTokenId, RedirectUri, Scope, CodeChallenge, CodeVerifier, Username, UserId) follow this pattern. Remove ValidationError and AuthorizationError (moved to Errors module).","status":"open","priority":0,"issue_type":"task","created_at":"2025-12-19T11:36:11.195075535+01:00","updated_at":"2025-12-19T11:36:11.195075535+01:00","labels":["fr:004c","phase:b"],"dependencies":[{"issue_id":"mcp-5wk.41","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:36:11.197255755+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.41","depends_on_id":"mcp-5wk.30","type":"blocks","created_at":"2025-12-19T11:37:32.797249728+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.42","title":"Add OAuthEnv field to AppEnv and create adapters","description":"FR-007 (P1 High): Update src/MCP/Server/HTTP/AppEnv.hs. Add envOAuthEnv :: OAuthEnv field to AppEnv record. Create mkOAuthEnv :: HTTPServerConfig -\u003e OAuthEnv function to construct OAuthEnv from existing config. Create mkOAuthTracer :: IOTracer HTTPTrace -\u003e IOTracer OAuthTrace via contramap to adapt trace types. Update handler call sites to use new OAuthEnv.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:36:20.156853698+01:00","updated_at":"2025-12-19T11:36:20.156853698+01:00","labels":["fr:007","phase:c"],"dependencies":[{"issue_id":"mcp-5wk.42","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:36:20.15826562+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.42","depends_on_id":"mcp-5wk.39","type":"blocks","created_at":"2025-12-19T11:37:51.970027384+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.43","title":"Create MCPOAuthConfig for demo-specific fields","description":"FR-004/FR-008: Create MCPOAuthConfig record in MCP.Server.Auth with demo-specific fields extracted from OAuthConfig: autoApproveAuth :: Bool, demoUserIdTemplate :: Text, demoEmailDomain :: Text, authorizationSuccessTemplate :: Text. These fields stay in MCP namespace as they are demo/MCP-specific.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-19T11:36:29.187156801+01:00","updated_at":"2025-12-19T11:36:29.187156801+01:00","labels":["fr:004","fr:008","phase:c"],"dependencies":[{"issue_id":"mcp-5wk.43","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:36:29.188410206+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.44","title":"Remove moved types from MCP.Server.Auth exports","description":"FR-008 (P2 Medium): Clean break - remove moved types from src/MCP/Server/Auth.hs exports. Remove: OAuthMetadata (→ Servant.OAuth2.IDP.Metadata), ProtectedResourceMetadata (→ Metadata), OAuthGrantType (→ Types), generateCodeVerifier (→ PKCE), validateCodeVerifier (→ PKCE), generateCodeChallenge (→ PKCE), ValidationError (→ Errors), AuthorizationError (→ Errors). Keep: OAuthProvider, TokenInfo, extractBearerToken, PKCEChallenge, ProtectedResourceAuth, ProtectedResourceAuthConfig.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-19T11:36:44.902296807+01:00","updated_at":"2025-12-19T11:36:44.902296807+01:00","labels":["fr:008","phase:d"],"dependencies":[{"issue_id":"mcp-5wk.44","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:36:44.903648943+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.44","depends_on_id":"mcp-5wk.42","type":"blocks","created_at":"2025-12-19T11:37:51.989991306+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.44","depends_on_id":"mcp-5wk.43","type":"blocks","created_at":"2025-12-19T11:37:52.007238304+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.45","title":"Delete LoginFlowError.hs (consolidated into Errors)","description":"Delete src/Servant/OAuth2/IDP/LoginFlowError.hs. Content has been moved to Servant.OAuth2.IDP.Errors module. Ensure all imports have been updated to use Errors module before deletion.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-19T11:36:51.574116903+01:00","updated_at":"2025-12-19T11:36:51.574116903+01:00","labels":["phase:d"],"dependencies":[{"issue_id":"mcp-5wk.45","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:36:51.575644981+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.45","depends_on_id":"mcp-5wk.23","type":"blocks","created_at":"2025-12-19T11:37:52.023107504+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.46","title":"Verify no MCP imports in Servant modules","description":"Acceptance Criteria 1: Run 'rg \"^import MCP\\.\" src/Servant/' and verify it returns empty (no MCP imports in any Servant.OAuth2.IDP.* module). This is the primary success metric for the refactoring.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:37:03.33794574+01:00","updated_at":"2025-12-19T11:37:03.33794574+01:00","labels":["acceptance","phase:d"],"dependencies":[{"issue_id":"mcp-5wk.46","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:37:03.339167256+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.46","depends_on_id":"mcp-5wk.44","type":"blocks","created_at":"2025-12-19T11:37:52.038764876+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.46","depends_on_id":"mcp-5wk.45","type":"blocks","created_at":"2025-12-19T11:37:52.055523243+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.47","title":"Verify cabal build succeeds","description":"Acceptance Criteria 2: Run 'cabal build' and verify it completes with no errors. All modules must compile cleanly after the refactoring.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:37:09.256647205+01:00","updated_at":"2025-12-19T11:37:09.256647205+01:00","labels":["acceptance","phase:d"],"dependencies":[{"issue_id":"mcp-5wk.47","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:37:09.258039623+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.47","depends_on_id":"mcp-5wk.46","type":"blocks","created_at":"2025-12-19T11:37:52.071662566+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.48","title":"Verify cabal test passes","description":"Acceptance Criteria 3: Run 'cabal test' and verify all existing tests pass. This is a pure refactoring - no functionality changes, so all tests must continue to pass.","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-19T11:37:15.602184615+01:00","updated_at":"2025-12-19T11:37:15.602184615+01:00","labels":["acceptance","phase:d"],"dependencies":[{"issue_id":"mcp-5wk.48","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-19T11:37:15.603280333+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.48","depends_on_id":"mcp-5wk.47","type":"blocks","created_at":"2025-12-19T11:37:52.087957008+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.5","title":"Update mcp-haskell.cabal with new Servant.OAuth2.IDP modules","description":"WHERE: mcp-haskell.cabal\nWHAT: Add new modules to exposed-modules list: Servant.OAuth2.IDP.Trace, Servant.OAuth2.IDP.Config, Servant.OAuth2.IDP.Metadata, Servant.OAuth2.IDP.PKCE.\nWHY: New modules must be registered in cabal file for build to succeed.\nHOW: Add to exposed-modules section in alphabetical order with existing Servant.OAuth2.IDP.* modules.","status":"closed","priority":0,"issue_type":"task","created_at":"2025-12-18T20:09:37.877486488+01:00","updated_at":"2025-12-19T11:38:33.890279795+01:00","closed_at":"2025-12-19T11:38:33.890279795+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:a-new-modules"],"dependencies":[{"issue_id":"mcp-5wk.5","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:37.879613797+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.6","title":"Update Store.hs and Store/InMemory.hs - replace MCP.Server.Time with Control.Monad.Time","description":"WHERE: src/Servant/OAuth2/IDP/Store.hs, src/Servant/OAuth2/IDP/Store/InMemory.hs\nWHAT: Replace 'import MCP.Server.Time (MonadTime (..))' with 'import Control.Monad.Time (MonadTime (..))'.\nWHY: MCP.Server.Time is just a thin re-export. Direct import from monad-time package removes MCP dependency.\nHOW: Simple find-replace in both files. monad-time is already a transitive dependency.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:09:45.709194204+01:00","updated_at":"2025-12-19T11:38:33.906298235+01:00","closed_at":"2025-12-19T11:38:33.906298235+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["parallel:true","phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.6","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:45.710537517+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.7","title":"Update API.hs - import Metadata from Servant.OAuth2.IDP.Metadata","description":"WHERE: src/Servant/OAuth2/IDP/API.hs\nWHAT: Replace 'import MCP.Server.Auth (OAuthMetadata, ProtectedResourceMetadata)' with 'import Servant.OAuth2.IDP.Metadata (OAuthMetadata, ProtectedResourceMetadata)'.\nWHY: API.hs defines the Servant API type which includes metadata endpoints. Types must come from Servant namespace.\nHOW: Change import statement. Types and JSON instances are identical.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:09:52.950806611+01:00","updated_at":"2025-12-19T11:38:33.9220744+01:00","closed_at":"2025-12-19T11:38:33.9220744+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["parallel:true","phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.7","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:09:52.952842344+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.8","title":"Update Handlers/Metadata.hs - use Servant Metadata types and OAuthEnv","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Metadata.hs\nWHAT: Replace MCP imports with Servant imports. Change HasType HTTPServerConfig env to HasType OAuthEnv env.\nWHY: Metadata handler constructs OAuthMetadata response using config values. Must use OAuthEnv instead.\nHOW: 1) Replace MCP.Server.Auth imports with Servant.OAuth2.IDP.Metadata. 2) Replace MCP.Server.HTTP.AppEnv import with Servant.OAuth2.IDP.Config. 3) Update handler signature: HasType OAuthEnv env. 4) Update field access: use oauthBaseUrl, oauthSupportedScopes, etc. from OAuthEnv.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:02.563440081+01:00","updated_at":"2025-12-19T11:38:33.938342175+01:00","closed_at":"2025-12-19T11:38:33.938342175+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.8","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:02.565078596+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.8","depends_on_id":"mcp-5wk.3","type":"blocks","created_at":"2025-12-18T20:12:21.327900887+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.8","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:21.367469395+01:00","created_by":"daemon"}]} +{"id":"mcp-5wk.9","title":"Update Handlers/Token.hs - use PKCE from Servant.OAuth2.IDP.PKCE and OAuthEnv/OAuthTrace","description":"WHERE: src/Servant/OAuth2/IDP/Handlers/Token.hs\nWHAT: Replace MCP imports. Use validateCodeVerifier from Servant.OAuth2.IDP.PKCE. Use OAuthEnv and OAuthTrace.\nWHY: Token handler validates PKCE and emits traces. Must not depend on MCP modules.\nHOW: 1) Replace MCP.Server.Auth imports with Servant.OAuth2.IDP.PKCE for validateCodeVerifier. 2) Import OAuthEnv from Servant.OAuth2.IDP.Config. 3) Import OAuthTrace from Servant.OAuth2.IDP.Trace. 4) Replace HasType HTTPServerConfig with HasType OAuthEnv. 5) Replace HasType (IOTracer HTTPTrace) with HasType (IOTracer OAuthTrace). 6) Update trace emissions to use OAuthTrace constructors.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T20:10:11.105498164+01:00","updated_at":"2025-12-19T11:38:33.953914891+01:00","closed_at":"2025-12-19T11:38:33.953914891+01:00","close_reason":"Superseded by reorganized tasks mcp-5wk.23-48 with better phase organization and dependencies","labels":["phase:b-update-servant"],"dependencies":[{"issue_id":"mcp-5wk.9","depends_on_id":"mcp-5wk","type":"parent-child","created_at":"2025-12-18T20:10:11.106689977+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.9","depends_on_id":"mcp-5wk.4","type":"blocks","created_at":"2025-12-18T20:12:21.348386054+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.9","depends_on_id":"mcp-5wk.1","type":"blocks","created_at":"2025-12-18T20:12:21.385221409+01:00","created_by":"daemon"},{"issue_id":"mcp-5wk.9","depends_on_id":"mcp-5wk.2","type":"blocks","created_at":"2025-12-18T20:12:21.406470244+01:00","created_by":"daemon"}]} {"id":"mcp-6k9","title":"Epic: Structured Tracing with plow-log","description":"Add comprehensive richly-typed structured logging/tracing to the MCP library using plow-log and plow-log-async.\n\nSpec: specs/003-structured-tracing/spec.md\nPlan: specs/003-structured-tracing/plan.md\nTasks: specs/003-structured-tracing/tasks.md\nData Model: specs/003-structured-tracing/data-model.md\n\nKey constraint: Implement composite trace types and plumbing first (skeleton), then fill in leaf traces incrementally.","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-10T11:37:06.7165381+01:00","updated_at":"2025-12-11T13:31:08.445917142+01:00","closed_at":"2025-12-11T13:31:08.445917142+01:00"} {"id":"mcp-6k9.1","title":"Phase 1: Setup","description":"Project initialization: add plow-log dependencies, create Trace directory structure.\n\nTasks:\n- T001: Add plow-log dependencies to mcp.cabal\n- T002: Create src/MCP/Trace/ directory\n- T003: Verify dependencies resolve\n\nRef: specs/003-structured-tracing/tasks.md Phase 1","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-10T11:37:14.074275157+01:00","updated_at":"2025-12-10T12:06:29.332162176+01:00","closed_at":"2025-12-10T12:06:29.332162176+01:00","dependencies":[{"issue_id":"mcp-6k9.1","depends_on_id":"mcp-6k9","type":"parent-child","created_at":"2025-12-10T11:37:14.074815506+01:00","created_by":"daemon"}]} {"id":"mcp-6k9.1.1","title":"T001: Add plow-log dependencies to mcp.cabal","description":"Add to mcp.cabal build-depends:\n- plow-log \u003e= 0.1.6 \u0026\u0026 \u003c 0.2\n- plow-log-async \u003e= 0.1.4 \u0026\u0026 \u003c 0.2\n- unliftio-core \u003e= 0.2 \u0026\u0026 \u003c 0.3\n\nFile: mcp.cabal","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-10T11:37:24.353860371+01:00","updated_at":"2025-12-10T12:11:39.22546301+01:00","closed_at":"2025-12-10T12:11:39.22546301+01:00","dependencies":[{"issue_id":"mcp-6k9.1.1","depends_on_id":"mcp-6k9.1","type":"parent-child","created_at":"2025-12-10T11:37:24.354181702+01:00","created_by":"daemon"}]} diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md index 212c539..c7adba1 100644 --- a/.specify/memory/constitution.md +++ b/.specify/memory/constitution.md @@ -1,11 +1,13 @@