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
11 changes: 8 additions & 3 deletions MCPSharp.Example.OllamaChatCLI/MCPClientPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ class MCPClientPool : ICollection<MCPClient>
{
private readonly List<MCPClient> clients = [];

public List<AITool> GetAllAIFunctions()
public async Task<List<AITool>> GetAllAIFunctionsAsync()
{
var functions = new List<AITool>();
clients.ForEach(c => functions.AddRange(c.GetFunctionsAsync().Result));

foreach (var c in clients)
{
functions.AddRange(await c.GetFunctionsAsync());
}

return functions;
}

public int Count => clients.Count;
public bool IsReadOnly => false;
public void Add(string name, McpServerConfiguration server, Func<Dictionary<string, object>, bool> permissionFunction = null)
public void Add(string name, McpServerConfiguration server, Func<Dictionary<string, object>, bool>? permissionFunction = null)
{
clients.Add(new MCPClient(name, "0.1.0", server.Command, string.Join(' ', server.Args ?? []), server.Env)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\MCPSharp.Example\MCPSharp.Example.csproj" />
<ProjectReference Include="..\MCPSharp\MCPSharp.csproj" />
</ItemGroup>

Expand Down
24 changes: 13 additions & 11 deletions MCPSharp.Example.OllamaChatCLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
using System.Text;
using System.Text.Json;

JsonSerializerOptions jsonSerializerOptions = new() { PropertyNameCaseInsensitive = true };

McpServerConfigurationCollection conf = JsonSerializer.Deserialize<McpServerConfigurationCollection>(File.ReadAllText("config.json"),
jsonSerializerOptions)!;
McpServerConfigurationCollection conf = JsonSerializer.Deserialize<McpServerConfigurationCollection>(File.ReadAllText("config.json"), JsonOptionsManager.Standard)!;

MCPClientPool clients = [];
foreach (var server in conf.McpServers)
Expand All @@ -23,8 +21,8 @@
}

var chatOptions = new ChatOptions {
Tools = clients.GetAllAIFunctions(),
ToolMode = ChatToolMode.Auto //let the assistant choose not to use a tool if it doesn't need to
Tools = clients.GetAllAIFunctionsAsync().Result,
ToolMode = ChatToolMode.Auto //let the assistant choose not to use a tool if it doesn't need to (should be default)
};
var chatHistory = new List<ChatMessage>() { new(ChatRole.System, conf.Models["ollama"].SystemPrompt) };
var chatClient = new OllamaChatClient(conf.Models["ollama"].Endpoint, conf.Models["ollama"].ModelId).AsBuilder().UseFunctionInvocation().Build();
Expand All @@ -37,7 +35,7 @@
chatHistory.Add(new ChatMessage(ChatRole.User, input));
var response = await chatClient.GetResponseAsync(chatHistory, chatOptions);
Console.WriteLine($"\n\n[Assistant] {DateTime.Now.ToShortTimeString()}: {response}");
chatHistory.Add(response.Message);
chatHistory.AddRange(response.Messages);
}

class McpServerConfiguration
Expand All @@ -47,17 +45,21 @@ class McpServerConfiguration
public Dictionary<string, string> Env { get; set; } = [];
}

class JsonOptionsManager
{
public static JsonSerializerOptions Standard = new() { PropertyNameCaseInsensitive = true };
}
class McpServerConfigurationCollection
{
public Dictionary<string, McpServerConfiguration> McpServers { get; set; }
public Dictionary<string, ModelConfiguration> Models { get; set; }
public Dictionary<string, McpServerConfiguration> McpServers { get; set; } = [];
public Dictionary<string, ModelConfiguration> Models { get; set; } = [];
}


class ModelConfiguration
{
public string Endpoint { get; set; }
public string ModelId { get; set; }
public string SystemPrompt { get; set; }
public string Endpoint { get; set; } = "";
public string ModelId { get; set; } = "";
public string SystemPrompt { get; set; } = "";
}

24 changes: 19 additions & 5 deletions MCPSharp.Example/MCPDev.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,26 @@
///<summary>testing interface for custom .net mcp server</summary>
public class MCPDev()
{
[McpResource("name", "test://{name}")]
public string Name(string name) => $"hello {name}";


[McpResource("settings", "test://settings", "string", "the settings document")]
/// <summary>
/// example of a simple resource that returns a string
/// </summary>
[McpResource("settings", "test://settings", "text/plain", "the settings document")]
public string Settings { get; set; } = "settings";

/// <summary>
/// example of a function that changes the value of the settings resource
/// </summary>
/// <param name="value"></param>
[McpTool("change_setings", "change the value of the settings resource")]
public bool ChangeSettings(string value) {
Settings = value;
return true;
}

/// <summary>
/// example of a function that attempts to write to console - to ensure this does not break the stream
/// </summary>
/// <param name="message"></param>
[McpTool("write-to-console", "write a string to the console")]
public static void WriteToConsole(string message) => Console.WriteLine(message);

Expand Down Expand Up @@ -43,7 +55,9 @@ public class MCPDev()
/// </summary>
/// <returns></returns>
/// <exception cref="Exception"></exception>
#pragma warning disable CS0618 // Type or member is obsolete
[McpFunction("throw_exception")] //leaving this one as [McpFunction] for testing purposes
#pragma warning restore CS0618 // Type or member is obsolete
public static string Exception() => throw new Exception("This is an exception");
}
}
22 changes: 19 additions & 3 deletions MCPSharp.Example/Program.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
using MCPSharp;
using MCPSharp.Model;
using MCPSharp.ExternalExample;
using MCPSharp.Model.Schemas;
using MCPSharp.Example.Import;
using Microsoft.Extensions.AI;

MCPServer.Register<ExternalTool>();
//register tools that are not part of the core assembly
MCPServer.Register<ExternalTool>();

//register tools that are build with semantic kernel attributes
MCPServer.Register<SemKerExample>();

//example AIFunctions created from lambdas
var myFunc = AIFunctionFactory.Create(() => { return "ahoyhoy!"; }, "AI_Function", "an AIFunction that has been imported");
var myOtherFunc = AIFunctionFactory.Create((string input) => { return input.ToUpperInvariant(); }, "to_upper", "converts a string to uppercase (culture invariant)");

//register the AIFunctions
MCPServer.RegisterAIFunction(myFunc);
MCPServer.RegisterAIFunction(myOtherFunc);

//add a dynamically built tool handler
MCPServer.AddToolHandler( new Tool()
{
Name = "dynamicTool",
Expand All @@ -18,6 +31,9 @@
{"input2", new ParameterSchema{Type="string", Description="the input2"}}
}
}
}, (string input, string? input2 = null) => { return $"hello, {input}.\n{input2 ?? "didn't feel like filling in the second value just because it wasn't required? shame. just kidding! thanks for your help!"}"; });

}, (string input, string? input2 = null) => { return $"hello, {input}.\n{input2 ??
"didn't feel like filling in the second value just because it wasn't required? shame. just kidding! thanks for your help!"}"; });

//start the server
await MCPServer.StartAsync("TestServer", "1.0");
5 changes: 2 additions & 3 deletions MCPSharp.ExternalExample/ExternalTool.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using Microsoft.SemanticKernel;
using System.ComponentModel;

namespace MCPSharp.ExternalExample
namespace MCPSharp.Example.Import
{

[McpTool("external_tools", "for testing accessing tool classes loaded from a library")]
public class ExternalTool
{

[McpFunction("dll-tool", "attempts to use a tool that is loaded from an external assembly dll. should return 'success'")]
[McpTool("dll-tool", "attempts to use a tool that is loaded from an external assembly dll. should return 'success'")]
public static async Task<string> UseAsync()
{
return await Task.Run(()=>"success");
Expand Down
18 changes: 9 additions & 9 deletions MCPSharp.Test/AIFunctionAbstractionTests.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
using MCPSharp.Model.Results;
using Microsoft.Extensions.AI;
using System.Text.Json;
using System.Xml.Linq;

namespace MCPSharp.Test
{
[TestClass]
public class AIFunctionAbstractionTests
{
private static MCPClient client;
private static IList<AIFunction> functions;
private static MCPClient? client;
private static IList<AIFunction>? functions;

[ClassInitialize]
public static async Task ClassInitialize(TestContext context)
{
Console.WriteLine(context.TestName);
client = new("Test Client", "1.0.0", "dotnet", "MCPSharp.Example.dll");
functions = await client.GetFunctionsAsync();
}
Expand All @@ -23,7 +22,7 @@ public static async Task ClassInitialize(TestContext context)
public async Task TestInvokingAnAIFunction()
{

var function = functions.First(f => f.Name == "Hello");
var function = functions?.First(f => f.Name == "Hello");
CallToolResult result = (CallToolResult)(await function!.InvokeAsync())!;
Assert.IsFalse(result.IsError, $"{result.Content[0].Text}");
Assert.AreEqual("hello, claude.", result.Content[0].Text);
Expand All @@ -34,10 +33,10 @@ public async Task TestInvokingAnAIFunction()
public async Task TestInvokingAnAIFunctionWithParameters()
{

var function = functions.First(f => f.Name == "Echo");
var Schema = function.JsonSchema;
var function = functions?.First(f => f.Name == "Echo");
var Schema = function?.JsonSchema;
Console.WriteLine(Schema);
CallToolResult result = (CallToolResult)(await function.InvokeAsync(new Dictionary<string, object?> { { "input", "hello there" } }))!;
CallToolResult result = (CallToolResult)(await function!.InvokeAsync(new Dictionary<string, object?> { { "input", "hello there" } }))!;

Assert.IsFalse(result.IsError);

Expand All @@ -46,7 +45,8 @@ public async Task TestInvokingAnAIFunctionWithParameters()

}

[ClassCleanup]

[ClassCleanup(ClassCleanupBehavior.EndOfClass)]
public static void ClassCleanup()
{
client?.Dispose();
Expand Down
20 changes: 12 additions & 8 deletions MCPSharp.Test/ClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,25 @@ namespace MCPSharp.Test
[TestClass]
public class ClientTests
{
public static MCPClient client;
private static MCPClient? client;

[ClassInitialize]
public static void ClassInitialize(TestContext context)
{
Console.WriteLine(context.TestName);
client = new("Test Client", "1.0.0",
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "c:\\program files\\nodejs\\npx.cmd" : "npx",
"-y @modelcontextprotocol/server-everything")
{ GetPermission = (Dictionary<string, object> parameters) => { return true; } };
}


[ClassCleanup]
public static void ClassCleanup() { client?.Dispose(); }


[TestCategory("Tools")]
[TestMethod("Client - Tools/list")]
public async Task TestListTools()
{
var tools = await client.GetToolsAsync();
var tools = await client!.GetToolsAsync();
Assert.IsNotNull(tools);
Assert.IsTrue(tools.Count > 0);
tools.ForEach(tool =>
Expand All @@ -40,7 +39,7 @@ public async Task TestListTools()
public async Task TestCallTool()
{

var result = await client.CallToolAsync("echo", new Dictionary<string, object> { { "message", "test" } });
var result = await client!.CallToolAsync("echo", new Dictionary<string, object> { { "message", "test" } });

string response = result.Content[0].Text;
Assert.AreEqual("Echo: test", response);
Expand All @@ -52,20 +51,25 @@ public async Task TestCallTool()
[TestMethod("Client - Prompts/List")]
public async Task TestListPrompts()
{
var result = await client.GetPromptListAsync();
var result = await client!.GetPromptListAsync();
Assert.IsFalse(result.Prompts.Count != 0);
}

[TestCategory("Resources")]
[TestMethod("Client - Resources/List")]
public async Task TestResources()
{
var result = await client.GetResourcesAsync();
var result = await client!.GetResourcesAsync();
Assert.IsTrue(result.Resources.Count != 0);
result.Resources.ForEach(result =>
{
Console.WriteLine(result.Name);
});
}


[ClassCleanup(ClassCleanupBehavior.EndOfClass)]
public static void ClassCleanup() { client?.Dispose(); }

}
}
11 changes: 10 additions & 1 deletion MCPSharp.Test/MCPSharp.Test.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
Expand All @@ -8,6 +8,15 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.msbuild" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.40.1-alpha" />
<PackageReference Include="MSTest" Version="3.8.2" />
</ItemGroup>

Expand Down
Loading
Loading