Skip to main content
Glama
DeanWard

HAL (HTTP API Layer)

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`);
        }
      }
    }
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