Skip to content

Extensions on Gemini CLI: Best practices

This guide covers best practices for developing, securing, and maintaining Gemini CLI extensions.

Developing extensions for Gemini CLI is intended to be a lightweight, iterative process.

While simple extensions can just be a few files, we recommend a robust structure for complex extensions:

my-extension/
├── package.json
├── tsconfig.json
├── gemini-extension.json
├── src/
│ ├── index.ts
│ └── tools/
└── dist/
  • Use TypeScript: We strongly recommend using TypeScript for type safety and better tooling.
  • Separate source and build: Keep your source code in src and build to dist.
  • Bundle dependencies: If your extension has many dependencies, consider bundling them (e.g., with esbuild or webpack) to reduce install time and potential conflicts.

Use gemini extensions link to develop locally without constantly reinstalling:

Terminal window
cd my-extension
gemini extensions link .

Changes to your code (after rebuilding) will be immediately available in the CLI on restart.

Your GEMINI.md file provides context to the model. Keep it focused:

  • Do: Explain high-level goals and how to use the provided tools.
  • Don’t: Dump your entire documentation.
  • Do: Use clear, concise language.

When building a Gemini CLI extension, follow general security best practices (such as least privilege and input validation) to reduce risk.

When defining tools in your MCP server, only request the permissions necessary. Avoid giving the model broad access (like full shell access) if a more restricted set of tools will suffice.

If you must use powerful tools like run_shell_command, consider restricting them to specific commands in your gemini-extension.json:

{
"name": "my-safe-extension",
"excludeTools": ["run_shell_command(rm -rf *)"]
}

This ensures that even if the model tries to execute a dangerous command, it will be blocked at the CLI level.

Your MCP server is running on the user’s machine. Always validate inputs to your tools to prevent arbitrary code execution or filesystem access outside the intended scope.

// Good: Validating paths
if (!path.resolve(inputPath).startsWith(path.resolve(allowedDir) + path.sep)) {
throw new Error('Access denied');
}

If your extension requires API keys, use the sensitive: true option in gemini-extension.json. This ensures keys are stored securely in the system keychain and obfuscated in the UI.

"settings": [
{
"name": "API Key",
"envVar": "MY_API_KEY",
"sensitive": true
}
]

You can upload your extension directly to GitHub to list it in the gallery. Gemini CLI extensions also offers support for more complicated releases.

Follow Semantic Versioning.

  • Major: Breaking changes (renaming tools, changing arguments).
  • Minor: New features (new tools, commands).
  • Patch: Bug fixes.

Use git branches to manage release channels (e.g., main for stable, dev for bleeding edge). This allows users to choose their stability level:

Terminal window
# Stable
gemini extensions install github.com/user/repo
# Dev
gemini extensions install github.com/user/repo --ref dev

If you are using GitHub Releases, ensure your release artifacts only contain the necessary files (dist/, gemini-extension.json, package.json). Exclude node_modules (users will install them) and src/ to keep downloads small.