adjust mcp
This commit is contained in:
@@ -4,6 +4,7 @@ module Mcp
|
|||||||
module HandleRequest
|
module HandleRequest
|
||||||
TOOLS = [
|
TOOLS = [
|
||||||
Mcp::Tools::SearchTemplates,
|
Mcp::Tools::SearchTemplates,
|
||||||
|
Mcp::Tools::LoadTemplate,
|
||||||
Mcp::Tools::CreateTemplate,
|
Mcp::Tools::CreateTemplate,
|
||||||
Mcp::Tools::SendDocuments,
|
Mcp::Tools::SendDocuments,
|
||||||
Mcp::Tools::SearchDocuments
|
Mcp::Tools::SearchDocuments
|
||||||
|
|||||||
@@ -6,28 +6,23 @@ module Mcp
|
|||||||
SCHEMA = {
|
SCHEMA = {
|
||||||
name: 'create_template',
|
name: 'create_template',
|
||||||
title: 'Create Template',
|
title: 'Create Template',
|
||||||
description: 'Create a template from a PDF. Provide a URL or base64-encoded file content.',
|
description: 'Create a document template. Provide a URL to upload a PDF/DOCX file, or provide only a name ' \
|
||||||
|
'to create an empty template and receive an edit URL where the file can be uploaded via the UI.',
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
url: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'URL of the document file to upload'
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Base64-encoded file content'
|
|
||||||
},
|
|
||||||
filename: {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Filename with extension (required when using file)'
|
|
||||||
},
|
|
||||||
name: {
|
name: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Template name (defaults to filename)'
|
description: 'Template name (used as the template name and required when url is not provided)'
|
||||||
}
|
},
|
||||||
|
url: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Optional URL of a PDF or DOCX file to upload. If omitted, an empty template is ' \
|
||||||
|
'created and the returned edit_url can be used to upload a file via the UI.'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
required: %w[name]
|
||||||
|
},
|
||||||
annotations: {
|
annotations: {
|
||||||
readOnlyHint: false,
|
readOnlyHint: false,
|
||||||
destructiveHint: false,
|
destructiveHint: false,
|
||||||
@@ -44,23 +39,22 @@ module Mcp
|
|||||||
|
|
||||||
account = current_user.account
|
account = current_user.account
|
||||||
|
|
||||||
if arguments['file'].present?
|
template = Template.new(
|
||||||
tempfile = Tempfile.new
|
account:,
|
||||||
tempfile.binmode
|
author: current_user,
|
||||||
tempfile.write(Base64.decode64(arguments['file']))
|
folder: account.default_template_folder,
|
||||||
tempfile.rewind
|
name: arguments['name'].to_s.presence || 'New Template',
|
||||||
|
fields: [],
|
||||||
|
schema: []
|
||||||
|
)
|
||||||
|
|
||||||
filename = arguments['filename'] || 'document.pdf'
|
if arguments['url'].present?
|
||||||
elsif arguments['url'].present?
|
|
||||||
tempfile = Tempfile.new
|
tempfile = Tempfile.new
|
||||||
tempfile.binmode
|
tempfile.binmode
|
||||||
tempfile.write(DownloadUtils.call(arguments['url'], validate: true).body)
|
tempfile.write(DownloadUtils.call(arguments['url'], validate: true).body)
|
||||||
tempfile.rewind
|
tempfile.rewind
|
||||||
|
|
||||||
filename = File.basename(URI.decode_www_form_component(arguments['url']))
|
filename = File.basename(URI.decode_www_form_component(arguments['url']))
|
||||||
else
|
|
||||||
return { content: [{ type: 'text', text: 'Provide either url or file' }], isError: true }
|
|
||||||
end
|
|
||||||
|
|
||||||
file = ActionDispatch::Http::UploadedFile.new(
|
file = ActionDispatch::Http::UploadedFile.new(
|
||||||
tempfile:,
|
tempfile:,
|
||||||
@@ -68,13 +62,7 @@ module Mcp
|
|||||||
type: Marcel::MimeType.for(tempfile)
|
type: Marcel::MimeType.for(tempfile)
|
||||||
)
|
)
|
||||||
|
|
||||||
template = Template.new(
|
template.name = arguments['name'].presence || File.basename(filename, '.*')
|
||||||
account:,
|
|
||||||
author: current_user,
|
|
||||||
folder: account.default_template_folder,
|
|
||||||
name: arguments['name'].presence || File.basename(filename, '.*')
|
|
||||||
)
|
|
||||||
|
|
||||||
template.save!
|
template.save!
|
||||||
|
|
||||||
documents, = Templates::CreateAttachments.call(template, { files: [file] }, extract_fields: true)
|
documents, = Templates::CreateAttachments.call(template, { files: [file] }, extract_fields: true)
|
||||||
@@ -85,6 +73,9 @@ module Mcp
|
|||||||
end
|
end
|
||||||
|
|
||||||
template.update!(schema:)
|
template.update!(schema:)
|
||||||
|
else
|
||||||
|
template.save!
|
||||||
|
end
|
||||||
|
|
||||||
WebhookUrls.enqueue_events(template, 'template.created')
|
WebhookUrls.enqueue_events(template, 'template.created')
|
||||||
|
|
||||||
@@ -104,7 +95,7 @@ module Mcp
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
|
||||||
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Mcp
|
||||||
|
module Tools
|
||||||
|
module LoadTemplate
|
||||||
|
SCHEMA = {
|
||||||
|
name: 'load_template',
|
||||||
|
title: 'Load Template',
|
||||||
|
description: 'Load a template with its fields. Each field includes name, type, and the signing role name.',
|
||||||
|
inputSchema: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
template_id: {
|
||||||
|
type: 'integer',
|
||||||
|
description: 'Template identifier'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: %w[template_id]
|
||||||
|
},
|
||||||
|
annotations: {
|
||||||
|
readOnlyHint: true,
|
||||||
|
destructiveHint: false,
|
||||||
|
idempotentHint: true,
|
||||||
|
openWorldHint: false
|
||||||
|
}
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def call(arguments, _current_user, current_ability)
|
||||||
|
template = Template.accessible_by(current_ability).find_by(id: arguments['template_id'])
|
||||||
|
|
||||||
|
return { content: [{ type: 'text', text: 'Template not found' }], isError: true } unless template
|
||||||
|
|
||||||
|
current_ability.authorize!(:read, template)
|
||||||
|
|
||||||
|
submitters_index = template.submitters.index_by { |s| s['uuid'] }
|
||||||
|
|
||||||
|
roles = template.submitters.pluck('name')
|
||||||
|
|
||||||
|
fields = template.fields.filter_map do |field|
|
||||||
|
next if field['name'].blank?
|
||||||
|
|
||||||
|
{
|
||||||
|
name: field['name'],
|
||||||
|
type: field['type'],
|
||||||
|
role: submitters_index[field['submitter_uuid']]&.dig('name')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
{
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: {
|
||||||
|
id: template.id,
|
||||||
|
name: template.name,
|
||||||
|
roles: roles,
|
||||||
|
fields: fields
|
||||||
|
}.to_json
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -31,6 +31,27 @@ module Mcp
|
|||||||
phone: {
|
phone: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Submitter phone number in E.164 format'
|
description: 'Submitter phone number in E.164 format'
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Signing role name from the template'
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
type: 'array',
|
||||||
|
description: 'Prefill field values for this submitter (fields become readonly)',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
description: 'Field name'
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
description: 'Prefilled value for the field'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: %w[name value]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,9 +80,17 @@ module Mcp
|
|||||||
return { content: [{ type: 'text', text: 'Template has no fields' }], isError: true } if template.fields.blank?
|
return { content: [{ type: 'text', text: 'Template has no fields' }], isError: true } if template.fields.blank?
|
||||||
|
|
||||||
submitters = (arguments['submitters'] || []).map do |s|
|
submitters = (arguments['submitters'] || []).map do |s|
|
||||||
s.slice('email', 'name', 'role', 'phone')
|
attrs = s.slice('email', 'name', 'role', 'phone').compact_blank
|
||||||
.compact_blank
|
|
||||||
.with_indifferent_access
|
fields = Array.wrap(s['fields']).filter_map do |f|
|
||||||
|
next if f['name'].blank?
|
||||||
|
|
||||||
|
{ 'name' => f['name'], 'default_value' => f['value'], 'readonly' => true }
|
||||||
|
end
|
||||||
|
|
||||||
|
attrs['fields'] = fields if fields.present?
|
||||||
|
|
||||||
|
attrs.with_indifferent_access
|
||||||
end
|
end
|
||||||
|
|
||||||
submissions = Submissions.create_from_submitters(
|
submissions = Submissions.create_from_submitters(
|
||||||
|
|||||||
Reference in New Issue
Block a user