Skip to content

Commit afc29ec

Browse files
Update snowflake-mcp.md
1 parent 305ae9c commit afc29ec

1 file changed

Lines changed: 114 additions & 108 deletions

File tree

  • content/en/docs/marketplace/platform-supported-content/modules/snowflake

content/en/docs/marketplace/platform-supported-content/modules/snowflake/snowflake-mcp.md

Lines changed: 114 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ To establish a connection between a Mendix AI Agent and a Snowflake-managed MCP
2323

2424
Alternatively, to start from scratch or to add the capability to an exsiting application, you must also install the following modules and their prerequisites:
2525

26-
* [MCP Client](https://marketplace.mendix.com/link/component/244893) (version 3.1.0 or higher)
26+
* [MCP Client](https://marketplace.mendix.com/link/component/244893) (version 3.1.0 or newer)
2727
* [Conversational UI](https://marketplace.mendix.com/link/component/239450)
2828

2929
## Preparing a Snowflake-Managed MCP Server
@@ -32,117 +32,15 @@ To configure a Snowflake-managed MCP server, follow these steps:
3232

3333
1. In Snowflake, set up the database and schemas which will be used by the server.
3434

35-
For a code sample with test data, see [Database and Schema Setup](#code-db-schema)
35+
For a code sample, see [Database and Schema Setup](#code-db-schema)
3636

3737
2. Create the stored procedures which the MCP server will expose as tools.
3838

39-
39+
For code samples, see the following:
4040

41-
42-
<summary>Expand for example code for a generic stored procdure for retrieving records</summary>
43-
44-
```sql
45-
-- You can run this example/demo under sysadmin role, for real production screnario's use proper authorisation
46-
CREATE OR REPLACE PROCEDURE SNOWFLAKE_MCP_DEMO.TOOLS.RETRIEVE_RECORDS(
47-
fully_qualified_table VARCHAR,
48-
filter_column VARCHAR,
49-
filter_value VARCHAR
50-
)
51-
RETURNS VARCHAR
52-
LANGUAGE PYTHON
53-
RUNTIME_VERSION = '3.11'
54-
PACKAGES = ('snowflake-snowpark-python')
55-
HANDLER = 'run'
56-
AS
57-
$$
58-
import json
59-
def run(session, fully_qualified_table, filter_column, filter_value):
60-
parts = fully_qualified_table.split('.')
61-
if len(parts) != 3:
62-
return json.dumps({"status": "error", "message": "Table must be fully qualified: DATABASE.SCHEMA.TABLE"})
63-
try:
64-
if filter_column and filter_value:
65-
escaped = filter_value.replace("'", "''")
66-
sql = f"SELECT * FROM {fully_qualified_table} WHERE {filter_column} = '{escaped}'"
67-
else:
68-
sql = f"SELECT * FROM {fully_qualified_table}"
69-
rows = session.sql(sql).collect()
70-
results = [row.as_dict() for row in rows]
71-
for r in results:
72-
for k, v in r.items():
73-
if not isinstance(v, (str, int, float, bool, type(None))):
74-
r[k] = str(v)
75-
return json.dumps({"status": "success", "row_count": len(results), "data": results})
76-
except Exception as e:
77-
return json.dumps({"status": "error", "message": str(e)})
78-
$$;
79-
```
80-
</details>
81-
<details>
82-
<summary>Expand for example code for a generic stored procdure for inserting records</summary>
83-
84-
```sql
85-
-- inputs can be for example:
86-
-- fully_qualified_table = 'SNOWFLAKE_MCP_DEMO.TESTDATA.TICKETS'
87-
-- column_values: '{"PRIORITY": "Low", "TEKST": "text here"}'
88-
89-
-- You can run this example/demo under sysadmin role, for real production screnario's use proper authorisation
90-
CREATE OR REPLACE PROCEDURE SNOWFLAKE_MCP_DEMO.TOOLS.INSERT_RECORD(
91-
fully_qualified_table VARCHAR,
92-
column_values VARCHAR
93-
)
94-
RETURNS VARCHAR
95-
LANGUAGE PYTHON
96-
RUNTIME_VERSION = '3.11'
97-
PACKAGES = ('snowflake-snowpark-python')
98-
HANDLER = 'run'
99-
AS
100-
$$
101-
import json
102-
def run(session, fully_qualified_table, column_values):
103-
parts = fully_qualified_table.split('.')
104-
if len(parts) != 3:
105-
return json.dumps({"status": "error", "message": "Table must be fully qualified: DATABASE.SCHEMA.TABLE"})
106-
db, schema, table = parts
107-
cols_rows = session.sql(f"""
108-
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE
109-
FROM {db}.INFORMATION_SCHEMA.COLUMNS
110-
WHERE TABLE_SCHEMA = '{schema}' AND TABLE_NAME = '{table}'
111-
ORDER BY ORDINAL_POSITION
112-
""").collect()
113-
if not cols_rows:
114-
return json.dumps({"status": "error", "message": f"Table {fully_qualified_table} not found or has no columns"})
115-
schema_info = {row["COLUMN_NAME"]: row["DATA_TYPE"] for row in cols_rows}
116-
try:
117-
values = json.loads(column_values)
118-
except json.JSONDecodeError as e:
119-
return json.dumps({"status": "error", "message": f"Invalid JSON in column_values: {str(e)}", "expected_columns": list(schema_info.keys())})
120-
for col_name in values:
121-
if col_name not in schema_info:
122-
return json.dumps({"status": "error", "message": f"Column '{col_name}' does not exist in {fully_qualified_table}", "valid_columns": list(schema_info.keys())})
123-
col_names = list(values.keys())
124-
val_parts = []
125-
for col in col_names:
126-
val = values[col]
127-
if val is None:
128-
val_parts.append("NULL")
129-
elif isinstance(val, (int, float)):
130-
val_parts.append(str(val))
131-
else:
132-
escaped = str(val).replace("'", "''")
133-
val_parts.append(f"'{escaped}'")
134-
col_list = ", ".join(col_names)
135-
val_list = ", ".join(val_parts)
136-
sql = f"INSERT INTO {fully_qualified_table} ({col_list}) VALUES ({val_list})"
137-
try:
138-
session.sql(sql).collect()
139-
return json.dumps({"status": "success", "message": f"Record inserted into {fully_qualified_table}", "columns_inserted": col_names})
140-
except Exception as e:
141-
return json.dumps({"status": "error", "message": str(e)})
142-
$$;
143-
```
144-
</details>
145-
41+
* [Procedure to Return Metadata](#code-metadata)
42+
* [Procedure to Retrieve Records](#code-records)
43+
* [Procedure to Insert Records](#code-records-insert)
14644

14745
3. Create the Snowflake MCP server exposing the stored procedures as tools.
14846
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.
@@ -388,6 +286,114 @@ def run(session, db_name, schema_name):
388286
$$;
389287
```
390288

289+
#### Procedure to Retrieve Records {#code-records}
290+
291+
The following is an example of a generic stored procedure which retrieves records:
292+
293+
```sql
294+
-- You can run this example/demo under sysadmin role, for real production screnario's use proper authorisation
295+
CREATE OR REPLACE PROCEDURE SNOWFLAKE_MCP_DEMO.TOOLS.RETRIEVE_RECORDS(
296+
fully_qualified_table VARCHAR,
297+
filter_column VARCHAR,
298+
filter_value VARCHAR
299+
)
300+
RETURNS VARCHAR
301+
LANGUAGE PYTHON
302+
RUNTIME_VERSION = '3.11'
303+
PACKAGES = ('snowflake-snowpark-python')
304+
HANDLER = 'run'
305+
AS
306+
$$
307+
import json
308+
def run(session, fully_qualified_table, filter_column, filter_value):
309+
parts = fully_qualified_table.split('.')
310+
if len(parts) != 3:
311+
return json.dumps({"status": "error", "message": "Table must be fully qualified: DATABASE.SCHEMA.TABLE"})
312+
try:
313+
if filter_column and filter_value:
314+
escaped = filter_value.replace("'", "''")
315+
sql = f"SELECT * FROM {fully_qualified_table} WHERE {filter_column} = '{escaped}'"
316+
else:
317+
sql = f"SELECT * FROM {fully_qualified_table}"
318+
rows = session.sql(sql).collect()
319+
results = [row.as_dict() for row in rows]
320+
for r in results:
321+
for k, v in r.items():
322+
if not isinstance(v, (str, int, float, bool, type(None))):
323+
r[k] = str(v)
324+
return json.dumps({"status": "success", "row_count": len(results), "data": results})
325+
except Exception as e:
326+
return json.dumps({"status": "error", "message": str(e)})
327+
$$;
328+
```
329+
330+
#### Procedure to Insert Records {#code-records-insert}
331+
332+
<details>
333+
<summary>Expand for example code for a generic stored procedure for inserting records</summary>
334+
335+
```sql
336+
-- inputs can be for example:
337+
-- fully_qualified_table = 'SNOWFLAKE_MCP_DEMO.TESTDATA.TICKETS'
338+
-- column_values: '{"PRIORITY": "Low", "TEKST": "text here"}'
339+
340+
-- You can run this example/demo under sysadmin role, for real production screnario's use proper authorisation
341+
CREATE OR REPLACE PROCEDURE SNOWFLAKE_MCP_DEMO.TOOLS.INSERT_RECORD(
342+
fully_qualified_table VARCHAR,
343+
column_values VARCHAR
344+
)
345+
RETURNS VARCHAR
346+
LANGUAGE PYTHON
347+
RUNTIME_VERSION = '3.11'
348+
PACKAGES = ('snowflake-snowpark-python')
349+
HANDLER = 'run'
350+
AS
351+
$$
352+
import json
353+
def run(session, fully_qualified_table, column_values):
354+
parts = fully_qualified_table.split('.')
355+
if len(parts) != 3:
356+
return json.dumps({"status": "error", "message": "Table must be fully qualified: DATABASE.SCHEMA.TABLE"})
357+
db, schema, table = parts
358+
cols_rows = session.sql(f"""
359+
SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE
360+
FROM {db}.INFORMATION_SCHEMA.COLUMNS
361+
WHERE TABLE_SCHEMA = '{schema}' AND TABLE_NAME = '{table}'
362+
ORDER BY ORDINAL_POSITION
363+
""").collect()
364+
if not cols_rows:
365+
return json.dumps({"status": "error", "message": f"Table {fully_qualified_table} not found or has no columns"})
366+
schema_info = {row["COLUMN_NAME"]: row["DATA_TYPE"] for row in cols_rows}
367+
try:
368+
values = json.loads(column_values)
369+
except json.JSONDecodeError as e:
370+
return json.dumps({"status": "error", "message": f"Invalid JSON in column_values: {str(e)}", "expected_columns": list(schema_info.keys())})
371+
for col_name in values:
372+
if col_name not in schema_info:
373+
return json.dumps({"status": "error", "message": f"Column '{col_name}' does not exist in {fully_qualified_table}", "valid_columns": list(schema_info.keys())})
374+
col_names = list(values.keys())
375+
val_parts = []
376+
for col in col_names:
377+
val = values[col]
378+
if val is None:
379+
val_parts.append("NULL")
380+
elif isinstance(val, (int, float)):
381+
val_parts.append(str(val))
382+
else:
383+
escaped = str(val).replace("'", "''")
384+
val_parts.append(f"'{escaped}'")
385+
col_list = ", ".join(col_names)
386+
val_list = ", ".join(val_parts)
387+
sql = f"INSERT INTO {fully_qualified_table} ({col_list}) VALUES ({val_list})"
388+
try:
389+
session.sql(sql).collect()
390+
return json.dumps({"status": "success", "message": f"Record inserted into {fully_qualified_table}", "columns_inserted": col_names})
391+
except Exception as e:
392+
return json.dumps({"status": "error", "message": str(e)})
393+
$$;
394+
```
395+
</details>
396+
391397
## Connecting a Mendix Agent to the MCP Server
392398

393399
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:

0 commit comments

Comments
 (0)