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

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