Skip to main content
Glama

key_locker

Save, list, and forget terminal credentials for SSH, sudo, and login prompts; autofill stored secrets from a secure dialog without exposing them to the assistant.

Instructions

Purpose: Manage credentials the terminal autofills for you (SSH key passphrases, sudo / login passwords). Secrets are entered once into the locker's own secure dialog and stored encrypted on this machine (Windows DPAPI, current user); these stored secrets are NEVER shown to the assistant or sent to this tool — this covers what the locker holds, not every password on the machine. Details: action='save' pre-seeds a credential for a binding URI (ssh://user@host:22, sudo://host/root, https-cred://host:443, sshkey:SHA256:…): it opens the locker's secure entry dialog and stores the secret; the first save also shows a one-time enable confirmation. action='list' shows saved bindings (metadata only, never secrets). action='forget' deletes a binding and its secret. action='set_policy' toggles per-binding autofill confirmation. action='status' reports whether the locker is enabled (consent) and how many bindings exist. action='launch_console' opens (or reuses) an autofill-capable anchored pane and returns {paneId, windowTitle} — by default a new tab in the user's current Windows Terminal window (host:'classic' opens a dedicated classic console window instead). action='fill_once' asks the user to type a secret into the locker's dialog and types it once into the password prompt (or ssh's 'Enter passphrase for key' prompt) currently shown in a launch_console pane, then forgets it. Prefer: Autofill is AUTOMATIC when a bound command triggers a credential prompt in the terminal — you never fill a SAVED secret by hand. For a prompt autofill does not handle (an ssh inside another ssh session, or a login the user does not want saved), send the command, then call fill_once on that pane. Autofill and fill_once ONLY work in a pane opened by launch_console (a pre-existing terminal is never autofilled): to autofill, first launch_console, then run the ssh / sudo command with terminal({action:'run'|'send', paneId}) — pass the paneId field, not the windowTitle. Keep the returned paneId; there is no pane-listing action, but launch_console with fresh:false reuses the most-recent pane and returns its paneId again. Use save to enroll, list/status to inspect. Caveats: Windows-only. The anchored pane defaults to a Windows Terminal tab (autofill and terminal reads operate while that tab is the ACTIVE tab — switching away pauses them safely); host:'classic' opens a dedicated classic console window instead, and is the retry when Windows Terminal is not installed (KeyLockerWtUnavailable). The human can also see and type into the pane. Enabling the locker (first save or launch_console) grants BOTH credential autofill AND the ability for the assistant to launch a locker-owned pane. Disable the whole feature with DESKTOP_TOUCH_DISABLE_KEY_LOCKER=1. An ssh save needs the host key already in known_hosts (connect once first). API-token / env-var credentials are not supported yet. Examples: key_locker({action:'status'}) → {consentAccepted:false, disabled:false, bindingCount:0} key_locker({action:'save', uri:'sudo://buildbox/root'}) → opens the secure dialog → {captured:true} key_locker({action:'list'}) → {bindings:[{displayUri:'sudo://buildbox/root', scheme:'sudo', …}]} key_locker({action:'launch_console'}) → {paneId:'wt:31264:13322426700123', windowTitle:'dtm-locker-console-…'} → then terminal({action:'send', paneId:'wt:31264:13322426700123', input:'ssh user@host'}) key_locker({action:'launch_console', host:'classic'}) → {paneId:'12345678', windowTitle:'dtm-locker-console-…'} (dedicated classic console window)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
uriNoA binding URI identifying the credential target — e.g. `ssh://user@host:22`, `sudo://host/root`, `https-cred://github.com:443`, `sshkey:SHA256:…` (no `//`). Parsed per the L1 grammar; malformed input is rejected.
hostNo'windows-terminal' (default) = open a new tab in the user's current Windows Terminal window. 'classic' = open a dedicated classic console window instead (the fallback when Windows Terminal is not installed — a KeyLockerWtUnavailable error tells you to retry with this). A Windows Terminal pane autofills/reads only while its tab is the ACTIVE tab; a classic console has its own window.
freshNofalse (default) = reuse the most-recent still-open anchored pane of the requested host; true = open a NEW one (bounded).
actionYesAction selector — one of: list, save, forget, set_policy, status, launch_console, fill_once. Per-action required fields are enforced at call time (see the tool description); this flat schema lists every action's fields as optional.
paneIdNoThe paneId returned by launch_console (only those panes can be filled).
reasonNoOne short sentence the dialog shows the user as YOUR reason (e.g. 'log in to 192.168.1.20 via the jump host'). Shown as the assistant's words; never put a secret here.
submitNotrue (default) = press Enter after typing the secret; false = type only.
includeNoOptional response-shape opt-in. `['envelope']` returns the self-documenting envelope (`_version` / `data` / `as_of` / `confidence`). `['raw']` forces raw shape (overrides DESKTOP_TOUCH_ENVELOPE=1 server default). Default behaviour is raw shape (compat with existing clients).
confirmEveryInjectionNotrue (default) = confirm every autofill for this binding; false = opt out (autofill without asking).

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed12 schema fields changedv2.3.1
    • addedInput schema / $schema
      Added value: +"http://json-schema.org/draft-07/schema#"
    • removedInput schema / additionalProperties
      Removed value: -true
    • addedInput schema / properties / action
      Added value: +{
      +  "description": "Action selector — one of: list, save, forget, set_policy, status, launch_console, fill_once. Per-action required fields are enforced at call time (see the tool description); this flat schema lists every action's fields as optional.",
      +  "enum": [
      +    "list",
      +    "save",
      +    "forget",
      +    "set_policy",
      +    "status",
      +    "launch_console",
      +    "fill_once"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / confirmEveryInjection
      Added value: +{
      +  "description": "true (default) = confirm every autofill for this binding; false = opt out (autofill without asking).",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / fresh
      Added value: +{
      +  "description": "false (default) = reuse the most-recent still-open anchored pane of the requested host; true = open a NEW one (bounded).",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / host
      Added value: +{
      +  "description": "'windows-terminal' (default) = open a new tab in the user's current Windows Terminal window. 'classic' = open a dedicated classic console window instead (the fallback when Windows Terminal is not installed — a KeyLockerWtUnavailable error tells you to retry with this). A Windows Terminal pane autofills/reads only while its tab is the ACTIVE tab; a classic console has its own window.",
      +  "enum": [
      +    "windows-terminal",
      +    "classic"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / include
      Added value: +{
      +  "description": "Optional response-shape opt-in. `['envelope']` returns the self-documenting envelope (`_version` / `data` / `as_of` / `confidence`). `['raw']` forces raw shape (overrides DESKTOP_TOUCH_ENVELOPE=1 server default). Default behaviour is raw shape (compat with existing clients).",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
    • addedInput schema / properties / paneId
      Added value: +{
      +  "description": "The paneId returned by launch_console (only those panes can be filled).",
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / reason
      Added value: +{
      +  "description": "One short sentence the dialog shows the user as YOUR reason (e.g. 'log in to 192.168.1.20 via the jump host'). Shown as the assistant's words; never put a secret here.",
      +  "maxLength": 200,
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / properties / submit
      Added value: +{
      +  "description": "true (default) = press Enter after typing the secret; false = type only.",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / uri
      Added value: +{
      +  "description": "A binding URI identifying the credential target — e.g. `ssh://user@host:22`, `sudo://host/root`, `https-cred://github.com:443`, `sshkey:SHA256:…` (no `//`). Parsed per the L1 grammar; malformed input is rejected.",
      +  "minLength": 1,
      +  "type": "string"
      +}
    • addedInput schema / required
      Added value: +[
      +  "action"
      +]
  2. Addedv1.12.0

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does so: secrets are never shown to the assistant or sent to the tool, stored via Windows DPAPI for the current user, first save triggers a one-time consent confirmation, enabling grants autofill AND pane-launch capability, disabling via DESKTOP_TOUCH_DISABLE_KEY_LOCKER=1, Windows-only, and ssh saves need the host key already in known_hosts.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Long, but organized into Purpose/Details/Prefer/Caveats/Examples with the scoping and safety constraint front-loaded and worked examples at the end. Dense for a single description, though nearly every clause maps to a real invocation decision, so little is expendable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description supplies per-action return shapes and worked call/response examples (including the paneId flow into terminal), covering consent state, bindingCount, and captured/paneId returns. For a 9-parameter, seven-action tool this is complete enough to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% (baseline 3), but the description adds real meaning: it explains per-action required fields ('enforced at call time (see the tool description); this flat schema lists every action's fields as optional'), ties paneId to launch_console output, and warns never to put a secret in `reason`.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Manage credentials the terminal autofills for you') and enumerates each action's effect (save pre-seeds a binding, list shows metadata, forget deletes, fill_once types once). It clearly distinguishes itself from the terminal and browser_fill siblings by scoping to SSH/sudo/login credential prompts in a locker-owned pane.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'Prefer' block gives explicit routing: autofill is automatic for bound commands, fill_once is for prompts autofill can't handle (nested ssh, unsaved logins), and it names the sibling call to compose with ('then run the ssh / sudo command with terminal({action:'run'|'send', paneId})'). It also states the hard precondition that only launch_console panes can be filled.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.