simple-server.vala 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028
  1. /*
  2. * Copyright (C) 2025 Mcp-Vala Project
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. *
  18. * Author: Mcp-Vala Project
  19. */
  20. using Mcp;
  21. /**
  22. * Simple MCP server example.
  23. *
  24. * This example demonstrates basic usage of the MCP Vala library
  25. * with a simple echo tool and basic resource provider.
  26. */
  27. public class SimpleServer : GLib.Object {
  28. private Mcp.Core.Server server;
  29. /**
  30. * Creates a new SimpleServer.
  31. */
  32. public SimpleServer () {
  33. // Create server info
  34. var server_info = new Mcp.Types.Protocol.ServerInfo (
  35. "simple-server",
  36. "1.0.0"
  37. );
  38. server_info.description = "A simple MCP server example";
  39. // Create server capabilities
  40. var capabilities = new Mcp.Types.Protocol.ServerCapabilities ();
  41. capabilities.logging = true;
  42. // Create server
  43. server = new Mcp.Core.Server (server_info, capabilities);
  44. // Register tools with tool manager
  45. var echo_tool = new EchoTool ();
  46. try {
  47. server.tool_manager.register_executor ("echo", echo_tool);
  48. } catch (Error e) {
  49. // Error handling
  50. }
  51. var calculator_tool = new CalculatorTool ();
  52. try {
  53. server.tool_manager.register_executor ("calculator", calculator_tool);
  54. } catch (Error e) {
  55. // Error handling
  56. }
  57. var file_tool = new FileOperationTool ();
  58. try {
  59. server.tool_manager.register_executor ("file_ops", file_tool);
  60. } catch (Error e) {
  61. // Error handling
  62. }
  63. // Register resource providers with the resource manager
  64. var file_provider = new FileResourceProvider ();
  65. server.resource_manager.register_provider ("file", file_provider);
  66. var memory_provider = new MemoryResourceProvider ();
  67. server.resource_manager.register_provider ("memory", memory_provider);
  68. // Add a resource template
  69. var template = new Mcp.Resources.Types.Resource.ResourceTemplate (
  70. "note",
  71. "Create a new note"
  72. );
  73. template.description = "Creates a new text note with specified content";
  74. server.resource_manager.register_template ("note", template);
  75. // Register prompt templates with the prompt manager
  76. var code_review_prompt = new CodeReviewPromptTemplate ();
  77. try {
  78. server.prompt_manager.register_template ("code_review", code_review_prompt);
  79. } catch (Error e) {
  80. // Error handling
  81. }
  82. var summary_prompt = new SummaryPromptTemplate ();
  83. try {
  84. server.prompt_manager.register_template ("summary", summary_prompt);
  85. } catch (Error e) {
  86. // Error handling
  87. }
  88. var explain_prompt = new ExplainPromptTemplate ();
  89. try {
  90. server.prompt_manager.register_template ("explain", explain_prompt);
  91. } catch (Error e) {
  92. // Error handling
  93. }
  94. }
  95. /**
  96. * Runs the server.
  97. *
  98. * @return Exit code
  99. */
  100. public async int run () {
  101. try {
  102. // Start the server
  103. bool started = yield server.start ();
  104. if (!started) {
  105. return 1;
  106. }
  107. // Server started. Waiting for connections...
  108. // Start a demo timer to show resource updates
  109. Timeout.add_seconds (5, () => {
  110. demo_resource_updates ();
  111. return true; // Continue timer
  112. });
  113. // Run the main loop
  114. var main_loop = new MainLoop ();
  115. // Connect shutdown signal
  116. server.shutdown.connect (() => {
  117. main_loop.quit ();
  118. });
  119. main_loop.run ();
  120. return 0;
  121. } catch (Error e) {
  122. return 1;
  123. }
  124. }
  125. /**
  126. * Demonstrates resource updates and notifications.
  127. */
  128. private void demo_resource_updates () {
  129. // Get the memory provider
  130. // Get the memory provider from the resource manager's providers
  131. var providers = server.resource_manager.get_providers ();
  132. MemoryResourceProvider? provider = null;
  133. foreach (var key in providers.get_keys ()) {
  134. if (key == "memory") {
  135. provider = providers.get (key) as MemoryResourceProvider;
  136. break;
  137. }
  138. }
  139. if (provider != null) {
  140. // Increment the counter
  141. int new_value = provider.increment_counter ();
  142. // Create a new note every 30 seconds (6 * 5 second intervals)
  143. uint note_counter = 0;
  144. note_counter++;
  145. if (note_counter % 6 == 0) {
  146. string uri = provider.create_note (
  147. "Demo Note %u".printf (note_counter / 6),
  148. "This is an auto-generated note created at %s".printf (new DateTime.now_utc ().to_string ())
  149. );
  150. }
  151. }
  152. }
  153. }
  154. /**
  155. * Simple echo tool implementation.
  156. */
  157. public class EchoTool : Mcp.Tools.BaseExecutor {
  158. /**
  159. * Creates a new EchoTool.
  160. */
  161. public EchoTool () {
  162. // Create tool definition using Variant
  163. var builder = new VariantBuilder (new VariantType ("a{sv}"));
  164. // Add schema properties
  165. builder.add ("{sv}", "type", new Variant.string ("object"));
  166. builder.add ("{sv}", "description", new Variant.string ("Text to echo back"));
  167. var properties_builder = new VariantBuilder (new VariantType ("a{sv}"));
  168. var text_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
  169. text_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
  170. text_prop_builder.add ("{sv}", "description", new Variant.string ("The text to echo back"));
  171. properties_builder.add ("{sv}", "text", text_prop_builder.end ());
  172. builder.add ("{sv}", "properties", properties_builder.end ());
  173. var required_array_builder = new VariantBuilder (new VariantType ("as"));
  174. required_array_builder.add ("s", "text");
  175. builder.add ("{sv}", "required", required_array_builder.end ());
  176. var input_schema = builder.end ();
  177. var definition = new Mcp.Tools.Types.ToolDefinition ("echo", input_schema);
  178. definition.title = "Echo Tool";
  179. definition.description = "Echoes back the provided text";
  180. base (definition);
  181. }
  182. /**
  183. * {@inheritDoc}
  184. */
  185. protected override async Mcp.Tools.Types.CallToolResult do_execute (GLib.Variant arguments) throws Error {
  186. string text = get_string_arg (arguments, "text");
  187. return create_text_result ("Echo: %s".printf (text));
  188. }
  189. }
  190. /**
  191. * Calculator tool implementation.
  192. */
  193. public class CalculatorTool : Mcp.Tools.BaseExecutor {
  194. /**
  195. * Creates a new CalculatorTool.
  196. */
  197. public CalculatorTool () {
  198. // Create tool definition using Variant
  199. var builder = new VariantBuilder (new VariantType ("a{sv}"));
  200. // Add schema properties
  201. builder.add ("{sv}", "type", new Variant.string ("object"));
  202. builder.add ("{sv}", "description", new Variant.string ("Perform mathematical calculations"));
  203. var properties_builder = new VariantBuilder (new VariantType ("a{sv}"));
  204. // Expression property
  205. var expr_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
  206. expr_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
  207. expr_prop_builder.add ("{sv}", "description", new Variant.string ("Mathematical expression to evaluate (e.g., '2 + 3 * 4')"));
  208. properties_builder.add ("{sv}", "expression", expr_prop_builder.end ());
  209. // Operation property
  210. var op_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
  211. op_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
  212. op_prop_builder.add ("{sv}", "description", new Variant.string ("Operation: add, subtract, multiply, divide"));
  213. var enum_array_builder = new VariantBuilder (new VariantType ("as"));
  214. enum_array_builder.add ("s", "add");
  215. enum_array_builder.add ("s", "subtract");
  216. enum_array_builder.add ("s", "multiply");
  217. enum_array_builder.add ("s", "divide");
  218. op_prop_builder.add ("{sv}", "enum", enum_array_builder.end ());
  219. properties_builder.add ("{sv}", "operation", op_prop_builder.end ());
  220. // Operands property
  221. var operands_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
  222. operands_prop_builder.add ("{sv}", "type", new Variant.string ("array"));
  223. operands_prop_builder.add ("{sv}", "description", new Variant.string ("Array of numbers for the operation"));
  224. var items_schema_builder = new VariantBuilder (new VariantType ("a{sv}"));
  225. items_schema_builder.add ("{sv}", "type", new Variant.string ("number"));
  226. operands_prop_builder.add ("{sv}", "items", items_schema_builder.end ());
  227. properties_builder.add ("{sv}", "operands", operands_prop_builder.end ());
  228. builder.add ("{sv}", "properties", properties_builder.end ());
  229. var required_array_builder = new VariantBuilder (new VariantType ("as"));
  230. required_array_builder.add ("s", "expression");
  231. builder.add ("{sv}", "required", required_array_builder.end ());
  232. var input_schema = builder.end ();
  233. var definition = new Mcp.Tools.Types.ToolDefinition ("calculator", input_schema);
  234. definition.title = "Calculator";
  235. definition.description = "Perform mathematical calculations";
  236. base (definition);
  237. }
  238. /**
  239. * {@inheritDoc}
  240. */
  241. protected override async Mcp.Tools.Types.CallToolResult do_execute (GLib.Variant arguments) throws Error {
  242. string expression = get_string_arg (arguments, "expression");
  243. string operation = get_string_arg (arguments, "operation", "");
  244. // Parse operands if provided
  245. double[] operands = {};
  246. if (arguments.lookup_value ("operands", null) != null) {
  247. var operands_variant = arguments.lookup_value ("operands", new VariantType ("ad"));
  248. if (operands_variant != null) {
  249. // Get the array from variant using proper GLib.Variant API
  250. size_t n_elements;
  251. double* elements = (double*) operands_variant.get_data ();
  252. n_elements = operands_variant.n_children ();
  253. operands = new double[n_elements];
  254. for (size_t i = 0; i < n_elements; i++) {
  255. operands[i] = operands_variant.get_child_value (i).get_double ();
  256. }
  257. }
  258. }
  259. double result = 0.0;
  260. if (expression != "") {
  261. // Simple expression evaluation (in real implementation, use proper parser)
  262. try {
  263. result = evaluate_simple_expression (expression);
  264. } catch (Error e) {
  265. throw new Mcp.Core.McpError.INVALID_PARAMS ("Invalid expression: %s".printf (e.message));
  266. }
  267. } else if (operands.length > 0) {
  268. // Use operation with operands
  269. if (operands.length < 2) {
  270. throw new Mcp.Core.McpError.INVALID_PARAMS ("Operation requires at least 2 operands");
  271. }
  272. switch (operation) {
  273. case "add":
  274. result = operands[0] + operands[1];
  275. break;
  276. case "subtract":
  277. result = operands[0] - operands[1];
  278. break;
  279. case "multiply":
  280. result = operands[0] * operands[1];
  281. break;
  282. case "divide":
  283. if (operands[1] == 0) {
  284. throw new Mcp.Core.McpError.INVALID_PARAMS ("Division by zero");
  285. }
  286. result = operands[0] / operands[1];
  287. break;
  288. default:
  289. throw new Mcp.Core.McpError.INVALID_PARAMS ("Unknown operation: %s".printf (operation));
  290. }
  291. } else {
  292. throw new Mcp.Core.McpError.INVALID_PARAMS ("Either expression or operands must be provided");
  293. }
  294. // Create structured result using Variant
  295. var structured_data_builder = new VariantBuilder (new VariantType ("a{sv}"));
  296. structured_data_builder.add ("{sv}", "result", new Variant.double (result));
  297. structured_data_builder.add ("{sv}", "expression", new Variant.string (expression != "" ? expression : operation));
  298. var structured_data = structured_data_builder.end ();
  299. return create_structured_result ("Calculation result: %g".printf (result), structured_data);
  300. }
  301. /**
  302. * Evaluates a simple mathematical expression.
  303. *
  304. * @param expression The expression to evaluate
  305. * @return The result
  306. * @throws Error If evaluation fails
  307. */
  308. private double evaluate_simple_expression (string expression) throws Error {
  309. // Very simple expression evaluator for demonstration
  310. // In production, use a proper math expression parser
  311. // Handle basic operations
  312. if (expression.contains ("+")) {
  313. var parts = expression.split ("+");
  314. if (parts.length == 2) {
  315. double a = double.parse (parts[0].strip ());
  316. double b = double.parse (parts[1].strip ());
  317. return a + b;
  318. }
  319. } else if (expression.contains ("-")) {
  320. var parts = expression.split ("-");
  321. if (parts.length == 2) {
  322. double a = double.parse (parts[0].strip ());
  323. double b = double.parse (parts[1].strip ());
  324. return a - b;
  325. }
  326. } else if (expression.contains ("*")) {
  327. var parts = expression.split ("*");
  328. if (parts.length == 2) {
  329. double a = double.parse (parts[0].strip ());
  330. double b = double.parse (parts[1].strip ());
  331. return a * b;
  332. }
  333. } else if (expression.contains ("/")) {
  334. var parts = expression.split ("/");
  335. if (parts.length == 2) {
  336. double a = double.parse (parts[0].strip ());
  337. double b = double.parse (parts[1].strip ());
  338. if (b == 0) {
  339. throw new Mcp.Core.McpError.INVALID_PARAMS ("Division by zero");
  340. }
  341. return a / b;
  342. }
  343. }
  344. // Try to parse as single number
  345. return double.parse (expression);
  346. }
  347. }
  348. /**
  349. * File operations tool implementation.
  350. */
  351. public class FileOperationTool : Mcp.Tools.BaseExecutor {
  352. /**
  353. * Creates a new FileOperationTool.
  354. */
  355. public FileOperationTool () {
  356. // Create tool definition using Variant
  357. var builder = new VariantBuilder (new VariantType ("a{sv}"));
  358. // Add schema properties
  359. builder.add ("{sv}", "type", new Variant.string ("object"));
  360. builder.add ("{sv}", "description", new Variant.string ("Perform file operations"));
  361. var properties_builder = new VariantBuilder (new VariantType ("a{sv}"));
  362. // Operation property
  363. var op_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
  364. op_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
  365. op_prop_builder.add ("{sv}", "description", new Variant.string ("Operation: read, write, list, delete"));
  366. var enum_array_builder = new VariantBuilder (new VariantType ("as"));
  367. enum_array_builder.add ("s", "read");
  368. enum_array_builder.add ("s", "write");
  369. enum_array_builder.add ("s", "list");
  370. enum_array_builder.add ("s", "delete");
  371. op_prop_builder.add ("{sv}", "enum", enum_array_builder.end ());
  372. properties_builder.add ("{sv}", "operation", op_prop_builder.end ());
  373. // Path property
  374. var path_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
  375. path_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
  376. path_prop_builder.add ("{sv}", "description", new Variant.string ("File path"));
  377. properties_builder.add ("{sv}", "path", path_prop_builder.end ());
  378. // Content property (for write operation)
  379. var content_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
  380. content_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
  381. content_prop_builder.add ("{sv}", "description", new Variant.string ("Content to write (for write operation)"));
  382. properties_builder.add ("{sv}", "content", content_prop_builder.end ());
  383. builder.add ("{sv}", "properties", properties_builder.end ());
  384. var required_array_builder = new VariantBuilder (new VariantType ("as"));
  385. required_array_builder.add ("s", "operation");
  386. required_array_builder.add ("s", "path");
  387. builder.add ("{sv}", "required", required_array_builder.end ());
  388. var input_schema = builder.end ();
  389. var definition = new Mcp.Tools.Types.ToolDefinition ("file_ops", input_schema);
  390. definition.title = "File Operations";
  391. definition.description = "Perform basic file operations";
  392. base (definition);
  393. }
  394. /**
  395. * {@inheritDoc}
  396. */
  397. protected override async Mcp.Tools.Types.CallToolResult do_execute (GLib.Variant arguments) throws Error {
  398. string operation = get_string_arg (arguments, "operation");
  399. string path = get_string_arg (arguments, "path");
  400. string content = get_string_arg (arguments, "content", "");
  401. switch (operation) {
  402. case "read":
  403. return yield handle_read (path);
  404. case "write":
  405. return yield handle_write (path, content);
  406. case "list":
  407. return yield handle_list (path);
  408. case "delete":
  409. return yield handle_delete (path);
  410. default:
  411. throw new Mcp.Core.McpError.INVALID_PARAMS ("Unknown operation: %s".printf (operation));
  412. }
  413. }
  414. /**
  415. * Handles file read operation.
  416. *
  417. * @param path The file path
  418. * @return The operation result
  419. * @throws Error If reading fails
  420. */
  421. private async Mcp.Tools.Types.CallToolResult handle_read (string path) throws Error {
  422. var file = File.new_for_path (path);
  423. if (!file.query_exists ()) {
  424. throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("File not found: %s".printf (path));
  425. }
  426. uint8[] contents;
  427. string etag;
  428. yield file.load_contents_async (null, out contents, out etag);
  429. string text = (string) contents;
  430. return create_text_result ("File content:\n%s".printf (text));
  431. }
  432. /**
  433. * Handles file write operation.
  434. *
  435. * @param path The file path
  436. * @param content The content to write
  437. * @return The operation result
  438. * @throws Error If writing fails
  439. */
  440. private async Mcp.Tools.Types.CallToolResult handle_write (string path, string content) throws Error {
  441. var file = File.new_for_path (path);
  442. // Create parent directories if needed
  443. var parent = file.get_parent ();
  444. if (parent != null && !parent.query_exists ()) {
  445. try {
  446. parent.make_directory_with_parents ();
  447. } catch (Error e) {
  448. throw new Mcp.Core.McpError.INTERNAL_ERROR ("Failed to create parent directory: %s".printf (e.message));
  449. }
  450. }
  451. uint8[] data = content.data;
  452. string etag_out;
  453. try {
  454. yield file.replace_contents_async (
  455. data,
  456. null,
  457. false,
  458. FileCreateFlags.REPLACE_DESTINATION,
  459. null,
  460. out etag_out
  461. );
  462. } catch (Error e) {
  463. throw new Mcp.Core.McpError.INTERNAL_ERROR ("Failed to write file: %s".printf (e.message));
  464. }
  465. return create_text_result ("Wrote %u bytes to %s".printf (data.length, path));
  466. }
  467. /**
  468. * Handles file list operation.
  469. *
  470. * @param path The directory path
  471. * @return The operation result
  472. * @throws Error If listing fails
  473. */
  474. private async Mcp.Tools.Types.CallToolResult handle_list (string path) throws Error {
  475. var dir = File.new_for_path (path);
  476. if (!dir.query_exists ()) {
  477. throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("Directory not found: %s".printf (path));
  478. }
  479. var enumerator = yield dir.enumerate_children_async (
  480. "standard::name,standard::type,standard::size",
  481. FileQueryInfoFlags.NONE,
  482. Priority.DEFAULT,
  483. null
  484. );
  485. string[] entries = {};
  486. FileInfo? file_info = null;
  487. while ((file_info = enumerator.next_file ()) != null) {
  488. string entry = "%s (%s)".printf (
  489. file_info.get_name (),
  490. file_info.get_file_type () == FileType.DIRECTORY ? "dir" : "file"
  491. );
  492. entries += entry;
  493. }
  494. return create_text_result ("Directory listing:\n%s".printf (string.joinv ("\n", entries)));
  495. }
  496. /**
  497. * Handles file delete operation.
  498. *
  499. * @param path The file path
  500. * @return The operation result
  501. * @throws Error If deletion fails
  502. */
  503. private async Mcp.Tools.Types.CallToolResult handle_delete (string path) throws Error {
  504. var file = File.new_for_path (path);
  505. if (!file.query_exists ()) {
  506. throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("File not found: %s".printf (path));
  507. }
  508. yield file.delete_async ();
  509. return create_text_result ("Deleted: %s".printf (path));
  510. }
  511. }
  512. /**
  513. * Simple file resource provider implementation.
  514. */
  515. public class FileResourceProvider : Mcp.Resources.BaseProvider {
  516. /**
  517. * Creates a new FileResourceProvider.
  518. */
  519. public FileResourceProvider () {
  520. Object ();
  521. }
  522. /**
  523. * {@inheritDoc}
  524. */
  525. public override async Gee.ArrayList<Mcp.Resources.Types.Resource> list_resources (string? cursor) throws Error {
  526. var resources = new Gee.ArrayList<Mcp.Resources.Types.Resource> ();
  527. // List files in current directory
  528. var dir = File.new_for_path (".");
  529. var enumerator = yield dir.enumerate_children_async (
  530. "standard::name,standard::type,standard::size",
  531. FileQueryInfoFlags.NONE,
  532. Priority.DEFAULT,
  533. null
  534. );
  535. FileInfo? file_info = null;
  536. while ((file_info = enumerator.next_file ()) != null) {
  537. if (file_info.get_file_type () == FileType.REGULAR) {
  538. var file = File.new_for_path (file_info.get_name ());
  539. var resource = new Mcp.Resources.Types.Resource (
  540. file.get_uri (),
  541. file_info.get_name ()
  542. );
  543. resource.mime_type = guess_mime_type (file_info.get_name ());
  544. resource.size = file_info.get_size ();
  545. resources.add (resource);
  546. }
  547. }
  548. return resources;
  549. }
  550. /**
  551. * {@inheritDoc}
  552. */
  553. public override async Gee.ArrayList<Mcp.Types.Common.ResourceContents> read_resource (string uri) throws Error {
  554. var file = File.new_for_uri (uri);
  555. if (!file.query_exists ()) {
  556. throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("File not found: %s".printf (uri));
  557. }
  558. uint8[] contents;
  559. string etag;
  560. yield file.load_contents_async (null, out contents, out etag);
  561. string text = (string) contents;
  562. var result = new Gee.ArrayList<Mcp.Types.Common.ResourceContents> ();
  563. result.add (new Mcp.Types.Common.TextResourceContents (uri, text));
  564. return result;
  565. }
  566. /**
  567. * Guesses MIME type from filename.
  568. *
  569. * @param filename The filename
  570. * @return The guessed MIME type
  571. */
  572. private new string guess_mime_type (string filename) {
  573. // Simple MIME type guessing based on extension
  574. string extension = filename.down ();
  575. if (extension == "txt") {
  576. return "text/plain";
  577. } else if (extension == "json") {
  578. return "application/json";
  579. } else if (extension == "xml") {
  580. return "application/xml";
  581. } else {
  582. return "application/octet-stream";
  583. }
  584. }
  585. }
  586. /**
  587. * In-memory resource provider with subscription support.
  588. */
  589. public class MemoryResourceProvider : Mcp.Resources.BaseProvider {
  590. private HashTable<string, string> resources;
  591. private uint32 next_id;
  592. /**
  593. * Creates a new MemoryResourceProvider.
  594. */
  595. public MemoryResourceProvider () {
  596. Object ();
  597. resources = new HashTable<string, string> (str_hash, str_equal);
  598. next_id = 1;
  599. // Add some initial resources
  600. resources.insert ("memory://note/1", "This is a sample note in memory.");
  601. resources.insert ("memory://note/2", "Another note with some content.");
  602. resources.insert ("memory://counter", "0");
  603. }
  604. /**
  605. * {@inheritDoc}
  606. */
  607. public override async Gee.ArrayList<Mcp.Resources.Types.Resource> list_resources (string? cursor) throws Error {
  608. var result = new Gee.ArrayList<Mcp.Resources.Types.Resource> ();
  609. foreach (var uri in resources.get_keys ()) {
  610. var resource = new Mcp.Resources.Types.Resource (uri, get_name_from_uri (uri));
  611. resource.mime_type = "text/plain";
  612. resource.size = resources[uri].length;
  613. result.add (resource);
  614. }
  615. return result;
  616. }
  617. /**
  618. * {@inheritDoc}
  619. */
  620. public override async Gee.ArrayList<Mcp.Types.Common.ResourceContents> read_resource (string uri) throws Error {
  621. if (!resources.contains (uri)) {
  622. throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("Resource not found: %s".printf (uri));
  623. }
  624. var result = new Gee.ArrayList<Mcp.Types.Common.ResourceContents> ();
  625. result.add (new Mcp.Types.Common.TextResourceContents (uri, resources[uri]));
  626. return result;
  627. }
  628. /**
  629. * {@inheritDoc}
  630. */
  631. public override async void subscribe (string uri) throws Error {
  632. if (!resources.contains (uri)) {
  633. throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("Resource not found: %s".printf (uri));
  634. }
  635. // BaseProvider will handle subscription tracking
  636. yield base.subscribe (uri);
  637. }
  638. /**
  639. * Updates a resource and notifies subscribers.
  640. *
  641. * @param uri The URI of the resource to update
  642. * @param content The new content
  643. */
  644. public void update_resource (string uri, string content) {
  645. if (!resources.contains (uri)) {
  646. return;
  647. }
  648. resources[uri] = content;
  649. notify_resource_updated (uri);
  650. }
  651. /**
  652. * Creates a new note resource.
  653. *
  654. * @param title The title of the note
  655. * @param content The content of the note
  656. * @return The URI of the new resource
  657. */
  658. public string create_note (string title, string content) {
  659. string uri = "memory://note/%u".printf (next_id++);
  660. resources.insert (uri, "Title: %s\n\n%s".printf (title, content));
  661. // The ResourceManager will handle list_changed notifications
  662. // when resources are listed
  663. return uri;
  664. }
  665. /**
  666. * Increments the counter resource.
  667. *
  668. * @return The new counter value
  669. */
  670. public int increment_counter () {
  671. string uri = "memory://counter";
  672. if (!resources.contains (uri)) {
  673. resources.insert (uri, "0");
  674. }
  675. int value = int.parse (resources[uri]);
  676. value++;
  677. resources[uri] = value.to_string ();
  678. // Notify subscribers
  679. notify_resource_updated (uri);
  680. return value;
  681. }
  682. /**
  683. * Extracts name from URI.
  684. *
  685. * @param uri The URI
  686. * @return The name
  687. */
  688. private string get_name_from_uri (string uri) {
  689. var parts = uri.split ("/");
  690. return parts[parts.length - 1];
  691. }
  692. }
  693. /**
  694. * Code review prompt template implementation.
  695. */
  696. public class CodeReviewPromptTemplate : Mcp.Prompts.BaseTemplate {
  697. /**
  698. * Creates a new CodeReviewPromptTemplate.
  699. */
  700. public CodeReviewPromptTemplate () {
  701. base ("code_review", "Code Review", "Generates a code review prompt for analyzing code quality and best practices");
  702. // Add arguments
  703. add_argument ("code", "Code", "The code to review", true);
  704. add_argument ("language", "Language", "Programming language of the code", false);
  705. add_argument ("focus", "Focus Area", "Specific areas to focus on (e.g., security, performance, readability)", false);
  706. }
  707. /**
  708. * {@inheritDoc}
  709. */
  710. protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (GLib.Variant arguments) {
  711. var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
  712. string language = "";
  713. if (arguments.lookup_value ("language", null) != null) {
  714. language = arguments.lookup_value ("language", new VariantType ("s")).get_string ();
  715. }
  716. string focus = "";
  717. if (arguments.lookup_value ("focus", null) != null) {
  718. focus = arguments.lookup_value ("focus", new VariantType ("s")).get_string ();
  719. }
  720. string template = """Please review the following {{language}} code:
  721. ```{{language}}
  722. {{code}}
  723. ```
  724. {{#focus}}
  725. Please focus your review on: {{focus}}
  726. {{/focus}}
  727. Please analyze:
  728. 1. Code quality and readability
  729. 2. Potential bugs or issues
  730. 3. Performance considerations
  731. 4. Security vulnerabilities
  732. 5. Adherence to best practices
  733. 6. Suggestions for improvement
  734. Provide specific, actionable feedback.""";
  735. messages.add (create_user_message (template, arguments));
  736. return messages;
  737. }
  738. }
  739. /**
  740. * Summary prompt template implementation.
  741. */
  742. public class SummaryPromptTemplate : Mcp.Prompts.BaseTemplate {
  743. /**
  744. * Creates a new SummaryPromptTemplate.
  745. */
  746. public SummaryPromptTemplate () {
  747. base ("summary", "Text Summary", "Generates a summary of the provided text");
  748. // Add arguments
  749. add_argument ("text", "Text", "The text to summarize", true);
  750. add_argument ("length", "Summary Length", "Desired length of the summary (short, medium, long)", false);
  751. add_argument ("style", "Summary Style", "Style of the summary (bullet_points, paragraph, executive)", false);
  752. }
  753. /**
  754. * {@inheritDoc}
  755. */
  756. protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (GLib.Variant arguments) {
  757. var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
  758. string length = "medium";
  759. if (arguments.lookup_value ("length", null) != null) {
  760. length = arguments.lookup_value ("length", new VariantType ("s")).get_string ();
  761. }
  762. string style = "paragraph";
  763. if (arguments.lookup_value ("style", null) != null) {
  764. style = arguments.lookup_value ("style", new VariantType ("s")).get_string ();
  765. }
  766. string template = """Please provide a {{length}} summary of the following text:
  767. {{text}}
  768. Please format the summary as {{style}}.
  769. {{#style}}
  770. {{#bullet_points}}
  771. Use bullet points for key points.
  772. {{/bullet_points}}
  773. {{#paragraph}}
  774. Write in a clear, concise paragraph.
  775. {{/paragraph}}
  776. {{#executive}}
  777. Provide an executive summary with key takeaways.
  778. {{/executive}}
  779. {{/style}}""";
  780. messages.add (create_user_message (template, arguments));
  781. return messages;
  782. }
  783. }
  784. /**
  785. * Explain prompt template implementation.
  786. */
  787. public class ExplainPromptTemplate : Mcp.Prompts.BaseTemplate {
  788. /**
  789. * Creates a new ExplainPromptTemplate.
  790. */
  791. public ExplainPromptTemplate () {
  792. base ("explain", "Explain Concept", "Generates an explanation of a concept or topic");
  793. // Add arguments
  794. add_argument ("topic", "Topic", "The concept or topic to explain", true);
  795. add_argument ("audience", "Audience", "Target audience level (beginner, intermediate, advanced)", false);
  796. add_argument ("depth", "Depth", "Depth of explanation (brief, detailed, comprehensive)", false);
  797. add_argument ("examples", "Include Examples", "Whether to include examples", false);
  798. }
  799. /**
  800. * {@inheritDoc}
  801. */
  802. protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (GLib.Variant arguments) {
  803. var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
  804. string audience = "intermediate";
  805. if (arguments.lookup_value ("audience", null) != null) {
  806. audience = arguments.lookup_value ("audience", new VariantType ("s")).get_string ();
  807. }
  808. string depth = "detailed";
  809. if (arguments.lookup_value ("depth", null) != null) {
  810. depth = arguments.lookup_value ("depth", new VariantType ("s")).get_string ();
  811. }
  812. bool include_examples = false;
  813. if (arguments.lookup_value ("examples", null) != null) {
  814. include_examples = arguments.lookup_value ("examples", new VariantType ("b")).get_boolean ();
  815. }
  816. string template = """Please explain the concept of "{{topic}}" for a {{audience}} audience.
  817. Provide a {{depth}} explanation that covers:
  818. 1. Definition and core concepts
  819. 2. Key principles and mechanisms
  820. 3. Practical applications and use cases
  821. 4. Common challenges and considerations
  822. {{#examples}}
  823. Please include relevant examples to illustrate the concept.
  824. {{/examples}}
  825. Tailor your explanation to the specified audience level and ensure clarity and accuracy.""";
  826. // Create a modified arguments variant for examples
  827. var builder = new VariantBuilder (new VariantType ("a{sv}"));
  828. // Copy all existing arguments
  829. var iter = arguments.iterator ();
  830. string key;
  831. Variant value;
  832. while (iter.next ("{sv}", out key, out value)) {
  833. builder.add ("{sv}", key, value);
  834. }
  835. // Add/override the examples flag
  836. builder.add ("{sv}", "examples", new Variant.boolean (include_examples));
  837. var modified_args = builder.end ();
  838. messages.add (create_user_message (template, modified_args));
  839. return messages;
  840. }
  841. }
  842. /**
  843. * Main function.
  844. *
  845. * @param args Command line arguments
  846. * @return Exit code
  847. */
  848. public static int main (string[] args) {
  849. var server = new SimpleServer ();
  850. // Create a main loop
  851. var loop = new MainLoop ();
  852. server.run.begin ((obj, res) => {
  853. loop.quit ();
  854. });
  855. loop.run ();
  856. return 0;
  857. }
  858. /**
  859. Content-Length: 54
  860. {"jsonrpc":"2.0","id":1,"method":"initialize"}
  861. */