gcontext.aidocs

Running scripts in gcontext

gcontext stores the structure (scripts, docs, secret NAMES) in the cloud; execution and secret VALUES stay on the user's machine. tool_run_script runs Python locally via uv run with secret values injected into the environment from ~/.gcontext/.env (chmod 0600; OS keychain opt-in), so any machine with its secrets set runs the same script identically. Output is scrubbed of known secret values before it reaches the agent. Runs are capped at 300 seconds.

tool_run_shell(command) runs a shell command under the same rules (secret env injected, output scrubbed); use it for git, curl, or CLI tools, and tool_run_script for anything with real logic.

Three layers, promote upward

LayerWhenHow
Inline codeOne-off exploration, disposabletool_run_script(code="...")
Stored scriptAnything worth running twiceWrite to the workspace, run with tool_run_script(path="/...py", args=[...])
CommandA human triggers it by name/_commands/<name>.md + /_commands/<name>.py

Promotion is the workflow: an inline script that proved useful gets a PEP 723 header and a workspace path. A stored script that gets asked for repeatedly gets a command .md.

Dependency resolution

tool_run_script resolves dependencies automatically, in this order, all merged:

  1. modules=["stripe"]: each named folder's module.yaml dependencies: list.
  2. Running by path: the script's own folder's module.yaml, inherited automatically.
  3. A PEP 723 # /// script header in the code itself.

/_commands/ has no module.yaml, so scripts there MUST carry a PEP 723 header. Versions pass through verbatim (stripe==11.* works); pin what matters.

Script conventions

  • Deps: declare in a PEP 723 # /// script header or the module's module.yaml (see above).
  • Secrets: os.environ["NAME"] only, never hardcoded, never printed. The names come from the secret registry (see /_docs/secrets.md); values live in ~/.gcontext/.env on the user's machine.
  • Args: sys.argv[1:], main identifier first. Print a usage line and exit 1 when missing.
  • Output: markdown to stdout, the agent relays it verbatim. Exit 0 on success.
  • Resilience: wrap each per-item external call (one Stripe account, one API page) in try/except that prints a warning line and continues. One bad item must not kill the run, crashes cost the agent a whole edit-and-retry loop.
  • Safety: read-only by default, using read-only keys where they exist. A script that writes says so in its docstring and its name.

Command conventions (/_commands/<name>.md)

The connector exposes every /_commands/<name>.md as the slash command /<name>. Keep the .md to UX only; logic lives in the script. Body edits are live; a NEW file needs a client reconnect (clients cache the prompt list).

  • Frontmatter description:, shown in the client's command list.
  • {{input}} in the body is the only substitution the connector performs; it also makes the command declare its argument. $ARGUMENTS does nothing.
  • One execution step: tool_run_script(path="/_commands/<name>.py", args=["<input>"]), never "read the script and copy it to /tmp"; the path call is one round trip and the script body never enters the agent's context.
  • Relay instruction: output is markdown, relay verbatim (not in a code block), then at most a few lines of judgment.
  • Self-repair clause: if the script errors, fix it and write the corrected version back to the workspace so the stored copy stays good.

Reference implementation: /_commands/find-member.md + /_commands/find-member.py in the workspace.

On this page