Skip to content

Commit 335b839

Browse files
committed
Ensure proper app init by providing App.CreateBuilder
Add demos for supported modes: hosting, plain, MCP
1 parent 8ec251d commit 335b839

File tree

19 files changed

+493
-34
lines changed

19 files changed

+493
-34
lines changed

Smith.sln

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
4-
VisualStudioVersion = 17.14.36221.1 d17.14
4+
VisualStudioVersion = 17.14.36221.1
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Smith", "src\Smith\Smith.csproj", "{728856C5-A241-6AD6-5CDE-1991FE2F10D7}"
77
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Demos", "Demos", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HostedDemo", "src\HostedDemo\HostedDemo.csproj", "{7A572273-B346-1CA7-67AE-DC4906060B7A}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpectreDemo", "src\SpectreDemo\SpectreDemo.csproj", "{7C636145-4896-4FA2-BC7C-053AFDD6F6E9}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MCPDemo", "src\MCPDemo\MCPDemo.csproj", "{63E9B9A8-3EC5-4C0F-B8E5-30C475B2275E}"
15+
EndProject
816
Global
917
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1018
Debug|Any CPU = Debug|Any CPU
@@ -15,8 +23,28 @@ Global
1523
{728856C5-A241-6AD6-5CDE-1991FE2F10D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
1624
{728856C5-A241-6AD6-5CDE-1991FE2F10D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
1725
{728856C5-A241-6AD6-5CDE-1991FE2F10D7}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{7A572273-B346-1CA7-67AE-DC4906060B7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{7A572273-B346-1CA7-67AE-DC4906060B7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{7A572273-B346-1CA7-67AE-DC4906060B7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{7A572273-B346-1CA7-67AE-DC4906060B7A}.Release|Any CPU.Build.0 = Release|Any CPU
30+
{7C636145-4896-4FA2-BC7C-053AFDD6F6E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31+
{7C636145-4896-4FA2-BC7C-053AFDD6F6E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
32+
{7C636145-4896-4FA2-BC7C-053AFDD6F6E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
33+
{7C636145-4896-4FA2-BC7C-053AFDD6F6E9}.Release|Any CPU.Build.0 = Release|Any CPU
34+
{63E9B9A8-3EC5-4C0F-B8E5-30C475B2275E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35+
{63E9B9A8-3EC5-4C0F-B8E5-30C475B2275E}.Debug|Any CPU.Build.0 = Debug|Any CPU
36+
{63E9B9A8-3EC5-4C0F-B8E5-30C475B2275E}.Release|Any CPU.ActiveCfg = Release|Any CPU
37+
{63E9B9A8-3EC5-4C0F-B8E5-30C475B2275E}.Release|Any CPU.Build.0 = Release|Any CPU
1838
EndGlobalSection
1939
GlobalSection(SolutionProperties) = preSolution
2040
HideSolutionNode = FALSE
2141
EndGlobalSection
42+
GlobalSection(NestedProjects) = preSolution
43+
{7A572273-B346-1CA7-67AE-DC4906060B7A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
44+
{7C636145-4896-4FA2-BC7C-053AFDD6F6E9} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
45+
{63E9B9A8-3EC5-4C0F-B8E5-30C475B2275E} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
46+
EndGlobalSection
47+
GlobalSection(ExtensibilityGlobals) = postSolution
48+
SolutionGuid = {D5E491DA-619A-457D-BEBE-A137B0D9F527}
49+
EndGlobalSection
2250
EndGlobal

readme.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,20 @@ Example using Claude:
4747
```csharp
4848
#:package Smith@0.*
4949

50-
var chat = new Anthropic.AnthropicClient(Throw.
51-
configuration["Claude:Key"] ?? throw new InvalidOperationException("Missing Claude:Key configuration."),
50+
var client = new Anthropic.AnthropicClient(Throw.
51+
Env.Get("Claude:Key") ?? throw new InvalidOperationException("Missing Claude:Key configuration."),
5252
services.GetRequiredService<IHttpClientFactory>().CreateClient("ai")))
5353
.UseLogging()
5454
.UseFunctionInvocation();
5555

56-
var provider = services.BuildServiceProvider();
56+
var builder = App.CreateBuilder(args);
57+
builder.Services.AddChatClient(new );
58+
59+
var app = builder.Build();
60+
5761
var history = new List<ChatMessage> { new ChatMessage(ChatRole.System, Prompts.System) };
58-
var chat = provider.GetRequiredService<IChatClient>();
62+
var chat = app.Services.GetRequiredService<IChatClient>();
63+
5964
var options = new ChatOptions
6065
{
6166
ModelId = "claude-sonnet-4-20250514",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
description: 'An Ask mode for math-related queries, which can render LaTeX equations.'
3+
tools: ['smith-mcp']
4+
---
5+
Actively use the #latex_to_image_markdown tool to render LaTeX equations in your responses as inline markdown images to enhance clarity and visual appeal. This tool is particularly useful for displaying mathematical equations, formulas, and other LaTeX-rendered content in a visually engaging manner.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
mode: ask
3+
---
4+
Showcase popular equations in LaTeX format, and ensure they are rendered as images in the response by using the `latex_to_image_markdown` tool.

samples/appsettings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"logging": {
3+
"logLevel": {
4+
"default": "Information",
5+
"Polly": "Warning",
6+
"System.Net.Http.HttpClient": "Warning",
7+
"Microsoft.AspNetCore": "Warning",
8+
"Microsoft.Extensions": "Warning",
9+
"Microsoft.Azure": "Warning",
10+
"Microsoft.Hosting": "Warning"
11+
}
12+
}
13+
}

samples/latex.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#:package Smith@0.2.3
2+
#:package ModelContextProtocol@0.3.0-preview.*
3+
#:package Microsoft.Extensions.Http@9.*
4+
#:package SixLabors.ImageSharp@3.1.*
5+
6+
using Smith;
7+
using System.ComponentModel;
8+
using ModelContextProtocol.Server;
9+
using SixLabors.ImageSharp;
10+
using SixLabors.ImageSharp.PixelFormats;
11+
using Microsoft.Extensions.Logging;
12+
13+
var builder = Host.CreateApplicationBuilder(args);
14+
builder.Services.AddHttpClient();
15+
builder.Logging.AddConsole(consoleLogOptions =>
16+
{
17+
// Configure all logs to go to stderr
18+
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
19+
});
20+
21+
builder.Services
22+
.AddMcpServer()
23+
.WithStdioServerTransport()
24+
.WithToolsFromAssembly();
25+
26+
await builder.Build().RunAsync();
27+
28+
[McpServerToolType]
29+
public class LaTeX(IHttpClientFactory httpFactory)
30+
{
31+
[McpServerTool, Description("Converts LaTeX equations into markdown-formatted images for display inline.")]
32+
public async Task<string> LatexToImageMarkdown(
33+
[Description("The LaTeX equation to render.")] string latex,
34+
[Description("Use dark mode by inverting the colors in the output.")] bool darkMode)
35+
{
36+
var colors = darkMode ? @"\bg{black}\fg{white}" : @"\bg{white}\fg{black}";
37+
var query = WebUtility.UrlEncode(@"\small\dpi{300}" + colors + latex);
38+
var url = $"https://latex.codecogs.com/png.image?{query}";
39+
using var client = httpFactory.CreateClient();
40+
using var response = await client.GetAsync(url);
41+
42+
if (response.IsSuccessStatusCode)
43+
{
44+
using var image = Image.Load<Rgba32>(await response.Content.ReadAsStreamAsync());
45+
using var ms = new MemoryStream();
46+
image.SaveAsPng(ms);
47+
var base64 = Convert.ToBase64String(ms.ToArray());
48+
return
49+
$"""
50+
![{latex}](
51+
data:image/png;base64,{base64}
52+
)
53+
""";
54+
}
55+
else
56+
{
57+
return
58+
$"""
59+
```latex
60+
{latex}
61+
```
62+
> {response.ReasonPhrase}
63+
""";
64+
}
65+
}
66+
}

src/HostedDemo/HostedDemo.csproj

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<Import Project="..\Smith\Smith.props" />
4+
5+
<PropertyGroup>
6+
<OutputType>Exe</OutputType>
7+
<TargetFramework>net10.0</TargetFramework>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Anthropic" Version="3.7.1" />
12+
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.7" />
13+
<PackageReference Include="Spectre.Console" Version="0.50.0" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<ProjectReference Include="..\Smith\Smith.csproj" />
18+
</ItemGroup>
19+
20+
21+
<Import Project="..\Smith\Smith.targets" />
22+
23+
</Project>

src/HostedDemo/Program.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using Anthropic;
2+
3+
const string Instructions =
4+
"""
5+
Your responses will be rendered using Spectre.Console.AnsiConsole.Write(new Markup(string text))).
6+
This means that you can use rich text formatting, colors, and styles in your responses, but you must
7+
ensure that the text is valid markup syntax.
8+
""";
9+
10+
var builder = App.CreateBuilder(args);
11+
builder.Services.AddHttpClient();
12+
13+
builder.Services
14+
.AddChatClient(services => new AnthropicClient(
15+
Throw.IfNullOrEmpty(Env.Get("ANTHROPIC_KEY")),
16+
services.GetRequiredService<IHttpClientFactory>().CreateClient()))
17+
.UseLogging()
18+
.UseFunctionInvocation();
19+
20+
var app = builder.Build(async (IChatClient chat, CancellationToken token) =>
21+
{
22+
var history = new List<ChatMessage> { new(ChatRole.System, Instructions) };
23+
var options = new ChatOptions
24+
{
25+
ModelId = "claude-sonnet-4-20250514",
26+
MaxOutputTokens = 1000,
27+
Temperature = 0.7f,
28+
Tools = [AIFunctionFactory.Create(() => DateTime.Now, "get_datetime", "Gets the current date and time on the user's local machine.")]
29+
};
30+
31+
AnsiConsole.MarkupLine($":robot: Ready");
32+
AnsiConsole.Markup($":person_beard: ");
33+
while (!token.IsCancellationRequested)
34+
{
35+
var input = Console.ReadLine()?.Trim();
36+
if (string.IsNullOrEmpty(input))
37+
continue;
38+
39+
history.Add(new ChatMessage(ChatRole.User, input));
40+
try
41+
{
42+
var response = await AnsiConsole.Status().StartAsync(":robot: Thinking...",
43+
ctx => chat.GetResponseAsync(input, options));
44+
45+
history.AddRange(response.Messages);
46+
try
47+
{
48+
// Try rendering as formatted markup
49+
if (response.Text is { Length: > 0 })
50+
AnsiConsole.MarkupLine($":robot: {response.Text}");
51+
}
52+
catch (Exception)
53+
{
54+
// Fallback to escaped markup text if rendering fails
55+
AnsiConsole.MarkupLineInterpolated($":robot: {response.Text}");
56+
}
57+
58+
AnsiConsole.Markup($":person_beard: ");
59+
}
60+
catch (Exception e)
61+
{
62+
AnsiConsole.WriteException(e);
63+
}
64+
}
65+
66+
AnsiConsole.MarkupLine($":robot: Shutting down...");
67+
});
68+
69+
Console.WriteLine("Powered by Smith");
70+
71+
72+
await app.RunAsync();

src/HostedDemo/appsettings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"logging": {
3+
"logLevel": {
4+
"default": "Information",
5+
"Polly": "Warning",
6+
"System.Net.Http.HttpClient": "Warning",
7+
"Microsoft.AspNetCore": "Warning",
8+
"Microsoft.Extensions": "Warning",
9+
"Microsoft.Azure": "Warning",
10+
"Microsoft.Hosting": "Warning"
11+
}
12+
}
13+
}

src/MCPDemo/MCPDemo.csproj

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<Import Project="..\Smith\Smith.props" />
4+
5+
<PropertyGroup>
6+
<OutputType>Exe</OutputType>
7+
<TargetFramework>net10.0</TargetFramework>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.7" />
12+
<PackageReference Include="ModelContextProtocol" Version="0.3.0-preview.2" />
13+
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<ProjectReference Include="..\Smith\Smith.csproj" />
18+
</ItemGroup>
19+
20+
21+
<Import Project="..\Smith\Smith.targets" />
22+
23+
</Project>

0 commit comments

Comments
 (0)