From bde2323d9e295cf140a2d1047786018584abf252 Mon Sep 17 00:00:00 2001 From: Shahin Saadati Date: Thu, 20 Aug 2026 09:11:32 -0700 Subject: [PATCH 1/3] Add Kotlin tabs to the Skills page The Skills page documented Python, TypeScript and Go but not Kotlin, even though SkillToolset has existed since adk-kotlin v0.1.0. This was missed because every earlier coverage audit diffed one release tag against the next, so symbols that already existed at v0.1.0 were never checked. Kotlin's shape differs from Python's in two ways the tabs need to show. SkillToolset takes a single SkillSource rather than a list of loaded skills, so NewFileSystemSource discovers every skill under a base directory instead of loading them one by one. And like ADK Go, Kotlin ships no built-in source for skills defined in code, so the inline-skills tab implements SkillSource directly rather than pretending a Python-style model class exists. --- docs/skills/index.md | 29 +++- .../kotlin/snippets/skills/SkillsExample.kt | 133 ++++++++++++++++++ tools/kotlin-snippets/files_to_test.txt | 1 + 3 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 examples/kotlin/snippets/skills/SkillsExample.kt diff --git a/docs/skills/index.md b/docs/skills/index.md index 9143a7dad4..45778a7d6c 100644 --- a/docs/skills/index.md +++ b/docs/skills/index.md @@ -1,7 +1,7 @@ # Skills for ADK agents
- Supported in ADKPython v1.25.0TypeScript v0.6.1Go v1.2.0Experimental + Supported in ADKPython v1.25.0TypeScript v0.6.1Go v1.2.0Kotlin v0.8.0Experimental
An agent ***Skill*** is a self-contained unit of functionality that an ADK agent @@ -101,6 +101,15 @@ You can define [skills in code](#inline-skills) or load For a complete example, see the code sample in [skills](https://github.com/google/adk-go/tree/main/examples/skills). +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/skills/SkillsExample.kt:get_started" + ``` + + For a complete example, see the code sample in + [skills](https://github.com/google/adk-kotlin/tree/main/examples/src/main/kotlin/com/google/adk/kt/examples/skills). + !!! note "Check your working directory" Ensure that 'skills/' directory exist in your current working directory and contains the sub-directories for the Skills you want to use in your agent. @@ -288,6 +297,17 @@ You can define Skills within the code of your agent, as shown below. } ``` +=== "Kotlin" + + !!! note + ADK Kotlin does not currently provide a standard Source for inline skills. + To define skills directly in code, you must implement the `SkillSource` + interface yourself, as shown below. + + ```kotlin + --8<-- "examples/kotlin/snippets/skills/SkillsExample.kt:inline_skill" + ``` + !!! note The `Source` interface can be backed by any data store (such as a database) to support dynamic use cases like live updates and personalization. @@ -343,6 +363,12 @@ You can define Skills within the code of your agent, as shown below. } ``` +=== "Kotlin" + + ```kotlin + --8<-- "examples/kotlin/snippets/skills/SkillsExample.kt:filesystem_skill" + ``` + ## Skill processing and validation When you include skills in your agent, the agent uses a standardized process @@ -356,4 +382,5 @@ Check out these resources for building agents with Skills: - [Skills in Python - code sample](https://github.com/google/adk-python/tree/main/contributing/samples/environment_and_skills/skills_agent) - [Skills in Go - code sample](https://github.com/google/adk-go/tree/main/examples/skills) +- [Skills in Kotlin - code sample](https://github.com/google/adk-kotlin/tree/main/examples/src/main/kotlin/com/google/adk/kt/examples/skills) - Agent Skills [specification documentation](https://agentskills.io/) diff --git a/examples/kotlin/snippets/skills/SkillsExample.kt b/examples/kotlin/snippets/skills/SkillsExample.kt new file mode 100644 index 0000000000..5d4e0d9bd9 --- /dev/null +++ b/examples/kotlin/snippets/skills/SkillsExample.kt @@ -0,0 +1,133 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.adk.kt.examples.skills + +import com.google.adk.kt.agents.Instruction +import com.google.adk.kt.agents.LlmAgent +import com.google.adk.kt.models.Gemini +import com.google.adk.kt.skills.Frontmatter +import com.google.adk.kt.skills.NewFileSystemSource +import com.google.adk.kt.skills.SkillSource +import com.google.adk.kt.skills.SkillSourceException +import com.google.adk.kt.tools.SkillToolset + +// --8<-- [start:get_started] +// NewFileSystemSource discovers every skill directory under the base directory, +// so there is no per-skill load call. +val mySkillToolset = SkillToolset(NewFileSystemSource("skills")) + +val skillUserAgent = + LlmAgent( + name = "skill_user_agent", + model = Gemini(name = "gemini-flash-latest"), + description = "An agent that can use specialized skills.", + instruction = + Instruction("You are a helpful assistant that can leverage skills to perform tasks."), + // A SkillToolset contributes only the skill tools. Any other tool the agent + // needs is passed separately in `tools`. + toolsets = listOf(mySkillToolset), + ) +// --8<-- [end:get_started] + +// --8<-- [start:inline_skill] + +/** + * ADK Kotlin does not provide a standard [SkillSource] for skills defined in code, so implement the + * interface yourself to serve them from memory. + */ +class StaticSkillSource : SkillSource { + private val greetingSkill = + Frontmatter( + name = "greeting-skill", + description = "A friendly greeting skill that can say hello to a specific person.", + ) + + private val instructions = + "Step 1: Read the 'references/hello_world.txt' file to understand how to greet the " + + "user. Step 2: Return a greeting based on the reference." + + private val resources = + mapOf( + "references/hello_world.txt" to "Hello! So glad to have you here!", + "references/example.md" to "This is an example reference.", + ) + + private fun notFound(skillName: String) = SkillSourceException("Skill $skillName not found.") + + override suspend fun listFrontmatters(): Result> = + Result.success(listOf(greetingSkill)) + + override suspend fun loadFrontmatter(skillName: String): Result = + if (skillName == greetingSkill.name) { + Result.success(greetingSkill) + } else { + Result.failure(notFound(skillName)) + } + + override suspend fun loadInstructions(skillName: String): Result = + if (skillName == greetingSkill.name) { + Result.success(instructions) + } else { + Result.failure(notFound(skillName)) + } + + override suspend fun listResources( + skillName: String, + resourceDirectoryPath: String, + ): Result> { + if (skillName != greetingSkill.name) return Result.failure(notFound(skillName)) + val prefix = resourceDirectoryPath.removePrefix("./").removeSuffix("/") + if (prefix.isEmpty() || prefix == ".") return Result.success(resources.keys.toList()) + // Skill resources live only under references/, assets/ and scripts/. + if (prefix.substringBefore("/") !in SkillSource.VALID_RESOURCE_DIRS) { + return Result.failure( + SkillSourceException("Invalid resource path: $resourceDirectoryPath"), + ) + } + return Result.success(resources.keys.filter { it.startsWith("$prefix/") }) + } + + override suspend fun loadResource( + skillName: String, + resourcePath: String, + ): Result { + if (skillName != greetingSkill.name) return Result.failure(notFound(skillName)) + val content = + resources[resourcePath] + ?: return Result.failure( + SkillSourceException("Resource $resourcePath not found in skill $skillName."), + ) + return Result.success(content.encodeToByteArray()) + } +} + +val inlineSkillAgent = + LlmAgent( + name = "greeting_agent", + model = Gemini(name = "gemini-flash-latest"), + instruction = Instruction("Greet the user by following the greeting skill."), + toolsets = listOf(SkillToolset(StaticSkillSource())), + ) +// --8<-- [end:inline_skill] + +// --8<-- [start:filesystem_skill] +// Every immediate subdirectory of "skills" that contains a SKILL.md is exposed as +// a skill, so individual skills are discovered rather than named one by one. +val filesystemSource = NewFileSystemSource("skills") + +val filesystemSkillToolset = SkillToolset(filesystemSource) +// --8<-- [end:filesystem_skill] diff --git a/tools/kotlin-snippets/files_to_test.txt b/tools/kotlin-snippets/files_to_test.txt index b1902c362c..0c16697d27 100644 --- a/tools/kotlin-snippets/files_to_test.txt +++ b/tools/kotlin-snippets/files_to_test.txt @@ -40,3 +40,4 @@ snippets/tools/overview/UserPreferenceTools.kt snippets/tools/overview/CustomerSupport.kt snippets/tools/overview/DocAnalysisTools.kt snippets/tools/overview/OrderTools.kt +snippets/skills/SkillsExample.kt From 9b8216daafe6ce8d1d3b0d741373885969ef3aff Mon Sep 17 00:00:00 2001 From: Shahin Saadati Date: Thu, 20 Aug 2026 11:21:34 -0700 Subject: [PATCH 2/3] Badge the Skills page with the version SkillToolset shipped in The badge said Kotlin v0.8.0, which is the version adk-docs compiles against, not the version the feature landed in. Every other badge on the page and across the site names the introducing release - SkillToolset has been present since v0.1.0. --- docs/skills/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/skills/index.md b/docs/skills/index.md index 45778a7d6c..f297287c20 100644 --- a/docs/skills/index.md +++ b/docs/skills/index.md @@ -1,7 +1,7 @@ # Skills for ADK agents
- Supported in ADKPython v1.25.0TypeScript v0.6.1Go v1.2.0Kotlin v0.8.0Experimental + Supported in ADKPython v1.25.0TypeScript v0.6.1Go v1.2.0Kotlin v0.1.0Experimental
An agent ***Skill*** is a self-contained unit of functionality that an ADK agent From 23c48b7da38f3a5a4224a24feb075e12f37470b9 Mon Sep 17 00:00:00 2001 From: Shahin Saadati Date: Thu, 20 Aug 2026 11:23:06 -0700 Subject: [PATCH 3/3] Point Kotlin readers at the adk-kotlin repo for Skills feedback The Experimental callout invites feedback per SDK but listed only Python, TypeScript and Go, which is now inconsistent with the Kotlin badge this branch adds. The link has no template parameter, unlike its three siblings, because adk-kotlin has no issue templates - its .github directory holds only workflows, so ?template=feature_request.md would silently fall back to a blank issue. --- docs/skills/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/skills/index.md b/docs/skills/index.md index f297287c20..1e9d3dcc84 100644 --- a/docs/skills/index.md +++ b/docs/skills/index.md @@ -17,7 +17,8 @@ impact on the operating context window of the agent. respective ADK GitHub repositories: [ADK Python](https://github.com/google/adk-python/issues/new?template=feature_request.md&labels=skills), [ADK TypeScript](https://github.com/google/adk-js/issues/new?template=feature_request.md&labels=skills), - [ADK Go](https://github.com/google/adk-go/issues/new?template=feature_request.md&labels=skills). + [ADK Go](https://github.com/google/adk-go/issues/new?template=feature_request.md&labels=skills), + [ADK Kotlin](https://github.com/google/adk-kotlin/issues/new). ## Get started