{"owner":"ktorio","repo":"ktor","hasSkills":true,"totalSkillsCount":5,"totalTokensCount":4268,"categories":["root-instruction","mcp-config","marketplace","plugin-manifest","cursor-rule"],"hasMcp":true,"mcpConfig":{"mcpServers":{"ktor":{"command":"npx","args":["-y","@modelcontextprotocol/server-ktor"]}}},"found":["AGENTS.md",".cursor/mcp.json",".claude-plugin/marketplace.json",".claude-plugin/plugin.json",".cursorrules (kotlin-ktor-development-cursorrules-prompt-file.mdc)"],"skills":{"AGENTS.md":"# Agent Guidelines (Ktor)\n\nThis file is the primary playbook for agentic coding tools working in this repository.\n\nYou must follow the [AI Policy](AI_POLICY.md).\n\n## Critical Workflow Rule\n\n**NEVER return control to the user without ensuring:**\n1. Tests pass: `./gradlew :module-name:jvmTest` (and other platforms if touched)\n2. Code compiles: `./gradlew :module-name:assemble`\n3. Code is formatted and linting passes: run `./gradlew :module-name:formatKotlin`, then `./gradlew :module-name:lintKotlin`\n4. ABI validated: `./gradlew :module-name:updateKotlinAbi` (if public/protected API changed)\n\nPrefer this order to fail fast.\nAlways prefer **module-specific** commands (`:module-name:task`) over project-wide commands.\n\n## Project Requirements\n\n- JDK 21 is required.\n- Kotlin Multiplatform: JVM, JS, Native.\n- Apple targets require Xcode + command line tools.\n- Android targets are optional and enabled when Android SDK is available (`ANDROID_HOME` or `local.properties` `sdk.dir`).\n\n## Project Layout\n\n### Gradle module structure (flattened)\nThe project uses a **flattened Gradle structure** (see `settings.gradle.kts`). Nested directory paths do NOT translate to nested Gradle project names.\nExample: `ktor-client/ktor-client-curl` → `:ktor-client-curl` (not `:ktor-client:ktor-client-curl`).\n\n### Source set layout (platform-centric)\nKotlin Multiplatform sources use a flattened, platform-centric layout (do not re-introduce `src/<platform>Main/...`).\n\n```text\ncommon/src\njvm/src\njvm/resources\njvm/test\njvm/test-resources\n...\n```\n\nBuild logic enforces \"no surprise source sets\": manually registering extra source sets is rejected.\nTo add a new target/source set, add the directory or enable the target via `gradle.properties` (`target.<name>=true`).\n\n## Build Commands\n\n**Important**: Always prefer running tasks on the specific module you touched, not the entire project.\n\n### Common tasks\n```bash\n./gradlew :module-name:assemble          # Build the module\n```\n\nRun tests:\n```bash\n./gradlew :module-name:jvmTest           # Run JVM tests for the module\n./gradlew :module-name:allTests          # Run tests across all platforms for the module\n./gradlew :module-name:jvmTest --tests \"fully.qualified.TestClassName\" # Run a specific test\n./gradlew :module-name:jvmTest --tests \"fully.qualified.TestClassName.methodName\" # Run a specific test method\n```\n\nLinting:\n```bash\n./gradlew :module-name:formatKotlin      # Format the module\n./gradlew :module-name:lintKotlin        # Lint the module\n```\n\n## Code Style Guidelines\n\n### Formatting (EditorConfig is authoritative)\n- Kotlin style: IntelliJ + `KOTLIN_OFFICIAL` (`.editorconfig`).\n- Indent: 4 spaces (JSON/YAML: 2); max line length: 120; end of line: LF.\n- Use `./gradlew :module-name:formatKotlin` rather than manual formatting.\n\n### Imports\n- Star imports are preferred for `io.ktor.*` (configured in `.editorconfig`).\n\n### Naming\n- Follow Kotlin conventions unless the surrounding package has a strong established pattern.\n- Tests: prefer descriptive test names in backticks: `describe what is being tested`.\n\n### Types and API design\n- Prefer `internal` by default; keep the public surface intentional.\n- Public API requires KDoc (parameters, return, and notable exceptions).\n- Public-but-not-for-users APIs that cannot be `internal` should use `@InternalAPI`.\n- Keep `@OptIn(...)` scope minimal.\n- All types used as receivers in DSL should be annotated with `@KtorDsl` (for example, all plugin configs).\n\n### Error handling\n- `require(...)` for argument validation, `check(...)` for state validation, `error(\"...\")` for unreachable states.\n- Throw specific exceptions appropriate to the layer (IO parsing: `IOException`/`EOFException`; validation: Ktor exceptions like `BadRequestException`).\n- Make error messages actionable; include the problematic value/context.\n\n### Logging\n- Prefer Ktor log helpers where present.\n- Avoid noisy logs in hot paths.\n\n### Documentation and comments\n- Avoid redundant comments; add them only for tricky invariants or platform-specific behavior.\n- Keep KDoc correct when behavior/signatures change.\n- New source files must include the repository copyright header.\n\n## Development Practices\n\n- Prefer TDD where feasible: add/adjust tests, then implement.\n- For multiplatform changes: start JVM-first unless the task requires another platform.\n- Keep local-only build knobs (for example, developer `gradle.properties` overrides) out of commits.\n- If asked to create commits: use imperative mood and include `KTOR-<NUM>` when there is a related YouTrack issue.\n\n### Adding or removing modules\n\n- Module names must start with `ktor-`.\n- The project uses a flattened Gradle structure with custom DSL in `settings.gradle.kts` (see Project Layout section).\n- When modules are added/removed or new targets enabled, run `./update-artifact-dumps.sh` to update published artifact lists in `gradle/artifacts`.\n  Publishing will fail if these dumps are stale.\n\n## Binary Compatibility and ABI Validation\n\nBinary compatibility is **enforced** using Kotlin Gradle Plugin ABI validation.\nAll public API changes must be tracked in `/api/` directories within modules.\n\n### Release branches and API policy\n\n- Patch releases are maintained in `release/<major>.x` (for example, v3 uses `release/3.x`, v4 uses `release/4.x`).\n- The next minor release is developed on `main`.\n- Public API changes are allowed only for minor/major releases (typically on `main`), not for patch releases (on `release/<major>.x`).\n- Breaking changes are **only allowed in major version releases**.\n- The repo includes an interactive helper `./switch-base-branch.sh` for switching a feature branch base between `main` and `release/<major>.x`.\n  For agents: use `--dry-run` to print the git commands, then run them after user approval.\n\n### Validation commands\n```bash\n./gradlew :module-name:checkKotlinAbi    # Validate ABI compatibility\n./gradlew :module-name:updateKotlinAbi   # Update ABI signature files after changes\n```\n\n### Rules\n- **All** `public`/`protected` API changes require updating `api/*.api` dumps.\n- API changes must be **intentional and well-documented**.\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.claude-plugin/plugin.json)",".cursorrules (kotlin-ktor-development-cursorrules-prompt-file.mdc)":"---\ndescription: \"Cursor rules for Kotlin development with Ktor integration.\"\nglobs: **/*\nalwaysApply: false\n---\n## Instruction to developer: save this file as .cursorrules and place it on the root project directory\n\n## Core Principles\n- Follow **SOLID**, **DRY**, **KISS**, and **YAGNI** principles\n- Adhere to **OWASP** security best practices\n- Break tasks into smallest units and solve problems step-by-step\n\n## Technology Stack\n- **Framework**: Kotlin Ktor with Kotlin 2.1.20+\n- **JDK**: 21 (LTS)\n- **Build**: Gradle with Kotlin DSL\n- **Dependencies**: Ktor Server Core/Netty, kotlinx.serialization, Exposed, HikariCP, kotlin-logging, Koin, Kotest\n\n## Application Structure (Feature-Based)\n- **Organize by business features, not technical layers**\n- Each feature is self-contained with all related components\n- Promotes modularity, reusability, and better team collaboration\n- Makes codebase easier to navigate and maintain\n- Enables parallel development on different features\n```\nsrc/main/kotlin/com/company/app/\n├── common/              # Shared utilities, extensions\n├── config/              # Application configuration, DI\n└── features/\n    ├── auth/            # Feature directory\n    │   ├── models/\n    │   ├── repositories/\n    │   ├── services/\n    │   └── routes/\n    └── users/           # Another feature\n        ├── ...\n```\n\nTest structure mirrors the feature-based organization:\n```\nsrc/test/kotlin/com/company/app/\n├── common/\n└── features/\n    ├── auth/\n    │   ├── models/\n    │   ├── repositories/\n    │   ├── services/\n    │   └── routes/\n    └── users/\n        ├── ...\n```\n\n## Application Logic Design\n1. Route handlers: Handle requests/responses only\n2. Services: Contain business logic, call repositories\n3. Repositories: Handle database operations\n4. Entity classes: Data classes for database models\n5. DTOs: Data transfer between layers\n\n## Entities & Data Classes\n- Use Kotlin data classes with proper validation\n- Define Table objects when using Exposed ORM\n- Use UUID or auto-incrementing integers for IDs\n\n## Repository Pattern\n```kotlin\ninterface UserRepository {\n    suspend fun findById(id: UUID): UserDTO?\n    suspend fun create(user: CreateUserRequest): UserDTO\n    suspend fun update(id: UUID, user: UpdateUserRequest): UserDTO?\n    suspend fun delete(id: UUID): Boolean\n}\n\nclass UserRepositoryImpl : UserRepository {\n    override suspend fun findById(id: UUID): UserDTO? = withContext(Dispatchers.IO) {\n        transaction {\n            Users.select { Users.id eq id }\n                .mapNotNull { it.toUserDTO() }\n                .singleOrNull()\n        }\n    }\n    // Other implementations...\n}\n```\n\n## Service Layer\n```kotlin\ninterface UserService {\n    suspend fun getUserById(id: UUID): UserDTO\n    suspend fun createUser(request: CreateUserRequest): UserDTO\n    suspend fun updateUser(id: UUID, request: UpdateUserRequest): UserDTO\n    suspend fun deleteUser(id: UUID)\n}\n\nclass UserServiceImpl(\n    private val userRepository: UserRepository\n) : UserService {\n    override suspend fun getUserById(id: UUID): UserDTO {\n        return userRepository.findById(id) ?: throw ResourceNotFoundException(\"User\", id.toString())\n    }\n    // Other implementations...\n}\n```\n\n## Route Handlers\n```kotlin\nfun Application.configureUserRoutes(userService: UserService) {\n    routing {\n        route(\"/api/users\") {\n            get(\"/{id}\") {\n                val id = call.parameters[\"id\"]?.let { UUID.fromString(it) }\n                    ?: throw ValidationException(\"Invalid ID format\")\n                val user = userService.getUserById(id)\n                call.respond(ApiResponse(\"SUCCESS\", \"User retrieved\", user))\n            }\n            // Other routes...\n        }\n    }\n}\n```\n\n## Error Handling\n```kotlin\nopen class ApplicationException(\n    message: String,\n    val statusCode: HttpStatusCode = HttpStatusCode.InternalServerError\n) : RuntimeException(message)\n\nclass ResourceNotFoundException(resource: String, id: String) :\n    ApplicationException(\"$resource with ID $id not found\", HttpStatusCode.NotFound)\n\nfun Application.configureExceptions() {\n    install(StatusPages) {\n        exception<ResourceNotFoundException> { call, cause ->\n            call.respond(cause.statusCode, ApiResponse(\"ERROR\", cause.message ?: \"Resource not found\"))\n        }\n        exception<Throwable> { call, cause ->\n            call.respond(HttpStatusCode.InternalServerError, ApiResponse(\"ERROR\", \"An internal error occurred\"))\n        }\n    }\n}\n```\n\n## Testing Strategies and Coverage Requirements\n\n### Test Coverage Requirements\n- **Minimum coverage**: 80% overall code coverage required\n- **Critical components**: 90%+ coverage for repositories, services, and validation\n- **Test all edge cases**: Empty collections, null values, boundary conditions\n- **Test failure paths**: Exception handling, validation errors, timeouts\n- **All public APIs**: Must have integration tests\n- **Performance-critical paths**: Must have benchmarking tests\n\n### Unit Testing with Kotest\n```kotlin\nclass UserServiceTest : DescribeSpec({\n    describe(\"UserService\") {\n        val mockRepository = mockk<UserRepository>()\n        val userService = UserServiceImpl(mockRepository)\n\n        it(\"should return user when exists\") {\n            val userId = UUID.randomUUID()\n            val user = UserDTO(userId.toString(), \"Test User\", \"test@example.com\")\n            coEvery { mockRepository.findById(userId) } returns user\n\n            val result = runBlocking { userService.getUserById(userId) }\n\n            result shouldBe user\n        }\n\n        it(\"should throw exception when user not found\") {\n            val userId = UUID.randomUUID()\n            coEvery { mockRepository.findById(userId) } returns null\n\n            shouldThrow<ResourceNotFoundException> {\n                runBlocking { userService.getUserById(userId) }\n            }\n        }\n    }\n})\n```\n\n## Route Testing with Ktor 3.x\n```kotlin\nclass UserRoutesTest : FunSpec({\n    test(\"GET /api/users/{id} returns 200 when user exists\") {\n        val mockService = mockk<UserService>()\n        val userId = UUID.randomUUID()\n        val user = UserDTO(userId.toString(), \"Test User\", \"test@example.com\")\n\n        coEvery { mockService.getUserById(userId) } returns user\n\n        testApplication {\n            application {\n                configureRouting()\n                configureDI { single { mockService } }\n            }\n\n            client.get(\"/api/users/$userId\").apply {\n                status shouldBe HttpStatusCode.OK\n                bodyAsText().let {\n                    Json.decodeFromString<ApiResponse<UserDTO>>(it)\n                }.data shouldBe user\n            }\n        }\n    }\n})\n```\n\n## Key Principles for Testable Code\n1. **Single Responsibility**: Each method should do one thing well\n2. **Pure Functions**: Same input always produces same output\n3. **Dependency Injection**: Constructor injection for testable components\n4. **Clear Boundaries**: Well-defined inputs and outputs\n5. **Small Methods**: Extract complex logic into testable helper functions\n\n## Configuration Management\n```kotlin\n// Type-safe configuration\ninterface AppConfig {\n    val database: DatabaseConfig\n    val security: SecurityConfig\n}\n\ndata class DatabaseConfig(\n    val driver: String,\n    val url: String,\n    val user: String,\n    val password: String\n)\n\n// Access in application\nfun Application.configureDI() {\n    val appConfig = HoconAppConfig(environment.config)\n\n    install(Koin) {\n        modules(module {\n            single<AppConfig> { appConfig }\n            single { appConfig.database }\n        })\n    }\n}\n```\n\n## Security Best Practices\n```kotlin\nfun Application.configureSecurity() {\n    install(Authentication) {\n        jwt(\"auth-jwt\") {\n            // JWT configuration\n        }\n    }\n\n    install(DefaultHeaders) {\n        header(HttpHeaders.XContentTypeOptions, \"nosniff\")\n        header(HttpHeaders.XFrameOptions, \"DENY\")\n        header(HttpHeaders.ContentSecurityPolicy, \"default-src 'self'\")\n        header(\"Strict-Transport-Security\", \"max-age=31536000; includeSubDomains\")\n    }\n}\n```\n\n## Health Checks & Monitoring\n```kotlin\nfun Application.configureMonitoring() {\n    val startTime = System.currentTimeMillis()\n\n    routing {\n        get(\"/health\") {\n            call.respond(mapOf(\"status\" to \"UP\", \"uptime\" to \"${(System.currentTimeMillis() - startTime) / 1000}s\"))\n        }\n\n        get(\"/metrics\") {\n            call.respond(prometheusRegistry.scrape())\n        }\n    }\n\n    install(MicrometerMetrics) {\n        registry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)\n        meterBinders = listOf(\n            JvmMemoryMetrics(),\n            JvmGcMetrics(),\n            ProcessorMetrics(),\n            JvmThreadMetrics()\n        )\n    }\n}\n```\n\n## Performance Tuning\n- **JVM Settings**: `-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:MaxRAMPercentage=75.0`\n- **Connection Pooling**: Configure HikariCP with proper sizing based on workload\n- **Caching**: Use Caffeine for in-memory caching of frequently accessed data\n- **Coroutines**: Use structured concurrency for asynchronous processing\n- **Database Queries**: Optimize with proper indexing, batch operations, pagination\n"},"files":{"AGENTS.md":"# Agent Guidelines (Ktor)\n\nThis file is the primary playbook for agentic coding tools working in this repository.\n\nYou must follow the [AI Policy](AI_POLICY.md).\n\n## Critical Workflow Rule\n\n**NEVER return control to the user without ensuring:**\n1. Tests pass: `./gradlew :module-name:jvmTest` (and other platforms if touched)\n2. Code compiles: `./gradlew :module-name:assemble`\n3. Code is formatted and linting passes: run `./gradlew :module-name:formatKotlin`, then `./gradlew :module-name:lintKotlin`\n4. ABI validated: `./gradlew :module-name:updateKotlinAbi` (if public/protected API changed)\n\nPrefer this order to fail fast.\nAlways prefer **module-specific** commands (`:module-name:task`) over project-wide commands.\n\n## Project Requirements\n\n- JDK 21 is required.\n- Kotlin Multiplatform: JVM, JS, Native.\n- Apple targets require Xcode + command line tools.\n- Android targets are optional and enabled when Android SDK is available (`ANDROID_HOME` or `local.properties` `sdk.dir`).\n\n## Project Layout\n\n### Gradle module structure (flattened)\nThe project uses a **flattened Gradle structure** (see `settings.gradle.kts`). Nested directory paths do NOT translate to nested Gradle project names.\nExample: `ktor-client/ktor-client-curl` → `:ktor-client-curl` (not `:ktor-client:ktor-client-curl`).\n\n### Source set layout (platform-centric)\nKotlin Multiplatform sources use a flattened, platform-centric layout (do not re-introduce `src/<platform>Main/...`).\n\n```text\ncommon/src\njvm/src\njvm/resources\njvm/test\njvm/test-resources\n...\n```\n\nBuild logic enforces \"no surprise source sets\": manually registering extra source sets is rejected.\nTo add a new target/source set, add the directory or enable the target via `gradle.properties` (`target.<name>=true`).\n\n## Build Commands\n\n**Important**: Always prefer running tasks on the specific module you touched, not the entire project.\n\n### Common tasks\n```bash\n./gradlew :module-name:assemble          # Build the module\n```\n\nRun tests:\n```bash\n./gradlew :module-name:jvmTest           # Run JVM tests for the module\n./gradlew :module-name:allTests          # Run tests across all platforms for the module\n./gradlew :module-name:jvmTest --tests \"fully.qualified.TestClassName\" # Run a specific test\n./gradlew :module-name:jvmTest --tests \"fully.qualified.TestClassName.methodName\" # Run a specific test method\n```\n\nLinting:\n```bash\n./gradlew :module-name:formatKotlin      # Format the module\n./gradlew :module-name:lintKotlin        # Lint the module\n```\n\n## Code Style Guidelines\n\n### Formatting (EditorConfig is authoritative)\n- Kotlin style: IntelliJ + `KOTLIN_OFFICIAL` (`.editorconfig`).\n- Indent: 4 spaces (JSON/YAML: 2); max line length: 120; end of line: LF.\n- Use `./gradlew :module-name:formatKotlin` rather than manual formatting.\n\n### Imports\n- Star imports are preferred for `io.ktor.*` (configured in `.editorconfig`).\n\n### Naming\n- Follow Kotlin conventions unless the surrounding package has a strong established pattern.\n- Tests: prefer descriptive test names in backticks: `describe what is being tested`.\n\n### Types and API design\n- Prefer `internal` by default; keep the public surface intentional.\n- Public API requires KDoc (parameters, return, and notable exceptions).\n- Public-but-not-for-users APIs that cannot be `internal` should use `@InternalAPI`.\n- Keep `@OptIn(...)` scope minimal.\n- All types used as receivers in DSL should be annotated with `@KtorDsl` (for example, all plugin configs).\n\n### Error handling\n- `require(...)` for argument validation, `check(...)` for state validation, `error(\"...\")` for unreachable states.\n- Throw specific exceptions appropriate to the layer (IO parsing: `IOException`/`EOFException`; validation: Ktor exceptions like `BadRequestException`).\n- Make error messages actionable; include the problematic value/context.\n\n### Logging\n- Prefer Ktor log helpers where present.\n- Avoid noisy logs in hot paths.\n\n### Documentation and comments\n- Avoid redundant comments; add them only for tricky invariants or platform-specific behavior.\n- Keep KDoc correct when behavior/signatures change.\n- New source files must include the repository copyright header.\n\n## Development Practices\n\n- Prefer TDD where feasible: add/adjust tests, then implement.\n- For multiplatform changes: start JVM-first unless the task requires another platform.\n- Keep local-only build knobs (for example, developer `gradle.properties` overrides) out of commits.\n- If asked to create commits: use imperative mood and include `KTOR-<NUM>` when there is a related YouTrack issue.\n\n### Adding or removing modules\n\n- Module names must start with `ktor-`.\n- The project uses a flattened Gradle structure with custom DSL in `settings.gradle.kts` (see Project Layout section).\n- When modules are added/removed or new targets enabled, run `./update-artifact-dumps.sh` to update published artifact lists in `gradle/artifacts`.\n  Publishing will fail if these dumps are stale.\n\n## Binary Compatibility and ABI Validation\n\nBinary compatibility is **enforced** using Kotlin Gradle Plugin ABI validation.\nAll public API changes must be tracked in `/api/` directories within modules.\n\n### Release branches and API policy\n\n- Patch releases are maintained in `release/<major>.x` (for example, v3 uses `release/3.x`, v4 uses `release/4.x`).\n- The next minor release is developed on `main`.\n- Public API changes are allowed only for minor/major releases (typically on `main`), not for patch releases (on `release/<major>.x`).\n- Breaking changes are **only allowed in major version releases**.\n- The repo includes an interactive helper `./switch-base-branch.sh` for switching a feature branch base between `main` and `release/<major>.x`.\n  For agents: use `--dry-run` to print the git commands, then run them after user approval.\n\n### Validation commands\n```bash\n./gradlew :module-name:checkKotlinAbi    # Validate ABI compatibility\n./gradlew :module-name:updateKotlinAbi   # Update ABI signature files after changes\n```\n\n### Rules\n- **All** `public`/`protected` API changes require updating `api/*.api` dumps.\n- API changes must be **intentional and well-documented**.\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.claude-plugin/plugin.json)",".cursorrules (kotlin-ktor-development-cursorrules-prompt-file.mdc)":"---\ndescription: \"Cursor rules for Kotlin development with Ktor integration.\"\nglobs: **/*\nalwaysApply: false\n---\n## Instruction to developer: save this file as .cursorrules and place it on the root project directory\n\n## Core Principles\n- Follow **SOLID**, **DRY**, **KISS**, and **YAGNI** principles\n- Adhere to **OWASP** security best practices\n- Break tasks into smallest units and solve problems step-by-step\n\n## Technology Stack\n- **Framework**: Kotlin Ktor with Kotlin 2.1.20+\n- **JDK**: 21 (LTS)\n- **Build**: Gradle with Kotlin DSL\n- **Dependencies**: Ktor Server Core/Netty, kotlinx.serialization, Exposed, HikariCP, kotlin-logging, Koin, Kotest\n\n## Application Structure (Feature-Based)\n- **Organize by business features, not technical layers**\n- Each feature is self-contained with all related components\n- Promotes modularity, reusability, and better team collaboration\n- Makes codebase easier to navigate and maintain\n- Enables parallel development on different features\n```\nsrc/main/kotlin/com/company/app/\n├── common/              # Shared utilities, extensions\n├── config/              # Application configuration, DI\n└── features/\n    ├── auth/            # Feature directory\n    │   ├── models/\n    │   ├── repositories/\n    │   ├── services/\n    │   └── routes/\n    └── users/           # Another feature\n        ├── ...\n```\n\nTest structure mirrors the feature-based organization:\n```\nsrc/test/kotlin/com/company/app/\n├── common/\n└── features/\n    ├── auth/\n    │   ├── models/\n    │   ├── repositories/\n    │   ├── services/\n    │   └── routes/\n    └── users/\n        ├── ...\n```\n\n## Application Logic Design\n1. Route handlers: Handle requests/responses only\n2. Services: Contain business logic, call repositories\n3. Repositories: Handle database operations\n4. Entity classes: Data classes for database models\n5. DTOs: Data transfer between layers\n\n## Entities & Data Classes\n- Use Kotlin data classes with proper validation\n- Define Table objects when using Exposed ORM\n- Use UUID or auto-incrementing integers for IDs\n\n## Repository Pattern\n```kotlin\ninterface UserRepository {\n    suspend fun findById(id: UUID): UserDTO?\n    suspend fun create(user: CreateUserRequest): UserDTO\n    suspend fun update(id: UUID, user: UpdateUserRequest): UserDTO?\n    suspend fun delete(id: UUID): Boolean\n}\n\nclass UserRepositoryImpl : UserRepository {\n    override suspend fun findById(id: UUID): UserDTO? = withContext(Dispatchers.IO) {\n        transaction {\n            Users.select { Users.id eq id }\n                .mapNotNull { it.toUserDTO() }\n                .singleOrNull()\n        }\n    }\n    // Other implementations...\n}\n```\n\n## Service Layer\n```kotlin\ninterface UserService {\n    suspend fun getUserById(id: UUID): UserDTO\n    suspend fun createUser(request: CreateUserRequest): UserDTO\n    suspend fun updateUser(id: UUID, request: UpdateUserRequest): UserDTO\n    suspend fun deleteUser(id: UUID)\n}\n\nclass UserServiceImpl(\n    private val userRepository: UserRepository\n) : UserService {\n    override suspend fun getUserById(id: UUID): UserDTO {\n        return userRepository.findById(id) ?: throw ResourceNotFoundException(\"User\", id.toString())\n    }\n    // Other implementations...\n}\n```\n\n## Route Handlers\n```kotlin\nfun Application.configureUserRoutes(userService: UserService) {\n    routing {\n        route(\"/api/users\") {\n            get(\"/{id}\") {\n                val id = call.parameters[\"id\"]?.let { UUID.fromString(it) }\n                    ?: throw ValidationException(\"Invalid ID format\")\n                val user = userService.getUserById(id)\n                call.respond(ApiResponse(\"SUCCESS\", \"User retrieved\", user))\n            }\n            // Other routes...\n        }\n    }\n}\n```\n\n## Error Handling\n```kotlin\nopen class ApplicationException(\n    message: String,\n    val statusCode: HttpStatusCode = HttpStatusCode.InternalServerError\n) : RuntimeException(message)\n\nclass ResourceNotFoundException(resource: String, id: String) :\n    ApplicationException(\"$resource with ID $id not found\", HttpStatusCode.NotFound)\n\nfun Application.configureExceptions() {\n    install(StatusPages) {\n        exception<ResourceNotFoundException> { call, cause ->\n            call.respond(cause.statusCode, ApiResponse(\"ERROR\", cause.message ?: \"Resource not found\"))\n        }\n        exception<Throwable> { call, cause ->\n            call.respond(HttpStatusCode.InternalServerError, ApiResponse(\"ERROR\", \"An internal error occurred\"))\n        }\n    }\n}\n```\n\n## Testing Strategies and Coverage Requirements\n\n### Test Coverage Requirements\n- **Minimum coverage**: 80% overall code coverage required\n- **Critical components**: 90%+ coverage for repositories, services, and validation\n- **Test all edge cases**: Empty collections, null values, boundary conditions\n- **Test failure paths**: Exception handling, validation errors, timeouts\n- **All public APIs**: Must have integration tests\n- **Performance-critical paths**: Must have benchmarking tests\n\n### Unit Testing with Kotest\n```kotlin\nclass UserServiceTest : DescribeSpec({\n    describe(\"UserService\") {\n        val mockRepository = mockk<UserRepository>()\n        val userService = UserServiceImpl(mockRepository)\n\n        it(\"should return user when exists\") {\n            val userId = UUID.randomUUID()\n            val user = UserDTO(userId.toString(), \"Test User\", \"test@example.com\")\n            coEvery { mockRepository.findById(userId) } returns user\n\n            val result = runBlocking { userService.getUserById(userId) }\n\n            result shouldBe user\n        }\n\n        it(\"should throw exception when user not found\") {\n            val userId = UUID.randomUUID()\n            coEvery { mockRepository.findById(userId) } returns null\n\n            shouldThrow<ResourceNotFoundException> {\n                runBlocking { userService.getUserById(userId) }\n            }\n        }\n    }\n})\n```\n\n## Route Testing with Ktor 3.x\n```kotlin\nclass UserRoutesTest : FunSpec({\n    test(\"GET /api/users/{id} returns 200 when user exists\") {\n        val mockService = mockk<UserService>()\n        val userId = UUID.randomUUID()\n        val user = UserDTO(userId.toString(), \"Test User\", \"test@example.com\")\n\n        coEvery { mockService.getUserById(userId) } returns user\n\n        testApplication {\n            application {\n                configureRouting()\n                configureDI { single { mockService } }\n            }\n\n            client.get(\"/api/users/$userId\").apply {\n                status shouldBe HttpStatusCode.OK\n                bodyAsText().let {\n                    Json.decodeFromString<ApiResponse<UserDTO>>(it)\n                }.data shouldBe user\n            }\n        }\n    }\n})\n```\n\n## Key Principles for Testable Code\n1. **Single Responsibility**: Each method should do one thing well\n2. **Pure Functions**: Same input always produces same output\n3. **Dependency Injection**: Constructor injection for testable components\n4. **Clear Boundaries**: Well-defined inputs and outputs\n5. **Small Methods**: Extract complex logic into testable helper functions\n\n## Configuration Management\n```kotlin\n// Type-safe configuration\ninterface AppConfig {\n    val database: DatabaseConfig\n    val security: SecurityConfig\n}\n\ndata class DatabaseConfig(\n    val driver: String,\n    val url: String,\n    val user: String,\n    val password: String\n)\n\n// Access in application\nfun Application.configureDI() {\n    val appConfig = HoconAppConfig(environment.config)\n\n    install(Koin) {\n        modules(module {\n            single<AppConfig> { appConfig }\n            single { appConfig.database }\n        })\n    }\n}\n```\n\n## Security Best Practices\n```kotlin\nfun Application.configureSecurity() {\n    install(Authentication) {\n        jwt(\"auth-jwt\") {\n            // JWT configuration\n        }\n    }\n\n    install(DefaultHeaders) {\n        header(HttpHeaders.XContentTypeOptions, \"nosniff\")\n        header(HttpHeaders.XFrameOptions, \"DENY\")\n        header(HttpHeaders.ContentSecurityPolicy, \"default-src 'self'\")\n        header(\"Strict-Transport-Security\", \"max-age=31536000; includeSubDomains\")\n    }\n}\n```\n\n## Health Checks & Monitoring\n```kotlin\nfun Application.configureMonitoring() {\n    val startTime = System.currentTimeMillis()\n\n    routing {\n        get(\"/health\") {\n            call.respond(mapOf(\"status\" to \"UP\", \"uptime\" to \"${(System.currentTimeMillis() - startTime) / 1000}s\"))\n        }\n\n        get(\"/metrics\") {\n            call.respond(prometheusRegistry.scrape())\n        }\n    }\n\n    install(MicrometerMetrics) {\n        registry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)\n        meterBinders = listOf(\n            JvmMemoryMetrics(),\n            JvmGcMetrics(),\n            ProcessorMetrics(),\n            JvmThreadMetrics()\n        )\n    }\n}\n```\n\n## Performance Tuning\n- **JVM Settings**: `-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:MaxRAMPercentage=75.0`\n- **Connection Pooling**: Configure HikariCP with proper sizing based on workload\n- **Caching**: Use Caffeine for in-memory caching of frequently accessed data\n- **Coroutines**: Use structured concurrency for asynchronous processing\n- **Database Queries**: Optimize with proper indexing, batch operations, pagination\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/ktorio/ktor/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Agent Guidelines (Ktor)\n\nThis file is the primary playbook for agentic coding tools working in this repository.\n\nYou must follow the [AI Policy](AI_POLICY.md).\n\n## Critical Workflow Rule\n\n**NEVER return control to the user without ensuring:**\n1. Tests pass: `./gradlew :module-name:jvmTest` (and other platforms if touched)\n2. Code compiles: `./gradlew :module-name:assemble`\n3. Code is formatted and linting passes: run `./gradlew :module-name:formatKotlin`, then `./gradlew :module-name:lintKotlin`\n4. ABI validated: `./gradlew :module-name:updateKotlinAbi` (if public/protected API changed)\n\nPrefer this order to fail fast.\nAlways prefer **module-specific** commands (`:module-name:task`) over project-wide commands.\n\n## Project Requirements\n\n- JDK 21 is required.\n- Kotlin Multiplatform: JVM, JS, Native.\n- Apple targets require Xcode + command line tools.\n- Android targets are optional and enabled when Android SDK is available (`ANDROID_HOME` or `local.properties` `sdk.dir`).\n\n## Project Layout\n\n### Gradle module structure (flattened)\nThe project uses a **flattened Gradle structure** (see `settings.gradle.kts`). Nested directory paths do NOT translate to nested Gradle project names.\nExample: `ktor-client/ktor-client-curl` → `:ktor-client-curl` (not `:ktor-client:ktor-client-curl`).\n\n### Source set layout (platform-centric)\nKotlin Multiplatform sources use a flattened, platform-centric layout (do not re-introduce `src/<platform>Main/...`).\n\n```text\ncommon/src\njvm/src\njvm/resources\njvm/test\njvm/test-resources\n...\n```\n\nBuild logic enforces \"no surprise source sets\": manually registering extra source sets is rejected.\nTo add a new target/source set, add the directory or enable the target via `gradle.properties` (`target.<name>=true`).\n\n## Build Commands\n\n**Important**: Always prefer running tasks on the specific module you touched, not the entire project.\n\n### Common tasks\n```bash\n./gradlew :module-name:assemble          # Build the module\n```\n\nRun tests:\n```bash\n./gradlew :module-name:jvmTest           # Run JVM tests for the module\n./gradlew :module-name:allTests          # Run tests across all platforms for the module\n./gradlew :module-name:jvmTest --tests \"fully.qualified.TestClassName\" # Run a specific test\n./gradlew :module-name:jvmTest --tests \"fully.qualified.TestClassName.methodName\" # Run a specific test method\n```\n\nLinting:\n```bash\n./gradlew :module-name:formatKotlin      # Format the module\n./gradlew :module-name:lintKotlin        # Lint the module\n```\n\n## Code Style Guidelines\n\n### Formatting (EditorConfig is authoritative)\n- Kotlin style: IntelliJ + `KOTLIN_OFFICIAL` (`.editorconfig`).\n- Indent: 4 spaces (JSON/YAML: 2); max line length: 120; end of line: LF.\n- Use `./gradlew :module-name:formatKotlin` rather than manual formatting.\n\n### Imports\n- Star imports are preferred for `io.ktor.*` (configured in `.editorconfig`).\n\n### Naming\n- Follow Kotlin conventions unless the surrounding package has a strong established pattern.\n- Tests: prefer descriptive test names in backticks: `describe what is being tested`.\n\n### Types and API design\n- Prefer `internal` by default; keep the public surface intentional.\n- Public API requires KDoc (parameters, return, and notable exceptions).\n- Public-but-not-for-users APIs that cannot be `internal` should use `@InternalAPI`.\n- Keep `@OptIn(...)` scope minimal.\n- All types used as receivers in DSL should be annotated with `@KtorDsl` (for example, all plugin configs).\n\n### Error handling\n- `require(...)` for argument validation, `check(...)` for state validation, `error(\"...\")` for unreachable states.\n- Throw specific exceptions appropriate to the layer (IO parsing: `IOException`/`EOFException`; validation: Ktor exceptions like `BadRequestException`).\n- Make error messages actionable; include the problematic value/context.\n\n### Logging\n- Prefer Ktor log helpers where present.\n- Avoid noisy logs in hot paths.\n\n### Documentation and comments\n- Avoid redundant comments; add them only for tricky invariants or platform-specific behavior.\n- Keep KDoc correct when behavior/signatures change.\n- New source files must include the repository copyright header.\n\n## Development Practices\n\n- Prefer TDD where feasible: add/adjust tests, then implement.\n- For multiplatform changes: start JVM-first unless the task requires another platform.\n- Keep local-only build knobs (for example, developer `gradle.properties` overrides) out of commits.\n- If asked to create commits: use imperative mood and include `KTOR-<NUM>` when there is a related YouTrack issue.\n\n### Adding or removing modules\n\n- Module names must start with `ktor-`.\n- The project uses a flattened Gradle structure with custom DSL in `settings.gradle.kts` (see Project Layout section).\n- When modules are added/removed or new targets enabled, run `./update-artifact-dumps.sh` to update published artifact lists in `gradle/artifacts`.\n  Publishing will fail if these dumps are stale.\n\n## Binary Compatibility and ABI Validation\n\nBinary compatibility is **enforced** using Kotlin Gradle Plugin ABI validation.\nAll public API changes must be tracked in `/api/` directories within modules.\n\n### Release branches and API policy\n\n- Patch releases are maintained in `release/<major>.x` (for example, v3 uses `release/3.x`, v4 uses `release/4.x`).\n- The next minor release is developed on `main`.\n- Public API changes are allowed only for minor/major releases (typically on `main`), not for patch releases (on `release/<major>.x`).\n- Breaking changes are **only allowed in major version releases**.\n- The repo includes an interactive helper `./switch-base-branch.sh` for switching a feature branch base between `main` and `release/<major>.x`.\n  For agents: use `--dry-run` to print the git commands, then run them after user approval.\n\n### Validation commands\n```bash\n./gradlew :module-name:checkKotlinAbi    # Validate ABI compatibility\n./gradlew :module-name:updateKotlinAbi   # Update ABI signature files after changes\n```\n\n### Rules\n- **All** `public`/`protected` API changes require updating `api/*.api` dumps.\n- API changes must be **intentional and well-documented**.\n","isInternal":false,"tokens":1429,"sizeBytes":6129},{"name":"marketplace.json","path":".claude-plugin/marketplace.json","rawUrl":"https://raw.githubusercontent.com/ktorio/ktor/HEAD/.claude-plugin/marketplace.json","title":"Claude Plugin Marketplace Catalog","category":"marketplace","format":"json","content":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.claude-plugin/marketplace.json)","tokens":300,"sizeBytes":190},{"name":"plugin.json","path":".claude-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/ktorio/ktor/HEAD/.claude-plugin/plugin.json","title":"claude-plugin Plugin Manifest","category":"plugin-manifest","format":"json","content":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.claude-plugin/plugin.json)","tokens":300,"sizeBytes":176},{"name":"mcp.json","path":".cursor/mcp.json","rawUrl":"https://raw.githubusercontent.com/ktorio/ktor/HEAD/.cursor/mcp.json","title":"Model Context Protocol (MCP) Configuration","category":"mcp-config","format":"json","content":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.cursor/mcp.json)","tokens":300,"sizeBytes":169},{"name":".cursorrules","path":".cursorrules (kotlin-ktor-development-cursorrules-prompt-file.mdc)","rawUrl":"https://raw.githubusercontent.com/PatrickJS/awesome-cursorrules/main/rules/kotlin-ktor-development-cursorrules-prompt-file.mdc","title":"Awesome Cursorrules: kotlin ktor development","category":"cursor-rule","format":"markdown","content":"---\ndescription: \"Cursor rules for Kotlin development with Ktor integration.\"\nglobs: **/*\nalwaysApply: false\n---\n## Instruction to developer: save this file as .cursorrules and place it on the root project directory\n\n## Core Principles\n- Follow **SOLID**, **DRY**, **KISS**, and **YAGNI** principles\n- Adhere to **OWASP** security best practices\n- Break tasks into smallest units and solve problems step-by-step\n\n## Technology Stack\n- **Framework**: Kotlin Ktor with Kotlin 2.1.20+\n- **JDK**: 21 (LTS)\n- **Build**: Gradle with Kotlin DSL\n- **Dependencies**: Ktor Server Core/Netty, kotlinx.serialization, Exposed, HikariCP, kotlin-logging, Koin, Kotest\n\n## Application Structure (Feature-Based)\n- **Organize by business features, not technical layers**\n- Each feature is self-contained with all related components\n- Promotes modularity, reusability, and better team collaboration\n- Makes codebase easier to navigate and maintain\n- Enables parallel development on different features\n```\nsrc/main/kotlin/com/company/app/\n├── common/              # Shared utilities, extensions\n├── config/              # Application configuration, DI\n└── features/\n    ├── auth/            # Feature directory\n    │   ├── models/\n    │   ├── repositories/\n    │   ├── services/\n    │   └── routes/\n    └── users/           # Another feature\n        ├── ...\n```\n\nTest structure mirrors the feature-based organization:\n```\nsrc/test/kotlin/com/company/app/\n├── common/\n└── features/\n    ├── auth/\n    │   ├── models/\n    │   ├── repositories/\n    │   ├── services/\n    │   └── routes/\n    └── users/\n        ├── ...\n```\n\n## Application Logic Design\n1. Route handlers: Handle requests/responses only\n2. Services: Contain business logic, call repositories\n3. Repositories: Handle database operations\n4. Entity classes: Data classes for database models\n5. DTOs: Data transfer between layers\n\n## Entities & Data Classes\n- Use Kotlin data classes with proper validation\n- Define Table objects when using Exposed ORM\n- Use UUID or auto-incrementing integers for IDs\n\n## Repository Pattern\n```kotlin\ninterface UserRepository {\n    suspend fun findById(id: UUID): UserDTO?\n    suspend fun create(user: CreateUserRequest): UserDTO\n    suspend fun update(id: UUID, user: UpdateUserRequest): UserDTO?\n    suspend fun delete(id: UUID): Boolean\n}\n\nclass UserRepositoryImpl : UserRepository {\n    override suspend fun findById(id: UUID): UserDTO? = withContext(Dispatchers.IO) {\n        transaction {\n            Users.select { Users.id eq id }\n                .mapNotNull { it.toUserDTO() }\n                .singleOrNull()\n        }\n    }\n    // Other implementations...\n}\n```\n\n## Service Layer\n```kotlin\ninterface UserService {\n    suspend fun getUserById(id: UUID): UserDTO\n    suspend fun createUser(request: CreateUserRequest): UserDTO\n    suspend fun updateUser(id: UUID, request: UpdateUserRequest): UserDTO\n    suspend fun deleteUser(id: UUID)\n}\n\nclass UserServiceImpl(\n    private val userRepository: UserRepository\n) : UserService {\n    override suspend fun getUserById(id: UUID): UserDTO {\n        return userRepository.findById(id) ?: throw ResourceNotFoundException(\"User\", id.toString())\n    }\n    // Other implementations...\n}\n```\n\n## Route Handlers\n```kotlin\nfun Application.configureUserRoutes(userService: UserService) {\n    routing {\n        route(\"/api/users\") {\n            get(\"/{id}\") {\n                val id = call.parameters[\"id\"]?.let { UUID.fromString(it) }\n                    ?: throw ValidationException(\"Invalid ID format\")\n                val user = userService.getUserById(id)\n                call.respond(ApiResponse(\"SUCCESS\", \"User retrieved\", user))\n            }\n            // Other routes...\n        }\n    }\n}\n```\n\n## Error Handling\n```kotlin\nopen class ApplicationException(\n    message: String,\n    val statusCode: HttpStatusCode = HttpStatusCode.InternalServerError\n) : RuntimeException(message)\n\nclass ResourceNotFoundException(resource: String, id: String) :\n    ApplicationException(\"$resource with ID $id not found\", HttpStatusCode.NotFound)\n\nfun Application.configureExceptions() {\n    install(StatusPages) {\n        exception<ResourceNotFoundException> { call, cause ->\n            call.respond(cause.statusCode, ApiResponse(\"ERROR\", cause.message ?: \"Resource not found\"))\n        }\n        exception<Throwable> { call, cause ->\n            call.respond(HttpStatusCode.InternalServerError, ApiResponse(\"ERROR\", \"An internal error occurred\"))\n        }\n    }\n}\n```\n\n## Testing Strategies and Coverage Requirements\n\n### Test Coverage Requirements\n- **Minimum coverage**: 80% overall code coverage required\n- **Critical components**: 90%+ coverage for repositories, services, and validation\n- **Test all edge cases**: Empty collections, null values, boundary conditions\n- **Test failure paths**: Exception handling, validation errors, timeouts\n- **All public APIs**: Must have integration tests\n- **Performance-critical paths**: Must have benchmarking tests\n\n### Unit Testing with Kotest\n```kotlin\nclass UserServiceTest : DescribeSpec({\n    describe(\"UserService\") {\n        val mockRepository = mockk<UserRepository>()\n        val userService = UserServiceImpl(mockRepository)\n\n        it(\"should return user when exists\") {\n            val userId = UUID.randomUUID()\n            val user = UserDTO(userId.toString(), \"Test User\", \"test@example.com\")\n            coEvery { mockRepository.findById(userId) } returns user\n\n            val result = runBlocking { userService.getUserById(userId) }\n\n            result shouldBe user\n        }\n\n        it(\"should throw exception when user not found\") {\n            val userId = UUID.randomUUID()\n            coEvery { mockRepository.findById(userId) } returns null\n\n            shouldThrow<ResourceNotFoundException> {\n                runBlocking { userService.getUserById(userId) }\n            }\n        }\n    }\n})\n```\n\n## Route Testing with Ktor 3.x\n```kotlin\nclass UserRoutesTest : FunSpec({\n    test(\"GET /api/users/{id} returns 200 when user exists\") {\n        val mockService = mockk<UserService>()\n        val userId = UUID.randomUUID()\n        val user = UserDTO(userId.toString(), \"Test User\", \"test@example.com\")\n\n        coEvery { mockService.getUserById(userId) } returns user\n\n        testApplication {\n            application {\n                configureRouting()\n                configureDI { single { mockService } }\n            }\n\n            client.get(\"/api/users/$userId\").apply {\n                status shouldBe HttpStatusCode.OK\n                bodyAsText().let {\n                    Json.decodeFromString<ApiResponse<UserDTO>>(it)\n                }.data shouldBe user\n            }\n        }\n    }\n})\n```\n\n## Key Principles for Testable Code\n1. **Single Responsibility**: Each method should do one thing well\n2. **Pure Functions**: Same input always produces same output\n3. **Dependency Injection**: Constructor injection for testable components\n4. **Clear Boundaries**: Well-defined inputs and outputs\n5. **Small Methods**: Extract complex logic into testable helper functions\n\n## Configuration Management\n```kotlin\n// Type-safe configuration\ninterface AppConfig {\n    val database: DatabaseConfig\n    val security: SecurityConfig\n}\n\ndata class DatabaseConfig(\n    val driver: String,\n    val url: String,\n    val user: String,\n    val password: String\n)\n\n// Access in application\nfun Application.configureDI() {\n    val appConfig = HoconAppConfig(environment.config)\n\n    install(Koin) {\n        modules(module {\n            single<AppConfig> { appConfig }\n            single { appConfig.database }\n        })\n    }\n}\n```\n\n## Security Best Practices\n```kotlin\nfun Application.configureSecurity() {\n    install(Authentication) {\n        jwt(\"auth-jwt\") {\n            // JWT configuration\n        }\n    }\n\n    install(DefaultHeaders) {\n        header(HttpHeaders.XContentTypeOptions, \"nosniff\")\n        header(HttpHeaders.XFrameOptions, \"DENY\")\n        header(HttpHeaders.ContentSecurityPolicy, \"default-src 'self'\")\n        header(\"Strict-Transport-Security\", \"max-age=31536000; includeSubDomains\")\n    }\n}\n```\n\n## Health Checks & Monitoring\n```kotlin\nfun Application.configureMonitoring() {\n    val startTime = System.currentTimeMillis()\n\n    routing {\n        get(\"/health\") {\n            call.respond(mapOf(\"status\" to \"UP\", \"uptime\" to \"${(System.currentTimeMillis() - startTime) / 1000}s\"))\n        }\n\n        get(\"/metrics\") {\n            call.respond(prometheusRegistry.scrape())\n        }\n    }\n\n    install(MicrometerMetrics) {\n        registry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)\n        meterBinders = listOf(\n            JvmMemoryMetrics(),\n            JvmGcMetrics(),\n            ProcessorMetrics(),\n            JvmThreadMetrics()\n        )\n    }\n}\n```\n\n## Performance Tuning\n- **JVM Settings**: `-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:MaxRAMPercentage=75.0`\n- **Connection Pooling**: Configure HikariCP with proper sizing based on workload\n- **Caching**: Use Caffeine for in-memory caching of frequently accessed data\n- **Coroutines**: Use structured concurrency for asynchronous processing\n- **Database Queries**: Optimize with proper indexing, batch operations, pagination\n","tokens":1939,"sizeBytes":9319}],"systemPromptSnippet":"<yakaai_skills repo=\"ktorio/ktor\">\n<!-- File: AGENTS.md (Tokens: ~1429 | Category: root-instruction) -->\n# Agent Guidelines (Ktor)\n\nThis file is the primary playbook for agentic coding tools working in this repository.\n\nYou must follow the [AI Policy](AI_POLICY.md).\n\n## Critical Workflow Rule\n\n**NEVER return control to the user without ensuring:**\n1. Tests pass: `./gradlew :module-name:jvmTest` (and other platforms if touched)\n2. Code compiles: `./gradlew :module-name:assemble`\n3. Code is formatted and linting passes: run `./gradlew :module-name:formatKotlin`, then `./gradlew :module-name:lintKotlin`\n4. ABI validated: `./gradlew :module-name:updateKotlinAbi` (if public/protected API changed)\n\nPrefer this order to fail fast.\nAlways prefer **module-specific** commands (`:module-name:task`) over project-wide commands.\n\n## Project Requirements\n\n- JDK 21 is required.\n- Kotlin Multiplatform: JVM, JS, Native.\n- Apple targets require Xcode + command line tools.\n- Android targets are optional and enabled when Android SDK is available (`ANDROID_HOME` or `local.properties` `sdk.dir`).\n\n## Project Layout\n\n### Gradle module structure (flattened)\nThe project uses a **flattened Gradle structure** (see `settings.gradle.kts`). Nested directory paths do NOT translate to nested Gradle project names.\nExample: `ktor-client/ktor-client-curl` → `:ktor-client-curl` (not `:ktor-client:ktor-client-curl`).\n\n### Source set layout (platform-centric)\nKotlin Multiplatform sources use a flattened, platform-centric layout (do not re-introduce `src/<platform>Main/...`).\n\n```text\ncommon/src\njvm/src\njvm/resources\njvm/test\njvm/test-resources\n...\n```\n\nBuild logic enforces \"no surprise source sets\": manually registering extra source sets is rejected.\nTo add a new target/source set, add the directory or enable the target via `gradle.properties` (`target.<name>=true`).\n\n## Build Commands\n\n**Important**: Always prefer running tasks on the specific module you touched, not the entire project.\n\n### Common tasks\n```bash\n./gradlew :module-name:assemble          # Build the module\n```\n\nRun tests:\n```bash\n./gradlew :module-name:jvmTest           # Run JVM tests for the module\n./gradlew :module-name:allTests          # Run tests across all platforms for the module\n./gradlew :module-name:jvmTest --tests \"fully.qualified.TestClassName\" # Run a specific test\n./gradlew :module-name:jvmTest --tests \"fully.qualified.TestClassName.methodName\" # Run a specific test method\n```\n\nLinting:\n```bash\n./gradlew :module-name:formatKotlin      # Format the module\n./gradlew :module-name:lintKotlin        # Lint the module\n```\n\n## Code Style Guidelines\n\n### Formatting (EditorConfig is authoritative)\n- Kotlin style: IntelliJ + `KOTLIN_OFFICIAL` (`.editorconfig`).\n- Indent: 4 spaces (JSON/YAML: 2); max line length: 120; end of line: LF.\n- Use `./gradlew :module-name:formatKotlin` rather than manual formatting.\n\n### Imports\n- Star imports are preferred for `io.ktor.*` (configured in `.editorconfig`).\n\n### Naming\n- Follow Kotlin conventions unless the surrounding package has a strong established pattern.\n- Tests: prefer descriptive test names in backticks: `describe what is being tested`.\n\n### Types and API design\n- Prefer `internal` by default; keep the public surface intentional.\n- Public API requires KDoc (parameters, return, and notable exceptions).\n- Public-but-not-for-users APIs that cannot be `internal` should use `@InternalAPI`.\n- Keep `@OptIn(...)` scope minimal.\n- All types used as receivers in DSL should be annotated with `@KtorDsl` (for example, all plugin configs).\n\n### Error handling\n- `require(...)` for argument validation, `check(...)` for state validation, `error(\"...\")` for unreachable states.\n- Throw specific exceptions appropriate to the layer (IO parsing: `IOException`/`EOFException`; validation: Ktor exceptions like `BadRequestException`).\n- Make error messages actionable; include the problematic value/context.\n\n### Logging\n- Prefer Ktor log helpers where present.\n- Avoid noisy logs in hot paths.\n\n### Documentation and comments\n- Avoid redundant comments; add them only for tricky invariants or platform-specific behavior.\n- Keep KDoc correct when behavior/signatures change.\n- New source files must include the repository copyright header.\n\n## Development Practices\n\n- Prefer TDD where feasible: add/adjust tests, then implement.\n- For multiplatform changes: start JVM-first unless the task requires another platform.\n- Keep local-only build knobs (for example, developer `gradle.properties` overrides) out of commits.\n- If asked to create commits: use imperative mood and include `KTOR-<NUM>` when there is a related YouTrack issue.\n\n### Adding or removing modules\n\n- Module names must start with `ktor-`.\n- The project uses a flattened Gradle structure with custom DSL in `settings.gradle.kts` (see Project Layout section).\n- When modules are added/removed or new targets enabled, run `./update-artifact-dumps.sh` to update published artifact lists in `gradle/artifacts`.\n  Publishing will fail if these dumps are stale.\n\n## Binary Compatibility and ABI Validation\n\nBinary compatibility is **enforced** using Kotlin Gradle Plugin ABI validation.\nAll public API changes must be tracked in `/api/` directories within modules.\n\n### Release branches and API policy\n\n- Patch releases are maintained in `release/<major>.x` (for example, v3 uses `release/3.x`, v4 uses `release/4.x`).\n- The next minor release is developed on `main`.\n- Public API changes are allowed only for minor/major releases (typically on `main`), not for patch releases (on `release/<major>.x`).\n- Breaking changes are **only allowed in major version releases**.\n- The repo includes an interactive helper `./switch-base-branch.sh` for switching a feature branch base between `main` and `release/<major>.x`.\n  For agents: use `--dry-run` to print the git commands, then run them after user approval.\n\n### Validation commands\n```bash\n./gradlew :module-name:checkKotlinAbi    # Validate ABI compatibility\n./gradlew :module-name:updateKotlinAbi   # Update ABI signature files after changes\n```\n\n### Rules\n- **All** `public`/`protected` API changes require updating `api/*.api` dumps.\n- API changes must be **intentional and well-documented**.\n\n<!-- File: .claude-plugin/marketplace.json (Tokens: ~300 | Category: marketplace) -->\n# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.claude-plugin/marketplace.json)\n\n<!-- File: .claude-plugin/plugin.json (Tokens: ~300 | Category: plugin-manifest) -->\n# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.claude-plugin/plugin.json)\n\n<!-- File: .cursor/mcp.json (Tokens: ~300 | Category: mcp-config) -->\n# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/ktorio/ktor/HEAD/.cursor/mcp.json)\n\n<!-- File: .cursorrules (kotlin-ktor-development-cursorrules-prompt-file.mdc) (Tokens: ~1939 | Category: cursor-rule) -->\n---\ndescription: \"Cursor rules for Kotlin development with Ktor integration.\"\nglobs: **/*\nalwaysApply: false\n---\n## Instruction to developer: save this file as .cursorrules and place it on the root project directory\n\n## Core Principles\n- Follow **SOLID**, **DRY**, **KISS**, and **YAGNI** principles\n- Adhere to **OWASP** security best practices\n- Break tasks into smallest units and solve problems step-by-step\n\n## Technology Stack\n- **Framework**: Kotlin Ktor with Kotlin 2.1.20+\n- **JDK**: 21 (LTS)\n- **Build**: Gradle with Kotlin DSL\n- **Dependencies**: Ktor Server Core/Netty, kotlinx.serialization, Exposed, HikariCP, kotlin-logging, Koin, Kotest\n\n## Application Structure (Feature-Based)\n- **Organize by business features, not technical layers**\n- Each feature is self-contained with all related components\n- Promotes modularity, reusability, and better team collaboration\n- Makes codebase easier to navigate and maintain\n- Enables parallel development on different features\n```\nsrc/main/kotlin/com/company/app/\n├── common/              # Shared utilities, extensions\n├── config/              # Application configuration, DI\n└── features/\n    ├── auth/            # Feature directory\n    │   ├── models/\n    │   ├── repositories/\n    │   ├── services/\n    │   └── routes/\n    └── users/           # Another feature\n        ├── ...\n```\n\nTest structure mirrors the feature-based organization:\n```\nsrc/test/kotlin/com/company/app/\n├── common/\n└── features/\n    ├── auth/\n    │   ├── models/\n    │   ├── repositories/\n    │   ├── services/\n    │   └── routes/\n    └── users/\n        ├── ...\n```\n\n## Application Logic Design\n1. Route handlers: Handle requests/responses only\n2. Services: Contain business logic, call repositories\n3. Repositories: Handle database operations\n4. Entity classes: Data classes for database models\n5. DTOs: Data transfer between layers\n\n## Entities & Data Classes\n- Use Kotlin data classes with proper validation\n- Define Table objects when using Exposed ORM\n- Use UUID or auto-incrementing integers for IDs\n\n## Repository Pattern\n```kotlin\ninterface UserRepository {\n    suspend fun findById(id: UUID): UserDTO?\n    suspend fun create(user: CreateUserRequest): UserDTO\n    suspend fun update(id: UUID, user: UpdateUserRequest): UserDTO?\n    suspend fun delete(id: UUID): Boolean\n}\n\nclass UserRepositoryImpl : UserRepository {\n    override suspend fun findById(id: UUID): UserDTO? = withContext(Dispatchers.IO) {\n        transaction {\n            Users.select { Users.id eq id }\n                .mapNotNull { it.toUserDTO() }\n                .singleOrNull()\n        }\n    }\n    // Other implementations...\n}\n```\n\n## Service Layer\n```kotlin\ninterface UserService {\n    suspend fun getUserById(id: UUID): UserDTO\n    suspend fun createUser(request: CreateUserRequest): UserDTO\n    suspend fun updateUser(id: UUID, request: UpdateUserRequest): UserDTO\n    suspend fun deleteUser(id: UUID)\n}\n\nclass UserServiceImpl(\n    private val userRepository: UserRepository\n) : UserService {\n    override suspend fun getUserById(id: UUID): UserDTO {\n        return userRepository.findById(id) ?: throw ResourceNotFoundException(\"User\", id.toString())\n    }\n    // Other implementations...\n}\n```\n\n## Route Handlers\n```kotlin\nfun Application.configureUserRoutes(userService: UserService) {\n    routing {\n        route(\"/api/users\") {\n            get(\"/{id}\") {\n                val id = call.parameters[\"id\"]?.let { UUID.fromString(it) }\n                    ?: throw ValidationException(\"Invalid ID format\")\n                val user = userService.getUserById(id)\n                call.respond(ApiResponse(\"SUCCESS\", \"User retrieved\", user))\n            }\n            // Other routes...\n        }\n    }\n}\n```\n\n## Error Handling\n```kotlin\nopen class ApplicationException(\n    message: String,\n    val statusCode: HttpStatusCode = HttpStatusCode.InternalServerError\n) : RuntimeException(message)\n\nclass ResourceNotFoundException(resource: String, id: String) :\n    ApplicationException(\"$resource with ID $id not found\", HttpStatusCode.NotFound)\n\nfun Application.configureExceptions() {\n    install(StatusPages) {\n        exception<ResourceNotFoundException> { call, cause ->\n            call.respond(cause.statusCode, ApiResponse(\"ERROR\", cause.message ?: \"Resource not found\"))\n        }\n        exception<Throwable> { call, cause ->\n            call.respond(HttpStatusCode.InternalServerError, ApiResponse(\"ERROR\", \"An internal error occurred\"))\n        }\n    }\n}\n```\n\n## Testing Strategies and Coverage Requirements\n\n### Test Coverage Requirements\n- **Minimum coverage**: 80% overall code coverage required\n- **Critical components**: 90%+ coverage for repositories, services, and validation\n- **Test all edge cases**: Empty collections, null values, boundary conditions\n- **Test failure paths**: Exception handling, validation errors, timeouts\n- **All public APIs**: Must have integration tests\n- **Performance-critical paths**: Must have benchmarking tests\n\n### Unit Testing with Kotest\n```kotlin\nclass UserServiceTest : DescribeSpec({\n    describe(\"UserService\") {\n        val mockRepository = mockk<UserRepository>()\n        val userService = UserServiceImpl(mockRepository)\n\n        it(\"should return user when exists\") {\n            val userId = UUID.randomUUID()\n            val user = UserDTO(userId.toString(), \"Test User\", \"test@example.com\")\n            coEvery { mockRepository.findById(userId) } returns user\n\n            val result = runBlocking { userService.getUserById(userId) }\n\n            result shouldBe user\n        }\n\n        it(\"should throw exception when user not found\") {\n            val userId = UUID.randomUUID()\n            coEvery { mockRepository.findById(userId) } returns null\n\n            shouldThrow<ResourceNotFoundException> {\n                runBlocking { userService.getUserById(userId) }\n            }\n        }\n    }\n})\n```\n\n## Route Testing with Ktor 3.x\n```kotlin\nclass UserRoutesTest : FunSpec({\n    test(\"GET /api/users/{id} returns 200 when user exists\") {\n        val mockService = mockk<UserService>()\n        val userId = UUID.randomUUID()\n        val user = UserDTO(userId.toString(), \"Test User\", \"test@example.com\")\n\n        coEvery { mockService.getUserById(userId) } returns user\n\n        testApplication {\n            application {\n                configureRouting()\n                configureDI { single { mockService } }\n            }\n\n            client.get(\"/api/users/$userId\").apply {\n                status shouldBe HttpStatusCode.OK\n                bodyAsText().let {\n                    Json.decodeFromString<ApiResponse<UserDTO>>(it)\n                }.data shouldBe user\n            }\n        }\n    }\n})\n```\n\n## Key Principles for Testable Code\n1. **Single Responsibility**: Each method should do one thing well\n2. **Pure Functions**: Same input always produces same output\n3. **Dependency Injection**: Constructor injection for testable components\n4. **Clear Boundaries**: Well-defined inputs and outputs\n5. **Small Methods**: Extract complex logic into testable helper functions\n\n## Configuration Management\n```kotlin\n// Type-safe configuration\ninterface AppConfig {\n    val database: DatabaseConfig\n    val security: SecurityConfig\n}\n\ndata class DatabaseConfig(\n    val driver: String,\n    val url: String,\n    val user: String,\n    val password: String\n)\n\n// Access in application\nfun Application.configureDI() {\n    val appConfig = HoconAppConfig(environment.config)\n\n    install(Koin) {\n        modules(module {\n            single<AppConfig> { appConfig }\n            single { appConfig.database }\n        })\n    }\n}\n```\n\n## Security Best Practices\n```kotlin\nfun Application.configureSecurity() {\n    install(Authentication) {\n        jwt(\"auth-jwt\") {\n            // JWT configuration\n        }\n    }\n\n    install(DefaultHeaders) {\n        header(HttpHeaders.XContentTypeOptions, \"nosniff\")\n        header(HttpHeaders.XFrameOptions, \"DENY\")\n        header(HttpHeaders.ContentSecurityPolicy, \"default-src 'self'\")\n        header(\"Strict-Transport-Security\", \"max-age=31536000; includeSubDomains\")\n    }\n}\n```\n\n## Health Checks & Monitoring\n```kotlin\nfun Application.configureMonitoring() {\n    val startTime = System.currentTimeMillis()\n\n    routing {\n        get(\"/health\") {\n            call.respond(mapOf(\"status\" to \"UP\", \"uptime\" to \"${(System.currentTimeMillis() - startTime) / 1000}s\"))\n        }\n\n        get(\"/metrics\") {\n            call.respond(prometheusRegistry.scrape())\n        }\n    }\n\n    install(MicrometerMetrics) {\n        registry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)\n        meterBinders = listOf(\n            JvmMemoryMetrics(),\n            JvmGcMetrics(),\n            ProcessorMetrics(),\n            JvmThreadMetrics()\n        )\n    }\n}\n```\n\n## Performance Tuning\n- **JVM Settings**: `-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:MaxRAMPercentage=75.0`\n- **Connection Pooling**: Configure HikariCP with proper sizing based on workload\n- **Caching**: Use Caffeine for in-memory caching of frequently accessed data\n- **Coroutines**: Use structured concurrency for asynchronous processing\n- **Database Queries**: Optimize with proper indexing, batch operations, pagination\n</yakaai_skills>"}