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
7 changes: 7 additions & 0 deletions AzureSignalR.sln
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagementPublisher", "samp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatSample.RazorPages", "samples\ChatSample\ChatSample.RazorPages\ChatSample.RazorPages.csproj", "{D7A38BB7-6416-4E15-AD87-D525F203F549}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatMcp", "samples\ChatSample\ChatMcp\ChatMcp.csproj", "{DCF069DE-1C30-EE2B-D0ED-3355E998847B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -196,6 +198,10 @@ Global
{D7A38BB7-6416-4E15-AD87-D525F203F549}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7A38BB7-6416-4E15-AD87-D525F203F549}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D7A38BB7-6416-4E15-AD87-D525F203F549}.Release|Any CPU.Build.0 = Release|Any CPU
{DCF069DE-1C30-EE2B-D0ED-3355E998847B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DCF069DE-1C30-EE2B-D0ED-3355E998847B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DCF069DE-1C30-EE2B-D0ED-3355E998847B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DCF069DE-1C30-EE2B-D0ED-3355E998847B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -229,6 +235,7 @@ Global
{82C1FF3D-EC6C-4B21-B6A4-E69E8D75D0D0} = {2429FBD8-1FCE-4C42-AA28-DF32F7249E77}
{0F32E624-7AC8-4CA7-8ED9-E1A877442020} = {C965ED06-6A17-4329-B3C6-811830F4F4ED}
{D7A38BB7-6416-4E15-AD87-D525F203F549} = {C965ED06-6A17-4329-B3C6-811830F4F4ED}
{DCF069DE-1C30-EE2B-D0ED-3355E998847B} = {C965ED06-6A17-4329-B3C6-811830F4F4ED}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7945A4E4-ACDB-4F6E-95CA-6AC6E7C2CD59}
Expand Down
20 changes: 20 additions & 0 deletions samples/ChatSample/ChatMcp/ChatMcp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\ChatSample\IChatHub.cs" Link="IChatHub.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.14" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="ModelContextProtocol" Version="0.1.0-preview.6" />
</ItemGroup>

</Project>
24 changes: 24 additions & 0 deletions samples/ChatSample/ChatMcp/ChatProxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Threading.Tasks;

using ChatSample;
using Microsoft.AspNetCore.SignalR.Client;

namespace ChatMcp;

public sealed class ChatProxy(HubConnection connection) : IChatHub
{
public async Task BroadcastMessage(string name, string message)
{
await connection.InvokeAsync(nameof(IChatHub.BroadcastMessage), name, message);
}

Task IChatHub.Echo(string name, string message)
{
// we dont need this ability in the MCP tool.
throw new NotSupportedException();
}
}
22 changes: 22 additions & 0 deletions samples/ChatSample/ChatMcp/ChatTool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.ComponentModel;
using System.Threading.Tasks;

using ModelContextProtocol.Server;

namespace ChatMcp;

[McpServerToolType]
public class ChatTool(ChatProxy proxy)
{
[McpServerTool]
[Description("Broadcast a message to all clients.")]
public async Task BroadcastMessage(
[Description("The text message.")]
string message)
{
await proxy.BroadcastMessage("MCP", message);
}
}
40 changes: 40 additions & 0 deletions samples/ChatSample/ChatMcp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;

using ChatMcp;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var url = Environment.GetEnvironmentVariable("ServerUrl");
if (string.IsNullOrEmpty(url))
{
url = "http://localhost:5050/chat";
}
if (!Uri.TryCreate(url, UriKind.Absolute, out var serverUrl))
{
Console.WriteLine($"Invalid ServerUrl: {url}");
return;
}

await using var connection = new HubConnectionBuilder()
.WithUrl(url)
.WithAutomaticReconnect()
.Build();
await connection.StartAsync();

var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(consoleLogOptions =>
{
// Configure all logs to go to stderr
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
});

builder.Services
.AddSingleton(new ChatProxy(connection))
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
await builder.Build().RunAsync();
10 changes: 5 additions & 5 deletions samples/ChatSample/ChatSample/ChatHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@

namespace ChatSample;

public class ChatHub(IHubContext<ChatHub> context) : Hub
public class ChatHub(IHubContext<ChatHub> context) : Hub, IChatHub
{
public void BroadcastMessage(string name, string message)
public async Task BroadcastMessage(string name, string message)
{
Clients.All.SendAsync("broadcastMessage", name, message);
await Clients.All.SendAsync("broadcastMessage", name, message);
Console.WriteLine("Broadcasting...");
}

public void Echo(string name, string message)
public async Task Echo(string name, string message)
{
Clients.Caller.SendAsync("echo", name,
await Clients.Caller.SendAsync("echo", name,
$"{message} (echo from server, Client IP: {Context.GetHttpContext().Connection.RemoteIpAddress}");
Console.WriteLine("Echo...");
}
Expand Down
12 changes: 12 additions & 0 deletions samples/ChatSample/ChatSample/IChatHub.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Threading.Tasks;

namespace ChatSample;

public interface IChatHub
{
Task BroadcastMessage(string name, string message);
Task Echo(string name, string message);
}
5 changes: 4 additions & 1 deletion samples/ChatSample/ChatSample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ private enum AuthTypes
{
VisualStudio = 0,

VisualStudioCode,

ApplicationWithCertificate,

ApplicationWithClientSecret,
Expand All @@ -66,7 +68,8 @@ public void ConfigureServices(IServiceCollection services)
{
TokenCredential credential = AuthType switch
{
AuthTypes.VisualStudio => new VisualStudioCodeCredential(),
AuthTypes.VisualStudio => new VisualStudioCredential(),
AuthTypes.VisualStudioCode => new VisualStudioCodeCredential(),
AuthTypes.ApplicationWithCertificate => new ClientCertificateCredential(TenantId, AppClientId, "path-to-cert-file"),
AuthTypes.ApplicationWithClientSecret => new ClientSecretCredential(TenantId, AppClientId, "client-secret-value"),
AuthTypes.ApplicationWithFederatedIdentity => GetClientAssertionCredential(TenantId, AppClientId, MsiClientId),
Expand Down