diff --git a/AGENTS.md b/AGENTS.md index 1646c55..6ef38be 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,334 +1,82 @@ # AGENTS: Development Guidelines for AI Contributors -This document is for autonomous agents and AI copilots contributing code to this repository. -Follow these rules to keep changes safe, comprehensible, and easy to maintain. +This file provides guidance to AI coding agents working with code in this repository. -## Repository Layout +## Project -- `kotlin-sdk-core`: Core protocol types, transport abstractions, serialization utilities -- `kotlin-sdk-client`: Client transports (STDIO, SSE, StreamableHttp, WebSocket) -- `kotlin-sdk-server`: Server transports + Ktor integration (STDIO, SSE, WebSocket) -- `kotlin-sdk`: Umbrella module that aggregates all three above -- `test-utils`: Shared test utilities -- `integration-test`: Integration tests with TypeScript/other implementations -- `conformance-test`: Protocol conformance tests -- `samples/`: Sample projects (composite builds) -- Package structure: `io.modelcontextprotocol.kotlin.sdk.*` -- JVM 11+, Kotlin 2.2+ +Kotlin Multiplatform SDK for the [Model Context Protocol (MCP)](https://modelcontextprotocol.io). Targets JVM, Native (macOS/Linux/Windows), JS, and Wasm. Implements both MCP clients and servers with pluggable transports. -## Prime Directives - -1. **Tests first, always.** - - Before changing code, identify or add tests that express the desired behavior. - - Prefer readable, minimal tests over clever ones. Tests are documentation. -2. **Keep tests simple and explicit.** - - Arrange/Act/Assert structure; avoid hidden magic and overuse of helpers. - - Prefer concrete inputs/outputs; avoid randomness and time dependence. -3. **Uphold SOLID principles** in production code: - - Single Responsibility: each class/function should do one thing well. - - Open/Closed: extend via new code, avoid risky edits to stable code paths. - - Liskov Substitution: honor contracts; keep types substitutable. - - Interface Segregation: keep abstractions small and focused. - - Dependency Inversion: depend on abstractions, not concretions. -4. **Make the minimal change** that satisfies the tests and the issue. - Stay focused on a single concern and avoid "drive-by" changes. -5. **Never commit changes!** It's human who should verify and commit to git. -6. **Keep the build green.** Do not merge changes that break existing tests. -7. **Prefer clarity** over micro-optimizations and cleverness. -8. **Ask when uncertain.** If requirements are ambiguous, request clarification with a concise question. -9. **Write code with the quality of a Kotlin Champion.** -10. **Use MCP servers** like `jetbrains` and `intellij-index` to explore the codebase, for code navigation, analysis, - refactoring, and running tests. -11. **Use the Bash tool for terminal commands** instead of MCP terminal execution features. -12. **Suggest updates** to AGENTS.md/CLAUDE.md with best practices and guidelines. - -## Code Style - -### Kotlin - -- Avoid changing public API signatures. Run `./gradlew apiCheck` before every commit. -- **Explicit API mode** is strict: all public APIs must have explicit visibility modifiers and return types. -- Anything under an `internal` directory is not part of the public API and may change freely. -- Package structure follows: `io.modelcontextprotocol.kotlin.sdk.*` -- Follow Kotlin coding conventions -- Use the provided `.editorconfig` for consistent formatting -- Use Kotlin typesafe DSL builders where possible; prioritize fluent builder style over standard builder methods -- Prefer DSL builder style (method with lambda blocks) over constructors, if possible -- Use `val` for immutable properties and `var` for mutable; consider `lateinit var` instead of nullable types when - appropriate -- Use multi-dollar interpolation prefix for strings where applicable -- Use fully qualified imports instead of star imports -- Ensure backward compatibility when making changes - -## Testing Guidance - -### Test Organization - -- **Location**: `src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/` -- **Platform-specific**: `src/jvmTest/`, `src/jsTest/`, etc. -- **Naming**: Use function `Names with backticks`, e.g., `fun \`should return 200 OK\`()` -- **Structure**: Arrange/Act/Assert pattern -- **Documentation**: Keep tests self-documenting; don't add KDocs to tests. - -### Test Principles - -- Write comprehensive tests for new features. Always add tests for new features or bug fixes, even if not explicitly - requested. -- **Prioritize test readability** -- Avoid creating too many test methods; use parametrized tests when testing multiple similar scenarios -- When running tests on Kotlin Multiplatform projects, run JVM tests only unless asked for other platforms - -### Test Framework Stack - -- **kotlin-test**: Core test framework -- **Kotest assertions**: Use infix style (`shouldBe`, `shouldContain`) instead of `assertEquals`. - - Use `shouldMatchJson` from kotest-assertions-json for JSON validation. - - For nullable objects with nested properties, prefer `shouldNotBeNull { ... }` blocks: - ```kotlin - content.annotations shouldNotBeNull { - priority shouldBe 1.0 - } - ``` -- **mockk**: Mocking library -- **Ktor MockEngine**: For HTTP client mocking (`io.ktor:ktor-client-mock`) -- **Java tests**: Use JUnit5, Mockito, AssertJ core -- **Serialization test utilities** (`io.modelcontextprotocol.kotlin.test.utils`): - - `verifySerialization(value, json, expectedJson)` — serializes, asserts match, round-trips back; use for most serialization tests - - `verifyDeserialization(json, payload)` — deserializes from JSON, re-serializes, asserts match; returns the object for further assertions - - Always test both empty/null/omitted and non-null cases for nullable fields; `McpJson` has `explicitNulls = false` so null properties must be absent from JSON, not `null` - -### Kotest Patterns - -- Basic assertions: Use infix style (`shouldBe`, `shouldContain`) instead of `assertEquals`. - ```kotlin - result shouldBe expected - list shouldContain item - ``` -- Nullable Assertions - ```kotlin - // Preferred: lambda-style for multiple assertions - content.annotations shouldNotBeNull { - priority shouldBe 1.0 - } - ``` - -- JSON Assertions (kotest-assertions-json): - ```kotlin - // language=json - val expected = """ - { - "id": 123 - } - """.trimIndent() - actual shouldEqualJson expected - ``` - -- Soft Assertions - ```kotlin - // Use for multiple assertions on SAME subject - assertSoftly(subject) { - name shouldBe "test" - value shouldBe 42 - } - // DON'T use for different subjects or single assertions - ``` -- Clues (Use Sparingly) - ```kotlin - // Only when assertion is NOT obvious - withClue("Failed to parse response") { - result shouldNotBe null - } - ``` - -#### Avoiding `this.` in Lambda Blocks - -```kotlin -// Avoid explicit `this.` unless necessary for disambiguation -prop.shouldNotBeNull { - enum shouldHaveSize 2 // Preferred - this.enum shouldHaveSize 2 // Avoid -} -``` - -### Multiplatform Patterns - -- Use `expect`/`actual` pattern for platform-specific implementations in `utils.*` files. -- Test changes on JVM first, then verify platform-specific behavior if needed. -- Supported targets: JVM (11+), JS/Wasm, iOS, watchOS, tvOS. - -## Coding Guidance - -### Module Boundaries - -- Respect separation of concerns between modules -- Keep abstractions framework-agnostic -- Avoid leaking implementation details across module boundaries - -### Serialization - -- Use Kotlinx Serialization with explicit `@Serializable` annotations -- JSON config is defined in `jsonUtils.kt` as `McpJson` — use it consistently -- Register custom serializers in companion objects - -### Error Handling - -- Use sealed classes for result states -- Map errors to JSON-RPC error codes in protocol handlers -- Log errors using `io.github.oshai.kotlinlogging.KotlinLogging` -- Never log sensitive data (credentials, tokens) - -### Code Quality - -- Handle nullability and references as established patterns -- Keep changes small and reversible; prefer adding functions over editing many call sites -- Add focused comments **only** where intent is non-obvious -- Use `git mv` command when moving version-controlled files -- Never commit or push changes. This is a Human responsibility. - -## Workflow for AI Agents - -1. **Understand the issue** - - Summarize requirement in 1-2 sentences - - Identify affected files and tests; ask if unclear - -2. **Plan minimal changes** - - Use TaskCreate/TaskUpdate to track work, or EnterPlanMode for complex changes - - Document affected files and approach - -3. **Work with code** - - View, edit, and run tests through MCP tools - -4. **Start with tests** - - Update existing or add new tests in relevant modules - -5. **Implement the change** - - Prefer composition and small functions - - Avoid breaking public APIs without tests and discussion - -6. **Run tests locally** - ```bash - ./gradlew test - ./gradlew :kotlin-sdk-core:test # Specific module - ./gradlew :integration-test:test # Integration tests - ``` - -7. **Verify results** - - Use Kotest JSON matchers for schema validation - - Double-check the readability of code and tests - -8. **Document key decisions** - - Brief notes in markdown document, if requested - -## Build and Run Commands +## Build Commands ```bash -# Build all modules -./gradlew build - -# Run all tests -./gradlew test - -# Run specific module tests -./gradlew :kotlin-sdk-core:test -./gradlew :kotlin-sdk-client:test -./gradlew :kotlin-sdk-server:test - -# Check API compatibility (required before commit) -./gradlew apiCheck - -# Update API dump (after intentional API changes) -./gradlew apiDump - -# Platform-specific tests -./gradlew jsTest -./gradlew wasmJsTest +./gradlew ktlintCheck # Lint check +./gradlew detekt # Static analysis +./gradlew ktlintFormat # Auto-fix lint issues +./gradlew apiCheck # Check public API compatibility (run before committing) +./gradlew apiDump # Update API dump after intentional API changes +./gradlew koverLog # Print coverage summary +./gradlew koverXmlReport # Generate XML coverage report ``` -## Definition of done +Run a single test class: +```bash +./gradlew :kotlin-sdk-server:jvmTest --tests "io.modelcontextprotocol.kotlin.sdk.server.StdioServerTransportTest" +``` -- Tests for affected functionality exist -- All tests pass across all targets -- Changes are focused on a single concern. -- Code respects module boundaries -- `./gradlew apiCheck` passes -- Documentation updated for user-facing changes +Run a single test method: +```bash +./gradlew :kotlin-sdk-core:jvmTest --tests "*.ProtocolTest.should preserve existing meta when adding progress token" +``` -## Documentation +Conformance tests (requires Node.js + npx): +```bash +./conformance-test/run-conformance.sh all # All suites sequentially +./conformance-test/run-conformance.sh server # Server suite only +./conformance-test/run-conformance.sh client # Client core suite (initialize, tools_call, elicitation, sse-retry) +./conformance-test/run-conformance.sh client-auth # Client auth suite (20 OAuth scenarios) +``` -### General Guidelines +## Module Structure -- **Never make up documentation/facts** -- Update README files when adding new features -- Document API changes in the appropriate module -- **Keep documentation concise and straight to the point** -- Always verify documentation matches code; fix discrepancies -- Write tutorials in Markdown format in README.md +``` +kotlin-sdk-core Core: protocol types, JSON-RPC, Transport abstraction, Protocol base class +kotlin-sdk-client Client: MCP client implementation (tools, prompts, resources, sampling) +kotlin-sdk-server Server: MCP server implementation (feature registries, sessions) +kotlin-sdk-testing ChannelTransport for in-memory client-server testing +kotlin-sdk Umbrella module aggregating client + server +test-utils Shared test helpers (mockk, kotest, awaitility) +conformance-test MCP specification compliance tests (external runner) +integration-test End-to-end tests +samples/ Runnable examples (servers and clients) +``` -### KDoc Guidelines +## Architecture -- Properly document interfaces and abstract classes in production code -- Avoid KDocs on override functions to reduce verbosity -- Update KDocs when API changes -- Use references: `[SendMessageRequest]` instead of `SendMessageRequest` -- Add brief code examples to KDocs -- Add links to specifications with verified URLs; never add broken links +Layered design: -### Module.md Files for Dokka +``` +Application (tools, prompts, resources) + → Client / Server (typed MCP operations, capability enforcement) + → Protocol (JSON-RPC framing, request correlation, timeouts) + → Transport (pluggable I/O: stdio, SSE, Streamable HTTP, WebSocket) +``` -Each module must have a `Module.md` file at the module root for Dokka-generated API documentation. +## Code Conventions -**Format Requirements** (per [Dokka specification](https://kotlinlang.org/docs/dokka-module-and-package-docs.html)): +- **Explicit API mode (Strict)**: all public declarations must have explicit visibility modifiers and return types +- **DSL builders**: prefer DSL builder style (lambda blocks) over direct constructors +- **Multi-dollar interpolation**: use `$$"""..."""` for strings containing literal `$` (e.g., JSON with `$ref`, `$defs`) +- **`McpJson` has `explicitNulls = false`**: null properties must be absent from JSON, not `"field": null` +- **Logging**: use `io.github.oshai.kotlinlogging.KotlinLogging` +- **Structured concurrency**: every coroutine must have a bounded scope and lifetime; always re-throw `CancellationException`; never hold `Mutex` across suspension points -- Must start with `# Module module-name` (level 1 header) -- Package documentation uses `# Package package.name` (level 1 header) -- All other content uses level 2+ headers (`##`, `###`, etc.) +## Testing -**Content Guidelines**: +- **JVM first**: run `jvmTest` instead of `test` — full multiplatform test runs are slow and rarely needed during development +- **Test naming**: use backtick syntax — `fun \`should return 200 OK\`()` +- **Frameworks**: JUnit 5 (runner) + Kotest (assertions) + MockK (mocking) + Awaitility (async waits) +- **In-memory testing**: `ChannelTransport.createLinkedPair()` connects client and server without network -- **Purpose**: Provide module-level overview in generated API docs -- **Detail level**: API documentation style (concise, focused on classes/interfaces) -- **Module section**: - - Brief module description (1-2 sentences) - - Platform support (format: "Multiplatform • Kotlin 2.2+" or "JVM only • Kotlin 2.2+") - - Key classes/components (use Dokka references like `[ClassName]`) - - Example (minimal, demonstrating primary usage) -- **Package sections**: Use `# Package package.name` headers with brief description -- **Optional sections**: Features, Limitations, Related Specifications -- **Style**: - - Use bullet points over prose - - Keep examples short (5-15 lines) - - Reference related modules using Dokka links - - Avoid duplicating README content +## Skills -**Example structure (markdown)**: - - # Module module-name - - Brief description. - - **Platform Support:** Multiplatform • Kotlin 2.2+ - - ## Key Classes - - - [MainClass] - what it does - - [HelperClass] - what it does - - ## Example - - ```kotlin - val example = MainClass() - ``` - - # Package package.name - - Package description. - - # Package another.package - - Another package description. - -## When to Ask for Help - -- Requirements conflict with existing tests or documentation -- The smallest change still requires altering core abstractions -- Unclear expected schema shape for a new feature—ask for a concrete, readable test case to anchor implementation +- `/mcp-docs` — fetches live MCP specification and docs; use when implementing or debugging MCP protocol features diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 43c994c..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -@AGENTS.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file