validate_presentation_definition
Validate presentation definitions for compliance with DIF PEX v2.0 specifications. Perform checks for adherence to best practices, identify issues, and provide actionable improvement recommendations.
Instructions
Validate presentation definitions against DIF PEX v2.0 specification. Performs comprehensive compliance checking, best practices validation, and provides improvement recommendations.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| checkSchemas | No | Whether to validate referenced credential schemas | |
| presentationDefinition | Yes | DIF PEX Presentation Definition | |
| strict | No | Whether to perform strict DIF PEX v2.0 validation |
Implementation Reference
- Core handler function implementing the validate_presentation_definition MCP tool. Performs detailed validation of DIF Presentation Exchange v2.0 presentation definitions including ID checks, input descriptors, constraints, schemas, status, submission requirements, with categorized issues, warnings, and recommendations.export async function validatePresentationDefinition(args: unknown): Promise<CallToolResult> { const validation = validateAndSanitizeInput( TOOL_SCHEMAS.validate_presentation_definition, args, 'validate_presentation_definition' ); if (!validation.success) { return createValidationErrorResult(validation.error!); } const data = validation.data!; const startTime = Date.now(); try { const validationIssues: string[] = []; const warnings: string[] = []; const recommendations: string[] = []; const { presentationDefinition, strict, checkSchemas } = data; // Basic structure validation (already validated by Zod, but we'll add business logic checks) // 1. ID validation if (!presentationDefinition.id || presentationDefinition.id.trim().length === 0) { validationIssues.push('Presentation definition ID is required and cannot be empty'); } else if (!/^[a-zA-Z0-9_-]+$/.test(presentationDefinition.id)) { warnings.push('ID should only contain alphanumeric characters, underscores, and hyphens for maximum compatibility'); } // 2. Input descriptors validation if (!presentationDefinition.input_descriptors || presentationDefinition.input_descriptors.length === 0) { validationIssues.push('At least one input descriptor is required'); } else { // Check for duplicate input descriptor IDs const descriptorIds = presentationDefinition.input_descriptors.map(desc => desc.id); const duplicateIds = descriptorIds.filter((id, index) => descriptorIds.indexOf(id) !== index); if (duplicateIds.length > 0) { validationIssues.push(`Duplicate input descriptor IDs found: ${duplicateIds.join(', ')}`); } // Validate each input descriptor presentationDefinition.input_descriptors.forEach((descriptor, index) => { const prefix = `Input Descriptor ${index + 1} (${descriptor.id})`; // ID validation if (!descriptor.id || descriptor.id.trim().length === 0) { validationIssues.push(`${prefix}: ID is required and cannot be empty`); } // Purpose/Name recommendation if (!descriptor.purpose && !descriptor.name) { recommendations.push(`${prefix}: Consider adding a 'purpose' or 'name' for better user experience`); } // Constraints validation if (descriptor.constraints) { if (descriptor.constraints.fields && descriptor.constraints.fields.length > 0) { descriptor.constraints.fields.forEach((field, fieldIndex) => { const fieldPrefix = `${prefix}, Field ${fieldIndex + 1}`; // Path validation if (!field.path || field.path.length === 0) { validationIssues.push(`${fieldPrefix}: At least one path is required`); } else { // JSONPath validation field.path.forEach((path, pathIndex) => { if (!path.startsWith('$.')) { warnings.push(`${fieldPrefix}, Path ${pathIndex + 1}: JSONPath should start with '$.' for clarity`); } }); } // Filter validation if (field.filter) { const filterKeys = Object.keys(field.filter); const validFilterKeys = ['type', 'const', 'contains', 'pattern', 'format', 'minimum', 'maximum', 'minLength', 'maxLength']; const invalidKeys = filterKeys.filter(key => !validFilterKeys.includes(key)); if (invalidKeys.length > 0) { warnings.push(`${fieldPrefix}: Unrecognized filter keys: ${invalidKeys.join(', ')}`); } } // Purpose recommendation if (!field.purpose) { recommendations.push(`${fieldPrefix}: Consider adding a 'purpose' to explain why this field is required`); } }); } else if (strict) { warnings.push(`${prefix}: No field constraints specified - this may accept any credential type`); } // Status validation if (descriptor.constraints.statuses) { const statuses = descriptor.constraints.statuses; Object.keys(statuses).forEach(statusType => { if (!['active', 'suspended', 'revoked'].includes(statusType)) { warnings.push(`${prefix}: Unrecognized status type '${statusType}'`); } }); } } else if (strict) { warnings.push(`${prefix}: No constraints specified - this may be overly permissive`); } // Schema validation if (descriptor.schema && descriptor.schema.length > 0) { descriptor.schema.forEach((schema, schemaIndex) => { const schemaPrefix = `${prefix}, Schema ${schemaIndex + 1}`; try { new URL(schema.uri); } catch (error) { validationIssues.push(`${schemaPrefix}: Invalid schema URI '${schema.uri}'`); } if (checkSchemas) { // Note: In a real implementation, you would fetch and validate the schema recommendations.push(`${schemaPrefix}: Schema validation not implemented - would validate ${schema.uri}`); } }); } }); } // 3. Submission requirements validation (if present) if (presentationDefinition.submission_requirements && presentationDefinition.submission_requirements.length > 0) { recommendations.push('Submission requirements detected - ensure they are properly structured according to DIF PEX v2.0 spec'); } // 4. Overall recommendations if (!presentationDefinition.purpose) { recommendations.push('Consider adding a top-level purpose to explain the intent of this presentation request'); } if (!presentationDefinition.name) { recommendations.push('Consider adding a name for better user identification of this presentation request'); } const processingTime = Date.now() - startTime; const isValid = validationIssues.length === 0; const hasWarnings = warnings.length > 0; const hasRecommendations = recommendations.length > 0; return { content: [ { type: 'text', text: `${isValid ? '✅' : '❌'} **Presentation Definition Validation ${isValid ? 'Passed' : 'Failed'}** **Validation Summary:** • **Status:** ${isValid ? 'Valid' : 'Invalid'} • **Processing Time:** ${processingTime}ms • **Strict Mode:** ${strict ? 'Enabled' : 'Disabled'} • **Schema Checking:** ${checkSchemas ? 'Enabled' : 'Disabled'} **📊 Results Overview:** • **Errors:** ${validationIssues.length} • **Warnings:** ${warnings.length} • **Recommendations:** ${recommendations.length} • **Input Descriptors:** ${presentationDefinition.input_descriptors?.length || 0} **🎯 Presentation Definition Details:** • **ID:** ${presentationDefinition.id} • **Name:** ${presentationDefinition.name || 'Not specified'} • **Purpose:** ${presentationDefinition.purpose || 'Not specified'} ${validationIssues.length > 0 ? `**❌ Validation Errors:** ${validationIssues.map((issue, index) => `${index + 1}. ${issue}`).join('\n')} ` : ''}${hasWarnings ? `**⚠️ Warnings:** ${warnings.map((warning, index) => `${index + 1}. ${warning}`).join('\n')} ` : ''}${hasRecommendations ? `**💡 Recommendations:** ${recommendations.map((rec, index) => `${index + 1}. ${rec}`).join('\n')} ` : ''}**📋 Input Descriptor Analysis:** ${presentationDefinition.input_descriptors?.map((desc, index) => { const fieldCount = desc.constraints?.fields?.length || 0; const hasConstraints = !!desc.constraints; const hasSchemas = desc.schema && desc.schema.length > 0; return `• **${desc.id}** ${desc.name ? `(${desc.name})` : ''} - Purpose: ${desc.purpose || 'Not specified'} - Fields: ${fieldCount} constraint${fieldCount !== 1 ? 's' : ''} - Schemas: ${hasSchemas ? desc.schema!.length : 0} referenced - Status: ${hasConstraints ? 'Has constraints' : 'No constraints'}`; }).join('\n') || '(No input descriptors)'} **🔍 DIF PEX v2.0 Compliance:** • **Structure:** ${isValid ? 'Compliant' : 'Non-compliant'} • **Required Fields:** ${presentationDefinition.id && presentationDefinition.input_descriptors ? 'Present' : 'Missing'} • **Best Practices:** ${hasWarnings ? 'Some issues found' : 'Following best practices'} ${isValid ? '🎉 **This presentation definition is valid and ready for use in credential requests.**' : '🔧 **Please address the validation errors before using this presentation definition.**'} ${hasRecommendations || hasWarnings ? '\n💡 **Next Steps:** Consider implementing the recommendations above for better interoperability and user experience.' : ''} ` } ] }; } catch (error: any) { const processingTime = Date.now() - startTime; return { content: [ { type: 'text', text: `❌ **Presentation Definition Validation Error** **Error:** ${error.message} **Processing Time:** ${processingTime}ms **Tool:** validate_presentation_definition **📋 Input Summary:** • **Definition ID:** ${data.presentationDefinition.id} • **Input Descriptors:** ${data.presentationDefinition.input_descriptors?.length || 0} • **Strict Mode:** ${data.strict} **🔧 Troubleshooting:** 1. **Definition Structure:** Ensure the presentation definition follows DIF PEX v2.0 format 2. **Required Fields:** Verify all required fields (id, input_descriptors) are present 3. **Field Constraints:** Check that constraint fields use valid JSONPath expressions 4. **Schema References:** Ensure any referenced schemas are accessible if checkSchemas is enabled **📚 Reference:** • DIF Presentation Exchange v2.0: https://identity.foundation/presentation-exchange/spec/v2.0.0/ • JSONPath specification: https://tools.ietf.org/rfc/rfc9535.txt For detailed validation requirements, consult the DIF PEX v2.0 specification.` } ], isError: true }; } }
- src/schemas/toolSchemas.ts:159-163 (schema)Zod input schema for the validate_presentation_definition tool, validating the presentationDefinition object against DIF PEX structure, with optional strict mode and schema checking flags.export const ValidatePresentationDefinitionInputSchema = z.object({ presentationDefinition: PresentationDefinitionSchema, strict: z.boolean().default(true).describe('Whether to perform strict DIF PEX v2.0 validation'), checkSchemas: z.boolean().default(false).describe('Whether to validate referenced credential schemas') });
- src/schemas/toolSchemas.ts:189-189 (schema)TOOL_SCHEMAS mapping entry that associates the tool name with its Zod input schema for lookup in tool implementations and registration.validate_presentation_definition: ValidatePresentationDefinitionInputSchema,
- src/utils/schemaConverter.ts:72-74 (registration)Tool metadata definition used by createMCPTools() to generate the MCP Tool object including name, description, and converted JSON input schema.name: 'validate_presentation_definition', description: 'Validate presentation definitions against DIF PEX v2.0 specification. Performs comprehensive compliance checking, best practices validation, and provides improvement recommendations.', inputSchema: TOOL_SCHEMAS.validate_presentation_definition
- src/index.ts:116-117 (registration)Dispatch registration in the MCP server\'s CallToolRequest handler switch statement, routing tool calls to the validatePresentationDefinition handler function.case 'validate_presentation_definition': return await validatePresentationDefinition(args);