-
Notifications
You must be signed in to change notification settings - Fork 166
Adding Mssql Mcp Azure Functions project. #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Adding Mssql Mcp Azure Functions project.
There was a problem hiding this 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.
| _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
AI
Nov 11, 2025
There was a problem hiding this comment.
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.
| _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); |
| // 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
AI
Nov 11, 2025
There was a problem hiding this comment.
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.
| // 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; | |
| } |
| 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) |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
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.
| [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) |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
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.
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "DropTable failed: {Message}", ex.Message); | ||
| return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message)); | ||
| } |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic catch clause.
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "InsertData failed: {Message}", ex.Message); | ||
| return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message)); | ||
| } |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic catch clause.
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "ListTables failed: {Message}", ex.Message); | ||
| return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message)); | ||
| } |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic catch clause.
| 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.")); | |
| } |
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "ReadData failed: {Message}", ex.Message); | ||
| return JsonSerializer.Serialize(new DbOperationResult(success: false, error: ex.Message)); | ||
| } |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic catch clause.
| 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)); | |
| } |
| catch (Exception ex) | ||
| { | ||
| _logger.LogError(ex, "UpdateData failed: {Message}", ex.Message); | ||
| return JsonSerializer.Serialize(new DbOperationResult(false, ex.Message)); | ||
| } |
Copilot
AI
Nov 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic catch clause.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Azure Functions just release Azure Functions MCP extension, and I believe this is an excellent opportunity to contribute to this samples.