Skip to main content
Glama

get_credential

Get credentials for any service from the Auth Box vault. Returns only fields allowed by the agent's access policy, ensuring secret values are never exposed without explicit permission.

Instructions

Retrieve a credential from the Auth Box vault. Returns credential fields filtered by the agent's access policy. Never returns the raw secret unless the policy explicitly allows "read" action.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
service_nameYesName of the service to retrieve credentials for (e.g., "GitHub", "AWS")
fieldsNoSpecific fields to retrieve. Omit to get all permitted fields.

Implementation Reference

  • Tool definition registration for 'get_credential' — defines its name, description, and input schema (service_name required, fields optional).
    export const authboxTools: ToolDefinition[] = [
      {
        name: 'get_credential',
        description:
          'Retrieve a credential from the Auth Box vault. Returns credential fields filtered by the agent\'s access policy. Never returns the raw secret unless the policy explicitly allows "read" action.',
        inputSchema: {
          type: 'object',
          properties: {
            service_name: {
              type: 'string',
              description: 'Name of the service to retrieve credentials for (e.g., "GitHub", "AWS")',
            },
            fields: {
              type: 'array',
              items: { type: 'string' },
              description: 'Specific fields to retrieve. Omit to get all permitted fields.',
            },
          },
          required: ['service_name'],
        },
      },
  • Handler implementation: evaluates policy (with optional step-up approval), fetches credential via bridge, filters requested fields, and returns JSON result.
    private async toolGetCredential(
      session: MCPSession,
      policies: AgentPolicy[],
      args: Record<string, unknown>,
    ): Promise<ToolCallResult> {
      const serviceName = args.service_name as string;
      const fields = args.fields as string[] | undefined;
    
      const request: AccessRequest = {
        agentId: session.agentId,
        action: 'read',
      };
    
      const decision = this.policyEngine.evaluate(policies, request);
    
      // Handle step-up approval: wait for user decision
      if (!decision.allowed && decision.pendingApprovalId) {
        const approved = await this.policyEngine.requestApproval(
          decision.pendingApprovalId,
          request,
        );
        if (!approved) {
          decision.reason = 'Step-up approval denied by user';
          await this.logAccess(session, 'get_credential', serviceName, decision);
          return {
            content: [{ type: 'text', text: 'Access denied: step-up approval was denied by the user' }],
            isError: true,
          };
        }
        // User approved -- continue with credential retrieval
        decision.allowed = true;
        decision.reason = 'Step-up approval granted by user';
      }
    
      await this.logAccess(session, 'get_credential', serviceName, decision);
    
      if (!decision.allowed) {
        return {
          content: [{ type: 'text', text: `Access denied: ${decision.reason}` }],
          isError: true,
        };
      }
    
      const credential = await this.bridge.getCredential(session.userId, serviceName);
      if (!credential) {
        return {
          content: [{ type: 'text', text: `No credential found for service: ${serviceName}` }],
          isError: true,
        };
      }
    
      // Filter fields if requested
      const filtered = fields
        ? Object.fromEntries(Object.entries(credential).filter(([k]) => fields.includes(k)))
        : credential;
    
      return {
        content: [{ type: 'text', text: JSON.stringify(filtered) }],
      };
    }
  • Input schema: requires service_name (string), optional fields (array of strings).
    inputSchema: {
      type: 'object',
      properties: {
        service_name: {
          type: 'string',
          description: 'Name of the service to retrieve credentials for (e.g., "GitHub", "AWS")',
        },
        fields: {
          type: 'array',
          items: { type: 'string' },
          description: 'Specific fields to retrieve. Omit to get all permitted fields.',
        },
      },
      required: ['service_name'],
    },
  • Standalone stdio MCP server registration for 'get_credential' — a stub that returns an error when the vault bridge is not connected.
    server.tool(
      'get_credential',
      "Retrieve a credential from the Auth Box vault. Returns credential fields filtered by the agent's access policy. Never returns the raw secret unless the policy explicitly allows \"read\" action.",
      {
        service_name: z.string().describe('Name of the service to retrieve credentials for (e.g., "GitHub", "AWS")'),
        fields: z.array(z.string()).optional().describe('Specific fields to retrieve. Omit to get all permitted fields.'),
      },
      async ({ service_name, fields }) => ({
        content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Vault bridge not configured. Connect to a running Auth Box instance.' }) }],
      }),
    );
  • PolicyEngine used by toolGetCredential to evaluate access policies (action permission checks for 'read' action) and handle step-up approval flow.
    export class PolicyEngine {
      private rateLimitCounters = new Map<string, { count: number; windowStart: number }>();
      private pendingApprovals = new Map<string, PendingApproval>();
    
      /** Callback invoked when step-up approval is needed. Set by the MCP server. */
      onApprovalNeeded?: (approval: PendingApproval) => void;
    
      evaluate(policies: AgentPolicy[], request: AccessRequest): AccessDecision {
        if (policies.length === 0) {
          return { allowed: false, reason: 'No policies defined', appliedPolicies: [] };
        }
    
        const enabled = policies
          .filter((p) => p.enabled)
          .sort((a, b) => b.priority - a.priority);
    
        if (enabled.length === 0) {
          return { allowed: false, reason: 'All policies disabled', appliedPolicies: [] };
        }
    
        const applied: string[] = [];
    
        for (const policy of enabled) {
          const result = this.evaluatePolicy(policy, request);
          applied.push(policy.id);
    
          if (!result.allowed) {
            return { allowed: false, reason: result.reason, appliedPolicies: applied };
          }
        }
    
        return { allowed: true, reason: 'All policies passed', appliedPolicies: applied };
      }
    
      private evaluatePolicy(
        policy: AgentPolicy,
        request: AccessRequest,
      ): { allowed: boolean; reason: string } {
        const rules = policy.rules;
    
        switch (policy.policyType) {
          case 'item_scope':
            return this.checkItemScope(rules, request);
          case 'action_perm':
            return this.checkActionPermission(rules, request);
          case 'rate_limit':
            return this.checkRateLimit(request.agentId, rules);
          case 'time_window':
            return this.checkTimeWindow(rules);
          case 'step_up':
            return this.checkStepUp(rules, request);
          default:
            return { allowed: true, reason: 'Unknown policy type, defaulting to allow' };
        }
      }
    
      private checkItemScope(
        rules: PolicyRules,
        request: AccessRequest,
      ): { allowed: boolean; reason: string } {
        if (rules.deniedItemIds?.includes(request.itemId ?? '')) {
          return { allowed: false, reason: 'Item explicitly denied' };
        }
    
        if (rules.allowedItemTypes && request.itemType) {
          const allowed = rules.allowedItemTypes as string[];
          if (!allowed.includes(request.itemType)) {
            return { allowed: false, reason: `Item type '${request.itemType}' not in allowed types` };
          }
        }
    
        if (rules.allowedItemIds && request.itemId) {
          if (!rules.allowedItemIds.includes(request.itemId)) {
            return { allowed: false, reason: 'Item ID not in allowed list' };
          }
        }
    
        if (rules.allowedFolderIds && request.folderId) {
          if (!rules.allowedFolderIds.includes(request.folderId)) {
            return { allowed: false, reason: 'Folder not in allowed list' };
          }
        }
    
        return { allowed: true, reason: 'Item scope check passed' };
      }
    
      private checkActionPermission(
        rules: PolicyRules,
        request: AccessRequest,
      ): { allowed: boolean; reason: string } {
        if (!rules.allowedActions || rules.allowedActions.length === 0) {
          return { allowed: true, reason: 'No action restrictions' };
        }
    
        if (!rules.allowedActions.includes(request.action)) {
          return { allowed: false, reason: `Action '${request.action}' not permitted` };
        }
    
        return { allowed: true, reason: 'Action permitted' };
      }
    
      private checkRateLimit(
        agentId: string,
        rules: PolicyRules,
      ): { allowed: boolean; reason: string } {
        if (!rules.maxRequests || !rules.windowSeconds) {
          return { allowed: true, reason: 'No rate limit configured' };
        }
    
        const now = Date.now();
        const windowMs = rules.windowSeconds * 1000;
        const key = agentId;
        const counter = this.rateLimitCounters.get(key);
    
        if (!counter || now - counter.windowStart > windowMs) {
          this.rateLimitCounters.set(key, { count: 1, windowStart: now });
          return { allowed: true, reason: 'Rate limit check passed' };
        }
    
        if (counter.count >= rules.maxRequests) {
          return { allowed: false, reason: `Rate limit exceeded: ${rules.maxRequests} requests per ${rules.windowSeconds}s` };
        }
    
        counter.count++;
        return { allowed: true, reason: 'Rate limit check passed' };
      }
    
      private checkTimeWindow(rules: PolicyRules): { allowed: boolean; reason: string } {
        if (!rules.allowedHours) {
          return { allowed: true, reason: 'No time window restriction' };
        }
    
        const now = new Date();
        const hour = now.getHours();
        const { start, end } = rules.allowedHours;
    
        const inWindow = start <= end
          ? hour >= start && hour < end
          : hour >= start || hour < end; // wraps midnight
    
        if (!inWindow) {
          return { allowed: false, reason: `Current hour ${hour} outside allowed window ${start}-${end}` };
        }
    
        if (rules.allowedDays) {
          const day = now.getDay();
          if (!rules.allowedDays.includes(day)) {
            return { allowed: false, reason: `Day ${day} not in allowed days` };
          }
        }
    
        return { allowed: true, reason: 'Time window check passed' };
      }
    
      private checkStepUp(
        rules: PolicyRules,
        request: AccessRequest,
      ): { allowed: boolean; reason: string; pendingApprovalId?: string } {
        if (!rules.requireApproval) {
          return { allowed: true, reason: 'No step-up required' };
        }
    
        // Create a pending approval entry so the caller can wait for user decision.
        const approvalId = `stepup_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    
        return {
          allowed: false,
          reason: 'Step-up approval required',
          pendingApprovalId: approvalId,
        };
      }
    
      /**
       * Request step-up approval asynchronously. Returns a promise that resolves
       * when the user approves or rejects the request. Times out after the
       * specified duration (default: 60s).
       */
      requestApproval(
        approvalId: string,
        request: AccessRequest,
        timeoutMs = 60_000,
      ): Promise<boolean> {
        return new Promise<boolean>((resolve) => {
          const pending: PendingApproval = {
            id: approvalId,
            agentId: request.agentId,
            action: request.action,
            itemType: request.itemType,
            itemId: request.itemId,
            reason: 'Step-up approval required for this action',
            createdAt: Date.now(),
            resolve: (approved: boolean) => {
              this.pendingApprovals.delete(approvalId);
              resolve(approved);
            },
          };
    
          this.pendingApprovals.set(approvalId, pending);
    
          // Notify the bridge (extension popup) about the approval request
          this.onApprovalNeeded?.(pending);
    
          // Auto-reject on timeout
          setTimeout(() => {
            if (this.pendingApprovals.has(approvalId)) {
              this.pendingApprovals.delete(approvalId);
              resolve(false);
            }
          }, timeoutMs);
        });
      }
    
      /**
       * Resolve a pending approval (called when user clicks approve/deny in the extension).
       */
      resolveApproval(approvalId: string, approved: boolean): boolean {
        const pending = this.pendingApprovals.get(approvalId);
        if (!pending) return false;
    
        pending.resolve(approved);
        return true;
      }
    
      /** Get all pending approvals (for UI display). */
      getPendingApprovals(): PendingApproval[] {
        return Array.from(this.pendingApprovals.values());
      }
    
      resetCounters(): void {
        this.rateLimitCounters.clear();
      }
    }
Behavior4/5

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

With no annotations provided, the description carries full burden. It discloses that returned fields are filtered by the agent's access policy and that raw secrets are only returned if policy allows 'read'. This is key behavioral information for a sensitive read operation, but it could mention error behavior (e.g., if service not found or insufficient permissions).

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, front-loaded sentences. Every sentence adds essential information: purpose and security behavior. No fluff or repetition.

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?

For a credential retrieval tool, it covers purpose, parameter usage with policy, and security constraints. The absence of an output schema is mitigated by describing what is returned. Could mention response format or example for completeness, but overall adequate.

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 input schema has 100% description coverage, but the description adds value by explaining how the 'fields' parameter interacts with access policy (only returns permitted fields). This extra context elevates the parameter understanding beyond the bare schema definitions.

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?

Clearly states 'Retrieve a credential from the Auth Box vault', specifying a verb and resource. It distinguishes from sibling 'list_available_services' by focusing on actual credential retrieval, and from 'proxy_authenticated_request' which is for making requests, not retrieving stored credentials.

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?

Provides clear context on when to use the tool—to retrieve a credential—and includes a critical security caveat about access policy and 'read' action. However, it does not explicitly state when not to use it or compare with alternatives, though the sibling distinction is implicit.

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/MARUCIE/authbox'

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