Skip to main content
Glama
AlyssonM

HiveAuth MCP Server

by AlyssonM
batchIssueCredentials.ts6.9 kB
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { BatchIssueCredentialsInputSchema } from '../schemas/toolSchemas.js'; import { validateAndSanitizeInput, createValidationErrorResult } from '../utils/validation.js'; /** * Issue multiple verifiable credentials in parallel using HiveAuth * Supports both parallel and sequential processing modes */ export async function batchIssueCredentials(args: any): Promise<CallToolResult> { // Validate and sanitize input const validation = validateAndSanitizeInput(BatchIssueCredentialsInputSchema, args, 'batch_issue_credentials'); if (!validation.success) { return createValidationErrorResult(validation.error!); } const data = validation.data!; const { credentials, parallel = true } = data; const HIVEAUTH_API_BASE_URL = process.env.HIVEAUTH_API_BASE_URL || 'http://localhost:3000'; const ISSUE_ENDPOINT = `${HIVEAUTH_API_BASE_URL}/api/issue`; const startTime = Date.now(); const results: any[] = []; const errors: any[] = []; try { console.log(`[BatchIssue] Starting batch issuance of ${credentials.length} credentials (parallel: ${parallel})`); if (parallel) { // Process credentials in parallel const promises = credentials.map(async (credentialData, index) => { try { const payload = { credentialSubject: credentialData.credentialSubject, type: credentialData.type, vcVersion: credentialData.vcVersion || '2.0', ...(credentialData.expirationDate && { expirationDate: credentialData.expirationDate }), ...(credentialData.validUntil && { validUntil: credentialData.validUntil }), ...(credentialData.context && { context: credentialData.context }), ...(credentialData.issuer && { issuer: credentialData.issuer }) }; const response = await fetch(ISSUE_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: response.statusText })); throw new Error(`Credential ${index + 1}: ${errorData.message}`); } const result = await response.json(); return { index, success: true, credential: result.credential, result }; } catch (error: any) { return { index, success: false, error: error.message }; } }); const allResults = await Promise.all(promises); // Separate successful results from errors allResults.forEach(result => { if (result.success) { results.push(result); } else { errors.push(result); } }); } else { // Process credentials sequentially for (let index = 0; index < credentials.length; index++) { const credentialData = credentials[index]; try { const payload = { credentialSubject: credentialData.credentialSubject, type: credentialData.type, vcVersion: credentialData.vcVersion || '2.0', ...(credentialData.expirationDate && { expirationDate: credentialData.expirationDate }), ...(credentialData.validUntil && { validUntil: credentialData.validUntil }), ...(credentialData.context && { context: credentialData.context }), ...(credentialData.issuer && { issuer: credentialData.issuer }) }; const response = await fetch(ISSUE_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: response.statusText })); throw new Error(`Credential ${index + 1}: ${errorData.message}`); } const result = await response.json(); results.push({ index, success: true, credential: result.credential, result }); } catch (error: any) { errors.push({ index, success: false, error: error.message }); } } } const endTime = Date.now(); const duration = endTime - startTime; const successCount = results.length; const errorCount = errors.length; const totalCount = credentials.length; // Generate summary const summary = [ `📊 **Batch Credential Issuance Results**`, ``, `• Total Credentials: ${totalCount}`, `• Successfully Issued: ${successCount} ✅`, `• Failed: ${errorCount} ❌`, `• Processing Mode: ${parallel ? 'Parallel' : 'Sequential'}`, `• Duration: ${duration}ms`, `• Average per credential: ${Math.round(duration / totalCount)}ms`, `` ]; if (successCount > 0) { summary.push(`**✅ Successfully Issued Credentials:**`); results.forEach(result => { const credId = result.credential?.id || 'unknown'; const vcVersion = result.result?.credential?.['@context']?.includes('credentials/v2') ? '2.0' : '1.1'; summary.push(`${result.index + 1}. **${credId}** (VC ${vcVersion})`); }); summary.push(``); } if (errorCount > 0) { summary.push(`**❌ Failed Credentials:**`); errors.forEach(error => { summary.push(`${error.index + 1}. ${error.error}`); }); summary.push(``); } // Performance insights if (parallel && totalCount > 1) { const sequentialEstimate = duration * totalCount; const speedup = Math.round((sequentialEstimate / duration) * 10) / 10; summary.push(`**⚡ Performance:**`); summary.push(`• Parallel speedup: ~${speedup}x faster than sequential`); summary.push(`• Estimated sequential time: ${sequentialEstimate}ms`); } return { content: [ { type: 'text', text: summary.join('\n') }, { type: 'text', text: `\`\`\`json\n${JSON.stringify({ summary: { total: totalCount, successful: successCount, failed: errorCount, duration: duration, parallel: parallel }, results: results.map(r => ({ index: r.index, credentialId: r.credential?.id, success: true })), errors: errors.map(e => ({ index: e.index, error: e.error, success: false })) }, null, 2)}\n\`\`\`` } ] }; } catch (error: any) { return { content: [ { type: 'text', text: `Failed to issue credentials in batch: ${error.message}` } ], isError: true }; } }

Implementation Reference

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/AlyssonM/hiveauth-mcp'

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