Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5b8ae74
gRPC Transport
asheshvidyut Oct 3, 2025
c85020d
import code
asheshvidyut Oct 3, 2025
7182bd7
fix pyproject.toml
asheshvidyut Oct 3, 2025
c6d1925
fix indent
asheshvidyut Oct 3, 2025
d0cc85d
fix imports
asheshvidyut Oct 3, 2025
8410f25
fix request_counter
asheshvidyut Oct 3, 2025
500bfe6
fix progress token
asheshvidyut Oct 3, 2025
27e4fda
fix import
asheshvidyut Oct 3, 2025
afd93f2
add test depdencies
asheshvidyut Oct 3, 2025
6024f57
removed grpc from test dep
asheshvidyut Oct 3, 2025
d177f4c
uv sync
asheshvidyut Oct 3, 2025
8c67eb4
fix test
asheshvidyut Oct 3, 2025
d99153e
add try catch
asheshvidyut Oct 3, 2025
8d308ff
fix type hints
asheshvidyut Oct 3, 2025
7c256a6
fix test
asheshvidyut Oct 3, 2025
7a89f8f
Revert "add try catch"
asheshvidyut Oct 3, 2025
873d05b
fix tests
asheshvidyut Oct 3, 2025
6e3f784
ruff format
asheshvidyut Oct 3, 2025
697a9e1
ruff fix
asheshvidyut Oct 3, 2025
0da7c23
ruff check
asheshvidyut Oct 3, 2025
8538cc7
fix ruff
asheshvidyut Oct 3, 2025
1aedccb
ignore ruff for gen code
asheshvidyut Oct 3, 2025
9f02198
ruff format
asheshvidyut Oct 3, 2025
973ed02
fix circular import
asheshvidyut Oct 3, 2025
6bdd03b
fix ruff
asheshvidyut Oct 3, 2025
8328231
ruff format
asheshvidyut Oct 3, 2025
e2272dc
fix cancel server
asheshvidyut Oct 3, 2025
eab4492
fix exmaple
asheshvidyut Oct 3, 2025
29bfd05
removed log
asheshvidyut Oct 3, 2025
26d04ac
commit to trigger ci
asheshvidyut Oct 3, 2025
9588bf5
add try catch incase prgress token is not int
asheshvidyut Oct 6, 2025
62d81f5
Merge branch 'main' into grpc-transport
asheshvidyut Oct 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions examples/grpc/client/cancel_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import asyncio
import logging

from mcp import types
from mcp.client.grpc_transport_session import GRPCTransportSession
from mcp.shared.exceptions import McpError

logger = logging.getLogger(__name__)


async def main():
"""Run the gRPC client."""
logging.basicConfig(level=logging.INFO)
transport = GRPCTransportSession(target="127.0.0.1:50051")

try:
logger.info("Calling long_running_tool twice")
call_tool_task1 = asyncio.create_task(transport.call_tool("long_running_tool", {}))
call_tool_task2 = asyncio.create_task(transport.call_tool("long_running_tool", {}))

# Allow some time for the calls to start
await asyncio.sleep(1)

# Send cancellation notification for request_id 1
request_id_to_cancel = 1
logger.info(f"Sending cancellation for request_id: {request_id_to_cancel}")
cancel_notification1 = types.ClientNotification(
root=types.CancelledNotification(
method="notifications/cancelled",
params=types.CancelledNotificationParams(requestId=request_id_to_cancel),
)
)
await transport.send_notification(cancel_notification1)

try:
result1 = await call_tool_task1
logger.info(f"Tool call 1 result: {result1}")
except McpError as e:
if e.error.code == types.REQUEST_CANCELLED:
logger.info("Tool call 1 successfully cancelled as expected.")
else:
logger.error(f"Tool call 1 failed with unexpected error: {e}")

# Check task 2 is still running then cancel it
try:
await asyncio.wait_for(asyncio.shield(call_tool_task2), 0.1)
except asyncio.TimeoutError:
logger.info("Tool call 2 is still running as expected.")
else:
logger.error("Tool call 2 finished unexpectedly.")

await asyncio.sleep(3)

logger.info("Sending cancellation for request_id 2")
cancel_notification2 = types.ClientNotification(
root=types.CancelledNotification(
method="notifications/cancelled",
params=types.CancelledNotificationParams(requestId=2),
)
)
await transport.send_notification(cancel_notification2)

try:
result2 = await call_tool_task2
logger.info(f"Tool call 2 result: {result2}")
except McpError as e:
if e.error.code == types.REQUEST_CANCELLED:
logger.info("Tool call 2 successfully cancelled as expected.")
else:
logger.error(f"Tool call 2 failed with unexpected error: {e}")

finally:
await transport.close()
logger.info("Connection closed")


if __name__ == "__main__":
asyncio.run(main())
93 changes: 93 additions & 0 deletions examples/grpc/client/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
Client example for the Simple gRPC Server using MCPClient class.

This script demonstrates how to connect to and interact with the Simple gRPC Server
using the MCPClient class pattern with gRPC transport.
"""

import argparse
import asyncio
import logging

from mcp import McpError
from mcp.client.grpc_transport_session import GRPCTransportSession

logging.basicConfig(level=logging.INFO)


async def main(host="localhost", port=50051):
"""Run the client example using MCPClient class."""
session = GRPCTransportSession(target=f"{host}:{port}")
try:
print("--- Listing Tools ---")
tools = await session.list_tools()
print(tools)
print("---------------------\n")

print("--- Calling download_file with progress ---")

async def progress_callback(progress: float, total: float | None, message: str | None):
if total:
print(f"Progress: {progress / total * 100:.2f}% - {message}")
else:
print(f"Progress: {progress} - {message}")

result = await session.call_tool(
name="download_file",
arguments={"filename": "test.txt", "size_mb": 1.0},
progress_callback=progress_callback,
)
print(f"Final Result: {result}")

print("-------------------------------------------\n")

print("--- Calling tools with structured output ---")
weather = await session.call_tool("get_weather", {"city": "London"})
print(f"Weather in London: {weather}")

location = await session.call_tool("get_location", {"address": "1600 Amphitheatre Parkway"})
print(f"Location: {location}")

stats = await session.call_tool("get_statistics", {"data_type": "sales"})
print(f"Statistics: {stats}")

user = await session.call_tool("get_user", {"user_id": "123"})
print(f"User Profile: {user}")

config = await session.call_tool("get_config", {})
print(f"Untyped Config: {config}")

cities = await session.call_tool("list_cities", {})
print(f"Cities: {cities}")

temp = await session.call_tool("get_temperature", {"city": "Paris"})
print(f"Temperature in Paris: {temp}")

shrimp_names = await session.call_tool(
"name_shrimp",
{
"tank": {"shrimp": [{"name": "shrimp1"}, {"name": "shrimp2"}]},
"extra_names": ["bubbles"],
},
)
print(f"Shrimp names: {shrimp_names}")
print("--------------------------------------------\n")

print("--- Calling tool with image output ---")
result = await session.call_tool("get_image", {})
print(f"Result: {result}")
print("--------------------------------------------\n")

except McpError as e:
print(f"An error occurred: {e}")
finally:
await session.close()


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="MCP gRPC Client with MCPClient Class")
parser.add_argument("--host", default="localhost", help="Server host (default: localhost)")
parser.add_argument("--port", type=int, default=50051, help="Server port (default: 50051)")
args = parser.parse_args()

asyncio.run(main(host=args.host, port=args.port))
44 changes: 44 additions & 0 deletions examples/grpc/server/cancel_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import asyncio
import logging

from mcp.server.fastmcp import FastMCP
from mcp.server.grpc import create_mcp_grpc_server

logger = logging.getLogger(__name__)


async def main():
"""Run the gRPC server"""
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
)
mcp = FastMCP(
name="CancelExample",
instructions="An example server with a cancellable tool.",
)

@mcp.tool()
async def long_running_tool():
"""
This tool runs for 60 seconds, but can be cancelled by the client.
"""
logger.info("long_running_tool started")
try:
await asyncio.sleep(60)
logger.info("long_running_tool finished")
return "Tool finished without cancellation"
except asyncio.CancelledError:
logger.info("long_running_tool cancelled")
return "Tool was cancelled"

server = await create_mcp_grpc_server(target="127.0.0.1:50051", mcp_server=mcp)
try:
await server.wait_for_termination()
except KeyboardInterrupt:
await server.stop(grace=1)
logger.info("gRPC server stopped")


if __name__ == "__main__":
asyncio.run(main())
Loading
Loading