1- using SixLabors . ImageSharp ;
1+ using System . Diagnostics ;
2+ using ModelContextProtocol . Protocol ;
3+ using ModelContextProtocol . Server ;
4+ using SixLabors . ImageSharp ;
25using SixLabors . ImageSharp . PixelFormats ;
36
47var builder = App . CreateBuilder ( args ) ;
5- builder . Services . AddHttpClient ( ) ;
8+ builder . Configuration . AddDotNetConfig ( ) ;
9+
10+ var initialized = false ;
11+ bool ? darkMode = bool . TryParse ( builder . Configuration [ "latex:darkMode" ] , out var dm ) ? dm : null ;
12+ string ? fontSize = builder . Configuration [ "latex:fontSize" ] ;
13+ // See https://editor.codecogs.com/docs/4-LaTeX_rendering.php#overview_anchor
14+ var fonts = new Dictionary < string , string >
15+ {
16+ { "Tiny" , "tiny" } ,
17+ { "Small" , "small" } ,
18+ { "Large" , "large" } ,
19+ { "LARGE" , "LARGE" } ,
20+ { "Huge" , "huge" }
21+ } ;
22+
23+ Debugger . Launch ( ) ;
624
725builder . Services
26+ . AddHttpClient ( )
827 . AddMcpServer ( )
928 . WithStdioServerTransport ( )
1029 . WithTool (
11- [ Description ( "Converts LaTeX equations into markdown-formatted images for display inline." ) ] async
12- ( IHttpClientFactory httpFactory ,
13- [ Description ( "The LaTeX equation to render." ) ] string latex ,
14- [ Description ( "Use dark mode by inverting the colors in the output." ) ] bool darkMode ) =>
15- {
16- var colors = darkMode ? @"\bg{black}\fg{white}" : @"\bg{white}\fg{black}" ;
17- var query = WebUtility . UrlEncode ( @"\small\dpi{300}" + colors + latex ) ;
18- var url = $ "https://latex.codecogs.com/png.image?{ query } ";
19- using var client = httpFactory . CreateClient ( ) ;
20- using var response = await client . GetAsync ( url ) ;
21-
22- if ( response . IsSuccessStatusCode )
30+ name : "latex" ,
31+ title : "LaTeX to Image" ,
32+ description : "Converts LaTeX equations into markdown-formatted images for inline display." ,
33+ tool : async ( IHttpClientFactory httpFactory , IMcpServer server ,
34+ [ Description ( "The LaTeX equation to render." ) ] string latex )
35+ =>
2336 {
37+ // On first tool run, we ask for preferences for dark mode and font size.
38+ if ( ! initialized )
39+ {
40+ initialized = true ;
41+ ( darkMode , fontSize ) = await SetPreferences ( server , darkMode , fontSize ) ;
42+ }
43+
44+ var colors = darkMode switch
45+ {
46+ true => @"\fg{white}" ,
47+ false => @"\fg{black}" ,
48+ null => @"\bg{white}\fg{black}"
49+ } ;
50+
51+ var query = WebUtility . UrlEncode ( @"\dpi{300}\" + ( fontSize ?? "small" ) + colors + new string ( [ .. latex . Where ( c => ! char . IsWhiteSpace ( c ) ) ] ) ) ;
52+ var url = $ "https://latex.codecogs.com/png.image?{ query } ";
53+
54+ using var client = httpFactory . CreateClient ( ) ;
55+ using var response = await client . GetAsync ( url ) ;
56+ response . EnsureSuccessStatusCode ( ) ;
57+
2458 using var image = Image . Load < Rgba32 > ( await response . Content . ReadAsStreamAsync ( ) ) ;
2559 using var ms = new MemoryStream ( ) ;
2660 image . SaveAsPng ( ms ) ;
2761 var base64 = Convert . ToBase64String ( ms . ToArray ( ) ) ;
28- return
29- $ """
30- 
33- """ ;
34- }
35- else
62+ return $ "> ";
63+ } )
64+ . WithTool (
65+ name : "latex_getprefs" ,
66+ title : "Get LaTeX Preferences" ,
67+ description : "Gets the saved LaTeX rendering preferences for dark mode and font size." ,
68+ tool : ( ) => new { darkMode , fontSize } )
69+ . WithTool (
70+ name : "latex_setprefs" ,
71+ title : "Set LaTeX Preferences" ,
72+ description : "Sets the LaTeX rendering preferences for dark mode and font size." ,
73+ tool : async ( IMcpServer server ,
74+ [ Description ( "Use dark mode by inverting the colors in the output." ) ] bool ? darkMode = null ,
75+ [ Description ( "Font size to use in the output: tiny=5pt, small=9pt, large=12pt, LARGE=18pt, huge=20pt" ) ] string ? fontSize = null )
76+ => ( darkMode , fontSize ) = await SetPreferences ( server , darkMode , fontSize ) ) ;
77+
78+ await builder . Build ( ) . RunAsync ( ) ;
79+
80+ /// <summary>Saves the LaTeX rendering preferences to configuration.</summary>
81+ async ValueTask < ( bool ? darkMode , string ? fontSize ) > SetPreferences ( IMcpServer server , bool ? darkMode , string ? fontSize )
82+ {
83+ if ( ( darkMode is null || fontSize is null || ! fonts . ContainsValue ( fontSize ) ) && server . ClientCapabilities ? . Elicitation != null )
84+ {
85+ var result = await server . ElicitAsync ( new ( )
86+ {
87+ Message = "Specify LaTeX rendering preferences" ,
88+ RequestedSchema = new ( )
3689 {
37- return
38- $ """
39- ```latex
40- { latex }
41- ```
42- > { response . ReasonPhrase }
43- """ ;
90+ Required = [ "darkMode" , "fontSize" ] ,
91+ Properties =
92+ {
93+ { "darkMode" , new ElicitRequestParams . BooleanSchema ( )
94+ {
95+ Title = "Dark Mode" ,
96+ Description = "Use dark mode?" ,
97+ Default = darkMode
98+ }
99+ } ,
100+ { "fontSize" , new ElicitRequestParams . EnumSchema ( )
101+ {
102+ Title = "Font Size" ,
103+ Description = "Font size to use for the LaTeX rendering." ,
104+ Enum = [ .. fonts . Values ] ,
105+ EnumNames = [ .. fonts . Keys ] ,
106+ }
107+ } ,
108+ } ,
44109 }
45110 } ) ;
46111
47- await builder . Build ( ) . RunAsync ( ) ;
112+ if ( result . Action == "accept" && result . Content is { } content )
113+ {
114+ darkMode = content [ "darkMode" ] . GetBoolean ( ) ;
115+ fontSize = content [ "fontSize" ] . GetString ( ) ?? "tiny" ;
116+
117+ DotNetConfig . Config . Build ( DotNetConfig . ConfigLevel . Global )
118+ . GetSection ( "latex" )
119+ . SetBoolean ( "darkMode" , darkMode . Value )
120+ . SetString ( "fontSize" , fontSize ) ;
121+ }
122+ // action == cancel is not supported in vscode
123+ // actoin == decline would be equal to "ignore" so we just don't set anything.
124+ return ( darkMode , fontSize ) ;
125+ }
126+ else
127+ {
128+ // We persist to ~/.netconfig
129+ var config = DotNetConfig . Config . Build ( DotNetConfig . ConfigLevel . Global ) . GetSection ( "latex" ) ;
130+ if ( darkMode != null )
131+ config = config . SetBoolean ( "darkMode" , darkMode . Value ) ;
132+ if ( fontSize != null && fonts . ContainsValue ( fontSize ) )
133+ config = config . SetString ( "fontSize" , fontSize ) ;
134+ else
135+ fontSize = null ;
136+
137+ return ( darkMode , fontSize ) ;
138+ }
139+ }
140+
0 commit comments