How to Load Agent Skills from URLs or Community Contributions in AI Edge Gallery
The AI Edge Gallery app supports three distinct methods to load Agent Skills: fetching directly from approved URLs via SkillManagerViewModel.validateAndAddSkillFromUrl(), importing from a curated community allowlist defined in SkillAllowlist, or importing from local device storage, with all paths converging on parsing SKILL.md metadata into protocol buffer objects persisted through DataStore.
The google-ai-edge/gallery repository implements a modular skill system that allows users to extend on-device LLM capabilities without rebuilding the app. To load Agent Skills from URLs or community contributions, the codebase provides dedicated ViewModel classes and UI components that handle validation, security checks, and persistence.
Agent Skill Architecture and Data Model
Every Agent Skill is defined by a SKILL.md file that follows a strict front-matter schema containing name, description, and optional metadata. According to the source code in Android/src/app/src/main/proto/skill.proto, this markdown is parsed into a Skill protobuf object (com.google.ai.edge.gallery.proto.Skill).
The application persists skills using a DataStore<Skills> implementation configured in AppModule.provideSkillsDataStore. The SkillsSerializer implements kotlinx.serialization.Serializer<Skills> to handle protobuf read/write operations to the app's internal storage, ensuring skills survive process restarts.
Loading Skills from a URL
The primary entry point for remote skill loading is SkillManagerViewModel.validateAndAddSkillFromUrl(). This method coordinates URL normalization, markdown fetching, and proto conversion.
URL Normalization and Security Checks
Before fetching content, the app validates the host against an allowlist defined in AddSkillFromUrlDialog.kt. The APPROVED_SKILL_HOSTS set currently includes google-ai-edge.github.io, and the isHostApproved() function normalizes URLs using java.net.URI to prevent malicious redirects.
The Fetching and Parsing Flow
When validateAndAddSkillFromUrl() executes, it performs the following operations:
- Normalizes the user-provided URL and appends
/SKILL.md - Fetches the markdown content using
java.net.URL.openConnection() - Parses the markdown via
convertSkillMdToProto()withbuiltIn = falseandselected = trueflags - Persists the resulting proto through
DataStoreRepository.setSkills()
// Inside SkillManagerViewModel.validateAndAddSkillFromUrl()
val skillMdUrl = "$normalizedUrl/SKILL.md"
val mdContent = URL(skillMdUrl).openConnection()
.getInputStream().reader().readText()
val (skillProto, errors) = convertSkillMdToProto(
mdContent,
builtIn = false,
selected = true,
skillUrl = normalizedUrl
)
addSkill(skill = skillProto, addToDataStore = true)
The method updates the in-memory UI state (_uiState) and triggers persistence, making the skill immediately available for agent chat sessions.
Importing Featured Community Skills
For curated community contributions, the app implements an allowlist system centered on SkillAllowlist data classes found in Android/src/app/src/main/java/com/google/ai/edge/gallery/data/SkillAllowlist.kt.
The Allowlist System
On application startup, SkillManagerViewModel.loadSkillAllowlist() downloads a JSON file from SKILL_ALLOWLIST_URL and deserializes it into a list of AllowedSkill objects. The JSON schema mirrors the SkillAllowlist data class structure, enabling type-safe parsing of community-vetted contributions.
The UI layer in AddSkillFromFeaturedListBottomSheet.kt exposes these entries through uiState.featuredSkills, presenting users with a curated selection of third-party capabilities.
Adding Featured Skills
When a user selects a featured skill, SkillManagerViewModel.addSkillFromFeatured() executes:
- Downloads the skill's remote assets (HTML/JS scripts)
- Copies files into the private
files/skills/directory - Generates and writes a local
SKILL.mdfile - Registers the skill via the same proto conversion path as URL-based loading
@Composable
fun FeaturedSkillPicker(viewModel: SkillManagerViewModel) {
val uiState by viewModel.uiState.collectAsState()
LazyColumn {
items(uiState.featuredSkills) { allowedSkill ->
ListItem(
headlineText = { Text(allowedSkill.name) },
supportingText = { Text(allowedSkill.description) },
trailingContent = {
IconButton(
onClick = { viewModel.addSkillFromFeatured(allowedSkill) }
) {
Icon(Icons.Default.Add, contentDescription = null)
}
}
)
}
}
}
Loading Skills from Local Folders
Users can also import skills from device storage using SkillManagerViewModel.checkLocalSkillExisted(). This flow leverages Android's file picker to select a folder containing SKILL.md and optional scripts/ subdirectories. The selected folder is copied into context.filesDir/skills/ and registered through the standard proto parsing pipeline without requiring network access.
Security Model for External Skills
The implementation includes security measures to mitigate malicious content. Only hosts listed in APPROVED_SKILL_HOSTS are allowed without displaying a disclaimer dialog. By default, this list contains google-ai-edge.github.io as defined in AddSkillFromUrlDialog.kt (lines 60-66). Unknown hosts trigger additional validation warnings before permitting skill installation, ensuring users explicitly approve code from unverified sources.
Summary
- URL-based loading utilizes
SkillManagerViewModel.validateAndAddSkillFromUrl()to fetchSKILL.mdfrom approved hosts, parse it viaconvertSkillMdToProto(), and persist throughDataStoreRepository. - Community contributions are managed through
SkillAllowlistJSON deserialization inloadSkillAllowlist()and installed viaaddSkillFromFeatured()into the privatefiles/skills/directory. - Local imports copy user-selected folders into internal storage using
checkLocalSkillExisted()and follow identical proto registration flows. - Security validation enforces host allowlists through
APPROVED_SKILL_HOSTSchecks inAddSkillFromUrlDialog.ktbefore permitting external skill execution.
Frequently Asked Questions
What file format defines an Agent Skill?
Agent Skills are defined by a SKILL.md file located at the root of the skill directory. This markdown file contains YAML front-matter specifying the skill name, description, and optional metadata. The file is parsed into a protocol buffer object defined in Android/src/app/src/main/proto/skill.proto using the convertSkillMdToProto() utility function.
How does the app verify that a URL is safe for loading skills?
The app validates URLs against an APPROVED_SKILL_HOSTS list implemented in AddSkillFromUrlDialog.kt. The validation uses java.net.URI normalization to extract and compare hosts, with google-ai-edge.github.io included as a default approved domain. URLs from unapproved hosts require explicit user confirmation before fetching content.
Where are downloaded skill files stored on the device?
Downloaded skill assets are written to the application's private files/skills/ directory within context.filesDir. This location is accessible via the SkillManagerViewModel methods that handle both featured skill downloads and local folder imports, ensuring that skill scripts and metadata remain sandboxed from other applications.
Can I programmatically add a skill without using the UI dialogs?
Yes. Direct programmatic access is available through SkillManagerViewModel.validateAndAddSkillFromUrl(), which accepts a URL string and callback handlers for success and validation error states. This method performs the complete workflow including markdown fetching, proto conversion, and DataStore persistence without requiring interaction with AddSkillFromUrlDialog.kt or AddSkillFromFeaturedListBottomSheet.kt.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →