Documentation Index
Fetch the complete documentation index at: https://mintlify.com/microsoft/mcp-for-beginners/llms.txt
Use this file to discover all available pages before exploring further.
The MCP Inspector is great for manual exploration, but real integrations need a programmatic client. In this lesson you write a client from scratch in your chosen language and verify it can connect to a server, list capabilities, and invoke tools.
Learning objectives
By the end of this lesson you will be able to:
- Understand what an MCP client does and why you need one
- Write a client that connects to an MCP server over stdio
- List and invoke tools, resources, and prompts programmatically
- Run both client and server together in a local workflow
What a client does
An MCP client:
- Imports the MCP SDK and a transport module
- Instantiates a transport that knows how to start the server process
- Creates a
Client object and connects it to the transport
- Calls
listTools, listResources, listPrompts to discover server capabilities
- Calls
callTool, readResource, getPrompt to invoke them
Example client
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "node",
args: ["build/index.js"]
});
const client = new Client({ name: "example-client", version: "1.0.0" });
await client.connect(transport);
// List capabilities
const tools = await client.listTools();
const resources = await client.listResources();
const prompts = await client.listPrompts();
// Call a tool
const result = await client.callTool({
name: "add",
arguments: { a: 5, b: 3 }
});
// Read a resource
const resource = await client.readResource({ uri: "greeting://world" });
// Get a prompt
const prompt = await client.getPrompt({
name: "review-code",
arguments: { code: "console.log('hello')" }
});
Step-by-step walkthrough
Import the libraries
You need two things: the Client class and a transport. For local servers, use stdio transport. For HTTP servers, use SSE or Streamable HTTP transport.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_client
Instantiate client and transport
The stdio transport takes command and args — the exact command used to start the server process. The client connects through that transport.const transport = new StdioClientTransport({
command: "node",
args: ["server.js"]
});
const client = new Client({ name: "example-client", version: "1.0.0" });
await client.connect(transport);
server_params = StdioServerParameters(
command="mcp",
args=["run", "server.py"],
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
List server capabilities
TypeScript
Python
.NET
Java
const tools = await client.listTools();
const resources = await client.listResources();
const prompts = await client.listPrompts();
resources = await session.list_resources()
tools = await session.list_tools()
for tool in tools.tools:
print("Tool:", tool.name)
foreach (var tool in await mcpClient.ListToolsAsync())
Console.WriteLine($"{tool.Name} ({tool.Description})");
ListToolsResult toolsList = client.listTools();
System.out.println("Available Tools = " + toolsList);
client.ping(); // verify connection
Invoke features
TypeScript
Python
.NET
Java
// Call a tool
const result = await client.callTool({
name: "add",
arguments: { a: 5, b: 3 }
});
// Read a resource
const resource = await client.readResource({
uri: "greeting://world"
});
// Get a prompt
const promptResult = await client.getPrompt({
name: "review-code",
arguments: { code: "console.log('hello')" }
});
# Read a resource
content, mime_type = await session.read_resource("greeting://hello")
# Call a tool
result = await session.call_tool("add", arguments={"a": 1, "b": 7})
print(result.content)
var result = await mcpClient.CallToolAsync(
"Add",
new Dictionary<string, object?>() { ["a"] = 1, ["b"] = 3 },
cancellationToken: CancellationToken.None);
Console.WriteLine(result.Content.First(c => c.Type == "text").Text);
// Sum 4
CallToolResult resultAdd = client.callTool(
new CallToolRequest("add", Map.of("a", 5.0, "b", 3.0)));
CallToolResult resultSubtract = client.callTool(
new CallToolRequest("subtract", Map.of("a", 10.0, "b", 4.0)));
System.out.println("Subtract Result = " + resultSubtract);
Run the client
TypeScript
Python
.NET
Java
Add to package.json scripts: "client": "tsc && node build/client.js", then: ./mvnw clean compile
./mvnw exec:java -Dexec.mainClass="com.microsoft.mcp.sample.client.SDKClient"
You should see output similar to:Prompt: Please review this code: console.log("hello");
Resource template: file
Tool result: { content: [ { type: 'text', text: '9' } ] }
Key takeaways
- A client can discover and invoke any capability on a server — tools, resources, and prompts.
- The stdio transport starts the server as a child process, so you don’t need to manage it separately.
- A programmatic client is ideal for automated workflows and CI/CD pipelines.
- In the next lesson, you’ll add an LLM to the client so users interact via natural language.