Skip to main content
Glama
DeanWard

HAL (HTTP API Layer)

List Available Secrets

list-secrets

Retrieve a list of secret key names available for use with {secrets.key} syntax, ensuring secure access without exposing actual secret values in HAL (HTTP API Layer).

Instructions

Get a list of available secret keys that can be used with {secrets.key} syntax. Only shows the key names, never the actual secret values.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • src/index.ts:695-794 (registration)
    Registration of the 'list-secrets' MCP tool, including its schema (empty input) and inline handler function that lists available secrets.
    server.registerTool(
      "list-secrets",
      {
        title: "List Available Secrets",
        description: "Get a list of available secret keys that can be used with {secrets.key} syntax. Only shows the key names, never the actual secret values.",
        inputSchema: {}
      },
      async () => {
        try {
          const secretKeys = Object.keys(secrets);
          
          if (secretKeys.length === 0) {
            return {
              content: [{
                type: "text" as const,
                text: "No secrets are currently configured. To add secrets, set environment variables with the HAL_SECRET_ prefix.\n\nExample:\n  HAL_SECRET_API_KEY=your_api_key\n  HAL_SECRET_TOKEN=your_token\n\nThen use them in requests like: {secrets.api_key} or {secrets.token}\n\nFor namespaced secrets with URL restrictions:\n  HAL_SECRET_MICROSOFT_API_KEY=your_api_key\n  HAL_ALLOW_MICROSOFT=\"https://azure.microsoft.com/*\"\n  Usage: {secrets.microsoft.api_key}"
              }]
            };
          }
          
          let response = `Available secrets (${secretKeys.length} total):\n\n`;
          
          // Group secrets by namespace
          const namespacedSecrets: Record<string, string[]> = {};
          const unrestrictedSecrets: string[] = [];
          
          for (const [key, secretInfo] of Object.entries(secrets)) {
            if (secretInfo.namespace) {
              const namespaceTemplate = secretInfo.namespace.toLowerCase().replace(/-/g, '.');
              if (!namespacedSecrets[namespaceTemplate]) {
                namespacedSecrets[namespaceTemplate] = [];
              }
              namespacedSecrets[namespaceTemplate].push(key);
            } else {
              unrestrictedSecrets.push(key);
            }
          }
          
          // Show unrestricted secrets first
          if (unrestrictedSecrets.length > 0) {
            response += "**Unrestricted Secrets** (can be used with any URL):\n";
            unrestrictedSecrets.forEach((key, index) => {
              response += `${index + 1}. {secrets.${key}}\n`;
            });
            response += "\n";
          }
          
          // Show namespaced secrets with their restrictions
          for (const [namespace, keys] of Object.entries(namespacedSecrets)) {
            const firstKey = keys[0];
            const secretInfo = secrets[firstKey];
            
            response += `**Namespace: ${namespace}**\n`;
            if (secretInfo.allowedUrls && secretInfo.allowedUrls.length > 0) {
              response += `Restricted to URLs: ${secretInfo.allowedUrls.join(', ')}\n`;
            } else {
              response += "No URL restrictions (can be used with any URL)\n";
            }
            response += "Secrets:\n";
            
            keys.forEach((key, index) => {
              response += `${index + 1}. {secrets.${key}}\n`;
            });
            response += "\n";
          }
          
          response += "**Usage examples:**\n";
          const exampleKey = secretKeys[0] || 'token';
          response += `- URL: "https://api.example.com/data?token={secrets.${exampleKey}}"\n`;
          response += `- Header: {"Authorization": "Bearer {secrets.${exampleKey}}"}\n`;
          response += `- Body: {"username": "{secrets.${secretKeys.find(k => k.includes('user')) || 'username'}}"}\n\n`;
          
          response += "**Security Notes:**\n";
          response += "- Only the key names are shown here. The actual secret values are never exposed to the AI.\n";
          response += "- Secrets are substituted securely at request time.\n";
          
          const restrictedCount = Object.values(secrets).filter(s => s.allowedUrls).length;
                  if (restrictedCount > 0) {
              response += `- ${restrictedCount} secrets have URL restrictions for enhanced security.\n`;
              response += "- If a secret is restricted, it will only work with URLs matching its allowed patterns.\n";
            }
          
          return {
            content: [{
              type: "text" as const,
              text: response
            }]
          };
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : 'Unknown error';
          return {
            content: [{
              type: "text" as const,
              text: `Error listing secrets: ${redactSecretsFromText(errorMessage)}`
            }],
            isError: true
          };
        }
      }
    );
  • The core handler logic for 'list-secrets': iterates over global 'secrets' store, formats response with grouped secret keys, namespace restrictions, usage examples, and security information without exposing values.
    async () => {
      try {
        const secretKeys = Object.keys(secrets);
        
        if (secretKeys.length === 0) {
          return {
            content: [{
              type: "text" as const,
              text: "No secrets are currently configured. To add secrets, set environment variables with the HAL_SECRET_ prefix.\n\nExample:\n  HAL_SECRET_API_KEY=your_api_key\n  HAL_SECRET_TOKEN=your_token\n\nThen use them in requests like: {secrets.api_key} or {secrets.token}\n\nFor namespaced secrets with URL restrictions:\n  HAL_SECRET_MICROSOFT_API_KEY=your_api_key\n  HAL_ALLOW_MICROSOFT=\"https://azure.microsoft.com/*\"\n  Usage: {secrets.microsoft.api_key}"
            }]
          };
        }
        
        let response = `Available secrets (${secretKeys.length} total):\n\n`;
        
        // Group secrets by namespace
        const namespacedSecrets: Record<string, string[]> = {};
        const unrestrictedSecrets: string[] = [];
        
        for (const [key, secretInfo] of Object.entries(secrets)) {
          if (secretInfo.namespace) {
            const namespaceTemplate = secretInfo.namespace.toLowerCase().replace(/-/g, '.');
            if (!namespacedSecrets[namespaceTemplate]) {
              namespacedSecrets[namespaceTemplate] = [];
            }
            namespacedSecrets[namespaceTemplate].push(key);
          } else {
            unrestrictedSecrets.push(key);
          }
        }
        
        // Show unrestricted secrets first
        if (unrestrictedSecrets.length > 0) {
          response += "**Unrestricted Secrets** (can be used with any URL):\n";
          unrestrictedSecrets.forEach((key, index) => {
            response += `${index + 1}. {secrets.${key}}\n`;
          });
          response += "\n";
        }
        
        // Show namespaced secrets with their restrictions
        for (const [namespace, keys] of Object.entries(namespacedSecrets)) {
          const firstKey = keys[0];
          const secretInfo = secrets[firstKey];
          
          response += `**Namespace: ${namespace}**\n`;
          if (secretInfo.allowedUrls && secretInfo.allowedUrls.length > 0) {
            response += `Restricted to URLs: ${secretInfo.allowedUrls.join(', ')}\n`;
          } else {
            response += "No URL restrictions (can be used with any URL)\n";
          }
          response += "Secrets:\n";
          
          keys.forEach((key, index) => {
            response += `${index + 1}. {secrets.${key}}\n`;
          });
          response += "\n";
        }
        
        response += "**Usage examples:**\n";
        const exampleKey = secretKeys[0] || 'token';
        response += `- URL: "https://api.example.com/data?token={secrets.${exampleKey}}"\n`;
        response += `- Header: {"Authorization": "Bearer {secrets.${exampleKey}}"}\n`;
        response += `- Body: {"username": "{secrets.${secretKeys.find(k => k.includes('user')) || 'username'}}"}\n\n`;
        
        response += "**Security Notes:**\n";
        response += "- Only the key names are shown here. The actual secret values are never exposed to the AI.\n";
        response += "- Secrets are substituted securely at request time.\n";
        
        const restrictedCount = Object.values(secrets).filter(s => s.allowedUrls).length;
                if (restrictedCount > 0) {
            response += `- ${restrictedCount} secrets have URL restrictions for enhanced security.\n`;
            response += "- If a secret is restricted, it will only work with URLs matching its allowed patterns.\n";
          }
        
        return {
          content: [{
            type: "text" as const,
            text: response
          }]
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : 'Unknown error';
        return {
          content: [{
            type: "text" as const,
            text: `Error listing secrets: ${redactSecretsFromText(errorMessage)}`
          }],
          isError: true
        };
      }
    }
  • Tool metadata and input schema definition (no input parameters required).
    {
      title: "List Available Secrets",
      description: "Get a list of available secret keys that can be used with {secrets.key} syntax. Only shows the key names, never the actual secret values.",
      inputSchema: {}
  • Helper function that populates the global 'secrets' store from HAL_SECRET_* environment variables, used by list-secrets handler.
    function loadSecrets(): void {
      secrets = {};
      const urlRestrictions = loadUrlRestrictions();
      
      for (const [key, value] of Object.entries(process.env)) {
        if (key.startsWith('HAL_SECRET_') && value) {
          const { namespace, key: secretKey, templateKey } = parseSecretKey(key);
          
          const secretInfo: SecretInfo = {
            value,
            namespace,
            allowedUrls: namespace ? urlRestrictions[namespace] : undefined,
            templateKey
          };
          
          secrets[templateKey] = secretInfo;
        }
      }
      
      if (Object.keys(secrets).length > 0) {
        console.error(`Loaded ${Object.keys(secrets).length} secrets from environment variables`);
        
        // Log namespace restrictions (without secret values)
        const restrictedCount = Object.values(secrets).filter(s => s.allowedUrls).length;
        if (restrictedCount > 0) {
          console.error(`${restrictedCount} secrets have URL restrictions`);
        }
      }
    }
Behavior4/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively describes key behavioral traits: it only shows key names (not values), which is a critical safety and privacy constraint. It does not mention other aspects like rate limits, authentication needs, or error handling, but the core behavior is well-covered for a read-only listing tool.

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?

The description is two concise sentences with zero waste. The first sentence states the purpose and usage context, and the second sentence adds a critical behavioral constraint. Every sentence earns its place by providing essential information without redundancy or fluff.

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

Completeness4/5

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

Given the tool's simplicity (0 parameters, no output schema, no annotations), the description is largely complete. It explains what the tool does, its output (key names only), and usage context. It could be slightly enhanced by mentioning the format of the returned list (e.g., array of strings) or any limitations, but it covers the core needs adequately for this low-complexity tool.

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?

The tool has 0 parameters, and schema description coverage is 100%, so there are no parameters to document. The description appropriately does not discuss parameters, focusing instead on the tool's behavior and output. A baseline of 4 is applied as it handles the zero-parameter case correctly without unnecessary details.

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 ('Get a list') and resource ('available secret keys'), and distinguishes this tool from its siblings by specifying it only shows key names, not values. It also mentions the usage context with {secrets.key} syntax, making the purpose highly specific and well-defined.

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

Usage Guidelines4/5

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

The description provides clear context for when to use this tool: to retrieve secret key names for use with {secrets.key} syntax. However, it does not explicitly state when not to use it or name alternatives (e.g., if a sibling tool exists for retrieving secret values), though the sibling tools listed are HTTP methods unrelated to secrets, so this omission is less critical.

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

Related 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/DeanWard/HAL'

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