Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions src/FunctionsMcpTool.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
Expand All @@ -10,13 +10,15 @@
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.23.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="2.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="2.50.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.51.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.3.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.2" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Mcp" Version="1.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Mcp" Version="1.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Mcp.Sdk" Version="1.0.0-preview.2" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs" Version="6.8.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.5" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.7" />
<PackageReference Include="QRCoder" Version="1.7.0" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
Expand Down
2 changes: 1 addition & 1 deletion src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
// input bindings:
builder
.ConfigureMcpTool(GetSnippetToolName)
.WithProperty(SnippetNamePropertyName, PropertyType, SnippetNamePropertyDescription, required: true);
.WithProperty(SnippetNamePropertyName, McpToolPropertyType.String, SnippetNamePropertyDescription, required: true);

builder.Build().Run();
33 changes: 33 additions & 0 deletions src/QRCodeTool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Extensions.Mcp;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Protocol;
using QRCoder;

namespace FunctionsSnippetTool;

public class QRCodeTool(ILogger<QRCodeTool> logger)
{
[Function(nameof(GenerateQRCode))]
public ImageContentBlock GenerateQRCode(
[McpToolTrigger(nameof(GenerateQRCode), "Generates a QR code for the provided text or URL.")]
ToolInvocationContext context,
[McpToolProperty(nameof(data), "Text or URL to encode", true)]
string data)
{
logger.LogInformation("Generating QR code for: {Data}", data);

using var qrGenerator = new QRCodeGenerator();
using var qrCodeData = qrGenerator.CreateQrCode(data, QRCodeGenerator.ECCLevel.Q);
using var qrCode = new PngByteQRCode(qrCodeData);

var qrCodeBytes = qrCode.GetGraphic(20);
var base64Data = Convert.ToBase64String(qrCodeBytes);

return new ImageContentBlock
{
Data = base64Data,
MimeType = "image/png"
};
}
}
86 changes: 86 additions & 0 deletions src/SnippetsTool.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Extensions.Mcp;
using ModelContextProtocol.Protocol;
using static FunctionsSnippetTool.ToolsInformation;

namespace FunctionsSnippetTool;

public class SnippetsTool()
{
private const string BlobPath = "snippets/{mcptoolargs." + SnippetNamePropertyName + "}.json";
private const string BlobPathCompare1 = "snippets/{mcptoolargs." + FirstSnippetPropertyName + "}.json";
private const string BlobPathCompare2 = "snippets/{mcptoolargs." + SecondSnippetPropertyName + "}.json";

[Function(nameof(GetSnippet))]
public object GetSnippet(
Expand All @@ -31,4 +34,87 @@ string snippet
{
return snippet;
}

[Function(nameof(CompareSnippets))]
public IList<ContentBlock> CompareSnippets(
[McpToolTrigger(CompareSnippetsToolName, CompareSnippetsToolDescription)]
ToolInvocationContext context,
[McpToolProperty(FirstSnippetPropertyName, FirstSnippetPropertyDescription, true)]
string snippet1,
[McpToolProperty(SecondSnippetPropertyName, SecondSnippetPropertyDescription, true)]
string snippet2,
[BlobInput(BlobPathCompare1)] string? content1,
[BlobInput(BlobPathCompare2)] string? content2
)
{
var lines1 = content1?.Split('\n').Length ?? 0;
var lines2 = content2?.Split('\n').Length ?? 0;
var chars1 = content1?.Length ?? 0;
var chars2 = content2?.Length ?? 0;
var charDiff = chars2 - chars1;

var diff = GenerateDiff(content1 ?? "", content2 ?? "");

return new List<ContentBlock>
{
new TextContentBlock
{
Text = $"## Comparison: {snippet1} vs {snippet2}\n\n" +
$"**{snippet1}:** {lines1} lines, {chars1} characters\n" +
$"**{snippet2}:** {lines2} lines, {chars2} characters\n" +
$"**Difference:** {(charDiff > 0 ? "+" : "")}{charDiff} characters\n\n" +
$"### Unified Diff\n```diff\n{diff}\n```"
},
new TextContentBlock
{
Text = content1 is not null
? $"### Content of {snippet1}\n```{content1}\n```"
: $"### Content of {snippet1} not found."
},
new TextContentBlock
{
Text = content2 is not null
? $"### Content of {snippet2}\n```{content2}\n```"
: $"### Content of {snippet2} not found."
}
};
}

private static string GenerateDiff(string content1, string content2)
{
var lines1 = content1.Split('\n');
var lines2 = content2.Split('\n');
var result = new System.Text.StringBuilder();
var maxLines = Math.Max(lines1.Length, lines2.Length);

for (int i = 0; i < maxLines; i++)
{
var line1 = i < lines1.Length ? lines1[i] : null;
var line2 = i < lines2.Length ? lines2[i] : null;

if (line1 == line2)
{
// Same line
result.AppendLine($" {line1}");
}
else if (line1 is not null && line2 is not null)
{
// Modified line
result.AppendLine($"- {line1}");
result.AppendLine($"+ {line2}");
}
else if (line1 is not null)
{
// Removed line
result.AppendLine($"- {line1}");
}
else if (line2 is not null)
{
// Added line
result.AppendLine($"+ {line2}");
}
}

return result.ToString().TrimEnd();
}
}
8 changes: 7 additions & 1 deletion src/ToolsInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ internal sealed class ToolsInformation
public const string SnippetPropertyName = "snippet";
public const string SnippetNamePropertyDescription = "The name of the snippet.";
public const string SnippetPropertyDescription = "The code snippet.";
public const string PropertyType = "string";
public const string HelloToolName = "hello";
public const string HelloToolDescription =
"Simple hello world MCP Tool that responses with a hello message.";
public const string CompareSnippetsToolName = "compare_snippets";
public const string CompareSnippetsToolDescription =
"Compares two code snippets side by side with statistics.";
public const string FirstSnippetPropertyName = "snippet1";
public const string FirstSnippetPropertyDescription = "Name of the first snippet to compare.";
public const string SecondSnippetPropertyName = "snippet2";
public const string SecondSnippetPropertyDescription = "Name of the second snippet to compare.";
}