Skip to main content
Glama
keywaysh

Keyway MCP Server

by keywaysh

keyway_inject_run

Execute commands with securely injected environment variables from Keyway secrets manager, isolating sensitive data to specific processes.

Instructions

Run a command with Keyway secrets injected as environment variables. Secrets are only available to this command.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
commandYesThe command to run (e.g., "npm", "python")
argsNoArguments to pass to the command
environmentNoEnvironment to pull secrets from (default: "development")
timeoutNoTimeout in milliseconds (default: 300000 = 5 minutes)

Implementation Reference

  • The main handler function `injectRun` implements the logic for the `keyway_inject_run` tool by pulling secrets, merging them into the environment, executing the requested command, and sanitizing the output.
    export async function injectRun(args: {
      command: string;
      args?: string[];
      environment?: string;
      timeout?: number;
    }): Promise<CallToolResult> {
      // Validate command is not empty
      if (!args.command || !args.command.trim()) {
        return {
          content: [{ type: 'text', text: 'Error: command is required' }],
          isError: true,
        };
      }
    
      const token = await getToken();
      const repository = getRepository();
      const environment = args.environment || 'development';
      const timeout = Math.min(args.timeout || DEFAULT_TIMEOUT_MS, DEFAULT_TIMEOUT_MS); // Cap at 5 min
    
      // Pull secrets
      const content = await pullSecrets(repository, environment, token);
      const secrets = parseEnvContent(content);
    
      // Merge secrets with current environment
      const env = { ...process.env, ...secrets };
    
      // Run command with secrets injected
      const result = await new Promise<{
        exitCode: number;
        stdout: string;
        stderr: string;
        stdoutTruncated: boolean;
        stderrTruncated: boolean;
      }>((resolve, reject) => {
        const child = spawn(args.command, args.args || [], {
          cwd: process.cwd(),
          env,
          shell: false, // Prevent shell injection
          stdio: ['ignore', 'pipe', 'pipe'],
        });
    
        let stdout = '';
        let stderr = '';
        let stdoutBytes = 0;
        let stderrBytes = 0;
    
        const timer = setTimeout(() => {
          child.kill('SIGTERM');
          // Give process time to cleanup, then force kill
          setTimeout(() => child.kill('SIGKILL'), 5000);
          reject(new Error(`Command timed out after ${timeout / 1000}s`));
        }, timeout);
    
        child.stdout.on('data', (data: Buffer) => {
          // Limit memory usage by stopping accumulation after max size
          if (stdoutBytes < MAX_OUTPUT_BYTES) {
            stdout += data.toString();
            stdoutBytes += data.length;
          }
        });
    
        child.stderr.on('data', (data: Buffer) => {
          if (stderrBytes < MAX_OUTPUT_BYTES) {
            stderr += data.toString();
            stderrBytes += data.length;
          }
        });
    
        child.on('close', (code) => {
          clearTimeout(timer);
    
          // Truncate and mask output
          const stdoutResult = truncateOutput(stdout, MAX_OUTPUT_BYTES);
          const stderrResult = truncateOutput(stderr, MAX_OUTPUT_BYTES);
    
          resolve({
            exitCode: code ?? 1,
            stdout: maskSecrets(stdoutResult.text, secrets),
            stderr: maskSecrets(stderrResult.text, secrets),
            stdoutTruncated: stdoutResult.truncated || stdoutBytes >= MAX_OUTPUT_BYTES,
            stderrTruncated: stderrResult.truncated || stderrBytes >= MAX_OUTPUT_BYTES,
          });
        });
    
        child.on('error', (err) => {
          clearTimeout(timer);
          reject(new Error(`Failed to execute command: ${err.message}`));
        });
      });
    
      const response: Record<string, unknown> = {
        exitCode: result.exitCode,
        stdout: result.stdout,
        stderr: result.stderr,
        secretsInjected: Object.keys(secrets).length,
      };
    
      // Include truncation warnings if applicable
      if (result.stdoutTruncated || result.stderrTruncated) {
        response.warnings = [];
        if (result.stdoutTruncated) {
          (response.warnings as string[]).push('stdout was truncated (exceeded 1MB)');
        }
        if (result.stderrTruncated) {
          (response.warnings as string[]).push('stderr was truncated (exceeded 1MB)');
        }
      }
    
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(response, null, 2),
          },
        ],
        isError: result.exitCode !== 0,
      };
    }
Behavior2/5

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

With no annotations provided, the description carries full burden but only mentions secret isolation. It doesn't disclose critical behavioral traits like execution environment, security implications, error handling, or whether this is a read/write operation. The description is insufficient for a tool that executes commands with secrets.

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

Conciseness5/5

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

Two concise sentences with zero waste. First sentence states the core functionality, second adds important security context. Perfectly front-loaded and appropriately sized.

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

Completeness2/5

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

For a command execution tool with secrets injection and no annotations/output schema, the description is incomplete. It lacks information about execution context, security boundaries, return values, error conditions, and how secrets are accessed/loaded. The security-critical nature demands more disclosure.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema fully documents all 4 parameters. The description adds no parameter-specific information beyond what's in the schema, maintaining the baseline score of 3 for high schema coverage.

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?

The description clearly states the specific action ('Run a command') with the specific resource ('Keyway secrets injected as environment variables'). It distinguishes from siblings by focusing on command execution with secret injection, unlike list/scan/set operations.

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

Usage Guidelines3/5

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

The description implies usage context ('Secrets are only available to this command') but doesn't explicitly state when to use this vs. alternatives like keyway_set_secret or keyway_list_secrets. No guidance on prerequisites or exclusions is provided.

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

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keywaysh/keyway-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server