Activity: Creating a client
To create a client, we'll use a similar set up as with the server. At high level, here's what we need to do:
- TypeScript
- Python
- Create a new Node.js project in the
client
folder. - Install the necessary dependencies.
- Create a
src
folder and add anindex.ts
file. - Write the client code to connect to the server and call the
add
tool.
- Create a new project by creating a new folder (or reuse the existing one).
- Install the necessary dependencies.
- Add a
server.py
andclient.py
file. - Write the client code to connect to the server and call the
add
tool.
-1- Create a new project
- TypeScript
- Python
This is straightforward. We will create a new Node.js project and install the necessary dependencies. The project structure will look like this:
|- client
|- src
| |- index.ts
|- package.json
|- tsconfig.json
-
Create a
client
folder in the root directory of your project.mkdir client
-
Initialize a new Node.js project in the
client
folder:cd client
npm init -y -
Update the
package.json
file to include the following:{
"name": "client",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc && node ./build/index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.8.0"
},
"devDependencies": {
"@types/node": "^22.13.17",
"typescript": "^5.8.2"
},
"description": ""
} -
Create a
tsconfig.json
file in theclient
folder with the following content:{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
--| src
----| server.py
----| client.py
You can use the following for server.py
:
# 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}!"
-2- Install the necessary dependencies
We need to install the necessary dependencies for the client.
- TypeScript
- Python
The main dependency is the @modelcontextprotocol/sdk
package, which provides the client SDK for interacting with the server. Also, we need zod
for validation.
-
Install the necessary dependencies:
npm install @modelcontextprotocol/sdk zod
-
Create a
src
folder in theclient
directory:mkdir src
-
Create a file named
index.ts
in thesrc
folder:
pip install "mcp[cli]"
-3- Write the client code
Now that we have the project set up, let's write the client code to connect to the server and call the add
tool.
- TypeScript
- Python
-
Add the following code to
index.ts
:import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
async function main() {
const transport = new StdioClientTransport({
command: "node",
args: ["../build/index.js"]
});
const client = new Client(
{
name: "example-client",
version: "1.0.0"
},
{
capabilities: {
prompts: {},
resources: {},
tools: {}
}
}
);
await client.connect(transport);
main().catch((error) => {
console.error("Fatal error: ", error);
process.exit(1);
});In the preceding code:
- The
Client
class is imported from the@modelcontextprotocol/sdk/client/index.js
module. - An instance of
StdioClientTransport
is created, which will be used to communicate with the server. - The
Client
instance is created with a name and version, and an empty capabilities object. We will add prompts, resources, and tools to the capabilities object as needed. - A connection to the server is established using the
connect
method of theClient
instance, passing in the transport instance.
At this point, we're not doing anything with the client yet. To interact with the server, we need to implement the logic for sending requests for prompts, resources, and tools. This will be done in the next steps.
- The
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())
In the preceding code we've:
- Created a client instance that also initialized the client-server communication.
- Started a server instance with
mcp run server.py
.
We're now ready to add features.
List tools and call the add
tool
- TypeScript
- Python
-
Add the following code to the
main
function to query the server for its tools:console.log("TOOLS");
const tools = await client.listTools();
tools.tools.forEach((tool) => {
console.log("Tool: ", tool.name);
});In the preceding code:
- The
listTools
method of theClient
instance is called to retrieve the list of available tools from the server. - Thereafter the tools are printed to the console.
- The
-
Let's invoke the
add
tool we created in the server. Add the following code to themain
function:// Call a tool
console.log("CALL TOOL");
const result = await client.callTool({
name: "add",
arguments: {
a: 2,
b: 2
}
});
console.log("\tResult: ", result);In the preceding code:
- The
callTool
method of theClient
instance is called to invoke theadd
tool on the server. callTool
takes an object with the tool name and arguments as parameters.
- The
Your code so far should look like this:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
async function main() {
const transport = new StdioClientTransport({
command: "node",
args: ["../build/index.js"]
});
const client = new Client(
{
name: "example-client",
version: "1.0.0"
},
{
capabilities: {
prompts: {},
resources: {},
tools: {}
}
}
);
await client.connect(transport);
console.log("TOOLS");
const tools = await client.listTools();
tools.tools.forEach((tool) => {
console.log("Tool: ", tool.name);
});
// Call a tool
console.log("CALL TOOL");
const result = await client.callTool({
name: "add",
arguments: {
a: 2,
b: 2
}
});
console.log("\tResult: ", result);
}
main().catch((error) => {
console.error("Fatal error: ", error);
process.exit(1);
});
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()
# 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)
# 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)
if __name__ == "__main__":
import asyncio
asyncio.run(run())
Now we've added all the features we need so we can:
- List resources and read resources.
- List and call tools.
-4- Run the client
To run the client type the following command in the terminal (make sure you stand in the client directory):
- TypeScript
- Python
npm run build
You should see the following output:
TOOLS
Tool: add
CALL TOOL
Result: { content: [ { type: 'text', text: '4' } ] }
python client.py
You should see the following output:
LISTING RESOURCES
Resource: ('meta', None)
Resource: ('nextCursor', None)
Resource: ('resources', [])
INFO Processing request of type ListToolsRequest server.py:625
LISTING TOOLS
Tool: add
READING RESOURCE
INFO Processing request of type ReadResourceRequest server.py:625
CALL TOOL
INFO Processing request of type CallToolRequest server.py:625
[TextContent(type='text', text='8', annotations=None, meta=None)]
In our next activity, let's learn how we can add a large language model (LLM) to our client. This will make it possible for the client to negotiate with the server. Why this is useflu is that you can now show an NLP, Natural Language Processing user interface to the user, and the user can interact using natural language prompts.
Summary
You've built a client capable of communicating with an MCP Server. The client can list tools and call the add
tool. Either run this client from your project or clone the repository below to see a working solution:
git clone https://github.com/softchris/mcp-workshop.git
cd tutorial-mcp