View source on GitHub
city-weather/ plugin bundles a small Python script, and its
command and skill tell the agent to run that script and show what it prints.
The agent doesn’t work out the API calls itself; it runs one command.
Put the work in a script when it should be the same every time: calling an API,
parsing a file, or formatting a report. The result is repeatable, uses fewer
tokens than having the agent improvise, and you can test the script on its own.
How It Works
- OpenHands fetches the plugin into the sandbox, under
~/.openhands/cache/plugins/. - The message
/city-weather:now Tokyotriggers the plugin’s command. The agent receives the command’s instructions along with the absolute path of the file they came from. - The instructions say where the script is relative to that file, so the agent runs it by its absolute path and shows the output.
Run It
- Load via API
- Launch via Badge
Use the companion
load-plugin example:--message "What's the weather in Paris?". The skill is triggered by “weather
in”, “weather for”, and “forecast for”, and runs the same script.
Run the Script Locally
The script uses only the Python standard library and the free Open-Meteo API, which needs no key. Run it directly to check it before the agent does:How the Agent Finds the Script
The plugin is fetched to a path in the sandbox that the plugin author can’t know in advance. When a command or skill fires, OpenHands adds the location of its file to what the agent sees:
The command spells out the relationship so the agent doesn’t have to guess:
city-weather/commands/now.md
This works for commands and skills, which the agent reads. Hooks are different:
OpenHands runs them from the agent’s workspace with no plugin-root path, so a
hook can’t call a script bundled in its plugin. That’s why the hook examples,
such as
command-blacklist, put their script inline in
hooks.json.Where Scripts Go
This plugin follows the Claude Code plugin layout, which OpenHands loads:- A script used by one skill goes in that skill’s directory, under
skills/<skill>/scripts/. That’s whereweather.pyis. - A script shared by several skills, or called by hooks, goes in
scripts/at the plugin root.
/city-weather:now and the plugin’s
entry_command can start it on launch. In OpenHands, entry_command and
/<plugin>:<name> refer to files in commands/, while a skill is triggered by
keywords in the message.
Why not use ${CLAUDE_SKILL_DIR}?
Why not use ${CLAUDE_SKILL_DIR}?
Claude Code replaces
${CLAUDE_SKILL_DIR} and ${CLAUDE_PLUGIN_ROOT} with
absolute paths when it loads a skill, and its docs show bundled scripts referred
to that way. OpenHands doesn’t replace these variables, so the agent would see
them as literal text. It gives the agent the skill’s location instead, which is
why this plugin’s instructions use paths relative to the file.Claude Code also puts a plugin’s bin/ directory on the PATH. OpenHands
doesn’t, so call scripts by their path.Plugin Structure
Related
load-plugin
Start a conversation with a plugin loaded through the REST API
launch-plugin-badge
Build launch links and badges like the ones above
command-blacklist
A plugin whose script runs as a hook, inlined in hooks.json
Plugins overview
What plugins are and the format they follow
Plugin Launcher
The /launch route the badges use
Claude Code skills
Supporting files in a skill directory, including scripts/

