@formio/uag 1.13.2 → 2.0.0-rc.1
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/README.md +235 -535
- package/bin/uag-eval.js +95 -0
- package/bin/uag.js +73 -0
- package/lib/UAGFormInterface.d.ts +60 -2
- package/lib/UAGFormInterface.js +172 -46
- package/lib/UAGProjectInterface.d.ts +37 -5
- package/lib/UAGProjectInterface.js +166 -33
- package/lib/actionLogs.d.ts +33 -0
- package/lib/actionLogs.js +147 -0
- package/lib/actions/runTask.d.ts +43 -0
- package/lib/actions/runTask.js +323 -0
- package/lib/agent/anonymous.d.ts +34 -0
- package/lib/agent/anonymous.js +98 -0
- package/lib/agent/chat.d.ts +113 -0
- package/lib/agent/chat.js +539 -0
- package/lib/agent/engines.d.ts +13 -0
- package/lib/agent/engines.js +37 -0
- package/lib/agent/index.d.ts +4 -0
- package/lib/agent/index.js +18 -0
- package/lib/agent/remote.d.ts +67 -0
- package/lib/agent/remote.js +229 -0
- package/lib/agent/requestId.d.ts +4 -0
- package/lib/agent/requestId.js +19 -0
- package/lib/agent/router.d.ts +31 -0
- package/lib/agent/router.js +295 -0
- package/lib/agent/stubs.d.ts +26 -0
- package/lib/agent/stubs.js +26 -0
- package/lib/agent/tools.d.ts +10 -0
- package/lib/agent/tools.js +33 -0
- package/lib/config.d.ts +67 -0
- package/lib/config.js +19 -0
- package/lib/database.d.ts +75 -0
- package/lib/database.js +229 -0
- package/lib/formflow.d.ts +40 -0
- package/lib/formflow.js +28 -0
- package/lib/identity.d.ts +53 -0
- package/lib/identity.js +107 -0
- package/lib/index.d.ts +41 -0
- package/lib/index.js +179 -7
- package/lib/router.js +15 -25
- package/lib/runner.d.ts +147 -0
- package/lib/runner.js +345 -0
- package/lib/runs.d.ts +89 -0
- package/lib/runs.js +74 -0
- package/lib/template.js +7 -9
- package/lib/templates/uagComponentNotFound.md +5 -5
- package/lib/tools/SchemaBuilder.d.ts +7 -2
- package/lib/tools/SchemaBuilder.js +11 -3
- package/lib/tools/agentDecide.d.ts +8 -0
- package/lib/tools/agentDecide.js +57 -0
- package/lib/tools/agentProvide.d.ts +13 -0
- package/lib/tools/agentProvide.js +130 -0
- package/lib/tools/collectData.js +1 -1
- package/lib/tools/confirmSubmission.js +1 -1
- package/lib/tools/fetchExternalData.js +8 -17
- package/lib/tools/findSubmission.js +62 -56
- package/lib/tools/getFieldInfo.js +1 -1
- package/lib/tools/getFormFields.js +1 -1
- package/lib/tools/getForms.js +1 -1
- package/lib/tools/index.d.ts +3 -1
- package/lib/tools/index.js +21 -14
- package/lib/tools/submissionUpdate.js +1 -1
- package/lib/tools/submitForm.js +1 -1
- package/package.json +41 -18
- package/lib/tools/agentProvideData.d.ts +0 -3
- package/lib/tools/agentProvideData.js +0 -62
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { UAGProjectInterface } from "../UAGProjectInterface";
|
|
2
|
+
import { ToolInfo } from "./utils";
|
|
3
|
+
/**
|
|
4
|
+
* `agent_provide`: a task's criteria, the submission's values and the task's
|
|
5
|
+
* fields, as instructions for an agent to fill those fields. It reads only; the
|
|
6
|
+
* agent writes its values with `submission_update`.
|
|
7
|
+
*/
|
|
8
|
+
export declare const agentProvide: (project: UAGProjectInterface) => Promise<ToolInfo>;
|
|
9
|
+
/**
|
|
10
|
+
* `agent_provide_data`: the earlier name of `agent_provide`, kept so MCP clients
|
|
11
|
+
* and integrations that call it keep working. Removed in a future major release.
|
|
12
|
+
*/
|
|
13
|
+
export declare const agentProvideData: (project: UAGProjectInterface) => Promise<ToolInfo>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.agentProvideData = exports.agentProvide = void 0;
|
|
4
|
+
const template_1 = require("../template");
|
|
5
|
+
const lodash_1 = require("lodash");
|
|
6
|
+
const SchemaBuilder_1 = require("./SchemaBuilder");
|
|
7
|
+
const annotations_1 = require("./annotations");
|
|
8
|
+
const formflow_1 = require("../formflow");
|
|
9
|
+
const DESCRIPTION = 'Provides a mechanism for agents to process existing submission data, understand it, and then to provide additional data values provided a criteria and form context.';
|
|
10
|
+
/**
|
|
11
|
+
* Agentic workflow over MCP is Agentic Formflow: it needs a license or a project
|
|
12
|
+
* on Form.io's hosted service, as the Agent API does. The stub engines do not
|
|
13
|
+
* count, since this tool calls no engine. Answers the refusal, or null.
|
|
14
|
+
*/
|
|
15
|
+
async function refusal(project) {
|
|
16
|
+
const state = await (0, formflow_1.formflow)(project).state();
|
|
17
|
+
if (state.allowed && (state.reason === 'licensed' || state.reason === 'hosted'))
|
|
18
|
+
return null;
|
|
19
|
+
return {
|
|
20
|
+
error: 'Agentic workflows require a license (LICENSE_KEY) that includes the UAG (an online license with UAG servers, or an offline license ' +
|
|
21
|
+
'with the "uag" option), or a project on Form.io\'s hosted service. ' +
|
|
22
|
+
'MCP tools and conversation forms do not need either.',
|
|
23
|
+
code: 'license_required',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* `agent_provide`: a task's criteria, the submission's values and the task's
|
|
28
|
+
* fields, as instructions for an agent to fill those fields. It reads only; the
|
|
29
|
+
* agent writes its values with `submission_update`.
|
|
30
|
+
*/
|
|
31
|
+
const agentProvide = async (project) => {
|
|
32
|
+
// Overrides written for the tool's earlier name still apply, except a rename,
|
|
33
|
+
// which belongs to the deprecated tool and would clash with it.
|
|
34
|
+
const overrides = project.config?.toolOverrides;
|
|
35
|
+
const own = overrides?.agent_provide || (overrides?.agent_provide_data ? (0, lodash_1.omit)(overrides.agent_provide_data, 'name') : {});
|
|
36
|
+
// Merged into a new object: the overrides are shared config, and the tools are
|
|
37
|
+
// built again for every request.
|
|
38
|
+
return (0, lodash_1.defaultsDeep)({}, own, provideTool(project, 'agent_provide'));
|
|
39
|
+
};
|
|
40
|
+
exports.agentProvide = agentProvide;
|
|
41
|
+
/**
|
|
42
|
+
* `agent_provide_data`: the earlier name of `agent_provide`, kept so MCP clients
|
|
43
|
+
* and integrations that call it keep working. Removed in a future major release.
|
|
44
|
+
*/
|
|
45
|
+
const agentProvideData = async (project) => {
|
|
46
|
+
const tool = provideTool(project, 'agent_provide_data');
|
|
47
|
+
const execute = tool.execute;
|
|
48
|
+
return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.agent_provide_data, {
|
|
49
|
+
...tool,
|
|
50
|
+
title: 'Agent Provide Data (deprecated)',
|
|
51
|
+
description: `Deprecated: use \`agent_provide\`, which takes the same arguments. ${DESCRIPTION}`,
|
|
52
|
+
execute: (args, extra) => {
|
|
53
|
+
(0, formflow_1.warnLegacy)('agent_provide_data', 'the agent_provide_data MCP tool; use agent_provide.');
|
|
54
|
+
return execute(args, extra);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
exports.agentProvideData = agentProvideData;
|
|
59
|
+
function provideTool(project, name) {
|
|
60
|
+
return {
|
|
61
|
+
name,
|
|
62
|
+
title: 'Agent Provide',
|
|
63
|
+
description: DESCRIPTION,
|
|
64
|
+
annotations: (0, annotations_1.reads)('Process submission data'),
|
|
65
|
+
inputSchema: (new SchemaBuilder_1.SchemaBuilder(project))
|
|
66
|
+
.form_name()
|
|
67
|
+
.submission_id()
|
|
68
|
+
.task()
|
|
69
|
+
.persona().schema,
|
|
70
|
+
execute: async (args, extra) => {
|
|
71
|
+
const { form_name, submission_id } = args;
|
|
72
|
+
const refused = await refusal(project);
|
|
73
|
+
if (refused)
|
|
74
|
+
return project.mcpJSONResponse(refused, true);
|
|
75
|
+
let requested;
|
|
76
|
+
try {
|
|
77
|
+
requested = (0, formflow_1.resolveTaskName)(args, name);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
if (err instanceof formflow_1.FormflowError)
|
|
81
|
+
return project.mcpJSONResponse({ error: err.message }, true);
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
const form = await project.getForm(form_name);
|
|
85
|
+
if (!form) {
|
|
86
|
+
return project.mcpResponse(template_1.ResponseTemplate.formNotFound, { formName: form_name }, true);
|
|
87
|
+
}
|
|
88
|
+
const task = requested || Object.keys(form.uagFields)[0] || 'default';
|
|
89
|
+
// Templates get the task under both names: deployments may have
|
|
90
|
+
// customized a template that still renders `persona`.
|
|
91
|
+
const names = { task, persona: task };
|
|
92
|
+
const uag = form.uagFields[task];
|
|
93
|
+
if (!uag) {
|
|
94
|
+
return project.mcpResponse(template_1.ResponseTemplate.uagComponentNotFound, {
|
|
95
|
+
...names,
|
|
96
|
+
error: 'no_uag'
|
|
97
|
+
}, true);
|
|
98
|
+
}
|
|
99
|
+
const submission = await form.loadSubmission(submission_id, extra.authInfo);
|
|
100
|
+
if (!submission) {
|
|
101
|
+
return project.mcpResponse(template_1.ResponseTemplate.submissionNotFound, {
|
|
102
|
+
form: form.form,
|
|
103
|
+
submissionId: submission_id
|
|
104
|
+
}, true);
|
|
105
|
+
}
|
|
106
|
+
if (!uag.criteria) {
|
|
107
|
+
return project.mcpResponse(template_1.ResponseTemplate.uagComponentNotFound, {
|
|
108
|
+
...names,
|
|
109
|
+
error: 'no_criteria'
|
|
110
|
+
}, true);
|
|
111
|
+
}
|
|
112
|
+
const agentFields = Object.values(uag.components);
|
|
113
|
+
if (agentFields.length === 0) {
|
|
114
|
+
return project.mcpResponse(template_1.ResponseTemplate.uagComponentNotFound, {
|
|
115
|
+
...names,
|
|
116
|
+
error: 'no_fields'
|
|
117
|
+
}, true);
|
|
118
|
+
}
|
|
119
|
+
// Collect more required fields.
|
|
120
|
+
return project.mcpResponse(template_1.ResponseTemplate.agentProcessData, {
|
|
121
|
+
...names,
|
|
122
|
+
criteria: uag.criteria,
|
|
123
|
+
values: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.fieldValues, {
|
|
124
|
+
data: form.formatData(submission.data)
|
|
125
|
+
}),
|
|
126
|
+
fields: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.fieldList, { fields: agentFields })
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
package/lib/tools/collectData.js
CHANGED
|
@@ -6,7 +6,7 @@ const lodash_1 = require("lodash");
|
|
|
6
6
|
const SchemaBuilder_1 = require("./SchemaBuilder");
|
|
7
7
|
const annotations_1 = require("./annotations");
|
|
8
8
|
const collectData = async (project) => {
|
|
9
|
-
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.collect_field_data
|
|
9
|
+
return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.collect_field_data, {
|
|
10
10
|
name: 'collect_field_data',
|
|
11
11
|
title: 'Collect Field Data',
|
|
12
12
|
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.',
|
|
@@ -6,7 +6,7 @@ const lodash_1 = require("lodash");
|
|
|
6
6
|
const SchemaBuilder_1 = require("./SchemaBuilder");
|
|
7
7
|
const annotations_1 = require("./annotations");
|
|
8
8
|
const confirmSubmission = async (project) => {
|
|
9
|
-
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.confirm_form_submission
|
|
9
|
+
return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.confirm_form_submission, {
|
|
10
10
|
name: 'confirm_form_submission',
|
|
11
11
|
title: 'Confirm Form Submission',
|
|
12
12
|
description: 'Show a summary of the collected form data if the user wishes to see a summary of the collected data',
|
|
@@ -41,9 +41,10 @@ function extractValue(item, component) {
|
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
43
|
* Check if a component is a select-type component with external data (url or resource).
|
|
44
|
+
* A radio can load its options from a URL too.
|
|
44
45
|
*/
|
|
45
46
|
function isExternalSelect(component) {
|
|
46
|
-
if (component.type !== 'select' && component.type !== 'selectboxes')
|
|
47
|
+
if (component.type !== 'select' && component.type !== 'selectboxes' && component.type !== 'radio')
|
|
47
48
|
return false;
|
|
48
49
|
const dataSrc = component.dataSrc;
|
|
49
50
|
return dataSrc === 'url' || dataSrc === 'resource';
|
|
@@ -71,7 +72,7 @@ function interpolateHeaders(headers, context) {
|
|
|
71
72
|
return result;
|
|
72
73
|
}
|
|
73
74
|
const fetchExternalData = async (project) => {
|
|
74
|
-
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.fetch_external_data
|
|
75
|
+
return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.fetch_external_data, {
|
|
75
76
|
name: 'fetch_external_data',
|
|
76
77
|
title: 'Fetch External Data',
|
|
77
78
|
description: 'Fetch external data for a component that loads data from an external URL, a Form.io Resource, or a DataSource. Use this tool when `get_form_fields` indicates that a field requires calling `fetch_external_data` to retrieve its data. For select components, this returns the available options. For datasource components, this returns the fetched data. You can optionally provide a `search_value` to filter results server-side. If the component\'s URL or headers contain interpolation tokens (e.g. `{{ data.someField }}`), you MUST provide `form_data` with the current collected data so the tokens can be resolved.',
|
|
@@ -123,24 +124,14 @@ const fetchExternalData = async (project) => {
|
|
|
123
124
|
if (!isExternalSelect(component)) {
|
|
124
125
|
return project.mcpResponse(template_1.ResponseTemplate.fetchExternalDataError, {
|
|
125
126
|
dataPath: data_path,
|
|
126
|
-
error: `Component at "${data_path}" is of type "${component.type}" and does not load external data. This tool only supports select
|
|
127
|
+
error: `Component at "${data_path}" is of type "${component.type}" and does not load external data. This tool only supports select, selectboxes and radio components with dataSrc "url" or "resource", and datasource components.`
|
|
127
128
|
}, true);
|
|
128
129
|
}
|
|
130
|
+
// isExternalSelect() has checked that dataSrc is "url" or "resource".
|
|
129
131
|
const selectComponent = component;
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
dataPath: data_path,
|
|
134
|
-
error: `Component at "${data_path}" has dataSrc "${dataSrc}". Only "url" and "resource" are supported by this tool.`
|
|
135
|
-
}, true);
|
|
136
|
-
}
|
|
137
|
-
let items = [];
|
|
138
|
-
if (dataSrc === 'url') {
|
|
139
|
-
items = await fetchUrlData(selectComponent, search_value, extra.authInfo, evalContext);
|
|
140
|
-
}
|
|
141
|
-
else if (dataSrc === 'resource') {
|
|
142
|
-
items = await fetchResourceData(selectComponent, search_value, extra.authInfo, project, evalContext);
|
|
143
|
-
}
|
|
132
|
+
const items = selectComponent.dataSrc === 'url'
|
|
133
|
+
? await fetchUrlData(selectComponent, search_value, extra.authInfo, evalContext)
|
|
134
|
+
: await fetchResourceData(selectComponent, search_value, extra.authInfo, project, evalContext);
|
|
144
135
|
const options = items.map(item => ({
|
|
145
136
|
label: extractLabel(item, selectComponent, evalContext),
|
|
146
137
|
value: extractValue(item, selectComponent)
|
|
@@ -7,8 +7,36 @@ const lodash_1 = require("lodash");
|
|
|
7
7
|
const SchemaBuilder_1 = require("./SchemaBuilder");
|
|
8
8
|
const annotations_1 = require("./annotations");
|
|
9
9
|
const error = require('debug')('formio:uag:findSubmission:error');
|
|
10
|
+
/** The query parameter suffix for each operator that compares the value as given. */
|
|
11
|
+
const COMPARISON_SUFFIXES = new Map([
|
|
12
|
+
['equals', ''],
|
|
13
|
+
['greater_than', '__gt'],
|
|
14
|
+
['greater_than_equal', '__gte'],
|
|
15
|
+
['less_than', '__lt'],
|
|
16
|
+
['less_than_equal', '__lte'],
|
|
17
|
+
['not_equals', '__ne'],
|
|
18
|
+
]);
|
|
19
|
+
/** The query parameter suffix for each operator that takes comma-separated values. */
|
|
20
|
+
const LIST_SUFFIXES = new Map([
|
|
21
|
+
['in', '__in'],
|
|
22
|
+
['nin', '__nin'],
|
|
23
|
+
]);
|
|
24
|
+
/** The `__regex` pattern for each operator that matches the value literally. */
|
|
25
|
+
const LITERAL_PATTERNS = new Map([
|
|
26
|
+
['contains', (value) => `/${(0, utils_1.escapeSearchPattern)(value)}/i`],
|
|
27
|
+
['starts_with', (value) => `/^${(0, utils_1.escapeSearchPattern)(value)}/i`],
|
|
28
|
+
['ends_with', (value) => `/${(0, utils_1.escapeSearchPattern)(value)}$/i`],
|
|
29
|
+
]);
|
|
30
|
+
/**
|
|
31
|
+
* A regex operator's pattern with every "/" in it, bare or escaped, written as
|
|
32
|
+
* the `\x2f` escape, which matches the same character. See escapeSearchPattern()
|
|
33
|
+
* for why the query parameter cannot carry a slash.
|
|
34
|
+
*/
|
|
35
|
+
const withoutSlashes = (pattern) => pattern.replace(/\\.|\//g, (match) => (match === '/' || match === '\\/') ? '\\x2f' : match);
|
|
36
|
+
/** The last 4 characters of a submission's id, which users disambiguate with. */
|
|
37
|
+
const partialIdOf = (submission) => submission._id ? submission._id.toString().slice(-4) : 'N/A';
|
|
10
38
|
const findSubmission = async (project) => {
|
|
11
|
-
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.find_submissions
|
|
39
|
+
return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.find_submissions, {
|
|
12
40
|
name: 'find_submissions',
|
|
13
41
|
title: 'Find submissions within a form',
|
|
14
42
|
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. To retrieve the total count of records within a form, use this tool without providing a `search_query`.',
|
|
@@ -36,6 +64,8 @@ const findSubmission = async (project) => {
|
|
|
36
64
|
error: 'Each search criterion must include both data_path and search_value'
|
|
37
65
|
}, true);
|
|
38
66
|
}
|
|
67
|
+
let key;
|
|
68
|
+
let value;
|
|
39
69
|
if (criterion.operator === 'regex') {
|
|
40
70
|
// The value is a pattern by request here, so it is passed through
|
|
41
71
|
// unescaped. It is still compiled first: the server drops a filter
|
|
@@ -51,42 +81,20 @@ const findSubmission = async (project) => {
|
|
|
51
81
|
error: `"${criterion.search_value}" is not a valid regular expression for "${criterion.data_path}". Use the "contains" operator to search for it literally.`
|
|
52
82
|
}, true);
|
|
53
83
|
}
|
|
54
|
-
|
|
84
|
+
key = `data.${criterion.data_path}__regex`;
|
|
85
|
+
value = `/${withoutSlashes(criterion.search_value)}/i`;
|
|
55
86
|
}
|
|
56
|
-
else if (criterion.operator
|
|
57
|
-
|
|
87
|
+
else if (LITERAL_PATTERNS.has(criterion.operator)) {
|
|
88
|
+
key = `data.${criterion.data_path}__regex`;
|
|
89
|
+
value = LITERAL_PATTERNS.get(criterion.operator)(criterion.search_value);
|
|
58
90
|
}
|
|
59
|
-
else if (criterion.operator
|
|
60
|
-
|
|
91
|
+
else if (COMPARISON_SUFFIXES.has(criterion.operator)) {
|
|
92
|
+
key = `data.${criterion.data_path}${COMPARISON_SUFFIXES.get(criterion.operator)}`;
|
|
93
|
+
value = criterion.search_value;
|
|
61
94
|
}
|
|
62
|
-
else if (criterion.operator
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
else if (criterion.operator === 'ends_with') {
|
|
66
|
-
query[`data.${criterion.data_path}__regex`] = `/${(0, utils_1.escapeSearchPattern)(criterion.search_value)}$/i`;
|
|
67
|
-
}
|
|
68
|
-
else if (criterion.operator === 'greater_than') {
|
|
69
|
-
query[`data.${criterion.data_path}__gt`] = criterion.search_value;
|
|
70
|
-
}
|
|
71
|
-
else if (criterion.operator === 'greater_than_equal') {
|
|
72
|
-
query[`data.${criterion.data_path}__gte`] = criterion.search_value;
|
|
73
|
-
}
|
|
74
|
-
else if (criterion.operator === 'less_than') {
|
|
75
|
-
query[`data.${criterion.data_path}__lt`] = criterion.search_value;
|
|
76
|
-
}
|
|
77
|
-
else if (criterion.operator === 'less_than_equal') {
|
|
78
|
-
query[`data.${criterion.data_path}__lte`] = criterion.search_value;
|
|
79
|
-
}
|
|
80
|
-
else if (criterion.operator === 'not_equals') {
|
|
81
|
-
query[`data.${criterion.data_path}__ne`] = criterion.search_value;
|
|
82
|
-
}
|
|
83
|
-
else if (criterion.operator === 'in') {
|
|
84
|
-
const values = criterion.search_value.split(',').map((v) => v.trim());
|
|
85
|
-
query[`data.${criterion.data_path}__in`] = values;
|
|
86
|
-
}
|
|
87
|
-
else if (criterion.operator === 'nin') {
|
|
88
|
-
const values = criterion.search_value.split(',').map((v) => v.trim());
|
|
89
|
-
query[`data.${criterion.data_path}__nin`] = values;
|
|
95
|
+
else if (LIST_SUFFIXES.has(criterion.operator)) {
|
|
96
|
+
key = `data.${criterion.data_path}${LIST_SUFFIXES.get(criterion.operator)}`;
|
|
97
|
+
value = criterion.search_value.split(',').map((v) => v.trim());
|
|
90
98
|
}
|
|
91
99
|
else {
|
|
92
100
|
return project.mcpResponse(template_1.ResponseTemplate.submissionSearchError, {
|
|
@@ -95,6 +103,16 @@ const findSubmission = async (project) => {
|
|
|
95
103
|
error: `Unsupported operator "${criterion.operator}"`
|
|
96
104
|
}, true);
|
|
97
105
|
}
|
|
106
|
+
// Criteria are ANDed, but each query parameter holds one value: a
|
|
107
|
+
// second one would replace the first and widen the search.
|
|
108
|
+
if (key in query) {
|
|
109
|
+
return project.mcpResponse(template_1.ResponseTemplate.submissionSearchError, {
|
|
110
|
+
form: form.form,
|
|
111
|
+
searchQuery: search_query,
|
|
112
|
+
error: `"${criterion.data_path}" has two criteria that cannot be combined (contains, starts_with, ends_with and regex count as one kind). Use a single "regex" criterion that matches both, or "in"/"nin" for several values.`
|
|
113
|
+
}, true);
|
|
114
|
+
}
|
|
115
|
+
query[key] = value;
|
|
98
116
|
}
|
|
99
117
|
}
|
|
100
118
|
try {
|
|
@@ -129,7 +147,7 @@ const findSubmission = async (project) => {
|
|
|
129
147
|
formName: form_name
|
|
130
148
|
});
|
|
131
149
|
}
|
|
132
|
-
if (submission_id_partial
|
|
150
|
+
if (submission_id_partial) {
|
|
133
151
|
const matchingSubmissions = submissions.filter(sub => sub._id && sub._id.toString().toLowerCase().includes(submission_id_partial.toLowerCase()));
|
|
134
152
|
if (matchingSubmissions.length === 0) {
|
|
135
153
|
return project.mcpResponse(template_1.ResponseTemplate.submissionPartialIdNotFound, {
|
|
@@ -138,7 +156,7 @@ const findSubmission = async (project) => {
|
|
|
138
156
|
partialId: submission_id_partial,
|
|
139
157
|
availableSubmissions: submissions.map(sub => ({
|
|
140
158
|
...form.formatSubmission(sub),
|
|
141
|
-
partialId: sub
|
|
159
|
+
partialId: partialIdOf(sub)
|
|
142
160
|
}))
|
|
143
161
|
}, true);
|
|
144
162
|
}
|
|
@@ -150,35 +168,23 @@ const findSubmission = async (project) => {
|
|
|
150
168
|
matchingSubmissions: matchingSubmissions.map(sub => ({
|
|
151
169
|
...form.formatSubmission(sub),
|
|
152
170
|
fullId: sub._id,
|
|
153
|
-
partialId: sub
|
|
171
|
+
partialId: partialIdOf(sub)
|
|
154
172
|
}))
|
|
155
173
|
}, true);
|
|
156
174
|
}
|
|
157
175
|
submissions = matchingSubmissions;
|
|
158
176
|
}
|
|
159
|
-
function getFieldValues(submission) {
|
|
160
|
-
if (!fields_requested || fields_requested.length === 0) {
|
|
161
|
-
return [];
|
|
162
|
-
}
|
|
163
|
-
const data = [];
|
|
164
|
-
for (const fieldPath of fields_requested) {
|
|
165
|
-
data.push({ path: fieldPath, value: (0, lodash_1.get)(submission.data, fieldPath, null) });
|
|
166
|
-
}
|
|
167
|
-
return data;
|
|
168
|
-
}
|
|
169
177
|
return project.mcpResponse(template_1.ResponseTemplate.submissionsFound, {
|
|
170
178
|
form: form.form,
|
|
171
179
|
searchQuery: JSON.stringify(search_query),
|
|
172
180
|
submissionCount,
|
|
173
|
-
submissions: submissions.map(sub => {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
};
|
|
181
|
-
})
|
|
181
|
+
submissions: submissions.map(sub => ({
|
|
182
|
+
_id: sub._id,
|
|
183
|
+
created: sub.created,
|
|
184
|
+
modified: sub.modified,
|
|
185
|
+
data: (fields_requested || []).map((path) => ({ path, value: (0, lodash_1.get)(sub.data, path, null) })),
|
|
186
|
+
partialId: partialIdOf(sub)
|
|
187
|
+
}))
|
|
182
188
|
});
|
|
183
189
|
}
|
|
184
190
|
catch (err) {
|
|
@@ -7,7 +7,7 @@ const SchemaBuilder_1 = require("./SchemaBuilder");
|
|
|
7
7
|
const annotations_1 = require("./annotations");
|
|
8
8
|
const error = require('debug')('formio:uag:getFieldInfo:error');
|
|
9
9
|
const getFieldInfo = async (project) => {
|
|
10
|
-
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.get_field_info
|
|
10
|
+
return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.get_field_info, {
|
|
11
11
|
name: 'get_field_info',
|
|
12
12
|
title: 'Get Field(s) Info',
|
|
13
13
|
description: 'Get detailed information about a specific field(s) that have been provided from the user. It is used to understand the properties, validation rules, and options of the specific field(s) the user has provided values for. This purpose of this tool is to provide detailed information for any field(s) (provided using the `field_paths` parameter) to help understand the structure and requirements of a form. PREREQUISITE: This tool should be called after the `get_form_fields` tool has been called AND once the user has provided some values for the provided fields.',
|
|
@@ -7,7 +7,7 @@ const SchemaBuilder_1 = require("./SchemaBuilder");
|
|
|
7
7
|
const annotations_1 = require("./annotations");
|
|
8
8
|
const error = require('debug')('formio:uag:getFormFields:error');
|
|
9
9
|
const getFormFields = async (project) => {
|
|
10
|
-
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.get_form_fields
|
|
10
|
+
return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.get_form_fields, {
|
|
11
11
|
name: 'get_form_fields',
|
|
12
12
|
title: 'Get Form Fields',
|
|
13
13
|
description: 'Get high level overview of the fields that are present in a form, and to understand the "rules" on how the data for each field type should be collected. The purpose of this tool is to determine what fields the user is requesting (and to understand how to format that values for that data), and use that to create a list of field data_path\'s that the user is providing context for. This list can then be provided to the `get_field_info` tool to determine the specific field level information for those fields (validation, required, collection rules, etc). PREREQUISITE: You must call the `get_forms` tool first to understand what forms are available to submit and the permissions associated with those forms.',
|
package/lib/tools/getForms.js
CHANGED
|
@@ -5,7 +5,7 @@ const lodash_1 = require("lodash");
|
|
|
5
5
|
const template_1 = require("../template");
|
|
6
6
|
const annotations_1 = require("./annotations");
|
|
7
7
|
const getForms = async (project) => {
|
|
8
|
-
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.get_forms
|
|
8
|
+
return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.get_forms, {
|
|
9
9
|
name: 'get_forms',
|
|
10
10
|
title: 'Get Available Forms',
|
|
11
11
|
description: 'Get a list of all available forms with their descriptions. Use this tool when a user expresses intent to add, create, submit, fill out, register for something (e.g., "I want to add a new Contact", "I need to submit a User Registration", "I met a contact who was the CMO", "What was the email of the contact"). This tool can also be used when they ask what forms are available.',
|
package/lib/tools/index.d.ts
CHANGED
|
@@ -8,8 +8,10 @@ import { findSubmission } from "./findSubmission";
|
|
|
8
8
|
import { submissionUpdate } from "./submissionUpdate";
|
|
9
9
|
import { ToolInfo } from "./utils";
|
|
10
10
|
import { UAGProjectInterface } from "../UAGProjectInterface";
|
|
11
|
+
import { agentProvide, agentProvideData } from "./agentProvide";
|
|
12
|
+
import { agentDecide } from "./agentDecide";
|
|
11
13
|
import { fetchExternalData } from "./fetchExternalData";
|
|
12
14
|
export * from './utils';
|
|
13
15
|
export * from './annotations';
|
|
14
|
-
export { getForms, getFormFields, getFieldInfo, collectData, confirmSubmission, submitCompletedForm, findSubmission, submissionUpdate, fetchExternalData };
|
|
16
|
+
export { getForms, getFormFields, getFieldInfo, collectData, confirmSubmission, submitCompletedForm, findSubmission, submissionUpdate, fetchExternalData, agentProvide, agentProvideData, agentDecide };
|
|
15
17
|
export declare const getTools: (project: UAGProjectInterface) => Promise<ToolInfo[]>;
|
package/lib/tools/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.getTools = exports.fetchExternalData = exports.submissionUpdate = exports.findSubmission = exports.submitCompletedForm = exports.confirmSubmission = exports.collectData = exports.getFieldInfo = exports.getFormFields = exports.getForms = void 0;
|
|
17
|
+
exports.getTools = exports.agentDecide = exports.agentProvideData = exports.agentProvide = exports.fetchExternalData = exports.submissionUpdate = exports.findSubmission = exports.submitCompletedForm = exports.confirmSubmission = exports.collectData = exports.getFieldInfo = exports.getFormFields = exports.getForms = void 0;
|
|
18
18
|
const getForms_1 = require("./getForms");
|
|
19
19
|
Object.defineProperty(exports, "getForms", { enumerable: true, get: function () { return getForms_1.getForms; } });
|
|
20
20
|
const getFormFields_1 = require("./getFormFields");
|
|
@@ -31,23 +31,30 @@ const findSubmission_1 = require("./findSubmission");
|
|
|
31
31
|
Object.defineProperty(exports, "findSubmission", { enumerable: true, get: function () { return findSubmission_1.findSubmission; } });
|
|
32
32
|
const submissionUpdate_1 = require("./submissionUpdate");
|
|
33
33
|
Object.defineProperty(exports, "submissionUpdate", { enumerable: true, get: function () { return submissionUpdate_1.submissionUpdate; } });
|
|
34
|
-
const
|
|
34
|
+
const agentProvide_1 = require("./agentProvide");
|
|
35
|
+
Object.defineProperty(exports, "agentProvide", { enumerable: true, get: function () { return agentProvide_1.agentProvide; } });
|
|
36
|
+
Object.defineProperty(exports, "agentProvideData", { enumerable: true, get: function () { return agentProvide_1.agentProvideData; } });
|
|
37
|
+
const agentDecide_1 = require("./agentDecide");
|
|
38
|
+
Object.defineProperty(exports, "agentDecide", { enumerable: true, get: function () { return agentDecide_1.agentDecide; } });
|
|
35
39
|
const fetchExternalData_1 = require("./fetchExternalData");
|
|
36
40
|
Object.defineProperty(exports, "fetchExternalData", { enumerable: true, get: function () { return fetchExternalData_1.fetchExternalData; } });
|
|
37
41
|
__exportStar(require("./utils"), exports);
|
|
38
42
|
__exportStar(require("./annotations"), exports);
|
|
39
43
|
const getTools = async (project) => {
|
|
40
|
-
return [
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
return Promise.all([
|
|
45
|
+
(0, getForms_1.getForms)(project),
|
|
46
|
+
(0, getFormFields_1.getFormFields)(project),
|
|
47
|
+
(0, getFieldInfo_1.getFieldInfo)(project),
|
|
48
|
+
(0, collectData_1.collectData)(project),
|
|
49
|
+
(0, confirmSubmission_1.confirmSubmission)(project),
|
|
50
|
+
(0, submitForm_1.submitCompletedForm)(project),
|
|
51
|
+
(0, findSubmission_1.findSubmission)(project),
|
|
52
|
+
(0, submissionUpdate_1.submissionUpdate)(project),
|
|
53
|
+
(0, agentProvide_1.agentProvide)(project),
|
|
54
|
+
(0, agentDecide_1.agentDecide)(project),
|
|
55
|
+
// Deprecated: the earlier name of agent_provide.
|
|
56
|
+
(0, agentProvide_1.agentProvideData)(project),
|
|
57
|
+
(0, fetchExternalData_1.fetchExternalData)(project)
|
|
58
|
+
]);
|
|
52
59
|
};
|
|
53
60
|
exports.getTools = getTools;
|
|
@@ -7,7 +7,7 @@ const SchemaBuilder_1 = require("./SchemaBuilder");
|
|
|
7
7
|
const annotations_1 = require("./annotations");
|
|
8
8
|
const error = require('debug')('formio:uag:submissionUpdate:error');
|
|
9
9
|
const submissionUpdate = async (project) => {
|
|
10
|
-
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.submission_update
|
|
10
|
+
return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.submission_update, {
|
|
11
11
|
name: 'submission_update',
|
|
12
12
|
title: 'Submission Update',
|
|
13
13
|
description: 'Apply multiple planned field updates to a selected submission after the user confirmed they wish to update the record.',
|
package/lib/tools/submitForm.js
CHANGED
|
@@ -7,7 +7,7 @@ const SchemaBuilder_1 = require("./SchemaBuilder");
|
|
|
7
7
|
const annotations_1 = require("./annotations");
|
|
8
8
|
const debug = require('debug')('formio:uag:submitForm');
|
|
9
9
|
const submitCompletedForm = async (project) => {
|
|
10
|
-
return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.submit_completed_form
|
|
10
|
+
return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.submit_completed_form, {
|
|
11
11
|
name: 'submit_completed_form',
|
|
12
12
|
title: 'Submit Completed Form',
|
|
13
13
|
description: 'Submit the completed form data. Should only be used once all the required fields have been collected, and the user has explicitly confirmed submission (e.g. has said "submit", "send", "done", etc)',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formio/uag",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-rc.1",
|
|
4
4
|
"mcpName": "io.form/formio-uag",
|
|
5
5
|
"description": "The Form.io Universal Agent Gateway (UAG).",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -9,19 +9,30 @@
|
|
|
9
9
|
"node": ">=22.0.0"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
|
-
"lib"
|
|
12
|
+
"lib",
|
|
13
|
+
"bin"
|
|
14
|
+
],
|
|
15
|
+
"workspaces": [
|
|
16
|
+
"engines/*",
|
|
17
|
+
"packages/*",
|
|
18
|
+
"demo"
|
|
13
19
|
],
|
|
14
20
|
"scripts": {
|
|
15
21
|
"clean": "rm -rf lib",
|
|
16
22
|
"build:ts": "tsc",
|
|
17
23
|
"build:copy-templates": "mkdir -p lib/templates && cp src/templates/*.md lib/templates/",
|
|
18
24
|
"build": "npm run clean && npm run build:ts && npm run build:copy-templates",
|
|
25
|
+
"prepublishOnly": "npm run build && node scripts/check-release.mjs",
|
|
19
26
|
"build:docker": "docker build -t formio/uag$(node -p \"require('./package.json').version.includes('-rc.') ? ':rc' : ''\") --platform=linux/amd64 .",
|
|
20
27
|
"test": "tsc --noEmit -p tsconfig.test.json && mocha --no-node-snapshot",
|
|
21
28
|
"test:e2e": "mocha --config .mocharc.e2e.json",
|
|
29
|
+
"release:version": "node scripts/set-version.mjs",
|
|
22
30
|
"dev": "tsx --no-node-snapshot index.js",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
31
|
+
"demo:dev": "node --no-node-snapshot demo/scripts/dev.mjs",
|
|
32
|
+
"demo:local": "node --no-node-snapshot demo/scripts/local.mjs",
|
|
33
|
+
"demo:start": "node --no-node-snapshot demo/scripts/start.mjs",
|
|
34
|
+
"demo:record": "node --no-node-snapshot demo/recordings/record.mjs",
|
|
35
|
+
"start": "node --no-node-snapshot index.js"
|
|
25
36
|
},
|
|
26
37
|
"repository": {
|
|
27
38
|
"type": "git",
|
|
@@ -30,18 +41,21 @@
|
|
|
30
41
|
"author": "Form.io Developers",
|
|
31
42
|
"license": "MIT",
|
|
32
43
|
"dependencies": {
|
|
33
|
-
"@formio/appserver": "^2.
|
|
34
|
-
"@formio/core": "2.
|
|
35
|
-
"@
|
|
44
|
+
"@formio/appserver": "^2.24.1",
|
|
45
|
+
"@formio/appserver-core": "^2.24.1",
|
|
46
|
+
"@formio/core": "2.9.0",
|
|
47
|
+
"@formio/uag-sdk": "^2.0.0-rc.1",
|
|
48
|
+
"@modelcontextprotocol/sdk": "1.32.2",
|
|
36
49
|
"cors": "^2.8.6",
|
|
37
50
|
"debug": "^4.4.3",
|
|
38
|
-
"dotenv": "^
|
|
39
|
-
"express": "^5.
|
|
51
|
+
"dotenv": "^18.0.9",
|
|
52
|
+
"express": "^5.3.0",
|
|
40
53
|
"jsonwebtoken": "^9.0.3",
|
|
41
54
|
"lodash": "^4.18.1",
|
|
55
|
+
"mongodb": "^7.7.0",
|
|
42
56
|
"node-cache": "^5.1.2",
|
|
43
57
|
"node-html-markdown": "^2.0.0",
|
|
44
|
-
"zod": "^4.
|
|
58
|
+
"zod": "^4.6.7"
|
|
45
59
|
},
|
|
46
60
|
"resolutions": {
|
|
47
61
|
"tar": ">=7.5.12",
|
|
@@ -52,22 +66,27 @@
|
|
|
52
66
|
"dompurify": ">=3.3.3"
|
|
53
67
|
},
|
|
54
68
|
"devDependencies": {
|
|
55
|
-
"@anthropic-ai/sdk": "^
|
|
69
|
+
"@anthropic-ai/sdk": "^1.3.0",
|
|
70
|
+
"@formio/js": "5.6.1",
|
|
56
71
|
"@types/chai": "^5.2.3",
|
|
57
72
|
"@types/cors": "^2.8.19",
|
|
58
73
|
"@types/debug": "^4.1.13",
|
|
59
74
|
"@types/dotenv": "^8.2.3",
|
|
60
75
|
"@types/express": "^5.0.6",
|
|
76
|
+
"@types/jsdom": "^30",
|
|
61
77
|
"@types/jsonwebtoken": "^9.0.10",
|
|
62
|
-
"@types/lodash": "^4.17.
|
|
78
|
+
"@types/lodash": "^4.17.27",
|
|
63
79
|
"@types/mocha": "^10.0.10",
|
|
64
|
-
"@types/node": "^26.
|
|
80
|
+
"@types/node": "^26.6.5",
|
|
65
81
|
"@types/supertest": "^7.2.1",
|
|
66
|
-
"chai": "^6.
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
82
|
+
"chai": "^6.3.0",
|
|
83
|
+
"jsdom": "30.1.2",
|
|
84
|
+
"mocha": "^12.0.3",
|
|
85
|
+
"mongodb-memory-server": "^11.3.0",
|
|
86
|
+
"playwright": "1.64.1",
|
|
87
|
+
"supertest": "^7.3.1",
|
|
88
|
+
"terser": "^5.51.2",
|
|
89
|
+
"tsx": "^4.23.15",
|
|
71
90
|
"typescript": "^7.0.2"
|
|
72
91
|
},
|
|
73
92
|
"packageManager": "yarn@4.17.1",
|
|
@@ -78,5 +97,9 @@
|
|
|
78
97
|
"isolated-vm": {
|
|
79
98
|
"built": true
|
|
80
99
|
}
|
|
100
|
+
},
|
|
101
|
+
"bin": {
|
|
102
|
+
"uag": "bin/uag.js",
|
|
103
|
+
"uag-eval": "bin/uag-eval.js"
|
|
81
104
|
}
|
|
82
105
|
}
|