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
| Name | Required | Description | Default |
|---|---|---|---|
| service_name | Yes | Name of the service to retrieve credentials for (e.g., "GitHub", "AWS") | |
| fields | No | Specific fields to retrieve. Omit to get all permitted fields. |
Implementation Reference
- packages/mcp-protocol/src/tools.ts:7-27 (registration)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'], }, - packages/mcp-protocol/src/stdio-server.ts:15-25 (registration)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(); } }