Skip to main content
Glama
AlyssonM

HiveAuth MCP Server

by AlyssonM

batch_issue_credentials

Issue up to 50 verifiable credentials in parallel or sequentially with HiveAuth MCP Server. Includes error handling and performance metrics for efficient credential management.

Instructions

Issue multiple verifiable credentials in parallel or sequentially. Supports up to 50 credentials per batch with comprehensive error handling and performance metrics.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
credentialsYes
parallelNoWhether to process credentials in parallel

Implementation Reference

  • Core handler function implementing batch_issue_credentials tool. Validates input using Zod schema, issues multiple credentials (up to 50) via HiveAuth API in parallel or sequential mode, handles errors, provides performance metrics, and returns formatted text/JSON results.
    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 }; } }
  • Zod input schema for batch_issue_credentials tool defining the expected parameters: array of credentials (1-50) and optional parallel flag.
    export const BatchIssueCredentialsInputSchema = z.object({ credentials: z.array(IssueCredentialInputSchema).min(1).max(50, 'Maximum 50 credentials per batch'), parallel: z.boolean().default(true).describe('Whether to process credentials in parallel') });
  • Tool registration in TOOL_DEFINITIONS array, converted to MCP Tool object including name, description, and JSON schema for listTools endpoint.
    name: 'batch_issue_credentials', description: 'Issue multiple verifiable credentials in parallel or sequentially. Supports up to 50 credentials per batch with comprehensive error handling and performance metrics.', inputSchema: TOOL_SCHEMAS.batch_issue_credentials },
  • src/index.ts:101-102 (registration)
    Dispatch handler in main server request handler switch statement that routes callTool requests to the batchIssueCredentials implementation.
    case 'batch_issue_credentials': return await batchIssueCredentials(args);
  • Mapping of tool name to its input schema in TOOL_SCHEMAS object used by schemaConverter for registration.
    batch_issue_credentials: BatchIssueCredentialsInputSchema,

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