generate_presentation_request
Create secure wallet-ready presentation requests using DIF PEX v2.0, supporting multiple formats, human-readable instructions, and verifiable credential operations within the HiveAuth ecosystem.
Instructions
Generate complete presentation request flows with DIF PEX v2.0 definitions. Creates wallet-ready requests with security features, human-readable instructions, and multiple format support.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| callbackUrl | No | URL where presentation should be submitted | |
| challenge | No | Challenge nonce for presentation security | |
| domain | No | Domain for presentation binding | |
| expiresIn | No | Request expiration time in seconds (1 minute to 24 hours) | |
| includeInstructions | No | Whether to include human-readable instructions | |
| presentationDefinition | Yes | DIF PEX Presentation Definition | |
| requestFormat | No | Format for the presentation request | json |
| verifierDid | No | DID of the verifier requesting the presentation |
Implementation Reference
- Main handler function that validates input using the schema, generates presentation request structure with security features, supports JWT/JSON formats, analyzes complexity, generates instructions, and returns detailed markdown-formatted results.export async function generatePresentationRequest(args: unknown): Promise<CallToolResult> { const validation = validateAndSanitizeInput( TOOL_SCHEMAS.generate_presentation_request, args, 'generate_presentation_request' ); if (!validation.success) { return createValidationErrorResult(validation.error!); } const data = validation.data!; const startTime = Date.now(); try { const { presentationDefinition, verifierDid, callbackUrl, challenge, domain, expiresIn, requestFormat, includeInstructions } = data; // Generate unique challenge if not provided const requestChallenge = challenge || randomBytes(32).toString('hex'); // Calculate expiration time const now = new Date(); const expiresAt = new Date(now.getTime() + expiresIn! * 1000); // Generate request ID const requestId = `pex_req_${randomBytes(16).toString('hex')}`; // Create the presentation request structure const presentationRequest = { id: requestId, type: 'PresentationRequest', from: verifierDid || 'unknown_verifier', created_time: now.toISOString(), expires_time: expiresAt.toISOString(), challenge: requestChallenge, domain: domain, presentation_definition: presentationDefinition, callback_url: callbackUrl, format: requestFormat }; // Generate human-readable instructions const instructions = includeInstructions ? generateInstructions(presentationDefinition) : null; // Create different formats based on requestFormat let formattedRequest: any; let contentType: string; if (requestFormat! === 'jwt') { // For JWT format, we'd typically sign this, but for demo purposes we'll show the structure const jwtPayload = { iss: verifierDid || 'unknown_verifier', aud: 'credential_holder', iat: Math.floor(now.getTime() / 1000), exp: Math.floor(expiresAt.getTime() / 1000), nonce: requestChallenge, presentation_definition: presentationDefinition, callback_url: callbackUrl }; formattedRequest = { format: 'jwt', jwt_header: { alg: 'EdDSA', typ: 'JWT', kid: verifierDid ? `${verifierDid}#key-1` : 'verification_key' }, jwt_payload: jwtPayload, note: 'In production, this would be a signed JWT token' }; contentType = 'application/jwt'; } else { formattedRequest = presentationRequest; contentType = 'application/json'; } // Analyze the request complexity const analysisResults = analyzePresentationRequest(presentationDefinition); const processingTime = Date.now() - startTime; return { content: [ { type: 'text', text: `✅ **Presentation Request Generated Successfully** **Request Overview:** • **Request ID:** ${requestId} • **Format:** ${requestFormat!.toUpperCase()} • **Content Type:** ${contentType} • **Processing Time:** ${processingTime}ms • **Expires:** ${expiresAt.toISOString()} (in ${Math.round(expiresIn! / 60)} minutes) **🎯 Request Details:** • **Verifier DID:** ${verifierDid || 'Not specified'} • **Challenge:** ${requestChallenge.substring(0, 16)}... (${requestChallenge.length} chars) • **Domain:** ${domain || 'Not specified'} • **Callback URL:** ${callbackUrl || 'Not specified'} **📋 Presentation Definition Analysis:** • **Definition ID:** ${presentationDefinition.id} • **Purpose:** ${presentationDefinition.purpose || 'Not specified'} • **Input Descriptors:** ${presentationDefinition.input_descriptors.length} • **Complexity:** ${analysisResults.complexity} • **Estimated Response Time:** ${analysisResults.estimatedTime} **🔍 Requirements Summary:** ${analysisResults.requirementsSummary.map((req: any, index: number) => `${index + 1}. **${req.id}**: ${req.description}` ).join('\n')} ${includeInstructions ? `**📖 Human-Readable Instructions:** ${instructions} ` : ''}**📄 Generated Request:** \`\`\`json ${JSON.stringify(formattedRequest, null, 2)} \`\`\` **🚀 Usage Instructions:** **For Wallet Integration:** 1. Present this request to the credential holder's wallet 2. The wallet should evaluate available credentials against the presentation definition 3. User selects which credentials to include in the response 4. Wallet creates a verifiable presentation matching the requirements 5. Presentation is submitted to the callback URL (if provided) **For API Integration:** \`\`\`javascript // Send request to wallet/holder const response = await fetch(walletEndpoint, { method: 'POST', headers: { 'Content-Type': '${contentType}', 'Accept': 'application/json' }, body: JSON.stringify(${requestFormat! === 'jwt' ? 'formattedRequest.jwt_payload' : 'formattedRequest'}) }); \`\`\` **For QR Code Generation:** The request can be encoded as a QR code for mobile wallet scanning: \`\`\` presentation-request://${Buffer.from(JSON.stringify(formattedRequest)).toString('base64')} \`\`\` **🔒 Security Features:** • **Challenge-Response:** ${requestChallenge ? 'Implemented' : 'Not used'} • **Domain Binding:** ${domain ? 'Configured' : 'Not configured'} • **Time-bound:** ${expiresIn!}s expiration • **Request Integrity:** ${requestFormat! === 'jwt' ? 'JWT signed (in production)' : 'JSON format'} **🔧 Verification Steps:** 1. **Receive Presentation:** Wait for holder to submit presentation 2. **Validate Challenge:** Ensure challenge matches this request 3. **Check Expiration:** Verify presentation submitted before expiry 4. **Evaluate Compliance:** Use \`evaluate_presentation\` tool to check requirements 5. **Verify Signatures:** Validate all credential and presentation signatures ${analysisResults.recommendations.length > 0 ? `**💡 Optimization Recommendations:** ${analysisResults.recommendations.map((rec, index) => `${index + 1}. ${rec}`).join('\n')} ` : ''}🎉 **Request ready for deployment!** Share this with credential holders or integrate into your verification workflow. ` } ] }; } catch (error: any) { const processingTime = Date.now() - startTime; return { content: [ { type: 'text', text: `❌ **Presentation Request Generation Error** **Error:** ${error.message} **Processing Time:** ${processingTime}ms **Tool:** generate_presentation_request **📋 Input Summary:** • **Definition ID:** ${data.presentationDefinition.id} • **Request Format:** ${data.requestFormat} • **Verifier DID:** ${data.verifierDid || 'Not specified'} • **Expires In:** ${data.expiresIn}s **🔧 Troubleshooting:** 1. **Definition Validity:** Ensure presentation definition is valid DIF PEX v2.0 format 2. **URL Validation:** Check that callback URL is properly formatted if provided 3. **DID Format:** Verify verifier DID follows proper DID specification 4. **Time Bounds:** Ensure expiresIn is reasonable (60s - 24h) **🌐 Common Issues:** • Invalid presentation definition structure • Malformed callback URL • Unrealistic expiration times • Missing required fields in input descriptors For assistance, validate your presentation definition using the \`validate_presentation_definition\` tool first.` } ], isError: true }; } }
- src/schemas/toolSchemas.ts:165-174 (schema)Zod input schema defining parameters for the generate_presentation_request tool, including presentation definition, verifier details, security parameters, expiration, format, and instructions flag.export const GeneratePresentationRequestInputSchema = z.object({ presentationDefinition: PresentationDefinitionSchema, verifierDid: z.string().optional().describe('DID of the verifier requesting the presentation'), callbackUrl: z.string().url('Callback URL must be valid').optional().describe('URL where presentation should be submitted'), challenge: z.string().optional().describe('Challenge nonce for presentation security'), domain: z.string().optional().describe('Domain for presentation binding'), expiresIn: z.number().int().min(60).max(86400).default(3600).describe('Request expiration time in seconds (1 minute to 24 hours)'), requestFormat: z.enum(['jwt', 'json']).default('json').describe('Format for the presentation request'), includeInstructions: z.boolean().default(true).describe('Whether to include human-readable instructions') });
- src/index.ts:119-120 (registration)Switch case in main server handler that dispatches calls to the generatePresentationRequest function.case 'generate_presentation_request': return await generatePresentationRequest(args);
- src/utils/schemaConverter.ts:77-80 (registration)Tool definition in TOOL_DEFINITIONS array used by createMCPTools() to register the tool with MCP server, including name, description, and input schema.name: 'generate_presentation_request', description: 'Generate complete presentation request flows with DIF PEX v2.0 definitions. Creates wallet-ready requests with security features, human-readable instructions, and multiple format support.', inputSchema: TOOL_SCHEMAS.generate_presentation_request }
- src/schemas/toolSchemas.ts:190-190 (schema)TOOL_SCHEMAS mapping entry that associates the tool name with its input schema for use in validation and tool definitions.generate_presentation_request: GeneratePresentationRequestInputSchema
- Helper function that analyzes the presentation definition complexity, field counts, and generates user-friendly summaries and recommendations.function analyzePresentationRequest(presentationDefinition: any) { const inputDescriptors = presentationDefinition.input_descriptors || []; const totalFields = inputDescriptors.reduce((sum: number, desc: any) => sum + (desc.constraints?.fields?.length || 0), 0 ); const complexity = totalFields === 0 ? 'Very Low' : totalFields <= 3 ? 'Low' : totalFields <= 8 ? 'Medium' : totalFields <= 15 ? 'High' : 'Very High'; const estimatedTime = totalFields === 0 ? '< 30 seconds' : totalFields <= 3 ? '30-60 seconds' : totalFields <= 8 ? '1-2 minutes' : totalFields <= 15 ? '2-5 minutes' : '5+ minutes'; const requirementsSummary = inputDescriptors.map((desc: any) => ({ id: desc.id, description: desc.purpose || desc.name || `Credential matching descriptor ${desc.id}` })); const recommendations: string[] = []; if (inputDescriptors.length > 5) { recommendations.push('Consider reducing the number of input descriptors for better user experience'); } if (totalFields > 10) { recommendations.push('High field count may lead to longer verification times'); } if (!presentationDefinition.purpose) { recommendations.push('Add a purpose field to help users understand the request'); } return { complexity, estimatedTime, requirementsSummary, recommendations }; }