Skip to content

Conversation

@jorgelevy
Copy link

Azure Functions just release Azure Functions MCP extension, and I believe this is an excellent opportunity to contribute to this samples.

Copilot AI review requested due to automatic review settings November 11, 2025 21:47
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces an Azure Functions implementation for MSSQL MCP (Model Context Protocol), leveraging the newly released Azure Functions MCP extension. The project provides a serverless API for SQL Server database operations including table management, data manipulation, and schema inspection.

  • Implements MCP tools as Azure Functions for CRUD operations on SQL Server databases
  • Provides eight distinct function endpoints for database operations (create, read, update, delete, describe, list, drop tables)
  • Includes connection factory pattern for managing SQL Server connections

Reviewed Changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 27 comments.

Show a summary per file
File Description
MssqlMcpAzFunc.csproj Project configuration defining .NET target framework and NuGet dependencies for Azure Functions and SQL Server
Program.cs Application entry point configuring Azure Functions host with Application Insights telemetry
SqlConnectionFactory.cs Factory implementation for creating and opening SQL Server connections from environment variables
ISqlConnectionFactory.cs Interface defining the connection factory contract
DbOperationResult.cs Result model for standardizing database operation responses with success status, errors, and data
Functions/Tools.cs Core implementation of eight MCP tool functions for database operations (CreateTable, DescribeTable, DropTable, InsertData, ListTables, ReadData, UpdateData)
host.json Azure Functions host configuration with Application Insights settings
local.settings.json Local development settings for Azure Functions runtime
Properties/launchSettings.json Launch configuration for local debugging on port 7277
Properties/serviceDependencies.json Azure service dependencies configuration
Properties/serviceDependencies.local.json Local service dependencies using storage emulator
.gitignore Standard Visual Studio and Azure Functions ignore patterns
MssqlMcpAzFunc.slnx Solution file referencing the project
Comments suppressed due to low confidence (2)

MssqlMcp/AzureFunctions/MssqlMcpAzFunc/Functions/Tools.cs:137

  • This assignment to _ is useless, since its value is never read.
                    var _ = cmd.Parameters.AddWithValue("@TableName", name);

MssqlMcp/AzureFunctions/MssqlMcpAzFunc/Functions/Tools.cs:138

  • This assignment to _ is useless, since its value is never read.
                    _ = cmd.Parameters.AddWithValue("@TableSchema", schema == null ? DBNull.Value : schema);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +298 to +303
_logger.LogWarning(context?.Transport?.Name);
var tmp = ((HttpTransport)(context?.Transport)).Headers;
_logger.LogWarning("Headers: {Headers}", string.Join(", ", tmp.Select(kv => $"{kv.Key}: {string.Join(";", kv.Value)}")));
_logger.LogWarning(((HttpTransport)(context?.Transport)).Headers.FirstOrDefault(h => h.Key == "testing-header").Value);


Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This debugging code should be removed before merging. The LogWarning calls and header inspection appear to be temporary testing code that adds no value to the production implementation and could expose sensitive information from headers in logs.

Suggested change
_logger.LogWarning(context?.Transport?.Name);
var tmp = ((HttpTransport)(context?.Transport)).Headers;
_logger.LogWarning("Headers: {Headers}", string.Join(", ", tmp.Select(kv => $"{kv.Key}: {string.Join(";", kv.Value)}")));
_logger.LogWarning(((HttpTransport)(context?.Transport)).Headers.FirstOrDefault(h => h.Key == "testing-header").Value);

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +29
// Helper to convert DataTable to a serializable list
private static List<Dictionary<string, object>> DataTableToList(DataTable table)
{
var result = new List<Dictionary<string, object>>();
foreach (DataRow row in table.Rows)
{
var dict = new Dictionary<string, object>();
foreach (DataColumn col in table.Columns)
{
dict[col.ColumnName] = row[col];
}
result.Add(dict);
}
return result;
}
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DataTableToList helper method is defined but never used anywhere in the code. Consider removing it to reduce code clutter, or use it if there was an intended purpose.

Suggested change
// Helper to convert DataTable to a serializable list
private static List<Dictionary<string, object>> DataTableToList(DataTable table)
{
var result = new List<Dictionary<string, object>>();
foreach (DataRow row in table.Rows)
{
var dict = new Dictionary<string, object>();
foreach (DataColumn col in table.Columns)
{
dict[col.ColumnName] = row[col];
}
result.Add(dict);
}
return result;
}

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +34
public async Task<string> CreateTable(
[McpToolTrigger("create_table", "Creates a new table in the SQL Database. Expects a valid CREATE TABLE SQL statement as input.")] ToolInvocationContext context,
[McpToolProperty("sql", "CREATE TABLE SQL statement")] string sql)
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CreateTable function accepts raw SQL statements without any validation or sanitization. This creates a significant security risk as malicious SQL could be executed. Consider adding validation to ensure only CREATE TABLE statements are executed, or use a safer approach like parameterized schema definitions.

Copilot uses AI. Check for mistakes.
Comment on lines +250 to +253
[Function("DropTable")]
public async Task<string> DropTable(
[McpToolTrigger("drop_table", "Drops a table in the SQL Database. Expects a valid DROP TABLE SQL statement as input.")] ToolInvocationContext context,
[McpToolProperty("sql", "DROP TABLE SQL statement")] string sql)
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DropTable function accepts raw SQL statements without any validation. This is a critical security risk as it allows execution of arbitrary DROP statements, potentially destroying data beyond the intended scope. Consider validating that the SQL only contains DROP TABLE statements for the specified table.

Copilot uses AI. Check for mistakes.
Comment on lines +265 to +269
catch (Exception ex)
{
_logger.LogError(ex, "DropTable failed: {Message}", ex.Message);
return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message));
}
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic catch clause.

Copilot uses AI. Check for mistakes.
Comment on lines +287 to +291
catch (Exception ex)
{
_logger.LogError(ex, "InsertData failed: {Message}", ex.Message);
return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message));
}
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic catch clause.

Copilot uses AI. Check for mistakes.
Comment on lines +321 to +325
catch (Exception ex)
{
_logger.LogError(ex, "ListTables failed: {Message}", ex.Message);
return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message));
}
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic catch clause.

Suggested change
catch (Exception ex)
{
_logger.LogError(ex, "ListTables failed: {Message}", ex.Message);
return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message));
}
catch (SqlException ex)
{
_logger.LogError(ex, "ListTables failed with SQL error: {Message}", ex.Message);
return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message));
}
catch (Exception ex)
{
_logger.LogError(ex, "ListTables failed with unexpected error: {Message}", ex.Message);
return JsonSerializer.Serialize(new DbOperationResult(success: false, error: "An unexpected error occurred."));
}

Copilot uses AI. Check for mistakes.
Comment on lines +353 to +357
catch (Exception ex)
{
_logger.LogError(ex, "ReadData failed: {Message}", ex.Message);
return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message));
}
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic catch clause.

Suggested change
catch (Exception ex)
{
_logger.LogError(ex, "ReadData failed: {Message}", ex.Message);
return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message));
}
catch (SqlException ex)
{
_logger.LogError(ex, "ReadData failed (SQL error): {Message}", ex.Message);
return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message));
}
catch (Exception ex) when (ex is not OutOfMemoryException && ex is not StackOverflowException && ex is not ThreadAbortException)
{
_logger.LogError(ex, "ReadData failed (unexpected error): {Message}", ex.Message);
return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message));
}

Copilot uses AI. Check for mistakes.
Comment on lines +375 to +379
catch (Exception ex)
{
_logger.LogError(ex, "UpdateData failed: {Message}", ex.Message);
return JsonSerializer.Serialize(new DbOperationResult(false, ex.Message));
}
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic catch clause.

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant