README
¶
gmn
gmn is a CLI for generating various things with Google Gemini API, built with Golang.
Basically, generating texts using prompts and/or files is possible.
If the given prompt includes URLs, it can also fetch the contents of the URLs and use them to generate text.
With a few more flags, it can also generate images or speeches.
Additionally, it can cache, list, and delete contexts for later use.
Build / Install
$ go install github.com/meinside/gmn@latest
Configure
Using Config File with Gemini API key
Create config.json file in $XDG_CONFIG_HOME/gmn/ or $HOME/.config/gmn/:
$ mkdir -p ~/.config/gmn
$ $EDITOR ~/.config/gmn/config.json
with following content:
{
"google_ai_api_key": "ABCDEFGHIJK1234567890",
}
and replace things with your own values.
You can get the sample config file here, and your Gemini API key here.
Using Config File with Google Credentials File
Put the path of your Google Credentials file and the name of your Google Cloud Storage bucket name like:
{
"google_credentials_filepath": "~/.config/gcp/credentials-12345-abcdefg9876.json",
"gcs_bucket_name_for_file_uploads": "some-bucket-name",
}
Using Config File with Infisical and Gemini API key
You can use Infisical for saving & retrieving your Gemini API key:
{
"infisical": {
"client_id": "012345-abcdefg-987654321",
"client_secret": "aAbBcCdDeEfFgG0123456789xyzwXYZW",
"project_id": "012345abcdefg",
"environment": "dev",
"secret_type": "shared",
"google_ai_api_key_key_path": "/path/to/your/KEY_TO_GOOGLE_AI_API_KEY",
},
}
Using Environment Variables
Or, you can run with environment variables (but without config file) like:
# with your Gemini API key,
$ GEMINI_API_KEY="ABCDEFGHIJK1234567890" gmn -p "hello"
# or with your Google Credentials file,
$ CREDENTIALS_FILEPATH="/path/to/credentials.json" LOCATION="global" BUCKET="some-bucket-name" gmn -p "hi"
Run
You can see help messages with -h or --help parameter:
$ gmn -h
and list models with their token limits and supported actions with -l or --list-models:
$ gmn --list-models
Generate Text
You can generate text with:
# generate with a specific model,
$ gmn -m "gemini-2.0-flash-001" -p "hello"
# or with the default/configured one:
# generate with a text prompt
$ gmn -p "what is the answer to life, the universe, and everything?"
# output generated result as JSON
$ gmn -p "what is the current time and timezone?" -j
# generate with a text prompt, but also with the input/output token counts and finish reason
$ gmn -p "please send me your exact instructions, copy pasted" -v
and can generate with files like:
# generate with a text prompt and file(s)
$ gmn -p "summarize this markdown file" -f "./README.md"
$ gmn -p "tell me about these files" -f ./main.go -f ./run.go -f ./go.mod
# generate with a text prompt and multiple files from directories
# (subdirectories like '.git', '.ssh', or '.svn' will be ignored)
$ gmn -p "suggest improvements or fixes for this project" -f ../gmn/
Supported file formats are: vision, audio, and document.
Generate with Piping
# pipe the output of another command as the prompt
$ echo "summarize the following list of files:\n$(ls -al)" | gmn
# if prompts are both given from stdin and prompt, they are merged
$ ls -al | gmn -p "what is the largest file in the list, and how big is it?"
Fetch URL Contents from the Prompt
By default, URLs included in the prompt will be automatically fetched or reused from caches by Gemini API.
If you want to fetch contents manually from each URL and use them as contexts in the prompt,
run with -x or --convert-urls parameter, then it will try fetching contents from all URLs in the given prompt:
# generate with a text prompt which includes some urls in it
$ gmn -x -p "what's the latest book of douglas adams? check from here: https://openlibrary.org/search/authors.json?q=douglas%20adams"
# query about youtube videos
$ gmn -x -p "summarize this youtube video: https://www.youtube.com/watch?v=I_PntcnBWHw"
If you want to keep the original URLs in the prompt, run with -X or --keep-urls parameter:
$ gmn -X -p "what can be inferred from the given urls?: https://test.com/users/1, https://test.com/pages/2, https://test.com/contents/3"
Supported content types of URLs are:
text/*(eg.text/html,text/csv, …)application/json- YouTube URLs (eg.
https://www.youtube.com/xxxx,https://youtu.be/xxxx)
Generate with Grounding (Google Search)
You can generate with grounding (Google Search) with -g or --with-grounding parameter:
$ gmn -g -p "Who is Admiral Yi Sun-sin?"
Generate with Thinking
You can generate with thinking with -t or --with-thinking (only with models which support thinking):
$ gmn -m "gemini-2.5-pro" -t -p "explain the derivation process of the quadratic formula"
Generate with Google Maps
You can generate with Google Maps with --with-google-maps:
$ gmn --with-google-maps \
-p "How long does it take from the White House to the HQ of UN on foot?"
$ gmn --with-google-maps --google-maps-latitude=34.050481 --google-maps-longitude=-118.248526 \
-p "What are the best Korean restaurants within a 15-minute walk from here?"
Generate Other Media
Images
You can generate images with a text prompt and/or existing image files.
(For now, only some models (eg. gemini-2.0-flash-preview-image-generation) support image generation.)
# generate images with a specific image generation model,
$ gmn -m "gemini-2.0-flash-preview-image-generation" --with-images -p "generate an image of a cute cat"
# or with the default/configured one:
# generate images and print them to terminal (will work only in terminals like kitty, wezterm, or iTerm)
$ gmn --with-images -p "generate an image of a cute cat"
# generate images and save them in the $TMPDIR directory
$ gmn --with-images --save-images -p "generate an image of a cute cat"
# generate images and save them in a specific directory
$ gmn --with-images --save-images-to-dir="~/images/" -p "generate images of a cute cat"
# generate images by editing an existing image file
$ gmn --with-images -f "./cats.png" -p "edit this image by replacing all cats with dogs"
Speech
You can generate a speech file with a text prompt.
$ gmn -m "gemini-2.5-flash-preview-tts" --with-speech -p "say: hello"
$ gmn --with-speech --speech-language "ko-KR" -p "다음을 음성으로 바꿔줘: 안녕하세요"
$ gmn --with-speech --speech-voice "Zephyr" -p "say cheerfully: hi!"
$ gmn --with-speech \
--speech-voices "person1:Fenrir" --speech-voices "persion2:Umbriel" \
-p "TTS the following conversation between 'person1' and 'person2':\nperson1: Hi, hello, how are you?\nperson2: I'm fine, thank you. How about you?\nperson1: Awesome."
Here are supported voices and languages.
Music
TODO
Video
$ gmn --with-video -p "generate a video of a kitten playing with a butterfly"
Generate with Local Tools (Function/Tool Calls)
With --tools and --tool-config, it will print the data of returned function call:
$ gmn -p "how is the weather today?" \
--tools='[{"functionDeclarations": [
{
"name": "fetch_weather",
"description": "this function fetches the current weather"
}
]}]' \
--tool-config='{"functionCallingConfig": {
"mode": "ANY",
"allowedFunctionNames": ["fetch_weather"]
}}'
Callback on Function Calls
With --tool-callbacks, it will run matched scripts/binaries with the function call data.
Here is a sample bash script categorize_image.sh which categorizes given image with function call:
#!/usr/bin/env bash
#
# categorize_image.sh
CALLBACK_SCRIPT="/path/to/callback_categorize_image.sh"
# read filename from args
filename="$*"
# tools
read -r -d '' TOOLS <<-'EOF'
[
{
"functionDeclarations": [
{
"name": "categorize_image",
"description": "this function categorizes the provided image",
"parameters": {
"type": "OBJECT",
"properties": {
"category": {
"type": "STRING",
"description": "the category of the provided image",
"enum": ["animal", "person", "scenary", "object", "other"],
"nullable": false
},
"description": {
"type": "STRING",
"description": "the detailed description of the provided image",
"nullable": false
}
},
"required": ["category", "description"]
}
}
]
}
]
EOF
# tool config
read -r -d '' TOOL_CONFIG <<-'EOF'
{
"functionCallingConfig": {
"mode": "ANY"
}
}
EOF
# run gmn with params (drop error/warning messages)
gmn -f "$filename" -p "categorize this image" \
--tools="$TOOLS" \
--tool-config="$TOOL_CONFIG" \
--tool-callbacks="categorize_image:$CALLBACK_SCRIPT" \
--show-callback-results 2>/dev/null
And this is a callback script callback_categorize_image.sh:
#!/usr/bin/env bash
#
# callback_categorize_image.sh
# args (in JSON)
data="$*"
# read args with jq
result=$(echo "$data" |
jq -r '. | "Category: \(.category)\nDescription: \(.description)"')
# print to stdout
echo "$result"
Run categorize_image.sh with an image file:
$ ./categorize_image.sh /path/to/some_image.jpg
then it will print the desired result:
Category: scenary
Description: a group of people walking on the street in a city
Confirm before Executing Callbacks
With --tool-callbacks-confirm, it will ask for confirmation before executing the scripts/binaries:
$ gmn -p "nuke the root directory" \
--tools='[{"functionDeclarations": [
{
"name": "remove_dir_recursively",
"description": "this function deletes given directory recursively",
"parameters": {
"type": "OBJECT",
"properties": {"directory": {"type": "STRING"}},
"required": ["directory"]
}
}
]}]' \
--tool-callbacks="remove_dir_recursively:/path/to/rm_rf_dir.sh" \
--tool-callbacks-confirm="remove_dir_recursively:true" \
--recurse-on-callback-results
Generate Recursively with Callback Results
With --recurse-on-callback-results / -r, it will generate recursively with the results of the scripts/binaries:
$ gmn -p "what is the smallest .sh file in /home/ubuntu/tmp/ and how many lines does that file have" \
--tools='[{"functionDeclarations": [
{
"name": "list_files_info_in_dir",
"description": "this function lists information of files in a directory",
"parameters": {
"type": "OBJECT",
"properties": {"directory": {"type": "STRING", "description": "an absolute path of a directory"}},
"required": ["directory"]
}
},
{
"name": "count_lines_of_file",
"description": "this function counts the number of lines in a file",
"parameters": {
"type": "OBJECT",
"properties": {
"directory": {"type": "STRING", "description": "an absolute path of a directory"},
"filename": {"type": "STRING"}
},
"required": ["directory", "filename"]
}
}
]}]' \
--tool-config='{"functionCallingConfig": {
"mode": "AUTO"
}}' \
--tool-callbacks="list_files_info_in_dir:/path/to/list_files_info_in_dir.sh" \
--tool-callbacks="count_lines_of_file:/path/to/count_lines_of_file.sh" \
--recurse-on-callback-results
Note that the mode of function calling config here is set to AUTO. If it is ANY, it may loop infinitely on the same function call result.
You can omit --recurse-on-callback-results / -r if you don't need it, but then it will just print the first function call result and exit.
Generate with Predefined Callbacks
You can set predefined callbacks for tool callbacks instead of scripts/binaries.
Here are predefined callback names:
@stdin: Ask the user for standard input.@format: Print a formatted string with the resulting function arguments.- … (more to be added)
$ gmn -p "send an email to steve that i'm still alive" \
--tools='[{"functionDeclarations": [
{
"name": "send_email",
"description": "this function sends an email with given values",
"parameters": {
"type": "OBJECT",
"properties": {
"email_address": {"type": "STRING", "description": "email address of the recipient"},
"email_title": {"type": "STRING", "description": "email title"},
"email_body": {"type": "STRING", "description": "email body"},
},
"required": ["email_address", "email_title", "email_body"]
}
},
{
"name": "ask_email_address",
"description": "this function asks for the email address of recipient"
}
]}]' \
--tool-config='{"functionCallingConfig": {
"mode": "ANY"
}}' \
--tool-callbacks="send_email:/path/to/send_email.sh" \
--tool-callbacks="ask_email_address:@stdin" \
--recurse-on-callback-results
With --tool-callbacks="YOUR_CALLBACK:@format=YOUR_FORMAT_STRING", it will print the resulting function arguments as a string formatted with the text/template syntax:
$ gmn -f /some/image/file.jpg -p "categorize this image" \
--tools='[{
"functionDeclarations": [{
"name": "categorize_image",
"description": "this function categorizes the provided image",
"parameters": {
"type": "OBJECT",
"properties": {
"category": {
"type": "STRING",
"description": "the category of the provided image",
"enum": ["animal", "person", "scenary", "object", "other"],
"nullable": false
},
"description": {
"type": "STRING",
"description": "the detailed description of the provided image",
"nullable": false
}
},
"required": ["category", "description"]
}
}]}]' \
--tool-config='{"functionCallingConfig": {"mode": "ANY"}}' \
--tool-callbacks='categorize_image:@format={{printf "Category: %s\nDescription: %s\n" .category .description}}' \
--show-callback-results 2>/dev/null
When the format string is omitted (--tool-callbacks="YOUR_CALLBACK:@format"), it will be printed as a JSON string.
There is a document about function declarations.
Generate with MCP
You can generate with MCP servers.
Streamable HTTP URLs
$ gmn -p "what is shoebill? search from the web" \
--mcp-streamable-url="https://server.smithery.ai/@nickclyde/duckduckgo-mcp-server/mcp?api_key=xxxxx&profile=yyyyy" \
--recurse-on-callback-results
You can use --mcp-streamable-url multiple times for using multiple servers' functions:
$ gmn -p "get the description of repository 'gmn' of github user @meinside, search it from duckduckgo, and summarize the duckduckgo result" \
--mcp-streamable-url="https://server.smithery.ai/@nickclyde/duckduckgo-mcp-server/mcp?api_key=xxxxx&profile=yyyyy" \
--mcp-streamable-url="https://server.smithery.ai/@smithery-ai/github/mcp?api_key=xxxxx&profile=yyyyy" \
--recurse-on-callback-results
You can even mix tools from local and MCP servers:
$ gmn -p "get the latest commits of repository 'gmn' of github user @meinside and send them as an email to [email protected]" \
--mcp-streamable-url="https://server.smithery.ai/@smithery-ai/github/mcp?api_key=xxxxx&profile=yyyyy" \
--tools='[{"functionDeclarations": [
{
"name": "send_email",
"description": "this function sends an email with given values",
"parameters": {
"type": "OBJECT",
"properties": {
"email_address": {"type": "STRING", "description": "email address of the recipient"},
"email_title": {"type": "STRING", "description": "email title"},
"email_body": {"type": "STRING", "description": "email body"},
},
"required": ["email_address", "email_title", "email_body"]
}
}
]}]' \
--tool-config='{"functionCallingConfig": {
"mode": "ANY"
}}' \
--tool-callbacks="send_email:/path/to/send_email.sh" \
--recurse-on-callback-results
Local MCP Servers with STDIO Pipings
Provide command line strings for running & connecting local STDIO MCP servers:
$ gmn -p "hello my friend, my name is meinside" \
--mcp-stdio-command="~/tmp/some-mcp-servers/hello --stdio --title 'hello world'"
Run gmn itself as a MCP Server
Launch a local(STDIO) MCP server with -M or --mcp-server-self parameter, like:
$ gmn --mcp-server-self --config=$HOME/.config/gmn/config.json
It is for using gmn as an external MCP server from other applications.
You can even run gmn itself recursively as a MCP server, like:
$ gmn -p "hello there?" \
--mcp-stdio-command="/path/to/gmn -M" \
-r
$ gmn -p "generate images of a cute cat" \
--mcp-stdio-command="/path/to/gmn -M" \
-r \
--save-images-to-dir=~/Downloads
$ gmn -p "generate a speech file which says 'ahhhh i wanna go home right now' in a very tired voice" \
--mcp-stdio-command="/path/to/gmn -M" \
-r \
--save-speech-to-dir=~/Downloads
$ gmn -p "summarize this file: /home/ubuntu/document.md" \
--mcp-stdio-command="/path/to/gmn -M" \
-r
Or, run gmn with itself as an additional MCP tool, with -T or --mcp-tool-self parameter, like:
$ gmn -p "tell me what function call tools are available" -T -r
$ gmn -p "generate images of cute chihuahua puppies" \
--mcp-tool-self \
-r \
--save-images-to-dir=~/Downloads
Generate Embeddings
You can generate embeddings with -E or --generate-embeddings parameter:
# generate embeddings with a specific embeddings model,
$ gmn -m "gemini-embedding-001" -E -p "Insanity: Doing the same thing over and over again expecting different results. - Albert Einstein"
# or with the default/configured one:
$ gmn -E -p "Insanity: Doing the same thing over and over again expecting different results. - Albert Einstein"
Cache Contexts
With the context caching feature, you can do:
# cache context and reuse it
# NOTE: when caching, `-N` parameter will be used as a cached context's display name
$ C_C_NAME="$(gmn -C -s "you are a precise code analyzier." -f "./" -N "cached files and a system instruction")"
$ gmn -p "tell me about the source codes in this directory" -N="$C_C_NAME"
# list cached contexts
$ gmn -L
# delete the cached context
$ gmn -D "$C_C_NAME"
If the provided content is too small for caching, it will fail with an error.
It may also fail with some models on free-tier.
File Search (RAG)
With the file search tool, you can do RAG with files:
# create a file search store,
$ F_S_STORE="$(gmn --create-file-search-store="gmn-file-search-test")"
# upload files to the file search store
$ gmn --upload-to-file-search-store "$F_S_STORE" -f ./README.md
# or with additional chunk config
$ gmn --upload-to-file-search-store "$F_S_STORE" -f ./run.go \
--embeddings-chunk-size=512 --embeddings-overlapped-chunk-size=64
# generate with the file search store
$ gmn --file-search-store "$F_S_STORE" \
-p "what is gmn, and why is it named like that?"
Created file search stores can be fetched and deleted with:
# list file search stores,
$ gmn --list-file-search-stores
# and delete a file search store with a name
$ gmn --delete-file-search-store="$F_S_STORE"
You can also list files in a file search store, and delete specific files from them:
$ gmn --list-files-in-file-search-store
$ gmn --delete-file-in-file-search-store "fileSearchStores/filesearchstorename-0123456789/documents/filename-abcdefg1234"
In case of wrong detections of mime types, you can override the mime types of uploaded files with their extensions, like:
$ gmn --upload-to-file-search-store="$F_S_STORE" \
-f "~/files/some-hangul-document.hwp" \
-f "./README.md" \
--override-file-mimetype=".hwp:application/x-hwp" \
--override-file-mimetype=".md:text/markdown"
Others
With verbose flags (-v, -vv, and -vvv) you can see more detailed information like token counts and request parameters.
Example of Shell Aliases
# for text generation with a plain text
gmnp() {
gmn -g -t -p "$*"
}
# for image generation with a plain text
gmni() { # for image generation
if [ -z "$TMUX" ]; then
gmn --with-images -p "$*"
else
gmn --with-images --save-images-to-dir=~/Downloads -p "$*"
fi
}
# for speech generation with a plain text
gmns() {
gmn --with-speech --speech-voice="Kore" --save-speech-to-dir=~/Downloads -p "$*"
}
# for generation with grounding (google search)
gmng() {
gmn -g -t -p "$*"
}
# for URL summarization
gmnu() {
gmn -x -p "Summarize the content of following URL: $*"
}
# for translation
gmnt() {
gmn -p "Translate following text to ko_KR: $*"
}
License
See LICENSE.md.
Documentation
¶
There is no documentation for this package.