Chapter 7: Adding a Backend Tool
TL;DR
- Open your MCP server file: Navigate to
mcp/my_server/src/main.rs
(the namemy_server
might vary). - Replace the file content with the code below. This code defines the input and output structures for our tool and adds a
greet
tool that responds with a personalized greeting.
use rmcp::server::{McpServer, McpServerSettings, Tool, ToolRequest, ToolResponse};
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Deserialize)]
struct GreetParams {
name: String,
}
#[derive(Serialize)]
struct GreetResponse {
greeting: String,
}
async fn greet_tool(req: ToolRequest) -> ToolResponse {
let params: GreetParams = serde_json::from_value(req.params).unwrap();
let response = GreetResponse {
greeting: format!("Hello, {}!", params.name),
};
ToolResponse::success(json!(response))
}
#[tokio::main]
async fn main() {
let tools = vec![
Tool::new("greet", "A simple tool that returns a greeting", greet_tool),
];
let settings = McpServerSettings {
port: 8080, // The port inside the container
tools,
};
McpServer::new(settings).start().await.unwrap();
}
- Save the file. The
worka dev
process automatically recompiles your Rust code and restarts the backend server. - Test it. The "Greeter View" UI from the previous chapter will now be fully functional.
Our user interface is ready, but it can't do anything yet because the greet
tool doesn't exist on the backend. In this chapter, we will implement it.
Step 1: Locate Your MCP Server
The worka init
command created a starter backend server for you inside the mcp/
directory. Navigate to it now. The path will be something like mcp/my_first_pack_svc/src/main.rs
.
This file contains the entry point for a minimal MCP server written in Rust.
Step 2: Define the Tool's Data Structures
It's a best practice to define the expected input and output of your tool using Rust structs. This makes your code type-safe and easier to understand. We will use the serde
library, which is included by default, to automatically serialize and deserialize these structs from JSON.
Add these structs to the top of your main.rs
file:
use serde::{Deserialize, Serialize};
// Describes the JSON parameters we expect from the frontend
#[derive(Deserialize)]
struct GreetParams {
name: String,
}
// Describes the JSON object we will send back
#[derive(Serialize)]
struct GreetResponse {
greeting: String,
}
Step 3: Implement the Tool Function
A "Tool" in an MCP server is simply an async
function that receives a ToolRequest
and returns a ToolResponse
. Let's create a function for our greet
tool.
use rmcp::server::{ToolRequest, ToolResponse};
use serde_json::json;
// ... (structs from above)
async fn greet_tool(req: ToolRequest) -> ToolResponse {
// Deserialize the incoming JSON params into our GreetParams struct
let params: GreetParams = serde_json::from_value(req.params).unwrap();
// Construct the response data
let response = GreetResponse {
greeting: format!("Hello, {}!", params.name),
};
// Return a successful response, converting our struct back to JSON
ToolResponse::success(json!(response))
}
Step 4: Register the Tool with the Server
Finally, we need to tell the MCP server that this tool exists. Find the main
function at the bottom of the file and modify it to register the greet
tool.
Your final main.rs
file should look like this:
use rmcp::server::{McpServer, McpServerSettings, Tool, ToolRequest, ToolResponse};
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Deserialize)]
struct GreetParams {
name: String,
}
#[derive(Serialize)]
struct GreetResponse {
greeting: String,
}
async fn greet_tool(req: ToolRequest) -> ToolResponse {
let params: GreetParams = serde_json::from_value(req.params).unwrap();
let response = GreetResponse {
greeting: format!("Hello, {}!", params.name),
};
ToolResponse::success(json!(response))
}
#[tokio::main]
async fn main() {
// A list of all tools this server provides
let tools = vec![
Tool::new("greet", "A simple tool that returns a greeting", greet_tool),
];
let settings = McpServerSettings {
port: 8080, // This is the port *inside* the container
tools,
};
McpServer::new(settings).start().await.unwrap();
}
Step 5: Test the Full Flow
Save the main.rs
file. In your terminal, the worka dev
server will notice the change, display compilation progress for your Rust code, and automatically restart the backend server.
Now, go back to the Worka application and open your "Greeter View". Type your name in the input field and click the "Greet Me" button. The UI will call the backend, the backend will execute your new greet_tool
function, and the personalized greeting will appear on the screen.
You have now built a complete, end-to-end feature in a Worka pack!