Sa ngayon, nakita mo kung paano gumawa ng server at isang kliyente. Ang kliyente ay nakapag-atawag nang tuwiran sa server para ilista ang mga tool, resources, at mga prompt nito. Gayunpaman, hindi ito isang praktikal na paraan. Ang iyong mga gumagamit ay nabubuhay sa panahon ng agentic at inaasahan nilang gamitin ang mga prompt at makipag-usap sa isang LLM. Hindi nila pinapansin kung ginamit mo ang MCP para iimbak ang iyong mga kakayahan; inaasahan nilang makipag-ugnayan gamit ang natural na wika. Paano natin ito sosolusyunan? Ang solusyon ay magdagdag ng isang LLM sa kliyente.
Sa araling ito, tututok tayo sa pagdaragdag ng isang LLM para sa iyong kliyente at ipapakita kung paano ito nagbibigay ng mas magandang karanasan para sa iyong gumagamit.
Sa pagtatapos ng araling ito, magiging kaya mong:
- Gumawa ng kliyente na may LLM.
- Magsagawa ng tuloy-tuloy na interaksyon sa isang MCP server gamit ang LLM.
- Magbigay ng mas magandang karanasan sa end user sa bahagi ng kliyente.
Subukan nating unawain ang pamamaraan na kailangan nating gawin. Ang pagdaragdag ng isang LLM ay tila simple, pero talaga ba natin ito gagawin?
Ganito ang pakikipag-ugnayan ng kliyente sa server:
-
Magtayo ng koneksyon sa server.
-
Ilista ang mga kakayahan, mga prompt, mga resources, at mga tool, at i-save ang kanilang schema.
-
Magdagdag ng isang LLM at ipasa ang mga naka-save na kakayahan pati na ang kanilang schema sa format na naiintindihan ng LLM.
-
Pamahalaan ang prompt ng gumagamit sa pamamagitan ng pagpapasa nito sa LLM kasabay ng mga tool na naka-lista ng kliyente.
Magaling, ngayon naiintindihan natin kung paano natin ito gagawin sa mataas na antas, subukan natin ito sa sumusunod na pagsasanay.
Sa pagsasanay na ito, matututuhan nating magdagdag ng LLM sa ating kliyente.
Ang paggawa ng GitHub token ay isang diretso na proseso. Ganito mo ito magagawa:
- Pumunta sa GitHub Settings – I-click ang iyong larawan ng profile sa kanang itaas na sulok at piliin ang Settings.
- Pumunta sa Developer Settings – Mag-scroll pababa at i-click ang Developer Settings.
- Piliin ang Personal Access Tokens – I-click ang Fine-grained tokens at pagkatapos ay Generate new token.
- I-configure ang Iyong Token – Magdagdag ng tala para reference, itakda ang expiration date, at piliin ang kinakailangang mga scope (mga permiso). Sa kasong ito ay siguraduhing idagdag ang Models permission.
- I-generate at Kopyahin ang Token – I-click ang Generate token, at siguraduhing kopyahin ito agad dahil hindi mo na ito makikita ulit.
Gumawa muna tayo ng ating kliyente:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
import OpenAI from "openai";
import { z } from "zod"; // I-import ang zod para sa schema validation
class MCPClient {
private openai: OpenAI;
private client: Client;
constructor(){
this.openai = new OpenAI({
baseURL: "https://models.inference.ai.azure.com",
apiKey: process.env.GITHUB_TOKEN,
});
this.client = new Client(
{
name: "example-client",
version: "1.0.0"
},
{
capabilities: {
prompts: {},
resources: {},
tools: {}
}
}
);
}
}Sa nakaraang code ay:
- In-import ang mga kinakailangang librarya
- Gumawa ng klase na may dalawang miyembro,
clientatopenaina tutulong sa atin na pamahalaan ang isang kliyente at makipag-ugnayan sa isang LLM ayon sa pagkakabanggit. - Ikinonpigura ang ating LLM instance upang gamitin ang GitHub Models sa pamamagitan ng pagtatakda ng
baseUrlpara tumuro sa inference API.
from mcp import ClientSession, StdioServerParameters, types
from mcp.client.stdio import stdio_client
# Gumawa ng mga parameter ng server para sa koneksyon ng stdio
server_params = StdioServerParameters(
command="mcp", # Maisasakatuparan
args=["run", "server.py"], # Opsyonal na mga argumento sa linya ng utos
env=None, # Opsyonal na mga variable ng kapaligiran
)
async def run():
async with stdio_client(server_params) as (read, write):
async with ClientSession(
read, write
) as session:
# Simulan ang koneksyon
await session.initialize()
if __name__ == "__main__":
import asyncio
asyncio.run(run())Sa nakaraang code ay:
- In-import ang mga kinakailangang librarya para sa MCP
- Lumilikha ng kliyente
using Azure;
using Azure.AI.Inference;
using Azure.Identity;
using System.Text.Json;
using ModelContextProtocol.Client;
using System.Text.Json;
var clientTransport = new StdioClientTransport(new()
{
Name = "Demo Server",
Command = "/workspaces/mcp-for-beginners/03-GettingStarted/02-client/solution/server/bin/Debug/net8.0/server",
Arguments = [],
});
await using var mcpClient = await McpClient.CreateAsync(clientTransport);Una, kailangan mong idagdag ang mga LangChain4j dependencies sa iyong pom.xml file. Idagdag ang mga dependencies na ito para payagan ang MCP integration at GitHub Models support:
<properties>
<langchain4j.version>1.0.0-beta3</langchain4j.version>
</properties>
<dependencies>
<!-- LangChain4j MCP Integration -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-mcp</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- OpenAI Official API Client -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai-official</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- GitHub Models Support -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-github-models</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- Spring Boot Starter (optional, for production apps) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>Pagkatapos, gumawa ng iyong Java client class:
import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.transport.McpTransport;
import dev.langchain4j.mcp.client.transport.http.HttpMcpTransport;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openaiofficial.OpenAiOfficialChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.tool.ToolProvider;
import java.time.Duration;
import java.util.List;
public class LangChain4jClient {
public static void main(String[] args) throws Exception { // I-configure ang LLM upang gamitin ang mga Modelong GitHub
ChatLanguageModel model = OpenAiOfficialChatModel.builder()
.isGitHubModels(true)
.apiKey(System.getenv("GITHUB_TOKEN"))
.timeout(Duration.ofSeconds(60))
.modelName("gpt-4.1-nano")
.build();
// Lumikha ng MCP transport para kumonekta sa server
McpTransport transport = new HttpMcpTransport.Builder()
.sseUrl("http://localhost:8080/sse")
.timeout(Duration.ofSeconds(60))
.logRequests(true)
.logResponses(true)
.build();
// Lumikha ng MCP client
McpClient mcpClient = new DefaultMcpClient.Builder()
.transport(transport)
.build();
}
}Sa nakaraang code ay:
- Idinagdag ang mga LangChain4j dependencies: Kinakailangan para sa integrasyon ng MCP, opisyal na OpenAI client, at GitHub Models support
- In-import ang mga LangChain4j librarya: Para sa MCP integration at OpenAI chat model functionality
- Gumawa ng
ChatLanguageModel: Nakikonpigura upang gamitin ang GitHub Models gamit ang iyong GitHub token - Naitakda ang HTTP transport: Gamit ang Server-Sent Events (SSE) para kumonekta sa MCP server
- Nagawa ang MCP client: Na siyang humahawak ng komunikasyon sa server
- Ginamit ang built-in MCP support ng LangChain4j: Na nagpapadali ng integrasyon sa pagitan ng LLMs at MCP servers
Ang halimbawang ito ay nangangailangan na mayroon kang Rust based MCP server na tumatakbo. Kung wala pa, balikan ang leksyon na 01-first-server para gumawa ng server.
Kapag nakuha mo na ang Rust MCP server, buksan ang terminal at pumunta sa parehong directory kung nasaan ang server. Pagkatapos patakbuhin ang sumusunod na utos para gumawa ng bagong LLM client project:
mkdir calculator-llmclient
cd calculator-llmclient
cargo initIdagdag ang mga sumusunod na dependencies sa iyong Cargo.toml file:
[dependencies]
async-openai = { version = "0.29.0", features = ["byot"] }
rmcp = { version = "0.5.0", features = ["client", "transport-child-process"] }
serde_json = "1.0.141"
tokio = { version = "1.46.1", features = ["rt-multi-thread"] }Note
Walang opisyal na Rust library para sa OpenAI, ngunit ang async-openai crate ay isang community maintained library na karaniwang ginagamit.
Buksan ang src/main.rs file at palitan ang nilalaman nito ng sumusunod na code:
use async_openai::{Client, config::OpenAIConfig};
use rmcp::{
RmcpError,
model::{CallToolRequestParam, ListToolsResult},
service::{RoleClient, RunningService, ServiceExt},
transport::{ConfigureCommandExt, TokioChildProcess},
};
use serde_json::{Value, json};
use std::error::Error;
use tokio::process::Command;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Paunang mensahe
let mut messages = vec![json!({"role": "user", "content": "What is the sum of 3 and 2?"})];
// I-setup ang OpenAI client
let api_key = std::env::var("OPENAI_API_KEY")?;
let openai_client = Client::with_config(
OpenAIConfig::new()
.with_api_base("https://models.github.ai/inference/chat")
.with_api_key(api_key),
);
// I-setup ang MCP client
let server_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("calculator-server");
let mcp_client = ()
.serve(
TokioChildProcess::new(Command::new("cargo").configure(|cmd| {
cmd.arg("run").current_dir(server_dir);
}))
.map_err(RmcpError::transport_creation::<TokioChildProcess>)?,
)
.await?;
// TODO: Kunin ang listahan ng MCP tool
// TODO: Pag-uusap ng LLM gamit ang mga tawag sa tool
Ok(())
}Ang code na ito ay nagse-set up ng isang pangunahing Rust application na magkokonekta sa MCP server at GitHub Models para sa LLM interactions.
Important
Siguraduhing itakda ang OPENAI_API_KEY na environment variable gamit ang iyong GitHub token bago patakbuhin ang application.
Magaling, para sa ating susunod na hakbang, listahan ang mga kakayahan sa server.
Ngayon ay kokonekta tayo sa server at hihilingin ang mga kakayahan nito:
Sa parehong klase, idagdag ang sumusunod na mga method:
async connectToServer(transport: Transport) {
await this.client.connect(transport);
this.run();
console.error("MCPClient started on stdin/stdout");
}
async run() {
console.log("Asking server for available tools");
// naglilista ng mga kasangkapan
const toolsResult = await this.client.listTools();
}Sa nakaraang code ay:
- Idinagdag ang code para kumonekta sa server,
connectToServer. - Gumawa ng
runna method na responsable sa paghawak ng daloy ng ating app. Sa ngayon inililista lamang nito ang mga tool ngunit magdaragdag tayo pa rito.
# Ilahad ang mga magagamit na resources
resources = await session.list_resources()
print("LISTING RESOURCES")
for resource in resources:
print("Resource: ", resource)
# Ilahad ang mga magagamit na tools
tools = await session.list_tools()
print("LISTING TOOLS")
for tool in tools.tools:
print("Tool: ", tool.name)
print("Tool", tool.inputSchema["properties"])Narito ang idinagdag namin:
- Paglilista ng mga resources at tools at ipinrint ang mga ito. Para sa mga tool, nilista rin ang
inputSchemana gagamitin natin mamaya.
async Task<List<ChatCompletionsToolDefinition>> GetMcpTools()
{
Console.WriteLine("Listing tools");
var tools = await mcpClient.ListToolsAsync();
List<ChatCompletionsToolDefinition> toolDefinitions = new List<ChatCompletionsToolDefinition>();
foreach (var tool in tools)
{
Console.WriteLine($"Connected to server with tools: {tool.Name}");
Console.WriteLine($"Tool description: {tool.Description}");
Console.WriteLine($"Tool parameters: {tool.JsonSchema}");
// TODO: convert tool definition from MCP tool to LLm tool
}
return toolDefinitions;
}Sa nakaraang code ay:
- Nilista ang mga tool na available sa MCP Server
- Para sa bawat tool, nilista ang pangalan, deskripsyon at ang schema nito. Ang huli ay gagamitin natin para tumawag sa mga tool sa lalong madaling panahon.
// Gumawa ng tagapagbigay ng tool na awtomatikong nakakakita ng mga MCP tool
ToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(List.of(mcpClient))
.build();
// Ang tagapagbigay ng MCP tool ay awtomatikong humahawak ng:
// - Paglilista ng mga magagamit na tool mula sa MCP server
// - Pag-convert ng mga schema ng MCP tool sa format ng LangChain4j
// - Pamamahala ng pagpapatupad ng tool at mga sagotSa nakaraang code ay:
- Gumawa ng isang
McpToolProviderna awtomatikong nagdi-discover at nagrerehistro ng lahat ng tool mula sa MCP server - Ang tool provider ang humahawak ng conversion sa pagitan ng MCP tool schemas at tool format ng LangChain4j nang internal
- Binabawasan ng pamamaraang ito ang manual na paglilista at conversion ng tool
Ang pagkuha ng tool mula sa MCP server ay ginagawa gamit ang list_tools na method. Sa iyong main function, pagkatapos i-setup ang MCP client, idagdag ang sumusunod na code:
// Kunin ang listahan ng MCP tool
let tools = mcp_client.list_tools(Default::default()).await?;Susunod na hakbang pagkatapos ilista ang kakayahan ng server ay i-convert ang mga ito sa format na naiintindihan ng LLM. Kapag nagawa na ito, maipapasa natin ang mga kakayahang ito bilang mga tool sa ating LLM.
-
Idagdag ang sumusunod na code para i-convert ang response mula sa MCP Server sa tool format na puwedeng gamitin ng LLM:
openAiToolAdapter(tool: { name: string; description?: string; input_schema: any; }) { // Gumawa ng zod schema batay sa input_schema const schema = z.object(tool.input_schema); return { type: "function" as const, // Tahasang itakda ang uri sa "function" function: { name: tool.name, description: tool.description, parameters: { type: "object", properties: tool.input_schema.properties, required: tool.input_schema.required, }, }, }; }
Ang code sa itaas ay kumukuha ng tugon mula sa MCP Server at kino-convert ito sa isang tool definition format na naiintindihan ng LLM.
-
I-update naman natin ang
runmethod upang ilista ang kakayahan ng server:async run() { console.log("Asking server for available tools"); const toolsResult = await this.client.listTools(); const tools = toolsResult.tools.map((tool) => { return this.openAiToolAdapter({ name: tool.name, description: tool.description, input_schema: tool.inputSchema, }); }); }
Sa nakaraang code, in-update natin ang
runmethod upang dumaan sa resulta at para sa bawat entry ay tawagin angopenAiToolAdapter.
-
Una, gumawa tayo ng sumusunod na converter function
def convert_to_llm_tool(tool): tool_schema = { "type": "function", "function": { "name": tool.name, "description": tool.description, "type": "function", "parameters": { "type": "object", "properties": tool.inputSchema["properties"] } } } return tool_schema
Sa function na
convert_to_llm_toolsay kinukuha natin ang MCP tool response at kino-convert ito sa format na naiintindihan ng LLM. -
Susunod, i-update natin ang kodigo ng kliyente gamit ang function na ito:
functions = [] for tool in tools.tools: print("Tool: ", tool.name) print("Tool", tool.inputSchema["properties"]) functions.append(convert_to_llm_tool(tool))
Dito, nagdagdag tayo ng tawag sa
convert_to_llm_toolpara i-convert ang response ng MCP tool sa isang bagay na maipapakain natin sa LLM mamaya.
- Magdagdag tayo ng code upang i-convert ang response ng MCP tool sa isang bagay na naiintindihan ng LLM
ChatCompletionsToolDefinition ConvertFrom(string name, string description, JsonElement jsonElement)
{
// convert the tool to a function definition
FunctionDefinition functionDefinition = new FunctionDefinition(name)
{
Description = description,
Parameters = BinaryData.FromObjectAsJson(new
{
Type = "object",
Properties = jsonElement
},
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })
};
// create a tool definition
ChatCompletionsToolDefinition toolDefinition = new ChatCompletionsToolDefinition(functionDefinition);
return toolDefinition;
}Sa nakaraang code ay:
- Gumawa tayo ng function na
ConvertFromna tumatanggap ng pangalan, deskripsyon, at input schema. - Itinakda ang functionality na lumilikha ng FunctionDefinition na ipapasa sa ChatCompletionsDefinition. Ang huli ay naiintindihan ng LLM.
-
Tingnan natin kung paano i-update ang ilang bahagi ng code upang samantalahin ang function na ito:
async Task<List<ChatCompletionsToolDefinition>> GetMcpTools() { Console.WriteLine("Listing tools"); var tools = await mcpClient.ListToolsAsync(); List<ChatCompletionsToolDefinition> toolDefinitions = new List<ChatCompletionsToolDefinition>(); foreach (var tool in tools) { Console.WriteLine($"Connected to server with tools: {tool.Name}"); Console.WriteLine($"Tool description: {tool.Description}"); Console.WriteLine($"Tool parameters: {tool.JsonSchema}"); JsonElement propertiesElement; tool.JsonSchema.TryGetProperty("properties", out propertiesElement); var def = ConvertFrom(tool.Name, tool.Description, propertiesElement); Console.WriteLine($"Tool definition: {def}"); toolDefinitions.Add(def); Console.WriteLine($"Properties: {propertiesElement}"); } return toolDefinitions; } ``` In the preceding code, we've: - Update the function to convert the MCP tool response to an LLm tool. Let's highlight the code we added: ```csharp JsonElement propertiesElement; tool.JsonSchema.TryGetProperty("properties", out propertiesElement); var def = ConvertFrom(tool.Name, tool.Description, propertiesElement); Console.WriteLine($"Tool definition: {def}"); toolDefinitions.Add(def); ``` The input schema is part of the tool response but on the "properties" attribute, so we need to extract. Furthermore, we now call `ConvertFrom` with the tool details. Now we've done the heavy lifting, let's see how it call comes together as we handle a user prompt next.
// Lumikha ng isang Bot interface para sa natural na pakikipag-ugnayan gamit ang wika
public interface Bot {
String chat(String prompt);
}
// I-configure ang AI serbisyo gamit ang LLM at MCP na mga kasangkapan
Bot bot = AiServices.builder(Bot.class)
.chatLanguageModel(model)
.toolProvider(toolProvider)
.build();Sa nakaraang code ay:
- Nagdefine ng simpleng
Botinterface para sa natural language interactions - Ginamit ang LangChain4j's
AiServicesupang awtomatikong i-bind ang LLM sa MCP tool provider - Ang framework ay awtomatikong humahawak sa conversion ng tool schema at pagtawag ng function sa likod ng eksena
- Pinapawi nito ang manual tool conversion — hinahandle ng LangChain4j ang lahat ng komplikasyon ng pag-convert ng MCP tools sa LLM-compatible na format
Para i-convert ang MCP tool response sa isang format na naiintindihan ng LLM, magdadagdag tayo ng helper function na nagfo-format ng listahan ng mga tool. Idagdag ang sumusunod na code sa iyong main.rs file sa ibaba ng main function. Ito ay tatawagin kapag gumagawa ng mga request sa LLM:
async fn format_tools(tools: &ListToolsResult) -> Result<Vec<Value>, Box<dyn Error>> {
let tools_json = serde_json::to_value(tools)?;
let Some(tools_array) = tools_json.get("tools").and_then(|t| t.as_array()) else {
return Ok(vec![]);
};
let formatted_tools = tools_array
.iter()
.filter_map(|tool| {
let name = tool.get("name")?.as_str()?;
let description = tool.get("description")?.as_str()?;
let schema = tool.get("inputSchema")?;
Some(json!({
"type": "function",
"function": {
"name": name,
"description": description,
"parameters": {
"type": "object",
"properties": schema.get("properties").unwrap_or(&json!({})),
"required": schema.get("required").unwrap_or(&json!([]))
}
}
}))
})
.collect();
Ok(formatted_tools)
}Magaling, handa na tayo para hawakan ang anumang request mula sa user, kaya gawin natin iyon sa susunod.
Sa bahaging ito ng code, hahawakan natin ang mga request ng gumagamit.
-
Magdagdag ng metodo na gagamitin para tawagan ang ating LLM:
async callTools( tool_calls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[], toolResults: any[] ) { for (const tool_call of tool_calls) { const toolName = tool_call.function.name; const args = tool_call.function.arguments; console.log(`Calling tool ${toolName} with args ${JSON.stringify(args)}`); // 2. Tawagin ang tool ng server const toolResult = await this.client.callTool({ name: toolName, arguments: JSON.parse(args), }); console.log("Tool result: ", toolResult); // 3. Gawin ang isang bagay gamit ang resulta // TODO } }
Sa nakaraang code ay:
-
Idinagdag ang method na
callTools. -
Tinitingnan ng method na ito ang response mula sa LLM kung anong mga tool ang tinawag, kung mayroon man:
for (const tool_call of tool_calls) { const toolName = tool_call.function.name; const args = tool_call.function.arguments; console.log(`Calling tool ${toolName} with args ${JSON.stringify(args)}`); // tawagin ang tool }
-
Tinatawag ang isang tool, kung sinasabi ng LLM na dapat iyon ang tawagin:
// 2. Tawagan ang kasangkapang server const toolResult = await this.client.callTool({ name: toolName, arguments: JSON.parse(args), }); console.log("Tool result: ", toolResult); // 3. Gawin ang isang bagay gamit ang resulta // TODO
-
-
I-update ang
runmethod upang isama ang tawag sa LLM at pagtawag sacallTools:// 1. Gumawa ng mga mensaheng input para sa LLM const prompt = "What is the sum of 2 and 3?" const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [ { role: "user", content: prompt, }, ]; console.log("Querying LLM: ", messages[0].content); // 2. Tawagan ang LLM let response = this.openai.chat.completions.create({ model: "gpt-4.1-mini", max_tokens: 1000, messages, tools: tools, }); let results: any[] = []; // 3. Suriin ang sagot ng LLM, para sa bawat pagpipilian, tingnan kung may mga tawag sa tool (await response).choices.map(async (choice: { message: any; }) => { const message = choice.message; if (message.tool_calls) { console.log("Making tool call") await this.callTools(message.tool_calls, results); } });
Magaling, narito ang buong code:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
import OpenAI from "openai";
import { z } from "zod"; // Mag-import ng zod para sa pag-validate ng schema
class MyClient {
private openai: OpenAI;
private client: Client;
constructor(){
this.openai = new OpenAI({
baseURL: "https://models.inference.ai.azure.com", // maaaring kailanganin baguhin ang url na ito sa hinaharap: https://models.github.ai/inference
apiKey: process.env.GITHUB_TOKEN,
});
this.client = new Client(
{
name: "example-client",
version: "1.0.0"
},
{
capabilities: {
prompts: {},
resources: {},
tools: {}
}
}
);
}
async connectToServer(transport: Transport) {
await this.client.connect(transport);
this.run();
console.error("MCPClient started on stdin/stdout");
}
openAiToolAdapter(tool: {
name: string;
description?: string;
input_schema: any;
}) {
// Gumawa ng zod schema batay sa input_schema
const schema = z.object(tool.input_schema);
return {
type: "function" as const, // Tiyaking itakda ang uri bilang "function"
function: {
name: tool.name,
description: tool.description,
parameters: {
type: "object",
properties: tool.input_schema.properties,
required: tool.input_schema.required,
},
},
};
}
async callTools(
tool_calls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[],
toolResults: any[]
) {
for (const tool_call of tool_calls) {
const toolName = tool_call.function.name;
const args = tool_call.function.arguments;
console.log(`Calling tool ${toolName} with args ${JSON.stringify(args)}`);
// 2. Tawagin ang tool ng server
const toolResult = await this.client.callTool({
name: toolName,
arguments: JSON.parse(args),
});
console.log("Tool result: ", toolResult);
// 3. Gawin ang isang bagay gamit ang resulta
// GAGAWIN PA
}
}
async run() {
console.log("Asking server for available tools");
const toolsResult = await this.client.listTools();
const tools = toolsResult.tools.map((tool) => {
return this.openAiToolAdapter({
name: tool.name,
description: tool.description,
input_schema: tool.inputSchema,
});
});
const prompt = "What is the sum of 2 and 3?";
const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
{
role: "user",
content: prompt,
},
];
console.log("Querying LLM: ", messages[0].content);
let response = this.openai.chat.completions.create({
model: "gpt-4.1-mini",
max_tokens: 1000,
messages,
tools: tools,
});
let results: any[] = [];
// 1. Suriin ang sagot ng LLM, para sa bawat pagpipilian, tingnan kung may tawag sa tool
(await response).choices.map(async (choice: { message: any; }) => {
const message = choice.message;
if (message.tool_calls) {
console.log("Making tool call")
await this.callTools(message.tool_calls, results);
}
});
}
}
let client = new MyClient();
const transport = new StdioClientTransport({
command: "node",
args: ["./build/index.js"]
});
client.connectToServer(transport);-
Magdagdag tayo ng ilang import na kailangan para tumawag ng LLM
# llm import os from azure.ai.inference import ChatCompletionsClient from azure.ai.inference.models import SystemMessage, UserMessage from azure.core.credentials import AzureKeyCredential import json
-
Ngayon naman, idagdag ang function na tatawag sa LLM:
# llm def call_llm(prompt, functions): token = os.environ["GITHUB_TOKEN"] endpoint = "https://models.inference.ai.azure.com" model_name = "gpt-4o" client = ChatCompletionsClient( endpoint=endpoint, credential=AzureKeyCredential(token), ) print("CALLING LLM") response = client.complete( messages=[ { "role": "system", "content": "You are a helpful assistant.", }, { "role": "user", "content": prompt, }, ], model=model_name, tools = functions, # Mga opsyonal na parameter temperature=1., max_tokens=1000, top_p=1. ) response_message = response.choices[0].message functions_to_call = [] if response_message.tool_calls: for tool_call in response_message.tool_calls: print("TOOL: ", tool_call) name = tool_call.function.name args = json.loads(tool_call.function.arguments) functions_to_call.append({ "name": name, "args": args }) return functions_to_call
Sa nakaraang code ay:
- Ipinasa natin ang mga functions na nahanap sa MCP server at na-convert natin sa LLM.
- Tinawag natin ang LLM gamit ang mga nasabing function.
- Sinusuri naman natin ang resulta para makita kung ano ang mga function na dapat tawagin, kung meron man.
- Sa wakas, nagpasa tayo ng array ng mga function na tatawagin.
-
Panghuling hakbang, i-update ang ating pangunahing kodigo:
prompt = "Add 2 to 20" # tanungin ang LLM kung may mga gamit na dapat gamitin, kung meron man functions_to_call = call_llm(prompt, functions) # tawagan ang mga mungkahing function for f in functions_to_call: result = await session.call_tool(f["name"], arguments=f["args"]) print("TOOLS result: ", result.content)
Yan ang huling hakbang, sa code sa itaas ay:
- Tinawag ang MCP tool gamit ang
call_toolgamit ang function na inisip ng LLM na dapat tawagin base sa prompt. - Ipiniprint ang resulta ng tool call sa MCP Server.
- Tinawag ang MCP tool gamit ang
-
Ipakita natin ang ilang code para sa pag-request ng prompt ng LLM:
var tools = await GetMcpTools(); for (int i = 0; i < tools.Count; i++) { var tool = tools[i]; Console.WriteLine($"MCP Tools def: {i}: {tool}"); } // 0. Define the chat history and the user message var userMessage = "add 2 and 4"; chatHistory.Add(new ChatRequestUserMessage(userMessage)); // 1. Define tools ChatCompletionsToolDefinition def = CreateToolDefinition(); // 2. Define options, including the tools var options = new ChatCompletionsOptions(chatHistory) { Model = "gpt-4.1-mini", Tools = { tools[0] } }; // 3. Call the model ChatCompletions? response = await client.CompleteAsync(options); var content = response.Content;
Sa nakaraang code ay:
- Kinuha ang mga tool mula sa MCP server,
var tools = await GetMcpTools(). - Dinisenyo ang user prompt
userMessage. - Gumawa ng options object na nagtatalaga ng modelo at mga tool.
- Nagpadala ng kahilingan sa LLM.
- Kinuha ang mga tool mula sa MCP server,
-
Panghuling hakbang, tingnan kung sa palagay ng LLM ay dapat tayong tumawag ng isang function:
// 4. Check if the response contains a function call ChatCompletionsToolCall? calls = response.ToolCalls.FirstOrDefault(); for (int i = 0; i < response.ToolCalls.Count; i++) { var call = response.ToolCalls[i]; Console.WriteLine($"Tool call {i}: {call.Name} with arguments {call.Arguments}"); //Tool call 0: add with arguments {"a":2,"b":4} var dict = JsonSerializer.Deserialize<Dictionary<string, object>>(call.Arguments); var result = await mcpClient.CallToolAsync( call.Name, dict!, cancellationToken: CancellationToken.None ); Console.WriteLine(result.Content.First(c => c.Type == "text").Text); }
Sa nakaraang code ay:
- Nou-loop ang listahan ng function calls.
- Para sa bawat tool call, pinuparse ang pangalan at argumento at tinawag ang tool sa MCP server gamit ang MCP client. Sa huli, priniprint ang mga resulta.
Narito ang buong code:
using Azure;
using Azure.AI.Inference;
using Azure.Identity;
using System.Text.Json;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
var endpoint = "https://models.inference.ai.azure.com";
var token = Environment.GetEnvironmentVariable("GITHUB_TOKEN"); // Your GitHub Access Token
var client = new ChatCompletionsClient(new Uri(endpoint), new AzureKeyCredential(token));
var chatHistory = new List<ChatRequestMessage>
{
new ChatRequestSystemMessage("You are a helpful assistant that knows about AI")
};
var clientTransport = new StdioClientTransport(new()
{
Name = "Demo Server",
Command = "/workspaces/mcp-for-beginners/03-GettingStarted/02-client/solution/server/bin/Debug/net8.0/server",
Arguments = [],
});
Console.WriteLine("Setting up stdio transport");
await using var mcpClient = await McpClient.CreateAsync(clientTransport);
ChatCompletionsToolDefinition ConvertFrom(string name, string description, JsonElement jsonElement)
{
// convert the tool to a function definition
FunctionDefinition functionDefinition = new FunctionDefinition(name)
{
Description = description,
Parameters = BinaryData.FromObjectAsJson(new
{
Type = "object",
Properties = jsonElement
},
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })
};
// create a tool definition
ChatCompletionsToolDefinition toolDefinition = new ChatCompletionsToolDefinition(functionDefinition);
return toolDefinition;
}
async Task<List<ChatCompletionsToolDefinition>> GetMcpTools()
{
Console.WriteLine("Listing tools");
var tools = await mcpClient.ListToolsAsync();
List<ChatCompletionsToolDefinition> toolDefinitions = new List<ChatCompletionsToolDefinition>();
foreach (var tool in tools)
{
Console.WriteLine($"Connected to server with tools: {tool.Name}");
Console.WriteLine($"Tool description: {tool.Description}");
Console.WriteLine($"Tool parameters: {tool.JsonSchema}");
JsonElement propertiesElement;
tool.JsonSchema.TryGetProperty("properties", out propertiesElement);
var def = ConvertFrom(tool.Name, tool.Description, propertiesElement);
Console.WriteLine($"Tool definition: {def}");
toolDefinitions.Add(def);
Console.WriteLine($"Properties: {propertiesElement}");
}
return toolDefinitions;
}
// 1. List tools on mcp server
var tools = await GetMcpTools();
for (int i = 0; i < tools.Count; i++)
{
var tool = tools[i];
Console.WriteLine($"MCP Tools def: {i}: {tool}");
}
// 2. Define the chat history and the user message
var userMessage = "add 2 and 4";
chatHistory.Add(new ChatRequestUserMessage(userMessage));
// 3. Define options, including the tools
var options = new ChatCompletionsOptions(chatHistory)
{
Model = "gpt-4.1-mini",
Tools = { tools[0] }
};
// 4. Call the model
ChatCompletions? response = await client.CompleteAsync(options);
var content = response.Content;
// 5. Check if the response contains a function call
ChatCompletionsToolCall? calls = response.ToolCalls.FirstOrDefault();
for (int i = 0; i < response.ToolCalls.Count; i++)
{
var call = response.ToolCalls[i];
Console.WriteLine($"Tool call {i}: {call.Name} with arguments {call.Arguments}");
//Tool call 0: add with arguments {"a":2,"b":4}
var dict = JsonSerializer.Deserialize<Dictionary<string, object>>(call.Arguments);
var result = await mcpClient.CallToolAsync(
call.Name,
dict!,
cancellationToken: CancellationToken.None
);
Console.WriteLine(result.Content.OfType<TextContentBlock>().First().Text);
}
// 5. Print the generic response
Console.WriteLine($"Assistant response: {content}");try {
// Isagawa ang mga kahilingan sa natural na wika na awtomatikong gumagamit ng mga kasangkapang MCP
String response = bot.chat("Calculate the sum of 24.5 and 17.3 using the calculator service");
System.out.println(response);
response = bot.chat("What's the square root of 144?");
System.out.println(response);
response = bot.chat("Show me the help for the calculator service");
System.out.println(response);
} finally {
mcpClient.close();
}Sa nakaraang code ay:
- Gumamit ng simpleng natural language prompts para makipag-ugnayan sa mga tool ng MCP server
- Awtomatikong humahawak ang LangChain4j framework ng:
- Pag-convert ng user prompts sa tool calls kapag kinakailangan
- Pagtawag sa tamang MCP tool base sa desisyon ng LLM
- Pamamahala ng daloy ng pag-uusap sa pagitan ng LLM at MCP server
- Ang
bot.chat()method ay nagbabalik ng natural language responses na maaaring may kasamang resulta mula sa pagpapatakbo ng mga MCP tool - Nagbibigay ito ng tuloy-tuloy na karanasan sa gumagamit kung saan hindi kailangang malaman ng mga user ang tungkol sa likod-ng-tagpo na MCP implementation
Kompletong halimbawa ng code:
public class LangChain4jClient {
public static void main(String[] args) throws Exception { ChatLanguageModel model = OpenAiOfficialChatModel.builder()
.isGitHubModels(true)
.apiKey(System.getenv("GITHUB_TOKEN"))
.timeout(Duration.ofSeconds(60))
.modelName("gpt-4.1-nano")
.timeout(Duration.ofSeconds(60))
.build();
McpTransport transport = new HttpMcpTransport.Builder()
.sseUrl("http://localhost:8080/sse")
.timeout(Duration.ofSeconds(60))
.logRequests(true)
.logResponses(true)
.build();
McpClient mcpClient = new DefaultMcpClient.Builder()
.transport(transport)
.build();
ToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(List.of(mcpClient))
.build();
Bot bot = AiServices.builder(Bot.class)
.chatLanguageModel(model)
.toolProvider(toolProvider)
.build();
try {
String response = bot.chat("Calculate the sum of 24.5 and 17.3 using the calculator service");
System.out.println(response);
response = bot.chat("What's the square root of 144?");
System.out.println(response);
response = bot.chat("Show me the help for the calculator service");
System.out.println(response);
} finally {
mcpClient.close();
}
}
}Dito nangyayari ang karamihan sa trabaho. Tatawagin natin ang LLM gamit ang unang prompt mula sa user, pagkatapos ay ipoproseso ang sagot upang tingnan kung may kailangan bang tawagin na mga tool. Kung mayroon, tatawagin natin ang mga tool na iyon at ipagpapatuloy ang pag-uusap sa LLM hanggang sa wala nang ibang tool na kailangang tawagin at mayroon na tayong pangwakas na sagot.
Gagawa tayo ng maraming tawag sa LLM, kaya gumawa tayo ng function na hahawak sa tawag sa LLM. Idagdag ang sumusunod na function sa iyong main.rs file:
async fn call_llm(
client: &Client<OpenAIConfig>,
messages: &[Value],
tools: &ListToolsResult,
) -> Result<Value, Box<dyn Error>> {
let response = client
.completions()
.create_byot(json!({
"messages": messages,
"model": "openai/gpt-4.1",
"tools": format_tools(tools).await?,
}))
.await?;
Ok(response)
}Ang function na ito ay tumatanggap ng LLM client, isang listahan ng mga mensahe (kasama ang prompt ng user), mga tool mula sa MCP server, at nagpapadala ng kahilingan sa LLM, pagkatapos ay ibinabalik ang sagot.
Ang tugon mula sa LLM ay maglalaman ng isang array ng choices. Kailangan nating iproseso ang resulta upang makita kung mayroon bang anumang tool_calls. Ipinapahiwatig nito na ang LLM ay humihiling na isang partikular na tool ay tatawagin kasama ang mga argumento. Idagdag ang sumusunod na code sa ilalim ng iyong main.rs file upang tukuyin ang isang function na hahawakan ang tugon ng LLM:
async fn process_llm_response(
llm_response: &Value,
mcp_client: &RunningService<RoleClient, ()>,
openai_client: &Client<OpenAIConfig>,
mcp_tools: &ListToolsResult,
messages: &mut Vec<Value>,
) -> Result<(), Box<dyn Error>> {
let Some(message) = llm_response
.get("choices")
.and_then(|c| c.as_array())
.and_then(|choices| choices.first())
.and_then(|choice| choice.get("message"))
else {
return Ok(());
};
// I-print ang nilalaman kung mayroon
if let Some(content) = message.get("content").and_then(|c| c.as_str()) {
println!("🤖 {}", content);
}
// Huwag mag-handle ng mga tawag sa tool
if let Some(tool_calls) = message.get("tool_calls").and_then(|tc| tc.as_array()) {
messages.push(message.clone()); // Magdagdag ng mensahe ng assistant
// Isagawa ang bawat tawag sa tool
for tool_call in tool_calls {
let (tool_id, name, args) = extract_tool_call_info(tool_call)?;
println!("⚡ Calling tool: {}", name);
let result = mcp_client
.call_tool(CallToolRequestParam {
name: name.into(),
arguments: serde_json::from_str::<Value>(&args)?.as_object().cloned(),
})
.await?;
// Idagdag ang resulta ng tool sa mga mensahe
messages.push(json!({
"role": "tool",
"tool_call_id": tool_id,
"content": serde_json::to_string_pretty(&result)?
}));
}
// Ipagpatuloy ang pag-uusap gamit ang mga resulta ng tool
let response = call_llm(openai_client, messages, mcp_tools).await?;
Box::pin(process_llm_response(
&response,
mcp_client,
openai_client,
mcp_tools,
messages,
))
.await?;
}
Ok(())
}Kung mayroong tool_calls, kinukuha nito ang impormasyon ng tool, tinatawagan ang MCP server gamit ang kahilingan ng tool, at idinadagdag ang mga resulta sa mga mensahe ng pag-uusap. Pagkatapos nito ay ipinagpapatuloy ang pag-uusap sa LLM at ang mga mensahe ay ina-update gamit ang tugon ng assistant at mga resulta ng pagtawag sa tool.
Upang makuha ang impormasyon ng pagtawag sa tool na ibinabalik ng LLM para sa mga MCP call, magdadagdag tayo ng isa pang helper function upang makuha ang lahat ng kailangan para gawin ang tawag. Idagdag ang sumusunod na code sa ilalim ng iyong main.rs file:
fn extract_tool_call_info(tool_call: &Value) -> Result<(String, String, String), Box<dyn Error>> {
let tool_id = tool_call
.get("id")
.and_then(|id| id.as_str())
.unwrap_or("")
.to_string();
let function = tool_call.get("function").ok_or("Missing function")?;
let name = function
.get("name")
.and_then(|n| n.as_str())
.unwrap_or("")
.to_string();
let args = function
.get("arguments")
.and_then(|a| a.as_str())
.unwrap_or("{}")
.to_string();
Ok((tool_id, name, args))
}Sa lahat ng bahagi na nasa lugar na, maaari na nating hawakan ang unang prompt ng user at tawagan ang LLM. I-update ang iyong main function upang isama ang sumusunod na code:
// Usapan ng LLM na may mga tawag sa kagamitan
let response = call_llm(&openai_client, &messages, &tools).await?;
process_llm_response(
&response,
&mcp_client,
&openai_client,
&tools,
&mut messages,
)
.await?;Ito ay magtatanong sa LLM gamit ang unang prompt ng user na humihiling ng kabuuan ng dalawang numero, at ipoproseso nito ang tugon upang dynamic na hawakan ang mga tawag sa tool.
Magaling, nagawa mo ito!
Kunin ang code mula sa ehersisyo at palawakin ang server gamit ang ilang iba pang mga tool. Pagkatapos gumawa ng isang client na may LLM, tulad ng sa ehersisyo, at subukan ito gamit ang iba't ibang mga prompt upang matiyak na lahat ng mga tool ng iyong server ay tinatawagan nang dynamic. Ang paraan ng paggawa ng isang client na ito ay nangangahulugan na magkakaroon ng mahusay na karanasan ang end user dahil magagamit nila ang mga prompt, sa halip na mga eksaktong command ng client, at hindi nila mapapansin ang anumang MCP server na tinatawag.
- Ang pagdagdag ng LLM sa iyong client ay nagbibigay ng mas mahusay na paraan para makipag-ugnayan ang mga user sa MCP Servers.
- Kailangan mong i-convert ang tugon ng MCP Server sa isang bagay na maiintindihan ng LLM.
- Java Calculator
- .Net Calculator
- JavaScript Calculator
- TypeScript Calculator
- Python Calculator
- Rust Calculator
Paunawa:
Ang dokumentong ito ay isinalin gamit ang AI translation service na Co-op Translator. Bagamat nagsusumikap kami na maging tumpak, pakatandaan na ang awtomatikong pagsasalin ay maaaring maglaman ng mga pagkakamali o hindi pagkakatugma. Ang orihinal na dokumento sa orihinal nitong wika ang dapat ituring na pangunahing sanggunian. Para sa mahahalagang impormasyon, inirerekomenda ang propesyonal na pagsasalin ng tao. Hindi kami mananagot sa anumang hindi pagkakaintindihan o maling interpretasyon na maaaring magmula sa paggamit ng salin na ito.