Os clientes são aplicações ou scripts personalizados que comunicam diretamente com um Servidor MCP para solicitar recursos, ferramentas e prompts. Diferentemente do uso da ferramenta de inspeção, que fornece uma interface gráfica para interagir com o servidor, escrever o seu próprio cliente permite interações programáticas e automatizadas. Isso possibilita que os programadores integrem as capacidades do MCP nos seus próprios fluxos de trabalho, automatizem tarefas e criem soluções personalizadas adaptadas a necessidades específicas.
Esta lição introduz o conceito de clientes no ecossistema do Model Context Protocol (MCP). Vais aprender a escrever o teu próprio cliente e a conectá-lo a um Servidor MCP.
No final desta lição, serás capaz de:
- Compreender o que um cliente pode fazer.
- Escrever o teu próprio cliente.
- Conectar e testar o cliente com um servidor MCP para garantir que este funciona como esperado.
Para escrever um cliente, precisas de fazer o seguinte:
- Importar as bibliotecas corretas. Vais usar a mesma biblioteca de antes, mas com diferentes construções.
- Instanciar um cliente. Isto envolve criar uma instância de cliente e conectá-la ao método de transporte escolhido.
- Decidir quais recursos listar. O teu servidor MCP vem com recursos, ferramentas e prompts; precisas decidir quais listar.
- Integrar o cliente numa aplicação anfitriã. Depois de conheceres as capacidades do servidor, precisas de integrar isso na tua aplicação anfitriã para que, se um utilizador digitar um prompt ou outro comando, a funcionalidade correspondente do servidor seja invocada.
Agora que entendemos, a um nível geral, o que vamos fazer, vejamos um exemplo a seguir.
Vamos analisar este exemplo de cliente:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "node",
args: ["server.js"]
});
const client = new Client(
{
name: "example-client",
version: "1.0.0"
}
);
await client.connect(transport);
// List prompts
const prompts = await client.listPrompts();
// Get a prompt
const prompt = await client.getPrompt({
name: "example-prompt",
arguments: {
arg1: "value"
}
});
// List resources
const resources = await client.listResources();
// Read a resource
const resource = await client.readResource({
uri: "file:///example.txt"
});
// Call a tool
const result = await client.callTool({
name: "example-tool",
arguments: {
arg1: "value"
}
});No código acima:
- Importamos as bibliotecas.
- Criamos uma instância de um cliente e conectamo-lo usando stdio como transporte.
- Listamos prompts, recursos e ferramentas e invocamo-los todos.
E pronto, tens um cliente que pode comunicar com um Servidor MCP.
Vamos dedicar algum tempo na próxima secção de exercícios para analisar cada trecho de código e explicar o que está a acontecer.
Como mencionado acima, vamos dedicar algum tempo a explicar o código e, se quiseres, podes acompanhar escrevendo o código.
Vamos importar as bibliotecas necessárias. Precisamos de referências a um cliente e ao protocolo de transporte escolhido, stdio. O stdio é um protocolo para coisas que devem ser executadas na tua máquina local. O SSE é outro protocolo de transporte que mostraremos em capítulos futuros, mas é a tua outra opção. Por agora, vamos continuar com stdio.
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";from mcp import ClientSession, StdioServerParameters, types
from mcp.client.stdio import stdio_clientusing Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;Para Java, vais criar um cliente que se conecta ao servidor MCP do exercício anterior. Usando a mesma estrutura de projeto Java Spring Boot de Introdução ao Servidor MCP, cria uma nova classe Java chamada SDKClient na pasta src/main/java/com/microsoft/mcp/sample/client/ e adiciona as seguintes importações:
import java.util.Map;
import org.springframework.web.reactive.function.client.WebClient;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;Precisarás de adicionar as seguintes dependências ao teu ficheiro Cargo.toml.
[package]
name = "calculator-client"
version = "0.1.0"
edition = "2024"
[dependencies]
rmcp = { version = "0.5.0", features = ["client", "transport-child-process"] }
serde_json = "1.0.141"
tokio = { version = "1.46.1", features = ["rt-multi-thread"] }A partir daí, podes importar as bibliotecas necessárias no teu código de cliente.
use rmcp::{
RmcpError,
model::CallToolRequestParam,
service::ServiceExt,
transport::{ConfigureCommandExt, TokioChildProcess},
};
use tokio::process::Command;Vamos avançar para a instanciação.
Precisamos de criar uma instância do transporte e outra do nosso cliente:
const transport = new StdioClientTransport({
command: "node",
args: ["server.js"]
});
const client = new Client(
{
name: "example-client",
version: "1.0.0"
}
);
await client.connect(transport);No código acima:
-
Criámos uma instância de transporte stdio. Nota como especifica o comando e os argumentos para localizar e iniciar o servidor, algo que precisaremos fazer ao criar o cliente.
const transport = new StdioClientTransport({ command: "node", args: ["server.js"] });
-
Instanciámos um cliente, fornecendo-lhe um nome e uma versão.
const client = new Client( { name: "example-client", version: "1.0.0" });
-
Conectámos o cliente ao transporte escolhido.
await client.connect(transport);
from mcp import ClientSession, StdioServerParameters, types
from mcp.client.stdio import stdio_client
# Create server parameters for stdio connection
server_params = StdioServerParameters(
command="mcp", # Executable
args=["run", "server.py"], # Optional command line arguments
env=None, # Optional environment variables
)
async def run():
async with stdio_client(server_params) as (read, write):
async with ClientSession(
read, write
) as session:
# Initialize the connection
await session.initialize()
if __name__ == "__main__":
import asyncio
asyncio.run(run())No código acima:
- Importámos as bibliotecas necessárias.
- Instanciámos um objeto de parâmetros do servidor, que usaremos para executar o servidor e conectarmo-nos a ele com o nosso cliente.
- Definimos um método
runque, por sua vez, chamastdio_client, iniciando uma sessão de cliente. - Criámos um ponto de entrada onde fornecemos o método
runaoasyncio.run.
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;
var builder = Host.CreateApplicationBuilder(args);
builder.Configuration
.AddEnvironmentVariables()
.AddUserSecrets<Program>();
var clientTransport = new StdioClientTransport(new()
{
Name = "Demo Server",
Command = "dotnet",
Arguments = ["run", "--project", "path/to/file.csproj"],
});
await using var mcpClient = await McpClientFactory.CreateAsync(clientTransport);
No código acima:
- Importámos as bibliotecas necessárias.
- Criámos um transporte stdio e um cliente
mcpClient. Este último será usado para listar e invocar funcionalidades no Servidor MCP.
Nota: em "Arguments", podes apontar para o .csproj ou para o executável.
public class SDKClient {
public static void main(String[] args) {
var transport = new WebFluxSseClientTransport(WebClient.builder().baseUrl("http://localhost:8080"));
new SDKClient(transport).run();
}
private final McpClientTransport transport;
public SDKClient(McpClientTransport transport) {
this.transport = transport;
}
public void run() {
var client = McpClient.sync(this.transport).build();
client.initialize();
// Your client logic goes here
}
}No código acima:
- Criámos um método principal que configura um transporte SSE apontando para
http://localhost:8080, onde o nosso servidor MCP estará em execução. - Criámos uma classe cliente que aceita o transporte como parâmetro do construtor.
- No método
run, criámos um cliente MCP síncrono usando o transporte e inicializámos a conexão. - Usámos o transporte SSE (Server-Sent Events), adequado para comunicação baseada em HTTP com servidores MCP Java Spring Boot.
Este cliente Rust assume que o servidor é um projeto irmão chamado "calculator-server" no mesmo diretório. O código abaixo iniciará o servidor e conectará a ele.
async fn main() -> Result<(), RmcpError> {
// Assume the server is a sibling project named "calculator-server" in the same directory
let server_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("failed to locate workspace root")
.join("calculator-server");
let client = ()
.serve(
TokioChildProcess::new(Command::new("cargo").configure(|cmd| {
cmd.arg("run").current_dir(server_dir);
}))
.map_err(RmcpError::transport_creation::<TokioChildProcess>)?,
)
.await?;
// TODO: Initialize
// TODO: List tools
// TODO: Call add tool with arguments = {"a": 3, "b": 2}
client.cancel().await?;
Ok(())
}Agora temos um cliente que pode conectar-se quando o programa for executado. No entanto, ele ainda não lista as suas funcionalidades, então vamos fazer isso a seguir:
// List prompts
const prompts = await client.listPrompts();
// List resources
const resources = await client.listResources();
// list tools
const tools = await client.listTools();# List available resources
resources = await session.list_resources()
print("LISTING RESOURCES")
for resource in resources:
print("Resource: ", resource)
# List available tools
tools = await session.list_tools()
print("LISTING TOOLS")
for tool in tools.tools:
print("Tool: ", tool.name)Aqui listamos os recursos disponíveis, list_resources(), e as ferramentas, list_tools, e imprimimos os resultados.
foreach (var tool in await client.ListToolsAsync())
{
Console.WriteLine($"{tool.Name} ({tool.Description})");
}
Acima está um exemplo de como podemos listar as ferramentas no servidor. Para cada ferramenta, imprimimos o seu nome.
// List and demonstrate tools
ListToolsResult toolsList = client.listTools();
System.out.println("Available Tools = " + toolsList);
// You can also ping the server to verify connection
client.ping();No código acima:
- Chamámos
listTools()para obter todas as ferramentas disponíveis no servidor MCP. - Usámos
ping()para verificar se a conexão com o servidor está a funcionar. - O
ListToolsResultcontém informações sobre todas as ferramentas, incluindo os seus nomes, descrições e esquemas de entrada.
Ótimo, agora capturámos todas as funcionalidades. A questão agora é: quando as usamos? Bem, este cliente é bastante simples, no sentido de que precisaremos chamar explicitamente as funcionalidades quando quisermos. No próximo capítulo, criaremos um cliente mais avançado que terá acesso ao seu próprio modelo de linguagem grande, LLM. Por agora, vejamos como podemos invocar as funcionalidades no servidor:
Na função principal, após inicializar o cliente, podemos inicializar o servidor e listar algumas das suas funcionalidades.
// Initialize
let server_info = client.peer_info();
println!("Server info: {:?}", server_info);
// List tools
let tools = client.list_tools(Default::default()).await?;
println!("Available tools: {:?}", tools);Para invocar as funcionalidades, precisamos garantir que especificamos os argumentos corretos e, em alguns casos, o nome do que estamos a tentar invocar.
// Read a resource
const resource = await client.readResource({
uri: "file:///example.txt"
});
// Call a tool
const result = await client.callTool({
name: "example-tool",
arguments: {
arg1: "value"
}
});
// call prompt
const promptResult = await client.getPrompt({
name: "review-code",
arguments: {
code: "console.log(\"Hello world\")"
}
})No código acima:
-
Lemos um recurso, chamando-o com
readResource()e especificandouri. Eis como isso provavelmente será no lado do servidor:server.resource( "readFile", new ResourceTemplate("file://{name}", { list: undefined }), async (uri, { name }) => ({ contents: [{ uri: uri.href, text: `Hello, ${name}!` }] }) );
O nosso valor
uri,file://example.txt, corresponde afile://{name}no servidor.example.txtserá mapeado paraname. -
Chamamos uma ferramenta, especificando o seu
namee os seusarguments, assim:const result = await client.callTool({ name: "example-tool", arguments: { arg1: "value" } });
-
Obtemos um prompt, chamando
getPrompt()comnameearguments. O código do servidor é assim:server.prompt( "review-code", { code: z.string() }, ({ code }) => ({ messages: [{ role: "user", content: { type: "text", text: `Please review this code:\n\n${code}` } }] }) );
E o código do cliente correspondente será assim para corresponder ao que está declarado no servidor:
const promptResult = await client.getPrompt({ name: "review-code", arguments: { code: "console.log(\"Hello world\")" } })
# Read a resource
print("READING RESOURCE")
content, mime_type = await session.read_resource("greeting://hello")
# Call a tool
print("CALL TOOL")
result = await session.call_tool("add", arguments={"a": 1, "b": 7})
print(result.content)No código acima:
- Chamámos um recurso chamado
greetingusandoread_resource. - Invocámos uma ferramenta chamada
addusandocall_tool.
- Vamos adicionar algum código para chamar uma ferramenta:
var result = await mcpClient.CallToolAsync(
"Add",
new Dictionary<string, object?>() { ["a"] = 1, ["b"] = 3 },
cancellationToken:CancellationToken.None);- Para imprimir o resultado, aqui está algum código para lidar com isso:
Console.WriteLine(result.Content.First(c => c.Type == "text").Text);
// Sum 4// Call various calculator tools
CallToolResult resultAdd = client.callTool(new CallToolRequest("add", Map.of("a", 5.0, "b", 3.0)));
System.out.println("Add Result = " + resultAdd);
CallToolResult resultSubtract = client.callTool(new CallToolRequest("subtract", Map.of("a", 10.0, "b", 4.0)));
System.out.println("Subtract Result = " + resultSubtract);
CallToolResult resultMultiply = client.callTool(new CallToolRequest("multiply", Map.of("a", 6.0, "b", 7.0)));
System.out.println("Multiply Result = " + resultMultiply);
CallToolResult resultDivide = client.callTool(new CallToolRequest("divide", Map.of("a", 20.0, "b", 4.0)));
System.out.println("Divide Result = " + resultDivide);
CallToolResult resultHelp = client.callTool(new CallToolRequest("help", Map.of()));
System.out.println("Help = " + resultHelp);No código acima:
- Chamámos várias ferramentas de calculadora usando o método
callTool()com objetosCallToolRequest. - Cada chamada de ferramenta especifica o nome da ferramenta e um
Mapde argumentos necessários para essa ferramenta. - As ferramentas do servidor esperam nomes de parâmetros específicos (como "a", "b" para operações matemáticas).
- Os resultados são retornados como objetos
CallToolResult, contendo a resposta do servidor.
// Call add tool with arguments = {"a": 3, "b": 2}
let a = 3;
let b = 2;
let tool_result = client
.call_tool(CallToolRequestParam {
name: "add".into(),
arguments: serde_json::json!({ "a": a, "b": b }).as_object().cloned(),
})
.await?;
println!("Result of {:?} + {:?}: {:?}", a, b, tool_result);Para executar o cliente, digita o seguinte comando no terminal:
Adiciona a seguinte entrada à secção "scripts" no package.json:
"client": "tsc && node build/client.js"npm run clientChama o cliente com o seguinte comando:
python client.pydotnet runPrimeiro, garante que o teu servidor MCP está a funcionar em http://localhost:8080. Depois, executa o cliente:
# Build you project
./mvnw clean compile
# Run the client
./mvnw exec:java -Dexec.mainClass="com.microsoft.mcp.sample.client.SDKClient"Alternativamente, podes executar o projeto completo do cliente fornecido na pasta de solução 03-GettingStarted\02-client\solution\java:
# Navigate to the solution directory
cd 03-GettingStarted/02-client/solution/java
# Build and run the JAR
./mvnw clean package
java -jar target/calculator-client-0.0.1-SNAPSHOT.jarcargo fmt
cargo runNesta tarefa, vais usar o que aprendeste para criar um cliente, mas criarás um cliente próprio.
Aqui está um servidor que podes usar e que precisas de chamar através do teu código de cliente. Vê se consegues adicionar mais funcionalidades ao servidor para torná-lo mais interessante.
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Create an MCP server
const server = new McpServer({
name: "Demo",
version: "1.0.0"
});
// Add an addition tool
server.tool("add",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }]
})
);
// Add a dynamic greeting resource
server.resource(
"greeting",
new ResourceTemplate("greeting://{name}", { list: undefined }),
async (uri, { name }) => ({
contents: [{
uri: uri.href,
text: `Hello, ${name}!`
}]
})
);
// Start receiving messages on stdin and sending messages on stdout
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCPServer started on stdin/stdout");
}
main().catch((error) => {
console.error("Fatal error: ", error);
process.exit(1);
});# server.py
from mcp.server.fastmcp import FastMCP
# Create an MCP server
mcp = FastMCP("Demo")
# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
# Add a dynamic greeting resource
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
using System.ComponentModel;
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(consoleLogOptions =>
{
// Configure all logs to go to stderr
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
await builder.Build().RunAsync();
[McpServerToolType]
public static class CalculatorTool
{
[McpServerTool, Description("Adds two numbers")]
public static string Add(int a, int b) => $"Sum {a + b}";
}Consulta este projeto para ver como podes adicionar prompts e recursos.
Também consulta este link para saber como invocar prompts e recursos.
Na secção anterior, aprendeste a criar um servidor MCP simples com Rust. Podes continuar a desenvolver isso ou consultar este link para mais exemplos de servidores MCP baseados em Rust: Exemplos de Servidores MCP
A pasta de soluções contém implementações completas e prontas para executar de clientes que demonstram todos os conceitos abordados neste tutorial. Cada solução inclui código de cliente e servidor organizados em projetos separados e autossuficientes.
O diretório de soluções está organizado por linguagem de programação:
solution/
├── typescript/ # TypeScript client with npm/Node.js setup
│ ├── package.json # Dependencies and scripts
│ ├── tsconfig.json # TypeScript configuration
│ └── src/ # Source code
├── java/ # Java Spring Boot client project
│ ├── pom.xml # Maven configuration
│ ├── src/ # Java source files
│ └── mvnw # Maven wrapper
├── python/ # Python client implementation
│ ├── client.py # Main client code
│ ├── server.py # Compatible server
│ └── README.md # Python-specific instructions
├── dotnet/ # .NET client project
│ ├── dotnet.csproj # Project configuration
│ ├── Program.cs # Main client code
│ └── dotnet.sln # Solution file
├── rust/ # Rust client implementation
| ├── Cargo.lock # Cargo lock file
| ├── Cargo.toml # Project configuration and dependencies
| ├── src # Source code
| │ └── main.rs # Main client code
└── server/ # Additional .NET server implementation
├── Program.cs # Server code
└── server.csproj # Server project file
Cada solução específica de linguagem fornece:
- Implementação completa do cliente com todas as funcionalidades do tutorial.
- Estrutura de projeto funcional com dependências e configuração adequadas.
- Scripts de compilação e execução para configuração e execução fáceis.
- README detalhado com instruções específicas da linguagem.
- Exemplos de tratamento de erros e processamento de resultados.
-
Navega até à pasta da tua linguagem preferida:
cd solution/typescript/ # For TypeScript cd solution/java/ # For Java cd solution/python/ # For Python cd solution/dotnet/ # For .NET
-
Segue as instruções do README em cada pasta para:
- Instalar dependências.
- Compilar o projeto.
- Executar o cliente.
-
Exemplo de saída que deves ver:
Prompt: Please review this code: console.log("hello"); Resource template: file Tool result: { content: [ { type: 'text', text: '9' } ] }
Para documentação completa e instruções passo a passo, consulta: 📖 Documentação da Solução
Fornecemos implementações completas e funcionais de clientes para todas as linguagens de programação abordadas neste tutorial. Estes exemplos demonstram toda a funcionalidade descrita acima e podem ser usados como implementações de referência ou pontos de partida para os teus próprios projetos.
| Linguagem | Ficheiro | Descrição |
|---|---|---|
| Java | client_example_java.java |
Cliente Java completo usando transporte SSE com tratamento de erros abrangente |
| C# | client_example_csharp.cs |
Cliente C# completo usando transporte stdio com inicialização automática do servidor |
| TypeScript | client_example_typescript.ts |
Cliente TypeScript completo com suporte total ao protocolo MCP |
| Python | client_example_python.py |
Cliente Python completo usando padrões async/await |
| Rust | client_example_rust.rs |
Cliente Rust completo usando Tokio para operações assíncronas |
| Cada exemplo completo inclui: |
- ✅ Estabelecimento de conexão e tratamento de erros
- ✅ Descoberta do servidor (ferramentas, recursos, prompts, quando aplicável)
- ✅ Operações da calculadora (somar, subtrair, multiplicar, dividir, ajuda)
- ✅ Processamento de resultados e saída formatada
- ✅ Tratamento abrangente de erros
- ✅ Código limpo e documentado com comentários passo a passo
- Escolha o seu idioma preferido na tabela acima
- Revise o ficheiro de exemplo completo para compreender a implementação completa
- Execute o exemplo seguindo as instruções em
complete_examples.md - Modifique e expanda o exemplo para o seu caso de uso específico
Para documentação detalhada sobre como executar e personalizar estes exemplos, veja: 📖 Documentação de Exemplos Completos
| Pasta de Solução | Exemplos Completos |
|---|---|
| Estrutura completa do projeto com ficheiros de build | Implementações em ficheiro único |
| Pronto para executar com dependências | Exemplos de código focados |
| Configuração semelhante a produção | Referência educacional |
| Ferramentas específicas do idioma | Comparação entre idiomas |
Ambas as abordagens são valiosas - use a pasta de solução para projetos completos e os exemplos completos para aprendizagem e referência.
As principais conclusões deste capítulo sobre clientes são as seguintes:
- Podem ser usados tanto para descobrir como para invocar funcionalidades no servidor.
- Podem iniciar um servidor enquanto se iniciam (como neste capítulo), mas os clientes também podem conectar-se a servidores já em execução.
- São uma ótima forma de testar as capacidades do servidor, além de alternativas como o Inspector, descrito no capítulo anterior.
- Calculadora em Java
- Calculadora em .Net
- Calculadora em JavaScript
- Calculadora em TypeScript
- Calculadora em Python
- Calculadora em Rust
- Próximo: Criar um cliente com um LLM
Aviso Legal:
Este documento foi traduzido utilizando o serviço de tradução por IA Co-op Translator. Embora nos esforcemos pela precisão, esteja ciente de que traduções automáticas podem conter erros ou imprecisões. O documento original na sua língua nativa deve ser considerado a fonte autoritária. Para informações críticas, recomenda-se a tradução profissional realizada por humanos. Não nos responsabilizamos por quaisquer mal-entendidos ou interpretações incorretas decorrentes do uso desta tradução.