@formio/uag 1.12.1 → 1.12.3

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 CHANGED
@@ -6,24 +6,20 @@ The Universal Agent Gateway (UAG) leverages the [Model Context Protocol (MCP)](h
6
6
 
7
7
  ![Diagram showing how the UAG provides an AI Agent the ability to read and understand a Form.io Form JSON. Similar to how the JavaScript renderer provides a Human the ability to visually see a rendered form.](./examples/images/uag-agent-rendering.png)
8
8
 
9
- Official Documentation
10
- --------------------------
11
- For the latest documentation, release information, and guides, always refer to the official Form.io Help Documentation available here:
12
-
13
- **[https://help.form.io](https://help.form.io/dev/ai/uag)**
14
-
15
9
  --------------------------
16
10
  There are two primary scenarios that the UAG enables:
17
11
 
18
12
  - **Unstructured 'chat' input to deterministic form submission**: This capability uses the JSON schema of a Form.io form to produce a deterministic structured data object provided natural language conversational input.
19
13
 
20
14
  ![Conversion of natural language to structured data using UAG.](./examples/images/uag-natural-language.png)
15
+
16
+ For a working example of this flow, please try out the [Conversation Form Example](./examples/conversation-form).
21
17
 
22
18
  - **Agentic Form.io Workflows**: UAG provides an AI Agent the ability to take existing submission data + "context" criteria in order to prompt an AI Agent to provide its own data and analysis within a workflow scenario. The following example, which shows a college application process using a Form.io application form, shows how this primary scenario works.
23
19
 
24
20
  ![Agentic workflow automation example diagram using UAG](./examples/images/uag-agent-provide-data.png)
25
21
 
26
- For more information on Agentic workflows, checkout the dedicated [Agentic Workflows](#agentic-workflows) section
22
+ For more information on Agentic workflows, checkout the dedicated [Agentic Workflows](#agentic-workflows) section. For a working example of this flow, please try out the [Agentic Workflow Example](./examples/agentic-workflow).
27
23
 
28
24
  ---
29
25
 
@@ -36,12 +32,10 @@ There are three working examples in this repo, each spinning up a Form.io server
36
32
  |---------|---------|-----------|
37
33
  | **[Custom Module](examples/custom-module)** | **Conversational, existing client.** Connect Claude Desktop to your forms and talk to them: "I would like to add a new customer." Built as a custom module, so roles and permissions decide what the agent may do. | Open Source — free |
38
34
  | **[Conversation Form](examples/conversation-form)** | **Conversational, your own UI.** A chat application whose questions come entirely from a form. About 200 lines, no field names in the code — edit the form and the conversation changes. | Open Source — free |
39
- | **[Agentic Workflow](examples/agentic-workflow)** | **Autonomous.** A submission triggers an agent that scores it against a rubric and writes its decision back, which in turn triggers a second agent. No human, no chat window. | Enterprise — the trigger needs a Webhook action that can set headers and transform its payload |
35
+ | **[Agentic Workflow](examples/agentic-workflow)** | **Autonomous.** A submission triggers an agent that scores it against a rubric and writes its decision back, which in turn triggers a second agent. No human, no chat window. | Enterprise |
40
36
 
41
37
  There is also a [Flow Viewer](examples/flow-viewer) — a small proxy that shows every leg of an agentic run live, which is the fastest way to see what an agent is actually doing.
42
38
 
43
- **[Conversation »](examples/conversation-form)**  |  **[Claude Desktop »](examples/custom-module)**  |  **[Autonomous »](examples/agentic-workflow)**
44
-
45
39
  ## Documentation in this repository
46
40
 
47
41
  This file is the main reference: what the UAG is, how to configure a form for it, how to deploy it, and how to troubleshoot it. The rest of the documentation lives next to the code it describes.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fetchExternalData = void 0;
4
4
  const template_1 = require("../template");
5
+ const utils_1 = require("./utils");
5
6
  const core_1 = require("@formio/core");
6
7
  const SchemaBuilder_1 = require("./SchemaBuilder");
7
8
  const lodash_1 = require("lodash");
@@ -251,7 +252,7 @@ async function fetchResourceData(component, search_value, authInfo, project, con
251
252
  const query = {};
252
253
  // Add search filter if provided
253
254
  if (search_value && component.searchField) {
254
- query[`data.${component.searchField}__regex`] = `/${search_value}/i`;
255
+ query[`data.${component.searchField}__regex`] = `/${(0, utils_1.escapeSearchPattern)(search_value)}/i`;
255
256
  }
256
257
  // Add component filter if configured (interpolate the filter string)
257
258
  if (component.filter) {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findSubmission = void 0;
4
4
  const template_1 = require("../template");
5
+ const utils_1 = require("./utils");
5
6
  const lodash_1 = require("lodash");
6
7
  const SchemaBuilder_1 = require("./SchemaBuilder");
7
8
  const error = require('debug')('formio:uag:findSubmission:error');
@@ -34,19 +35,33 @@ const findSubmission = async (project) => {
34
35
  }, true);
35
36
  }
36
37
  if (criterion.operator === 'regex') {
38
+ // The value is a pattern by request here, so it is passed through
39
+ // unescaped. It is still compiled first: the server drops a filter
40
+ // it cannot compile, which would widen the search instead of
41
+ // failing it, so a bad pattern is better reported back.
42
+ try {
43
+ new RegExp(criterion.search_value);
44
+ }
45
+ catch (err) {
46
+ return project.mcpResponse(template_1.ResponseTemplate.submissionSearchError, {
47
+ form: form.form,
48
+ searchQuery: search_query,
49
+ error: `"${criterion.search_value}" is not a valid regular expression for "${criterion.data_path}". Use the "contains" operator to search for it literally.`
50
+ }, true);
51
+ }
37
52
  query[`data.${criterion.data_path}__regex`] = `/${criterion.search_value}/i`;
38
53
  }
39
54
  else if (criterion.operator === 'equals') {
40
55
  query[`data.${criterion.data_path}`] = criterion.search_value;
41
56
  }
42
57
  else if (criterion.operator === 'contains') {
43
- query[`data.${criterion.data_path}__regex`] = `/${criterion.search_value}/i`;
58
+ query[`data.${criterion.data_path}__regex`] = `/${(0, utils_1.escapeSearchPattern)(criterion.search_value)}/i`;
44
59
  }
45
60
  else if (criterion.operator === 'starts_with') {
46
- query[`data.${criterion.data_path}__regex`] = `/^${criterion.search_value}/i`;
61
+ query[`data.${criterion.data_path}__regex`] = `/^${(0, utils_1.escapeSearchPattern)(criterion.search_value)}/i`;
47
62
  }
48
63
  else if (criterion.operator === 'ends_with') {
49
- query[`data.${criterion.data_path}__regex`] = `/${criterion.search_value}$/i`;
64
+ query[`data.${criterion.data_path}__regex`] = `/${(0, utils_1.escapeSearchPattern)(criterion.search_value)}$/i`;
50
65
  }
51
66
  else if (criterion.operator === 'greater_than') {
52
67
  query[`data.${criterion.data_path}__gt`] = criterion.search_value;
@@ -1,4 +1,24 @@
1
1
  import { ZodRawShape } from "zod";
2
+ /**
3
+ * Escape a literal value so it can be carried in a `__regex` query parameter.
4
+ *
5
+ * Operators like "contains" promise a literal match, so every regular expression
6
+ * metacharacter in the value has to be neutered first. Left raw, a search for
7
+ * "joe.thompson@example.com" becomes a pattern whose dots match any character,
8
+ * and a value such as "[" is not a valid pattern at all — the server compiles
9
+ * these with a try/catch and silently drops the filter when compilation fails,
10
+ * which turns a narrow search into one that matches everything.
11
+ *
12
+ * Forward slashes get a further step. The server reads the parameter as
13
+ * `/pattern/flags` and takes the pattern with `[^/]+`, so an embedded slash
14
+ * truncates the pattern and the remainder is parsed as flags — again dropping
15
+ * the filter. Writing it as the equivalent `\x2f` escape keeps the character
16
+ * out of the transport while still matching a literal slash.
17
+ *
18
+ * @param value - The literal text to search for.
19
+ * @returns The value as a regular expression pattern that matches it literally.
20
+ */
21
+ export declare const escapeSearchPattern: (value: string) => string;
2
22
  export type ToolInfo = {
3
23
  name?: string;
4
24
  title?: string;
@@ -1,2 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.escapeSearchPattern = void 0;
4
+ const lodash_1 = require("lodash");
5
+ /**
6
+ * Escape a literal value so it can be carried in a `__regex` query parameter.
7
+ *
8
+ * Operators like "contains" promise a literal match, so every regular expression
9
+ * metacharacter in the value has to be neutered first. Left raw, a search for
10
+ * "joe.thompson@example.com" becomes a pattern whose dots match any character,
11
+ * and a value such as "[" is not a valid pattern at all — the server compiles
12
+ * these with a try/catch and silently drops the filter when compilation fails,
13
+ * which turns a narrow search into one that matches everything.
14
+ *
15
+ * Forward slashes get a further step. The server reads the parameter as
16
+ * `/pattern/flags` and takes the pattern with `[^/]+`, so an embedded slash
17
+ * truncates the pattern and the remainder is parsed as flags — again dropping
18
+ * the filter. Writing it as the equivalent `\x2f` escape keeps the character
19
+ * out of the transport while still matching a literal slash.
20
+ *
21
+ * @param value - The literal text to search for.
22
+ * @returns The value as a regular expression pattern that matches it literally.
23
+ */
24
+ const escapeSearchPattern = (value) => (0, lodash_1.escapeRegExp)(value).replace(/\//g, '\\x2f');
25
+ exports.escapeSearchPattern = escapeSearchPattern;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formio/uag",
3
- "version": "1.12.1",
4
- "mcpName": "io.form/uag",
3
+ "version": "1.12.3",
4
+ "mcpName": "io.form/formio-uag",
5
5
  "description": "The Form.io Universal Agent Gateway (UAG).",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -30,7 +30,7 @@
30
30
  "author": "Form.io Developers",
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "@formio/appserver": "^2.13.1",
33
+ "@formio/appserver": "^2.13.2",
34
34
  "@formio/core": "2.8.0",
35
35
  "@modelcontextprotocol/sdk": "1.30.0",
36
36
  "cors": "^2.8.6",
@@ -70,5 +70,13 @@
70
70
  "tsx": "^4.23.1",
71
71
  "typescript": "^7.0.2"
72
72
  },
73
- "packageManager": "yarn@4.17.1"
73
+ "packageManager": "yarn@4.17.1",
74
+ "dependenciesMeta": {
75
+ "esbuild": {
76
+ "built": true
77
+ },
78
+ "isolated-vm": {
79
+ "built": true
80
+ }
81
+ }
74
82
  }