diff --git a/content/en/docs/marketplace/genai/concepts/model-context-protocol.md b/content/en/docs/marketplace/genai/concepts/model-context-protocol.md
index 68e15d24d41..47b81aa17ac 100644
--- a/content/en/docs/marketplace/genai/concepts/model-context-protocol.md
+++ b/content/en/docs/marketplace/genai/concepts/model-context-protocol.md
@@ -60,3 +60,4 @@ Furthermore, an MCP Client example teaches you how to establish a connection to
* The official [MCP docs](https://modelcontextprotocol.io/introduction)
* The [MCP Java SDK GitHub Repository](https://github.com/modelcontextprotocol/java-sdk)
* A blog post on [How to use MCP to bring Mendix Business Logic into Claude for Desktop](https://www.mendix.com/blog/how-to-use-mcp-to-bring-mendix-business-logic-into-claude-for-desktop/)
+* [Connect a Mendix AI Agent to a Snowflake-Managed MCP Server](/appstore/modules/snowflake/)
\ No newline at end of file
diff --git a/content/en/docs/marketplace/platform-supported-content/modules/snowflake/snowflake-mcp.md b/content/en/docs/marketplace/platform-supported-content/modules/snowflake/snowflake-mcp.md
new file mode 100644
index 00000000000..d5521d946e7
--- /dev/null
+++ b/content/en/docs/marketplace/platform-supported-content/modules/snowflake/snowflake-mcp.md
@@ -0,0 +1,451 @@
+---
+title: "Connect a Mendix AI Agent to a Snowflake-Managed MCP Server"
+linktitle: "Connect Mendix to a Snowflake MCP Server"
+url: /appstore/modules/snowflake/connect-ai-agent-to-snowflake-mcp/
+description: "Describes the steps required to use a Snowflake-managed MCP server with a Mendix AI agent."
+weight: 80
+---
+
+## Introduction
+
+The Model Context Protocol (MCP) is an open protocol that standardizes how Large Language Models (LLMs) can autonomously connect to apps. Many AI platforms and third-party systems have already adopted MCP for easier integration and empowerment of LLMs. Mendix provides an MCP Server module to facilitate an MCP server from a Mendix app, as well as an MCP Client module. For more information, see [Model Context Protocol (MCP)](/appstore/modules/genai/mcp/).
+
+[Snowflake-managed MCP servers](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-agents-mcp) let AI agents securely use data ([CRUD](## "Create Read Update Delete")) and leverage functionaliy, e.g stored procedures and cortext functionality, from Snowflake accounts without needing to deploy separate infrastructure. Mendix users can configure the [MCP Client Module](/appstore/modules/genai/mcp-modules/mcp-client/) to enable the connection from a Mendix AI agent to a Snowflake MCP server.
+
+### Typical Use Cases
+
+* A chat interface where the user can retrieve and modify data in Snowflake Cloud by requesting in natural language.
+* Reusing existing functionality of stored procedures in Snowflake Cloud by task oriented AI agents.
+
+### Prerequisites {#prerequisites}
+
+[Any specific versions of Studio Pro? Other prereqs?]
+
+To establish a connection between a Mendix AI Agent and a Snowflake-managed MCP server, you can either start with the [Blank GenAI App](https://marketplace.mendix.com/link/component/227934) or [Agent Builder Starter App](https://marketplace.mendix.com/link/component/240369), but make sure to update the [MCP Client](https://marketplace.mendix.com/link/component/244893) module to version 3.1.0 (or higher) when its version is lower.
+
+Or when you want to start from scratch or you want add to an exsiting application, you must also install the following modules and their prerequisites:
+
+* [MCP Client](https://marketplace.mendix.com/link/component/244893) (version 3.1.0 or higher)
+* [Conversational UI](https://marketplace.mendix.com/link/component/239450)
+
+## Preparing a Snowflake-Managed MCP Server
+
+To configure a Snowflake-managed MCP server, follow these steps:
+
+1. In Snowflake, set up the database and schema's which will be used by the server.
+
+ Expand for example code including some testdata
+
+ ```sql
+ -- You can run this example/demo under sysadmin role, for real production screnario's use proper authorisation
+ CREATE DATABASE IF NOT EXISTS SNOWFLAKE_MCP_DEMO;
+ CREATE SCHEMA IF NOT EXISTS SNOWFLAKE_MCP_DEMO.TOOLS;
+ CREATE SCHEMA IF NOT EXISTS SNOWFLAKE_MCP_DEMO.MCPSERVERS;
+ CREATE SCHEMA IF NOT EXISTS SNOWFLAKE_MCP_DEMO.TESTDATA;
+
+ CREATE OR REPLACE TABLE SNOWFLAKE_MCP_DEMO.TESTDATA.TICKETS (
+ TICKETID NUMBER AUTOINCREMENT START 1 INCREMENT 1,
+ PRIORITY VARCHAR(10),
+ TEXT VARCHAR(500)
+ );
+
+ INSERT INTO SNOWFLAKE_MCP_DEMO.TESTDATA.TICKETS (PRIORITY, TEXT)
+ VALUES
+ ('High', 'Server is down in production environment'),
+ ('Medium', 'User unable to reset password'),
+ ('Low', 'Request for additional monitor'),
+ ('High', 'Database connection timeout on checkout page'),
+ ('Medium', 'Email notifications not being sent');
+ ```
+
+
+
+2. Create the stored procedures which the MCP server will expose as tools.
+
+ Expand for example code for a generic stored procdure returning meta data
+
+ ```sql
+ -- You can run this example/demo under sysadmin role, for real production screnario's use proper authorisation
+ CREATE OR REPLACE PROCEDURE SNOWFLAKE_MCP_DEMO.TOOLS.GET_SCHEMA_METADATA(
+ db_name VARCHAR,
+ schema_name VARCHAR
+ )
+ RETURNS VARIANT
+ LANGUAGE PYTHON
+ RUNTIME_VERSION = '3.11'
+ PACKAGES = ('snowflake-snowpark-python')
+ HANDLER = 'run'
+ AS
+ $$
+ import json
+ def run(session, db_name, schema_name):
+ rows = session.sql(f"""
+ SELECT
+ c.TABLE_CATALOG,
+ c.TABLE_SCHEMA,
+ c.TABLE_NAME,
+ t.TABLE_TYPE,
+ t.ROW_COUNT,
+ t.COMMENT AS TABLE_COMMENT,
+ c.COLUMN_NAME,
+ c.ORDINAL_POSITION,
+ c.DATA_TYPE,
+ c.IS_NULLABLE,
+ c.COLUMN_DEFAULT,
+ c.CHARACTER_MAXIMUM_LENGTH,
+ c.NUMERIC_PRECISION,
+ c.NUMERIC_SCALE,
+ c.COMMENT AS COLUMN_COMMENT
+ FROM {db_name}.INFORMATION_SCHEMA.COLUMNS c
+ JOIN {db_name}.INFORMATION_SCHEMA.TABLES t
+ ON c.TABLE_CATALOG = t.TABLE_CATALOG
+ AND c.TABLE_SCHEMA = t.TABLE_SCHEMA
+ AND c.TABLE_NAME = t.TABLE_NAME
+ WHERE c.TABLE_SCHEMA = '{schema_name}'
+ ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION
+ """).collect()
+ tables = {}
+ for row in rows:
+ tname = row["TABLE_NAME"]
+ if tname not in tables:
+ tables[tname] = {
+ "database": row["TABLE_CATALOG"],
+ "schema": row["TABLE_SCHEMA"],
+ "table_type": row["TABLE_TYPE"],
+ "row_count": row["ROW_COUNT"],
+ "comment": row["TABLE_COMMENT"],
+ "columns": []
+ }
+ tables[tname]["columns"].append({
+ "name": row["COLUMN_NAME"],
+ "position": row["ORDINAL_POSITION"],
+ "data_type": row["DATA_TYPE"],
+ "nullable": row["IS_NULLABLE"],
+ "default": row["COLUMN_DEFAULT"],
+ "max_length": row["CHARACTER_MAXIMUM_LENGTH"],
+ "precision": row["NUMERIC_PRECISION"],
+ "scale": row["NUMERIC_SCALE"],
+ "comment": row["COLUMN_COMMENT"]
+ })
+ return tables
+ $$;
+ ```
+
+
+ Expand for example code for a generic stored procdure for retrieving records
+
+ ```sql
+ -- You can run this example/demo under sysadmin role, for real production screnario's use proper authorisation
+ CREATE OR REPLACE PROCEDURE SNOWFLAKE_MCP_DEMO.TOOLS.RETRIEVE_RECORDS(
+ fully_qualified_table VARCHAR,
+ filter_column VARCHAR,
+ filter_value VARCHAR
+ )
+ RETURNS VARCHAR
+ LANGUAGE PYTHON
+ RUNTIME_VERSION = '3.11'
+ PACKAGES = ('snowflake-snowpark-python')
+ HANDLER = 'run'
+ AS
+ $$
+ import json
+ def run(session, fully_qualified_table, filter_column, filter_value):
+ parts = fully_qualified_table.split('.')
+ if len(parts) != 3:
+ return json.dumps({"status": "error", "message": "Table must be fully qualified: DATABASE.SCHEMA.TABLE"})
+ try:
+ if filter_column and filter_value:
+ escaped = filter_value.replace("'", "''")
+ sql = f"SELECT * FROM {fully_qualified_table} WHERE {filter_column} = '{escaped}'"
+ else:
+ sql = f"SELECT * FROM {fully_qualified_table}"
+ rows = session.sql(sql).collect()
+ results = [row.as_dict() for row in rows]
+ for r in results:
+ for k, v in r.items():
+ if not isinstance(v, (str, int, float, bool, type(None))):
+ r[k] = str(v)
+ return json.dumps({"status": "success", "row_count": len(results), "data": results})
+ except Exception as e:
+ return json.dumps({"status": "error", "message": str(e)})
+ $$;
+ ```
+
+
+ Expand for example code for a generic stored procdure for inserting records
+
+ ```sql
+ -- inputs can be for example:
+ -- fully_qualified_table = 'SNOWFLAKE_MCP_DEMO.TESTDATA.TICKETS'
+ -- column_values: '{"PRIORITY": "Low", "TEKST": "text here"}'
+
+ -- You can run this example/demo under sysadmin role, for real production screnario's use proper authorisation
+ CREATE OR REPLACE PROCEDURE SNOWFLAKE_MCP_DEMO.TOOLS.INSERT_RECORD(
+ fully_qualified_table VARCHAR,
+ column_values VARCHAR
+ )
+ RETURNS VARCHAR
+ LANGUAGE PYTHON
+ RUNTIME_VERSION = '3.11'
+ PACKAGES = ('snowflake-snowpark-python')
+ HANDLER = 'run'
+ AS
+ $$
+ import json
+ def run(session, fully_qualified_table, column_values):
+ parts = fully_qualified_table.split('.')
+ if len(parts) != 3:
+ return json.dumps({"status": "error", "message": "Table must be fully qualified: DATABASE.SCHEMA.TABLE"})
+ db, schema, table = parts
+ cols_rows = session.sql(f"""
+ SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE
+ FROM {db}.INFORMATION_SCHEMA.COLUMNS
+ WHERE TABLE_SCHEMA = '{schema}' AND TABLE_NAME = '{table}'
+ ORDER BY ORDINAL_POSITION
+ """).collect()
+ if not cols_rows:
+ return json.dumps({"status": "error", "message": f"Table {fully_qualified_table} not found or has no columns"})
+ schema_info = {row["COLUMN_NAME"]: row["DATA_TYPE"] for row in cols_rows}
+ try:
+ values = json.loads(column_values)
+ except json.JSONDecodeError as e:
+ return json.dumps({"status": "error", "message": f"Invalid JSON in column_values: {str(e)}", "expected_columns": list(schema_info.keys())})
+ for col_name in values:
+ if col_name not in schema_info:
+ return json.dumps({"status": "error", "message": f"Column '{col_name}' does not exist in {fully_qualified_table}", "valid_columns": list(schema_info.keys())})
+ col_names = list(values.keys())
+ val_parts = []
+ for col in col_names:
+ val = values[col]
+ if val is None:
+ val_parts.append("NULL")
+ elif isinstance(val, (int, float)):
+ val_parts.append(str(val))
+ else:
+ escaped = str(val).replace("'", "''")
+ val_parts.append(f"'{escaped}'")
+ col_list = ", ".join(col_names)
+ val_list = ", ".join(val_parts)
+ sql = f"INSERT INTO {fully_qualified_table} ({col_list}) VALUES ({val_list})"
+ try:
+ session.sql(sql).collect()
+ return json.dumps({"status": "success", "message": f"Record inserted into {fully_qualified_table}", "columns_inserted": col_names})
+ except Exception as e:
+ return json.dumps({"status": "error", "message": str(e)})
+ $$;
+ ```
+
+
+
+3. Create the Snowflake MCP server exposing the stored procedures as tools.
+ For more information, see [Create an MCP Server object](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-agents-mcp#create-an-mcp-server-object) in Snowflake documentation.
+
+ Expand for example code to generate a Snowflake MCP server with the stored procdures above as tools
+
+ ```sql
+ -- You can run this example/demo under sysadmin role, for real production screnario's use proper authorisation
+ CREATE OR REPLACE MCP SERVER SNOWFLAKE_MCP_DEMO.MCPSERVERS.DEMO_MCP_SERVER
+ FROM SPECIFICATION $$
+ tools:
+ - title: "Get Schema Metadata"
+ identifier: "SNOWFLAKE_MCP_DEMO.TOOLS.GET_SCHEMA_METADATA"
+ name: "get_schema_metadata"
+ type: "GENERIC"
+ description: "Returns metadata for all tables and columns in a given database schema, including data types, nullability, row counts, and comments."
+ config:
+ type: "procedure"
+ warehouse: "MYWAREHOUSE"
+ input_schema:
+ type: "object"
+ properties:
+ db_name:
+ description: "The database name to inspect"
+ type: "string"
+ schema_name:
+ description: "The schema name to inspect"
+ type: "string"
+ - title: "Insert Record"
+ identifier: "SNOWFLAKE_MCP_DEMO.TOOLS.INSERT_RECORD"
+ name: "insert_record"
+ type: "GENERIC"
+ description: "Inserts a single record into a specified table. Accepts a fully qualified table name and a JSON string of column-value pairs."
+ config:
+ type: "procedure"
+ warehouse: "MYWAREHOUSE"
+ input_schema:
+ type: "object"
+ properties:
+ fully_qualified_table:
+ description: "Fully qualified table name in DATABASE.SCHEMA.TABLE format"
+ type: "string"
+ column_values:
+ description: "JSON string of column names and values to insert, e.g. {\"PRIORITY\": \"High\", \"TEKST\": \"New ticket\"}"
+ type: "string"
+ - title: "Retrieve Records"
+ identifier: "SNOWFLAKE_MCP_DEMO.TOOLS.RETRIEVE_RECORDS"
+ name: "retrieve_records"
+ type: "GENERIC"
+ description: "Retrieves records from a specified table. Optionally filter by a single column value. Returns all rows if no filter is provided."
+ config:
+ type: "procedure"
+ warehouse: "MYWAREHOUSE"
+ input_schema:
+ type: "object"
+ properties:
+ fully_qualified_table:
+ description: "Fully qualified table name in DATABASE.SCHEMA.TABLE format"
+ type: "string"
+ filter_column:
+ description: "Optional column name to filter on. Pass empty string for no filter."
+ type: "string"
+ filter_value:
+ description: "Optional value to match in the filter column. Pass empty string for no filter."
+ type: "string"
+ $$;
+ ```
+
+
+
+4. Create the authentication and access configuration, so it can invoked by the Mendix application with the MCP Client module.
+
+ * Retrieve the IP addresses from where the MCP Server is connector.
+ You can retrieve your own IP address from [whatismyipaddress.com](https://whatismyipaddress.com/) when your applicatons runs locally in Studio Pro.
+ When it runs on Mendix public cloud, you can retrieve the IP addresses [here](https://docs.mendix.com/developerportal/deploy/mendix-ip-addresses/#mendix-cloud).
+
+ * Create a `NETWORK RULE` using the IP addresses that you retrieved.
+
+ Expand for example code
+
+ ```sql
+ --Run under accountadmin rol or securityadmin role
+ CREATE OR REPLACE NETWORK RULE SNOWFLAKE_MCP_DEMO.MCPSERVERS.MCP_DEMO_ALLOWED_IPS
+ TYPE = IPV4
+ MODE = INGRESS
+ VALUE_LIST = ('1.2.3.4', '5.4.6.8', '9.10.11.12');
+ ```
+
+
+ * Create a Snowflake user, to be used for the Mendix Agent.
+ As this user is used by a system, a 'service' type user is created.
+
+ Expand for example code
+
+ ```sql
+ -- Run under useradmin (or higher) role
+ CREATE USER IF NOT EXISTS MX_AGENT
+ default_role = SYSADMIN
+ TYPE = SERVICE
+ ALLOWED_INTERFACES = ();
+ ```
+
+
+ * Create a `NETWORK POLICY` for this user.
+
+ Expand for example code
+
+ ```sql
+ -- Run under accountadmin rol or securityadmin role
+ CREATE OR REPLACE NETWORK POLICY MX_AGENT_NETWORK_POLICY
+ ALLOWED_NETWORK_RULE_LIST = ('SNOWFLAKE_MCP_DEMO.MCPSERVERS.MCP_DEMO_ALLOWED_IPS');
+ ```
+
+
+ * Set the user to use this policy.
+
+ Expand for example code
+
+ ```sql
+ -- Run under accountadmin rol or securityadmin role
+ ALTER USER MX_AGENT SET NETWORK_POLICY = MX_AGENT_NETWORK_POLICY;
+ ```
+
+
+ * Create a Personal Access Token (PAT) for the user.
+ Be aware the for a Service type user, a role has to be granted.
+
+ Expand for example code
+
+ ```sql
+ -- Run under accountadmin rol or securityadmin role
+ GRANT ROLE SYSADMIN TO USER MX_AGENT;
+
+ ALTER USER MX_AGENT ADD PAT MX_AGENT_MCP_PAT
+ ROLE_RESTRICTION = SYSADMIN
+ DAYS_TO_EXPIRY = 30
+ COMMENT = 'PAT for MCP demo';
+ ```
+
+
+## Connecting a Mendix Agent to the MCP Server
+
+After setting up the MCP server, you can now create a Mendix AI agent and connect it to the MCP server by performing the following steps:
+
+1. In Studio Pro, create a new app using the [Agent Builder Starter App](https://marketplace.mendix.com/link/component/240369) and when neeeded update module [MCP Client](/appstore/modules/genai/mcp-modules/mcp-client/) to version 3.1.0 or higher.
+2. Create a constant for the Snowflake user PAT that you created in the previous section, and in the runtime configuration set its value.
+3. Copy microflow `App/Marketplace Modules/MCPClient/Example Implementations/MCP Client/GetCredentials_EXAMPLE` to your own app module and give it a decent name to reflect getting Snowflake PAT authentication, e.g. `GetCredentials_SF_PAT`
+4. Change this microflow so it only adds the PAT as Bearer token to the header.
+ To do this you can remove the first 'Config: Create Http Header And Add to List' activity and change the Value attribute of the second one to `'Bearer ' + @General.SnowflakePAT`.
+5. After starting the app and login, the "Administrator functionalities" page is shown.
+ Use the LLM connections section to configure your Large Language Model subscription and retrieve the list of available LLM models.
+6. Press menu item "MCP Client" and configure our Snowflake MCP server in the Consumed MCP Services page by providing
+ * name of your lining
+ * MCP endpoint of format `https://.snowflakecomputing.com/api/v2/databases//schemas//mcp-servers/ `
+ Important! Wheh your snowflake account id containt underscores '_', replace them with '-' in the endpoint (only for account id, not for database name, schema name and mcp server name).
+ * Select for protocol value 'v2025_03_26'
+ * Enter a version nr of your liking
+ * Set connections time out, eg. 60 seconds
+ * Configure for the 'Get credentials microflow' the microflow you've created in step 3 and 4 (`GetCredentials_SF_PAT`).
+ * After saving, it should connect and set Server status to ok with green checkmark
+ * You can inpsect the MCP server, by pressing the 'View MCP tools and prompts' button
+7. Create an AI agent and configure the following properties:
+ * LLM model
+ * Set a good System prompt (important!) where you specify the schema and table name to use and instruct also how to use the tools of the MCP server
+
+ Expand for example code
+
+ ```
+ You are a support ticket management assistant connected to a Snowflake database via MCP tools.
+ You help users create, retrieve, and inspect support tickets stored in the SNOWFLAKE_MCP_DEMO.TESTDATA schema.
+
+ ## Available Tools
+
+ You have access to the following MCP tools:
+
+ 1. **get_schema_metadata** - Retrieves table and column metadata for a database schema.
+ - Use this FIRST when you need to understand the data structure before performing other operations.
+ - Parameters: db_name (e.g. "SNOWFLAKE_MCP_DEMO"), schema_name (e.g. "TESTDATA")
+
+ 2. **insert_record** - Inserts a single record into a table.
+ - Parameters: fully_qualified_table (e.g. "SNOWFLAKE_MCP_DEMO.TESTDATA.TICKETS"), column_values (JSON string)
+ - The value for the TICKETID column is automatically generated in the database, so omit this column when inserting.
+ - Always provide both PRIORITY and TEXT columns.
+ - PRIORITY must be one of: "High", "Medium", "Low"
+
+ 3. **retrieve_records** - Retrieves records from a table with optional filtering.
+ - Parameters: fully_qualified_table, filter_column (optional), filter_value (optional)
+ - Pass empty strings for filter_column and filter_value to retrieve all records.
+
+ ## Data Model
+
+ The primary table is SNOWFLAKE_MCP_DEMO.TESTDATA.TICKETS:
+ - TICKETID (NUMBER, auto-increment) - Unique ticket identifier
+ - PRIORITY (VARCHAR) - Ticket priority: High, Medium, or Low
+ - TEXT (VARCHAR) - Description of the ticket/issue
+
+ ## Guidelines
+
+ - When a user asks to create a ticket, extract the priority and description from their request. If priority is not specified, ask for it.
+ - When a user asks to view or search tickets, use retrieve_records. Use the filter parameters when they want to filter by a specific column.
+ - If a user asks about the data structure or available tables, use get_schema_metadata.
+ - Always confirm successful operations by showing the user what was created or retrieved.
+ - Use fully qualified table names (DATABASE.SCHEMA.TABLE) in all tool calls.
+ - If a tool call returns an error, explain the issue clearly and suggest a correction.
+ ```
+
+ * In Tools, select our just configured Snowflake-managed MCP server.
+
+8. Test your agent by asking questions related to the excposed tools of the Snowflake MCP server, e.g. "Which tickets have priority High?".
+
+## Example
+
+[video link when available]