From 1a503fdffff2cb50bd68ac506b185a79cca182fd Mon Sep 17 00:00:00 2001 From: Ortes Date: Wed, 22 Apr 2026 12:44:54 -0400 Subject: [PATCH] feat(mcp): accept field-fill params on send_documents tool Mirror the REST API by accepting values, fields, readonly_fields, metadata, external_id, completed, role and message per submitter. Input is normalized via Submissions::NormalizeParamUtils so field names resolve to UUIDs and default_value entries merge into values. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/mcp/tools/send_documents.rb | 76 ++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/lib/mcp/tools/send_documents.rb b/lib/mcp/tools/send_documents.rb index 25b1be72..ca97aec7 100644 --- a/lib/mcp/tools/send_documents.rb +++ b/lib/mcp/tools/send_documents.rb @@ -3,6 +3,11 @@ module Mcp module Tools module SendDocuments + SUBMITTER_KEYS = %w[ + email name phone role completed external_id + metadata values readonly_fields message fields + ].freeze + SCHEMA = { name: 'send_documents', title: 'Send Documents', @@ -31,6 +36,60 @@ module Mcp phone: { type: 'string', description: 'Submitter phone number in E.164 format' + }, + role: { + type: 'string', + description: 'Template role name (required when the template has multiple roles)' + }, + completed: { + type: 'boolean', + description: 'Mark this submitter as already completed (skips the signing UI)' + }, + external_id: { + type: 'string', + description: 'Your application identifier for this submitter' + }, + metadata: { + type: 'object', + description: 'Arbitrary key/value metadata stored on the submitter', + additionalProperties: true + }, + values: { + type: 'object', + description: 'Pre-fill field values, keyed by field UUID or field name', + additionalProperties: true + }, + readonly_fields: { + type: 'array', + description: 'Field names to mark read-only for this submitter', + items: { type: 'string' } + }, + message: { + type: 'object', + description: 'Custom email subject/body sent to this submitter', + properties: { + subject: { type: 'string' }, + body: { type: 'string' } + } + }, + fields: { + type: 'array', + description: 'Per-field overrides (default_value, title, required, validation, etc.)', + items: { + type: 'object', + properties: { + name: { type: 'string', description: 'Field name as defined in the template' }, + uuid: { type: 'string', description: 'Field UUID (use instead of name to target a specific field)' }, + default_value: { description: 'Pre-filled value the submitter can still edit; string or array depending on field type' }, + value: { description: 'Locked value (cannot be edited); string or array depending on field type' }, + title: { type: 'string' }, + description: { type: 'string' }, + readonly: { type: 'boolean' }, + required: { type: 'boolean' }, + validation_pattern: { type: 'string' }, + invalid_message: { type: 'string' } + } + } } } } @@ -48,7 +107,7 @@ module Mcp module_function - # rubocop:disable Metrics/MethodLength + # rubocop:disable Metrics/MethodLength, Metrics/AbcSize def call(arguments, current_user, current_ability) template = Template.accessible_by(current_ability).find_by(id: arguments['template_id']) @@ -59,17 +118,19 @@ module Mcp return { content: [{ type: 'text', text: 'Template has no fields' }], isError: true } if template.fields.blank? submitters = (arguments['submitters'] || []).map do |s| - s.slice('email', 'name', 'role', 'phone') - .compact_blank - .with_indifferent_access + s.slice(*SUBMITTER_KEYS).compact.with_indifferent_access end + submissions_attrs = [{ submitters: submitters }.with_indifferent_access] + + Submissions::NormalizeParamUtils.normalize_submissions_params!(submissions_attrs, template, purpose: :api) + submissions = Submissions.create_from_submitters( template:, user: current_user, source: :api, submitters_order: 'random', - submissions_attrs: { submitters: submitters }, + submissions_attrs:, params: { 'send_email' => true, 'submitters' => submitters } ) @@ -105,10 +166,11 @@ module Mcp } ] } - rescue Submissions::CreateFromSubmitters::BaseError => e + rescue Submitters::NormalizeValues::BaseError, Submissions::CreateFromSubmitters::BaseError, + DownloadUtils::UnableToDownload => e { content: [{ type: 'text', text: e.message }], isError: true } end - # rubocop:enable Metrics/MethodLength + # rubocop:enable Metrics/MethodLength, Metrics/AbcSize end end end