Skip to content

Commit a3200ed

Browse files
MCP validateSQL tool
1 parent dcc8e32 commit a3200ed

1 file changed

Lines changed: 49 additions & 3 deletions

File tree

query/src/org/labkey/query/controllers/QueryMcp.java

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
import org.labkey.api.query.QueryForeignKey;
1818
import org.labkey.api.query.QueryKey;
1919
import org.labkey.api.query.QueryParseException;
20+
import org.labkey.api.query.QueryParseWarning;
21+
import org.labkey.api.query.QuerySchema;
22+
import org.labkey.api.query.QueryService;
2023
import org.labkey.api.query.SchemaKey;
2124
import org.labkey.api.query.SimpleSchemaTreeVisitor;
2225
import org.labkey.api.query.UserSchema;
@@ -78,7 +81,7 @@ String listSchemas(ToolContext toolContext)
7881

7982
@Tool(description = "Provide list of tables within the provided schema.")
8083
@RequiresPermission(ReadPermission.class)
81-
String listTables(ToolContext toolContext, @ToolParam(description = "Fully qualified schema name as it would appear in SQL e.g. Study or \"Study.Datasets\"") String schemaName)
84+
String listTables(ToolContext toolContext, @ToolParam(description = "Fully qualified schema name as it would appear in SQL e.g. Study or \"Study\".\"Datasets\"") String schemaName)
8285
{
8386
var json = _listTables(getContext(toolContext), schemaName);
8487
return json.toString();
@@ -100,7 +103,7 @@ String listColumns(
100103
@RequiresPermission(ReadPermission.class)
101104
String getSourceForSavedQuery(
102105
ToolContext toolContext,
103-
@ToolParam(description = "Fully qualified schema name as it would appear in SQL e.g. Study or \"Study.Datasets\"") String schemaName,
106+
@ToolParam(description = "Fully qualified schema name as it would appear in SQL e.g. Study or \"Study\".\"Datasets\"") String schemaName,
104107
@ToolParam(description = "Table or query name as it would appear in SQL e.g. MyTable, MyQuery, or \"MyTable\"") String queryName
105108
)
106109
{
@@ -111,6 +114,49 @@ String getSourceForSavedQuery(
111114
throw new NotFoundException("Could not find the source for " + schemaName + "." + queryName);
112115
}
113116

117+
@Tool(description = "Validate SQL syntax.")
118+
@RequiresPermission(ReadPermission.class)
119+
String validateSQL(
120+
ToolContext toolContext,
121+
@ToolParam(description = "Fully qualified schema name as it would appear in SQL e.g. Study or \"Study\".\"Datasets\"") String schemaName,
122+
@ToolParam(description = "SQL source") String sql
123+
)
124+
{
125+
var context = getContext(toolContext);
126+
127+
SchemaKey schemaKey = getSchemaKey(schemaName);
128+
QuerySchema schema = DefaultSchema.get(context.getUser(), context.getContainer(), schemaKey);
129+
130+
try
131+
{
132+
TableInfo ti = QueryService.get().createTable(schema, sql, null, true);
133+
var warnings = ti.getWarnings();
134+
if (null != warnings)
135+
{
136+
var warning = warnings.stream().findFirst();
137+
if (warning.isPresent())
138+
throw warning.get();
139+
}
140+
// TODO if that worked, let have the DB check it too
141+
// if (ti.getSqlDialect().isPostgreSQL())
142+
// {
143+
// var parameters = ti.getNamedParameters();
144+
// if (parameters.isEmpty())
145+
// {
146+
// SQLFragment sqlPrepare = new SQLFragment("PREPARE validate AS SELECT * FROM ").append(ti.getFromSQL("MYVALIDATEQUERY__"));
147+
// new SqlExecutor(ti.getSchema().getScope()).execute(sqlPrepare);
148+
// }
149+
// }
150+
}
151+
catch (Exception x)
152+
{
153+
// CONSIDER remove line line/character information from DB errors as they won't match the LabKey SQL
154+
return "That SQL caused the " + (x instanceof QueryParseWarning ? "warning" : "error") + " below:\n```" + x.getMessage() + "```";
155+
}
156+
return "success";
157+
}
158+
159+
114160
/* For now, list all schemas. CONSIDER support incremental querying. */
115161
public static Map<SchemaKey, UserSchema> _listAllSchemas(DefaultSchema root)
116162
{
@@ -309,7 +355,7 @@ static String normalizeIdentifier(String compoundIdentifier)
309355
return new SqlParser().parseIdentifier(compoundIdentifier).toSQLString(true).toLowerCase();
310356
}
311357

312-
/** JSON schema example provided by GEMINI, using triple tick-marks to delimit the machine-readable structured data
358+
/* JSON schema example provided by GEMINI, using triple tick-marks to delimit the machine-readable structured data
313359
*
314360
* Here is the database schema in JSON format:
315361
* ```{

0 commit comments

Comments
 (0)