@formio/uag 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +19 -0
- package/README.md +336 -0
- package/lib/UAGFormInterface.d.ts +113 -0
- package/lib/UAGFormInterface.js +371 -0
- package/lib/UAGProjectInterface.d.ts +26 -0
- package/lib/UAGProjectInterface.js +96 -0
- package/lib/config.d.ts +54 -0
- package/lib/config.js +2 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.js +43 -0
- package/lib/router.d.ts +3 -0
- package/lib/router.js +74 -0
- package/lib/template.d.ts +54 -0
- package/lib/template.js +120 -0
- package/lib/templates/allFieldsCollected.md +17 -0
- package/lib/templates/collectedData.md +5 -0
- package/lib/templates/confirmFormSubmission.md +18 -0
- package/lib/templates/fieldCollectedNext.md +14 -0
- package/lib/templates/fieldList.md +5 -0
- package/lib/templates/fieldRules.md +4 -0
- package/lib/templates/fieldValidationErrors.md +7 -0
- package/lib/templates/fields.md +19 -0
- package/lib/templates/formNotFound.md +1 -0
- package/lib/templates/formSubmitted.md +7 -0
- package/lib/templates/getAvailableForms.md +18 -0
- package/lib/templates/getFormFields.md +16 -0
- package/lib/templates/getFormFieldsEmpty.md +4 -0
- package/lib/templates/getFormFieldsError.md +7 -0
- package/lib/templates/getFormFieldsInfo.md +6 -0
- package/lib/templates/getOptionalFields.md +19 -0
- package/lib/templates/noFormsAvailable.md +3 -0
- package/lib/templates/noSubmissionsFound.md +11 -0
- package/lib/templates/submissionNotFound.md +6 -0
- package/lib/templates/submissionPartialIdAmbiguous.md +12 -0
- package/lib/templates/submissionPartialIdNotFound.md +12 -0
- package/lib/templates/submissionSearchError.md +8 -0
- package/lib/templates/submissionUpdateError.md +6 -0
- package/lib/templates/submissionUpdated.md +15 -0
- package/lib/templates/submissionsFound.md +25 -0
- package/lib/templates/submitValidationError.md +7 -0
- package/lib/templates/submittedData.md +4 -0
- package/lib/tools/SchemaBuilder.d.ts +136 -0
- package/lib/tools/SchemaBuilder.js +192 -0
- package/lib/tools/collectData.d.ts +3 -0
- package/lib/tools/collectData.js +72 -0
- package/lib/tools/confirmSubmission.d.ts +3 -0
- package/lib/tools/confirmSubmission.js +56 -0
- package/lib/tools/findSubmission.d.ts +3 -0
- package/lib/tools/findSubmission.js +165 -0
- package/lib/tools/getFieldInfo.d.ts +3 -0
- package/lib/tools/getFieldInfo.js +41 -0
- package/lib/tools/getFormFields.d.ts +3 -0
- package/lib/tools/getFormFields.js +99 -0
- package/lib/tools/getForms.d.ts +3 -0
- package/lib/tools/getForms.js +38 -0
- package/lib/tools/index.d.ts +13 -0
- package/lib/tools/index.js +47 -0
- package/lib/tools/submissionUpdate.d.ts +3 -0
- package/lib/tools/submissionUpdate.js +79 -0
- package/lib/tools/submitForm.d.ts +3 -0
- package/lib/tools/submitForm.js +62 -0
- package/lib/tools/utils.d.ts +28 -0
- package/lib/tools/utils.js +27 -0
- package/package.json +57 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
import { UAGProjectInterface } from "../UAGProjectInterface";
|
|
3
|
+
/**
|
|
4
|
+
* Schema builder for tool input schemas.
|
|
5
|
+
*/
|
|
6
|
+
export declare class SchemaBuilder {
|
|
7
|
+
private project;
|
|
8
|
+
schema: Record<string, z.ZodTypeAny>;
|
|
9
|
+
constructor(project: UAGProjectInterface);
|
|
10
|
+
get valueRules(): string;
|
|
11
|
+
/**
|
|
12
|
+
* Provide the form name.
|
|
13
|
+
*/
|
|
14
|
+
form_name(): this;
|
|
15
|
+
/**
|
|
16
|
+
* The current form data being collected.
|
|
17
|
+
* This is a flat key/value pair object where the key is the `data_path` of the field and
|
|
18
|
+
* the value is the collected value for that field.
|
|
19
|
+
*
|
|
20
|
+
* ```json
|
|
21
|
+
* {
|
|
22
|
+
* "firstName": "Joe",
|
|
23
|
+
* "lastName": "Smith",
|
|
24
|
+
* "company.name": "Acme Corp",
|
|
25
|
+
* "company.address.street": "123 Main St",
|
|
26
|
+
* "children[0].name": "Tom",
|
|
27
|
+
* "children[0].age": 5,
|
|
28
|
+
* "children[1].name": "Sally",
|
|
29
|
+
* "children[1].age": 3
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
form_data(): this;
|
|
34
|
+
/**
|
|
35
|
+
* An array of `data_path`s to get detailed information for specific fields.
|
|
36
|
+
* Each `data_path` within the array should correlate to the fields the user has provided values for.
|
|
37
|
+
*
|
|
38
|
+
* ```json
|
|
39
|
+
* [
|
|
40
|
+
* "firstName",
|
|
41
|
+
* "lastName",
|
|
42
|
+
* "company.name",
|
|
43
|
+
* "company.address.street",
|
|
44
|
+
* "children[0].name",
|
|
45
|
+
* "children[0].age",
|
|
46
|
+
* "children[1].name",
|
|
47
|
+
* "children[1].age"
|
|
48
|
+
* ]
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
field_paths(): this;
|
|
52
|
+
/**
|
|
53
|
+
* The field "criteria" which for the UAG is the "requiredness" of the field.
|
|
54
|
+
*
|
|
55
|
+
* ["all", "required", "optional"]
|
|
56
|
+
*
|
|
57
|
+
*/
|
|
58
|
+
criteria(): this;
|
|
59
|
+
/**
|
|
60
|
+
* An array of search criteria to find matching submissions.
|
|
61
|
+
* All criteria must match (AND logic).
|
|
62
|
+
*
|
|
63
|
+
* ```json
|
|
64
|
+
* [
|
|
65
|
+
* {
|
|
66
|
+
* "data_path": "customer.email",
|
|
67
|
+
* "operator": "equals",
|
|
68
|
+
* "search_value": "<email>"
|
|
69
|
+
* },
|
|
70
|
+
* {
|
|
71
|
+
* "data_path": "customer.name",
|
|
72
|
+
* "operator": "equals",
|
|
73
|
+
* "search_value": "<name>"
|
|
74
|
+
* }
|
|
75
|
+
* ]
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
search_query(): this;
|
|
79
|
+
/**
|
|
80
|
+
* An array of "data_path"(s) for values you wish to include in the result.
|
|
81
|
+
* If not provided, only submission IDs and timestamps are returned.
|
|
82
|
+
*
|
|
83
|
+
* ```json
|
|
84
|
+
* [
|
|
85
|
+
* "email",
|
|
86
|
+
* "firstName",
|
|
87
|
+
* "lastName"
|
|
88
|
+
* ]
|
|
89
|
+
*/
|
|
90
|
+
fields_requested(): this;
|
|
91
|
+
/**
|
|
92
|
+
* The maximum number of results to return. Defaults to 5.
|
|
93
|
+
*/
|
|
94
|
+
limit(): this;
|
|
95
|
+
/**
|
|
96
|
+
* The full ID of the submission to retrieve details for. This value is provided using the `find_submissions` tool.
|
|
97
|
+
*/
|
|
98
|
+
submission_id(): this;
|
|
99
|
+
/**
|
|
100
|
+
* The last 4 characters of a specific submission ID.
|
|
101
|
+
* Should ONLY be used when multiple matches are found from a previous query
|
|
102
|
+
* and the user provides the last 4 characters of the Submission ID to disambiguate.
|
|
103
|
+
*/
|
|
104
|
+
submission_id_partial(): this;
|
|
105
|
+
/**
|
|
106
|
+
* Optional parent component information if collecting data for a specific nested component within a form.
|
|
107
|
+
* If not provided, the tool assumes data is being collected for the root form.
|
|
108
|
+
*
|
|
109
|
+
* ```json
|
|
110
|
+
* {
|
|
111
|
+
* "type": "datagrid",
|
|
112
|
+
* "isTable": true,
|
|
113
|
+
* "data_path": "children"
|
|
114
|
+
* }
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
parent(): this;
|
|
118
|
+
/**
|
|
119
|
+
* An array of field updates to apply to the `form_data`.
|
|
120
|
+
* Each update specifies the `data_path` and the complete new value.
|
|
121
|
+
*
|
|
122
|
+
* ```json
|
|
123
|
+
* [
|
|
124
|
+
* {
|
|
125
|
+
* "data_path": "firstName",
|
|
126
|
+
* "new_value": "John"
|
|
127
|
+
* },
|
|
128
|
+
* {
|
|
129
|
+
* "data_path": "lastName",
|
|
130
|
+
* "new_value": "Doe"
|
|
131
|
+
* }
|
|
132
|
+
* ]
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
updates(): this;
|
|
136
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SchemaBuilder = void 0;
|
|
7
|
+
const zod_1 = __importDefault(require("zod"));
|
|
8
|
+
/**
|
|
9
|
+
* Schema builder for tool input schemas.
|
|
10
|
+
*/
|
|
11
|
+
class SchemaBuilder {
|
|
12
|
+
constructor(project) {
|
|
13
|
+
this.project = project;
|
|
14
|
+
this.schema = {};
|
|
15
|
+
}
|
|
16
|
+
get valueRules() {
|
|
17
|
+
return 'The rules for the value is determined using the `get_form_fields` tool within the **Field Rules** section for this component type, and formatted according the the **Format** section of that component (if applicable). If a value already exists, and it is a string, then you can append/prepend to it to the existing value in the proper order (append/prepend) determined by the user response. If the value is not a string, then replace the value entirely.';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Provide the form name.
|
|
21
|
+
*/
|
|
22
|
+
form_name() {
|
|
23
|
+
this.schema.form_name = zod_1.default.enum((this.project?.formNames || [])).describe('The name of the form being filled out');
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The current form data being collected.
|
|
28
|
+
* This is a flat key/value pair object where the key is the `data_path` of the field and
|
|
29
|
+
* the value is the collected value for that field.
|
|
30
|
+
*
|
|
31
|
+
* ```json
|
|
32
|
+
* {
|
|
33
|
+
* "firstName": "Joe",
|
|
34
|
+
* "lastName": "Smith",
|
|
35
|
+
* "company.name": "Acme Corp",
|
|
36
|
+
* "company.address.street": "123 Main St",
|
|
37
|
+
* "children[0].name": "Tom",
|
|
38
|
+
* "children[0].age": 5,
|
|
39
|
+
* "children[1].name": "Sally",
|
|
40
|
+
* "children[1].age": 3
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
form_data() {
|
|
45
|
+
this.schema.form_data = zod_1.default.record(zod_1.default.string().describe('The `data_path` of collected field.'), zod_1.default.any().describe(`The value collected for the field. ${this.valueRules}`)).optional().default({}).describe('The current data collected so far for the whole form without any updates applied. Include ALL previously collected field data to maintain session state.');
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* An array of `data_path`s to get detailed information for specific fields.
|
|
50
|
+
* Each `data_path` within the array should correlate to the fields the user has provided values for.
|
|
51
|
+
*
|
|
52
|
+
* ```json
|
|
53
|
+
* [
|
|
54
|
+
* "firstName",
|
|
55
|
+
* "lastName",
|
|
56
|
+
* "company.name",
|
|
57
|
+
* "company.address.street",
|
|
58
|
+
* "children[0].name",
|
|
59
|
+
* "children[0].age",
|
|
60
|
+
* "children[1].name",
|
|
61
|
+
* "children[1].age"
|
|
62
|
+
* ]
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
field_paths() {
|
|
66
|
+
this.schema.field_paths = zod_1.default.array(zod_1.default.string()).describe('An array of `data_path`s to get detailed information for specific fields. Each `data_path` within the array should correlate to the fields the user has provided values for. The `data_path`s can be found using the `get_form_fields` tool.');
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The field "criteria" which for the UAG is the "requiredness" of the field.
|
|
71
|
+
*
|
|
72
|
+
* ["all", "required", "optional"]
|
|
73
|
+
*
|
|
74
|
+
*/
|
|
75
|
+
criteria() {
|
|
76
|
+
this.schema.criteria = zod_1.default.enum(['all', 'required', 'optional']).default('required').describe('Returns only the fields of the specified criteria. "all" returns all fields, "required" returns only the required fields, and "optional" returns only optional fields. To start a new form collection flow, you typically want to get only the "required" fields first.');
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* An array of search criteria to find matching submissions.
|
|
81
|
+
* All criteria must match (AND logic).
|
|
82
|
+
*
|
|
83
|
+
* ```json
|
|
84
|
+
* [
|
|
85
|
+
* {
|
|
86
|
+
* "data_path": "customer.email",
|
|
87
|
+
* "operator": "equals",
|
|
88
|
+
* "search_value": "<email>"
|
|
89
|
+
* },
|
|
90
|
+
* {
|
|
91
|
+
* "data_path": "customer.name",
|
|
92
|
+
* "operator": "equals",
|
|
93
|
+
* "search_value": "<name>"
|
|
94
|
+
* }
|
|
95
|
+
* ]
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
search_query() {
|
|
99
|
+
this.schema.search_query = zod_1.default.array(zod_1.default.object({
|
|
100
|
+
data_path: zod_1.default.string().describe('The data path of the field used to search (e.g., "email", "customer.firstName", or "parent.email")'),
|
|
101
|
+
operator: zod_1.default.enum(['equals', 'not_equals', 'contains', 'starts_with', 'ends_with', 'regex', 'in', 'nin', 'greater_than', 'greater_than_equal', 'less_than', 'less_than_equal']).optional().default('contains').describe('The operator to use for matching. "equals" for exact match, "contains" for substring match, "starts_with" or "ends_with" for prefix/suffix match, "regex" for regular expression match, "greater_than" or "less_than" only for numeric values, "in" if searching for multiple values as comma-separated values, "nin" if excluding multiple values as comma-separated values.'),
|
|
102
|
+
search_value: zod_1.default.string().describe('The value of the field to use when searching.')
|
|
103
|
+
}).describe('Object containing the `data_path` and search value. Use `get_form_fields` to get all the field information to populate this search query object.')).describe('Array of search criteria to find matching submissions. All criteria must match (AND logic). Use `get_form_fields` to get `data_path`s.');
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* An array of "data_path"(s) for values you wish to include in the result.
|
|
108
|
+
* If not provided, only submission IDs and timestamps are returned.
|
|
109
|
+
*
|
|
110
|
+
* ```json
|
|
111
|
+
* [
|
|
112
|
+
* "email",
|
|
113
|
+
* "firstName",
|
|
114
|
+
* "lastName"
|
|
115
|
+
* ]
|
|
116
|
+
*/
|
|
117
|
+
fields_requested() {
|
|
118
|
+
this.schema.fields_requested = zod_1.default.array(zod_1.default.string()).optional().describe('An array of "data_path"(s) for values you wish to include in the result. If not provided, only submission IDs and timestamps are returned. Use `get_form_fields` to get valid `data_path`s. (e.g., ["email"] - "What is the email address for the employee Joe Smith?")');
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The maximum number of results to return. Defaults to 5.
|
|
123
|
+
*/
|
|
124
|
+
limit() {
|
|
125
|
+
this.schema.limit = zod_1.default.number().optional().default(5).describe('The maximum number of results to return. Defaults to 5.');
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The full ID of the submission to retrieve details for. This value is provided using the `find_submissions` tool.
|
|
130
|
+
*/
|
|
131
|
+
submission_id() {
|
|
132
|
+
this.schema.submission_id = zod_1.default.string().optional().describe('The full ID of the submission to retrieve details for. This value is provided using the `find_submissions` tool.');
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The last 4 characters of a specific submission ID.
|
|
137
|
+
* Should ONLY be used when multiple matches are found from a previous query
|
|
138
|
+
* and the user provides the last 4 characters of the Submission ID to disambiguate.
|
|
139
|
+
*/
|
|
140
|
+
submission_id_partial() {
|
|
141
|
+
this.schema.submission_id_partial = zod_1.default.string().optional().describe('Last 4 characters of a specific submission ID. Should ONLY be used when multiple matches are found from a previous query and the user provides the last 4 characters of the Submission ID to disambiguate.');
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Optional parent component information if collecting data for a specific nested component within a form.
|
|
146
|
+
* If not provided, the tool assumes data is being collected for the root form.
|
|
147
|
+
*
|
|
148
|
+
* ```json
|
|
149
|
+
* {
|
|
150
|
+
* "type": "datagrid",
|
|
151
|
+
* "isTable": true,
|
|
152
|
+
* "data_path": "children"
|
|
153
|
+
* }
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
parent() {
|
|
157
|
+
this.schema.parent = zod_1.default.object({
|
|
158
|
+
type: zod_1.default.string().describe('The type of the parent component that contains nested components to collect data for. To find the type, you can use the `get_form_fields` tool for this path and look at the **Type** of that field.'),
|
|
159
|
+
label: zod_1.default.string().optional().describe('The label of the parent component that contains nested components to collect data for. This comes from the **Label** property of the field provided by the `get_form_fields` tool.'),
|
|
160
|
+
isTable: zod_1.default.boolean().optional().describe('Indicates if the parent component is a table-like component (datagrid or editgrid). This means that the value of this component is an array of objects, where each row is a new context of data for the fields within. Set this value to `true` if the `get_form_fields` tool says to use `parent.isTable=true`.'),
|
|
161
|
+
isForm: zod_1.default.boolean().optional().describe('Indicates if the parent component is a nested form-like component. This means that the value of this component is in the format of `{data: {...}}` where `{...}` is the context of data for the fields within. Set this value to `true` if the `get_form_fields` tool says to use `parent.isForm=true`.'),
|
|
162
|
+
isContainer: zod_1.default.boolean().optional().describe('Indicates if the parent component is a container-like component. This means that the value of this component is in the format of `{...}` where `{...}` is the context of data for the fields within. Set this value to `true` if the `get_form_fields` tool says to use `parent.isContainer=true`.'),
|
|
163
|
+
data_path: zod_1.default.string().describe('The `data_path` of the parent component that contains nested components to collect data for. To find the data_path, you can use the `get_form_fields` tool.')
|
|
164
|
+
}).optional().describe('Optional parent component information if collecting data for a specific nested component within a form. If not provided, the tool assumes data is being collected for the root form.');
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* An array of field updates to apply to the `form_data`.
|
|
169
|
+
* Each update specifies the `data_path` and the complete new value.
|
|
170
|
+
*
|
|
171
|
+
* ```json
|
|
172
|
+
* [
|
|
173
|
+
* {
|
|
174
|
+
* "data_path": "firstName",
|
|
175
|
+
* "new_value": "John"
|
|
176
|
+
* },
|
|
177
|
+
* {
|
|
178
|
+
* "data_path": "lastName",
|
|
179
|
+
* "new_value": "Doe"
|
|
180
|
+
* }
|
|
181
|
+
* ]
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
updates() {
|
|
185
|
+
this.schema.updates = zod_1.default.array(zod_1.default.object({
|
|
186
|
+
data_path: zod_1.default.string().describe('The `data_path` of the field to update. To find the data_path, you can use the `get_form_fields` tool.'),
|
|
187
|
+
new_value: zod_1.default.any().describe(`The complete new value for the field. ${this.valueRules}`)
|
|
188
|
+
}).describe('A single field update including the `data_path` and new value.')).describe('An array of field updates to apply to the `form_data`. Each update specifies the `data_path` and the complete new value.');
|
|
189
|
+
return this;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
exports.SchemaBuilder = SchemaBuilder;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.collectData = void 0;
|
|
4
|
+
const template_1 = require("../template");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
const lodash_1 = require("lodash");
|
|
7
|
+
const SchemaBuilder_1 = require("./SchemaBuilder");
|
|
8
|
+
const lodash_2 = require("lodash");
|
|
9
|
+
const collectData = async (project) => {
|
|
10
|
+
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.collect_field_data || {}, {
|
|
11
|
+
name: 'collect_field_data',
|
|
12
|
+
title: 'Collect Field Data',
|
|
13
|
+
description: 'Collect data for a form, or a specific field with nested components. Identify the component by its path, validate the provided value, and update the form_data object. After updating, determine the next required field to collect or indicate if all required fields are complete. PREREQUISITE: This tool should only be called after the `get_form_fields` tool AND subsequentially the `get_field_info` tool have been called to understand the structure and validation rules of the fields.',
|
|
14
|
+
inputSchema: (new SchemaBuilder_1.SchemaBuilder(project))
|
|
15
|
+
.form_name()
|
|
16
|
+
.form_data()
|
|
17
|
+
.parent()
|
|
18
|
+
.updates().schema,
|
|
19
|
+
execute: async ({ form_name, form_data, parent, updates }, extra) => {
|
|
20
|
+
const form = await project.getForm(form_name);
|
|
21
|
+
if (!form) {
|
|
22
|
+
return project.mcpResponse(template_1.ResponseTemplate.formNotFound, { formName: form_name }, true);
|
|
23
|
+
}
|
|
24
|
+
// Ensure the parent is undefined if the type or data_path is missing.
|
|
25
|
+
if (!parent?.data_path || !parent.type) {
|
|
26
|
+
parent = undefined;
|
|
27
|
+
}
|
|
28
|
+
// Merge the updates into the current data.
|
|
29
|
+
for (const { data_path, new_value } of updates) {
|
|
30
|
+
form_data[data_path] = new_value;
|
|
31
|
+
}
|
|
32
|
+
// Get any form errors.
|
|
33
|
+
const submission = form.convertToSubmission(form_data);
|
|
34
|
+
// Find next required field that hasn't been filled
|
|
35
|
+
const fields = await form.getFields(submission, extra.authInfo, parent?.data_path);
|
|
36
|
+
if (fields.errors.length > 0) {
|
|
37
|
+
return project.mcpResponse(template_1.ResponseTemplate.fieldValidationErrors, { invalidFields: fields.errors });
|
|
38
|
+
}
|
|
39
|
+
const collectedData = parent ? (0, lodash_2.get)(submission, parent.data_path || '') : submission.data;
|
|
40
|
+
// If no required fields remain, then we can move onto a new tool.
|
|
41
|
+
if (!fields.required.components.length) {
|
|
42
|
+
return project.mcpResponse(template_1.ResponseTemplate.allFieldsCollected, {
|
|
43
|
+
form: form.form,
|
|
44
|
+
parent,
|
|
45
|
+
parentDataPath: (0, utils_1.getParentDataPath)(parent, fields.rowIndex),
|
|
46
|
+
parentLabel: (0, utils_1.getParentLabel)(parent),
|
|
47
|
+
rowIndex: fields.rowIndex,
|
|
48
|
+
dataSummary: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.collectedData, {
|
|
49
|
+
data: form.formatData(collectedData)
|
|
50
|
+
})
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// Collect more required fields.
|
|
54
|
+
return project.mcpResponse(template_1.ResponseTemplate.fieldCollectedNext, {
|
|
55
|
+
parent,
|
|
56
|
+
parentDataPath: (0, utils_1.getParentDataPath)(parent, fields.rowIndex),
|
|
57
|
+
parentLabel: (0, utils_1.getParentLabel)(parent, form.form),
|
|
58
|
+
message: 'Form data collected successfully!',
|
|
59
|
+
rules: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.fieldRules, { rules: Object.entries(fields.required.rules) }),
|
|
60
|
+
fields: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.fields, { fields: fields.required.components }),
|
|
61
|
+
dataSummary: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.collectedData, {
|
|
62
|
+
data: form.formatData(collectedData)
|
|
63
|
+
}),
|
|
64
|
+
progress: {
|
|
65
|
+
collected: Object.keys(form_data).length,
|
|
66
|
+
total: fields.required.components.length + Object.keys(form_data).length
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
exports.collectData = collectData;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.confirmSubmission = void 0;
|
|
4
|
+
const template_1 = require("../template");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
const lodash_1 = require("lodash");
|
|
7
|
+
const SchemaBuilder_1 = require("./SchemaBuilder");
|
|
8
|
+
const confirmSubmission = async (project) => {
|
|
9
|
+
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.confirm_form_submission || {}, {
|
|
10
|
+
name: 'confirm_form_submission',
|
|
11
|
+
title: 'Confirm Form Submission',
|
|
12
|
+
description: 'Show a summary of the collected form data and ask the user for confirmation before submitting',
|
|
13
|
+
inputSchema: (new SchemaBuilder_1.SchemaBuilder(project))
|
|
14
|
+
.form_name()
|
|
15
|
+
.form_data().schema,
|
|
16
|
+
execute: async ({ form_name, form_data }, extra) => {
|
|
17
|
+
const form = await project.getForm(form_name);
|
|
18
|
+
if (!form) {
|
|
19
|
+
return project.mcpResponse(template_1.ResponseTemplate.formNotFound, { formName: form_name }, true);
|
|
20
|
+
}
|
|
21
|
+
// Perform a validation check.
|
|
22
|
+
const submission = form.convertToSubmission(form_data);
|
|
23
|
+
// See which fields need to be filled out.
|
|
24
|
+
const fields = await form.getFields(submission, extra.authInfo);
|
|
25
|
+
if (fields.errors.length > 0) {
|
|
26
|
+
return project.mcpResponse(template_1.ResponseTemplate.fieldValidationErrors, { invalidFields: fields.errors });
|
|
27
|
+
}
|
|
28
|
+
// See if there are still required fields to fill out...
|
|
29
|
+
if (fields.required.components.length) {
|
|
30
|
+
return project.mcpResponse(template_1.ResponseTemplate.fieldCollectedNext, {
|
|
31
|
+
parent: undefined,
|
|
32
|
+
parentLabel: (0, utils_1.getParentLabel)(undefined, form.form),
|
|
33
|
+
message: 'There is additional data that needs to be collected before submission.',
|
|
34
|
+
rules: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.fieldRules, { rules: Object.entries(fields.required.rules) }),
|
|
35
|
+
fields: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.fields, { fields: fields.required.components }),
|
|
36
|
+
dataSummary: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.collectedData, {
|
|
37
|
+
data: form.formatData(submission.data)
|
|
38
|
+
}),
|
|
39
|
+
progress: {
|
|
40
|
+
collected: Object.keys(form_data).length,
|
|
41
|
+
total: fields.required.components.length + Object.keys(form_data).length
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// Confirm the submission.
|
|
46
|
+
return project.mcpResponse(template_1.ResponseTemplate.confirmFormSubmission, {
|
|
47
|
+
form,
|
|
48
|
+
dataSummary: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.collectedData, {
|
|
49
|
+
data: form.formatData(submission.data)
|
|
50
|
+
}),
|
|
51
|
+
currentData: form_data
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
exports.confirmSubmission = confirmSubmission;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findSubmission = void 0;
|
|
4
|
+
const template_1 = require("../template");
|
|
5
|
+
const lodash_1 = require("lodash");
|
|
6
|
+
const SchemaBuilder_1 = require("./SchemaBuilder");
|
|
7
|
+
const error = require('debug')('formio:uag:findSubmission:error');
|
|
8
|
+
const findSubmission = async (project) => {
|
|
9
|
+
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.find_submissions || {}, {
|
|
10
|
+
name: 'find_submissions',
|
|
11
|
+
title: 'Find submissions within a form',
|
|
12
|
+
description: 'Find existing form submissions based on field values (search query). Use this to search for people, records, or data by name, email, phone, position, company, or other field values. Examples: find a contact by name, find someone who works at a company, find records with specific criteria.',
|
|
13
|
+
inputSchema: (new SchemaBuilder_1.SchemaBuilder(project))
|
|
14
|
+
.form_name()
|
|
15
|
+
.search_query()
|
|
16
|
+
.fields_requested()
|
|
17
|
+
.limit()
|
|
18
|
+
.submission_id()
|
|
19
|
+
.submission_id_partial().schema,
|
|
20
|
+
execute: async ({ form_name, search_query, fields_requested, limit, submission_id, submission_id_partial }, extra) => {
|
|
21
|
+
const form = await project.getForm(form_name);
|
|
22
|
+
if (!form) {
|
|
23
|
+
return project.mcpResponse(template_1.ResponseTemplate.formNotFound, { formName: form_name }, true);
|
|
24
|
+
}
|
|
25
|
+
const query = {};
|
|
26
|
+
if (!submission_id) {
|
|
27
|
+
for (const criterion of search_query) {
|
|
28
|
+
if (!criterion.data_path || !criterion.search_value) {
|
|
29
|
+
return project.mcpResponse(template_1.ResponseTemplate.submissionSearchError, {
|
|
30
|
+
form: form.form,
|
|
31
|
+
searchQuery: search_query,
|
|
32
|
+
error: 'Each search criterion must include both data_path and search_value'
|
|
33
|
+
}, true);
|
|
34
|
+
}
|
|
35
|
+
if (criterion.operator === 'regex') {
|
|
36
|
+
query[`data.${criterion.data_path}__regex`] = `/${criterion.search_value}/i`;
|
|
37
|
+
}
|
|
38
|
+
else if (criterion.operator === 'equals') {
|
|
39
|
+
query[`data.${criterion.data_path}`] = criterion.search_value;
|
|
40
|
+
}
|
|
41
|
+
else if (criterion.operator === 'contains') {
|
|
42
|
+
query[`data.${criterion.data_path}__regex`] = `/${criterion.search_value}/i`;
|
|
43
|
+
}
|
|
44
|
+
else if (criterion.operator === 'starts_with') {
|
|
45
|
+
query[`data.${criterion.data_path}__regex`] = `/^${criterion.search_value}/i`;
|
|
46
|
+
}
|
|
47
|
+
else if (criterion.operator === 'ends_with') {
|
|
48
|
+
query[`data.${criterion.data_path}__regex`] = `/${criterion.search_value}$/i`;
|
|
49
|
+
}
|
|
50
|
+
else if (criterion.operator === 'greater_than') {
|
|
51
|
+
query[`data.${criterion.data_path}__gt`] = criterion.search_value;
|
|
52
|
+
}
|
|
53
|
+
else if (criterion.operator === 'greater_than_equal') {
|
|
54
|
+
query[`data.${criterion.data_path}__gte`] = criterion.search_value;
|
|
55
|
+
}
|
|
56
|
+
else if (criterion.operator === 'less_than') {
|
|
57
|
+
query[`data.${criterion.data_path}__lt`] = criterion.search_value;
|
|
58
|
+
}
|
|
59
|
+
else if (criterion.operator === 'less_than_equal') {
|
|
60
|
+
query[`data.${criterion.data_path}__lte`] = criterion.search_value;
|
|
61
|
+
}
|
|
62
|
+
else if (criterion.operator === 'not_equals') {
|
|
63
|
+
query[`data.${criterion.data_path}__ne`] = criterion.search_value;
|
|
64
|
+
}
|
|
65
|
+
else if (criterion.operator === 'in') {
|
|
66
|
+
const values = criterion.search_value.split(',').map((v) => v.trim());
|
|
67
|
+
query[`data.${criterion.data_path}__in`] = values;
|
|
68
|
+
}
|
|
69
|
+
else if (criterion.operator === 'nin') {
|
|
70
|
+
const values = criterion.search_value.split(',').map((v) => v.trim());
|
|
71
|
+
query[`data.${criterion.data_path}__nin`] = values;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
return project.mcpResponse(template_1.ResponseTemplate.submissionSearchError, {
|
|
75
|
+
form: form.form,
|
|
76
|
+
searchQuery: search_query,
|
|
77
|
+
error: `Unsupported operator "${criterion.operator}"`
|
|
78
|
+
}, true);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
let submissions = [];
|
|
84
|
+
if (submission_id) {
|
|
85
|
+
const result = await form.loadSubmission(submission_id, extra.authInfo);
|
|
86
|
+
if (result) {
|
|
87
|
+
submissions = [result];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
query.limit = ((0, lodash_1.isNumber)(limit) && limit > 0) ? limit : 10;
|
|
92
|
+
const result = await form.find(query, extra.authInfo);
|
|
93
|
+
submissions = result.items;
|
|
94
|
+
}
|
|
95
|
+
if (!submissions || submissions.length === 0) {
|
|
96
|
+
return project.mcpResponse(template_1.ResponseTemplate.noSubmissionsFound, {
|
|
97
|
+
form: form.form,
|
|
98
|
+
searchQuery: search_query,
|
|
99
|
+
formName: form_name
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (submission_id_partial && submissions.length > 1) {
|
|
103
|
+
const matchingSubmissions = submissions.filter(sub => sub._id && sub._id.toString().toLowerCase().includes(submission_id_partial.toLowerCase()));
|
|
104
|
+
if (matchingSubmissions.length === 0) {
|
|
105
|
+
return project.mcpResponse(template_1.ResponseTemplate.submissionPartialIdNotFound, {
|
|
106
|
+
form: form.form,
|
|
107
|
+
searchQuery: search_query,
|
|
108
|
+
partialId: submission_id_partial,
|
|
109
|
+
availableSubmissions: submissions.map(sub => ({
|
|
110
|
+
...form.formatSubmission(sub),
|
|
111
|
+
partialId: sub._id ? sub._id.toString().slice(-4) : 'N/A'
|
|
112
|
+
}))
|
|
113
|
+
}, true);
|
|
114
|
+
}
|
|
115
|
+
if (matchingSubmissions.length > 1) {
|
|
116
|
+
return project.mcpResponse(template_1.ResponseTemplate.submissionPartialIdAmbiguous, {
|
|
117
|
+
form: form.form,
|
|
118
|
+
searchQuery: search_query,
|
|
119
|
+
partialId: submission_id_partial,
|
|
120
|
+
matchingSubmissions: matchingSubmissions.map(sub => ({
|
|
121
|
+
...form.formatSubmission(sub),
|
|
122
|
+
fullId: sub._id,
|
|
123
|
+
partialId: sub._id ? sub._id.toString().slice(-4) : 'N/A'
|
|
124
|
+
}))
|
|
125
|
+
}, true);
|
|
126
|
+
}
|
|
127
|
+
submissions = matchingSubmissions;
|
|
128
|
+
}
|
|
129
|
+
function getFieldValues(submission) {
|
|
130
|
+
if (!fields_requested || fields_requested.length === 0) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
const data = [];
|
|
134
|
+
for (const fieldPath of fields_requested) {
|
|
135
|
+
data.push({ path: fieldPath, value: (0, lodash_1.get)(submission.data, fieldPath, null) });
|
|
136
|
+
}
|
|
137
|
+
return data;
|
|
138
|
+
}
|
|
139
|
+
return project.mcpResponse(template_1.ResponseTemplate.submissionsFound, {
|
|
140
|
+
form: form.form,
|
|
141
|
+
searchQuery: JSON.stringify(search_query),
|
|
142
|
+
submissions: submissions.map(sub => {
|
|
143
|
+
return {
|
|
144
|
+
_id: sub._id,
|
|
145
|
+
created: sub.created,
|
|
146
|
+
modified: sub.modified,
|
|
147
|
+
data: getFieldValues(sub),
|
|
148
|
+
partialId: sub._id ? sub._id.toString().slice(-4) : 'N/A'
|
|
149
|
+
};
|
|
150
|
+
}),
|
|
151
|
+
resultCount: submissions.length
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
error('Error searching submissions:', err);
|
|
156
|
+
return project.mcpResponse(template_1.ResponseTemplate.submissionSearchError, {
|
|
157
|
+
form: form.form,
|
|
158
|
+
searchQuery: JSON.stringify(search_query),
|
|
159
|
+
error: err instanceof Error ? err.message : 'Unknown error'
|
|
160
|
+
}, true);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
exports.findSubmission = findSubmission;
|