<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Atanas Oreshkov</title>
    <description>The latest articles on DEV Community by Atanas Oreshkov (@aoreshkov).</description>
    <link>https://dev.to/aoreshkov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4038812%2F77251568-8253-46d8-854d-233eb8c88569.png</url>
      <title>DEV Community: Atanas Oreshkov</title>
      <link>https://dev.to/aoreshkov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aoreshkov"/>
    <language>en</language>
    <item>
      <title>Give your AI agent the real sources of any Kotlin/Java library</title>
      <dc:creator>Atanas Oreshkov</dc:creator>
      <pubDate>Thu, 23 Jul 2026 20:02:14 +0000</pubDate>
      <link>https://dev.to/aoreshkov/give-your-ai-agent-the-real-sources-of-any-kotlinjava-library-4kol</link>
      <guid>https://dev.to/aoreshkov/give-your-ai-agent-the-real-sources-of-any-kotlinjava-library-4kol</guid>
      <description>&lt;p&gt;Your AI coding assistant will confidently hand you a Kotlin method signature that does not exist. For a strongly-typed ecosystem, "plausible but wrong" is the worst failure mode — it compiles in your head and breaks in the build.&lt;/p&gt;

&lt;p&gt;Here's it reading the &lt;strong&gt;real&lt;/strong&gt; sources instead:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv5bz4208lhuqkmy7su5w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv5bz4208lhuqkmy7su5w.gif" alt=" " width="512" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One prompt. &lt;code&gt;kotlin-lib-mcp&lt;/code&gt; downloaded Ktor's &lt;strong&gt;sources jar&lt;/strong&gt;, parsed it with the Kotlin &lt;strong&gt;Analysis API&lt;/strong&gt; (the same K2/FIR front-end that powers the IDE), and answered from the actual code.&lt;/p&gt;

&lt;h2&gt;
  
  
  One coordinate, ten tools
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fetch_library("io.ktor:ktor-client-core:3.5.1")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That's the whole setup. The server downloads the sources jar, extracts it, analyzes it once, and caches the parsed index on disk. Your agent then has ten tools over it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;list_packages&lt;/code&gt; / &lt;code&gt;list_declarations&lt;/code&gt; — the public API surface&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_api_signature&lt;/code&gt; — a &lt;strong&gt;type-resolved&lt;/strong&gt; signature (generics, nullability, receiver)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_kdoc&lt;/code&gt; — KDoc as structured data, not an HTML page&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_source&lt;/code&gt; / &lt;code&gt;search_source&lt;/code&gt; — the real implementation + bounded search&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_dependencies&lt;/code&gt; — the dependency tree from &lt;code&gt;.pom&lt;/code&gt; / &lt;code&gt;.module&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list_versions&lt;/code&gt; / &lt;code&gt;get_latest_version&lt;/code&gt; — resolve "latest stable" from Maven metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pinned to the &lt;strong&gt;exact version you depend on&lt;/strong&gt;, offline after the first fetch. No drift between "the docs" and the version in your build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "resolved" is the whole point
&lt;/h2&gt;

&lt;p&gt;Regex over source gives you a signature-shaped &lt;em&gt;string&lt;/em&gt;. The Analysis API actually ran the compiler front-end — so you get the resolved type, not a guess. When a transitive dependency is missing it degrades to a best-effort PSI signature and flags it &lt;code&gt;bestEffort: true&lt;/code&gt;, instead of failing. Graceful degradation over an empty answer.&lt;/p&gt;

&lt;p&gt;Two Kotlin-specific things made this more than "unzip a jar":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;KMP sources jars are per-target.&lt;/strong&gt; A Multiplatform library publishes &lt;code&gt;-jvm-sources.jar&lt;/code&gt;, a common one, and so on — resolved via &lt;code&gt;.module&lt;/code&gt; Gradle metadata, with every symbol tagged by target.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Analysis API is version-fragile.&lt;/strong&gt; Standalone mode must be version-locked to the exact Kotlin release, so it's quarantined behind one &lt;code&gt;SourceAnalyzer&lt;/code&gt; interface.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it in ~60 seconds
&lt;/h2&gt;

&lt;p&gt;Docker, no build required:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;claude mcp add kotlin-lib -- docker run -i --rm -v kotlin-lib-mcp-cache:/home/mcp/.cache ghcr.io/aoreshkov/kotlin-lib-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Or grab the release zip (Java 21+), or install from the &lt;strong&gt;official MCP registry&lt;/strong&gt; as &lt;code&gt;io.github.aoreshkov/kotlin-lib-mcp&lt;/code&gt;. Then ask your agent to fetch a library and explain an API — it answers from the code.&lt;/p&gt;

&lt;p&gt;It's also a tidy reference build: all three MCP primitives (tools, &lt;strong&gt;resources&lt;/strong&gt; per cached library + a resource template, and a &lt;strong&gt;prompt&lt;/strong&gt;), plus per-tool behavior annotations, typed &lt;code&gt;outputSchema&lt;/code&gt;, progress notifications, and server→client log forwarding. Built on the current stable MCP spec (&lt;code&gt;2025-11-25&lt;/code&gt;); the 2026 roadmap's stateless core and Tasks/Apps extensions are the natural next step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/aoreshkov/kotlin-lib-mcp" rel="noopener noreferrer"&gt;https://github.com/aoreshkov/kotlin-lib-mcp&lt;/a&gt; — Apache-2.0. Analysis-API edge cases especially welcome.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Enjoyed this? I also wrote about &lt;a href="https://dev.to/aoreshkov/generate-a-production-grade-kotlin-multiplatform-app-compose-room-3-navigation-3-koin-in-one-n6m"&gt;generating a full Kotlin Multiplatform app in one dialog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>kotlin</category>
      <category>mcp</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Generate a production-grade Kotlin Multiplatform app (Compose, Room 3, Navigation 3, Koin) in one dialog</title>
      <dc:creator>Atanas Oreshkov</dc:creator>
      <pubDate>Mon, 20 Jul 2026 20:54:23 +0000</pubDate>
      <link>https://dev.to/aoreshkov/generate-a-production-grade-kotlin-multiplatform-app-compose-room-3-navigation-3-koin-in-one-n6m</link>
      <guid>https://dev.to/aoreshkov/generate-a-production-grade-kotlin-multiplatform-app-compose-room-3-navigation-3-koin-in-one-n6m</guid>
      <description>&lt;h1&gt;
  
  
  Generate a production-grade Kotlin Multiplatform app (Compose, Room 3, Navigation 3, Koin) in one dialog
&lt;/h1&gt;

&lt;p&gt;Starting a Kotlin Multiplatform project has a strange cost curve. The first &lt;code&gt;Hello, world&lt;/code&gt; is cheap — the IDE's built-in wizard gives you a shared module and platform targets in a minute. The next two weeks are where the real setup lives: choosing an architecture, wiring a KMP-ready database, getting type-safe navigation working, setting up DI without service-locator soup, structuring Gradle so the build doesn't rot, and writing enough tests to trust any of it.&lt;/p&gt;

&lt;p&gt;This article walks through one opinionated answer to that setup — the architecture decisions themselves, and why they're baked the way they are. The vehicle is &lt;a href="https://plugins.jetbrains.com/plugin/31786-kmp-project-wizard" rel="noopener noreferrer"&gt;KMP Project Wizard&lt;/a&gt;, an open-source IntelliJ IDEA / Android Studio plugin I built that generates the whole thing from &lt;code&gt;File → New Project&lt;/code&gt;, but every decision below stands on its own — you can apply them by hand to any KMP codebase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft4vvxnhch8hovz8y3nxv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft4vvxnhch8hovz8y3nxv.png" alt=" " width="800" height="682"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The reference-app principle: templates that can't go stale by construction
&lt;/h2&gt;

&lt;p&gt;The most important decision isn't in the generated code at all. The templates aren't hand-authored files that drift out of date — they're generated from &lt;a href="https://github.com/aoreshkov/kmp-ledger" rel="noopener noreferrer"&gt;kmp-ledger&lt;/a&gt;, a real, working, MIT-licensed double-entry ledger app that builds, tests, and runs on all three platforms. At plugin build time, concrete names (&lt;code&gt;app.oreshkov.ledger&lt;/code&gt;, &lt;code&gt;Posting&lt;/code&gt;, &lt;code&gt;Ledger&lt;/code&gt;) are replaced with placeholders; at generation time, your names are substituted back in — package, app name, feature name, entity fields, everywhere including directory names.&lt;/p&gt;

&lt;p&gt;The consequence: if the reference app compiles and its tests pass, so does your generated project. "Template rot" — the classic starter-kit disease — becomes a build failure in &lt;em&gt;my&lt;/em&gt; pipeline instead of a runtime surprise in &lt;em&gt;your&lt;/em&gt; project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture: Clean layers with a feature API/impl split
&lt;/h2&gt;

&lt;p&gt;The generated project is modular from the first commit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domain / Data / UI layers&lt;/strong&gt; per feature, with the dependency arrow always pointing inward — UI and Data both depend on Domain; Domain depends on nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A feature API/impl split&lt;/strong&gt;: each feature exposes a small &lt;code&gt;api&lt;/code&gt; module (public contracts) and keeps its &lt;code&gt;impl&lt;/code&gt; private. Other features depend on &lt;code&gt;api&lt;/code&gt; only, so feature boundaries survive growth and builds stay parallelizable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Binary-compatibility validation&lt;/strong&gt;: the Kotlin BCV plugin's &lt;code&gt;apiDump&lt;/code&gt; snapshots every module's public ABI, and &lt;code&gt;apiCheck&lt;/code&gt; fails the build when a public contract changes accidentally. (A detail I got wrong the first time: ABI dumps are compiler-output snapshots — symbol names, declaration order, and Compose lambda keys derive from &lt;em&gt;your&lt;/em&gt; names, so shipping the reference app's dumps with names substituted can never match. The wizard instead runs &lt;code&gt;apiDump&lt;/code&gt; after your project's first Gradle sync, so the baseline is generated from your code.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The stack, and why each piece
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compose Multiplatform&lt;/strong&gt; for shared UI across Android, iOS, and Desktop — one rendering model, platform entry points kept thin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigation 3&lt;/strong&gt; for type-safe routing: destinations are serializable types, not string routes, so a refactor that breaks navigation breaks the &lt;em&gt;compile&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Room 3&lt;/strong&gt; for persistence — the KMP-ready generation of Room, meaning the same DAO and entity definitions run on all targets, with SQLite under the hood.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Koin + KSP annotations&lt;/strong&gt; for DI: annotation-driven definitions with compile-time processing, no reflective magic on the hot path, and no hand-maintained module lists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coroutines &amp;amp; Flow&lt;/strong&gt; end to end — repositories expose &lt;code&gt;Flow&lt;/code&gt;, use cases suspend, UI collects with lifecycle awareness.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Gradle that scales: convention plugins + version catalog
&lt;/h2&gt;

&lt;p&gt;All build logic lives in convention plugins (&lt;code&gt;build-logic/&lt;/code&gt;), applied by module type — a feature &lt;code&gt;impl&lt;/code&gt; module's build file is a few lines naming its type and dependencies. Versions are centralized in &lt;code&gt;gradle/libs.versions.toml&lt;/code&gt;. Adding a fifteenth module costs the same as adding the second, and a dependency bump is a one-line change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests as part of the template
&lt;/h2&gt;

&lt;p&gt;Every generated project ships a passing suite: unit tests for domain and data (including an in-memory Room setup) and Compose UI tests. Not because sample tests are valuable in themselves, but because a testing &lt;em&gt;pattern&lt;/em&gt; you can copy beats a blank &lt;code&gt;src/test&lt;/code&gt; directory every time you add a feature.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F32j6szsw1ir6tap3hof9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F32j6szsw1ir6tap3hof9.png" alt=" " width="799" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the one dialog actually asks
&lt;/h2&gt;

&lt;p&gt;Package name, app name, one feature name, one entity with a test value, and target platforms (Android / iOS / Desktop). From that it derives every identifier — PascalCase types, camelCase members, snake_case resources — and renders the whole tree with your names, then triggers Gradle sync and generates the ABI baselines.&lt;/p&gt;

&lt;p&gt;The plugin's core is free (all three platforms, the full architecture and tests). A paid Pro tier adds two things to the generated project: Claude Code agent config (&lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;.claude/&lt;/code&gt; skills) and GitHub Actions CI (&lt;code&gt;.github/&lt;/code&gt;) — flat files, no runtime services, and the same content you can inspect in kmp-ledger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Plugin: &lt;a href="https://plugins.jetbrains.com/plugin/31786-kmp-project-wizard" rel="noopener noreferrer"&gt;https://plugins.jetbrains.com/plugin/31786-kmp-project-wizard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Plugin source (MIT): &lt;a href="https://github.com/aoreshkov/kmp-wizard" rel="noopener noreferrer"&gt;https://github.com/aoreshkov/kmp-wizard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Reference app the templates come from: &lt;a href="https://github.com/aoreshkov/kmp-ledger" rel="noopener noreferrer"&gt;https://github.com/aoreshkov/kmp-ledger&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it, I'd genuinely value feedback — the issue tracker is open, and the architecture opinions above are exactly the kind of thing worth arguing about.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>kmp</category>
      <category>android</category>
      <category>ios</category>
    </item>
  </channel>
</rss>
