diff --git a/examples/grpc/README.md b/examples/grpc/README.md new file mode 100644 index 0000000000..9c41420390 --- /dev/null +++ b/examples/grpc/README.md @@ -0,0 +1,48 @@ +# gRPC Transport Example for MCP + +This example demonstrates how to implement a custom gRPC transport for the Model Context Protocol (MCP) Python SDK. + +## Overview + +- `mcp.proto`: Defines the gRPC service service mirroring the MCP protocol. +- `server.py`: Implements a gRPC server that bridges requests to the `mcp.server.Server`. +- `client.py`: A gRPC client that calls the server to list resources. + +## Prerequisites + +You need Python 3.10+ and the `grpcio-tools` package. + +## Setup & Running + +### Files + +- `mcp.proto`: The Protobuf definition for the MCP Service. +- `grpc_server.py`: A reusable gRPC Servicer adapter that wraps any MCP `Server` or `FastMCP` instance. +- `server.py`: An example user application that defines tools/resources using `FastMCP` and runs them over gRPC using the adapter. +- `client.py`: A gRPC client that calls the server's methods (ListResources, ListTools, CallTool). +- `run_server.sh`: Helper script to install dependencies, generate code, and run the server. +- `run_client.sh`: Helper script to run the client. + +### How to Run + +1. **Start the Server**: + ```bash + ./examples/grpc/run_server.sh + ``` + This script handles virtual environment creation, dependency installation, Protobuf code generation, and starting the server on port 50051. + +2. **Run the Client** (in a new terminal): + ```bash + ./examples/grpc/run_client.sh + ``` + You should see the client list resources, list tools (including the `add` tool), and successfully call the `add` tool to get the result `30`. + +### Implementation Details + +- **`GrpcMcpService` Adapter**: This class acts as a bridge. It accepts an `mcp.server.Server` instance. It starts the MCP server loop in the background using `anyio` memory streams. +- **Initialization Handshake**: The adapter automatically handles the MCP initialization sequence (`initialize` -> `notifications/initialized`) so the server is ready to accept requests immediately. +- **Message Mapping**: The adapter converts between gRPC messages (defined in `mcp.proto`) and the internal JSON-RPC messages used by the MCP SDK. It handles `CallTool` content type mapping (Text vs Image). +- **`FastMCP` Integration**: The example `server.py` uses `FastMCP` to define tools and resources, demonstrating how the `GrpcMcpService` adapter can wrap any `mcp.server.Server` instance. +- **Asynchronous Handling**: The adapter uses `anyio` to manage asynchronous operations, allowing it to bridge the gRPC stream interface with the MCP SDK's consume/produce stream interface. It wraps `JSONRPCRequest` objects into `SessionMessage` to be consumed by the SDK's `ServerSession`. + +> **Note**: This is an experimental example. diff --git a/examples/grpc/client.py b/examples/grpc/client.py new file mode 100644 index 0000000000..c482f2498a --- /dev/null +++ b/examples/grpc/client.py @@ -0,0 +1,56 @@ +import logging +import asyncio +import grpc +import sys +import os + +# Import generated classes +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +import mcp_pb2 +import mcp_pb2_grpc + +from mcp.client.session import ClientSession +from examples.grpc.grpc_client import grpc_client_channel + +async def run(): + print("Connecting to gRPC server...") + async with grpc.aio.insecure_channel('localhost:50051') as channel: + # Use our custom gRPC transport adapter + async with grpc_client_channel(channel) as (read_stream, write_stream): + async with ClientSession(read_stream, write_stream) as session: + print("Initializing...") + await session.initialize() + + print("\nCalling ListResources...") + resources = await session.list_resources() + print("Resources received:") + for res in resources.resources: + print(f"- Name: {res.name}") + print(f" URI: {res.uri}") + print(f" MimeType: {res.mimeType}") + + print("\nCalling ListTools...") + tools = await session.list_tools() + print("Tools received:") + for tool in tools.tools: + print(f"- Name: {tool.name}") + print(f" Description: {tool.description}") + + print("\nCalling CallTool 'add' with a=10, b=20...") + result = await session.call_tool("add", arguments={"a": 10, "b": 20}) + print("Tool Response:") + for content in result.content: + if content.type == "text": + print(f" Type: text") + print(f" Text: {content.text}") + elif content.type == "image": + print(f" Type: image") + print(f" Data: {len(content.data)} bytes") + +if __name__ == '__main__': + logging.basicConfig() + try: + asyncio.run(run()) + except KeyboardInterrupt: + pass diff --git a/examples/grpc/grpc_client.py b/examples/grpc/grpc_client.py new file mode 100644 index 0000000000..0ab2d30bf6 --- /dev/null +++ b/examples/grpc/grpc_client.py @@ -0,0 +1,141 @@ + +import asyncio +import logging +from contextlib import asynccontextmanager +import anyio +import grpc +import mcp.types as types +from mcp.shared.message import SessionMessage +from mcp.client.session import ClientSession + +# Import generated gRPC code +try: + import mcp_pb2 + import mcp_pb2_grpc +except ImportError: + import examples.grpc.mcp_pb2 as mcp_pb2 + import examples.grpc.mcp_pb2_grpc as mcp_pb2_grpc + +logger = logging.getLogger("grpc_client_transport") + +@asynccontextmanager +async def grpc_client_channel(channel): + """ + A transport context manager for MCP ClientSession that runs over a gRPC channel. + Yields (read_stream, write_stream). + """ + stub = mcp_pb2_grpc.McpStub(channel) + + # Streams for ClientSession + read_stream_writer, read_stream = anyio.create_memory_object_stream(100) + write_stream, write_stream_reader = anyio.create_memory_object_stream(100) + + async def process_outgoing_messages(): + """Reads JSON-RPC messages from ClientSession and sends them via gRPC.""" + async with write_stream_reader: + async for message in write_stream_reader: + # message is SessionMessage -> JSONRPCMessage + try: + json_msg = message.message + if isinstance(json_msg.root, types.JSONRPCRequest): + req = json_msg.root + logger.debug(f"Sending request: {req.method} ID: {req.id}") + await handle_request(req, stub, read_stream_writer) + elif isinstance(json_msg.root, types.JSONRPCNotification): + # Handle notifications (e.g. initialized) + logger.debug(f"Sending notification: {json_msg.root.method}") + # Provide dummy/no-op implementation for notifications if not critical + pass + except Exception as e: + logger.error(f"Error processing outgoing message: {e}", exc_info=True) + + async def handle_request(req, stub, result_writer): + """Dispatches JSON-RPC requests to appropriate gRPC methods.""" + response_result = None + error = None + + try: + if req.method == "initialize": + # Fake initialize response because gRPC transport doesn't need handshake + # OR forward to server if server supports generic method (it doesn't in our proto) + # But our GridServer expects initialization? + # Actually, our `GrpcMcpService` adapter allows one-way bridge. + # BUT `ClientSession` expects full protocol. + # In this specific gRPC example, we defined Typed methods. + # So we simulate the handshake locally or map to something? + # The MCP SDK Client REQUIRES initialize. + # Let's return a synthetic response. + response_result = types.InitializeResult( + protocolVersion="2024-11-05", + capabilities=types.ServerCapabilities(), + serverInfo=types.Implementation(name="grpc-server", version="1.0") + ).model_dump(by_alias=True) + + elif req.method == "resources/list": + grpc_resp = await stub.ListResources(mcp_pb2.ListResourcesRequest()) + # Map gRPC response to JSON-RPC result + resources = [] + for r in grpc_resp.resources: + resources.append(types.Resource( + uri=r.uri, name=r.name, mimeType=r.mime_type + )) + response_result = types.ListResourcesResult(resources=resources).model_dump(by_alias=True) + + elif req.method == "tools/list": + grpc_resp = await stub.ListTools(mcp_pb2.ListToolsRequest()) + tools = [] + for t in grpc_resp.tools: + tools.append(types.Tool( + name=t.name, description=t.description, inputSchema={} # Simplified + )) + response_result = types.ListToolsResult(tools=tools).model_dump(by_alias=True) + + elif req.method == "tools/call": + # Map params to gRPC + params = req.params # dict + name = params.get("name") + args = params.get("arguments", {}) + + from google.protobuf import struct_pb2 + args_struct = struct_pb2.Struct() + args_struct.update(args) + + req_proto = mcp_pb2.CallToolRequest.Request(name=name, arguments=args_struct) + + # Consume stream + content = [] + is_error = False + async for chunk in stub.CallTool(mcp_pb2.CallToolRequest(request=req_proto)): + is_error = chunk.is_error + for c in chunk.content: + if c.HasField("text"): + content.append(types.TextContent(type="text", text=c.text.text)) + elif c.HasField("image"): + content.append(types.ImageContent(type="image", data=c.image.data, mimeType=c.image.mime_type)) + + response_result = types.CallToolResult(content=content, isError=is_error).model_dump(by_alias=True) + + else: + raise ValueError(f"Method not supported by gRPC transport: {req.method}") + + # Send successful response back to ClientSession + resp = types.JSONRPCResponse( + jsonrpc="2.0", + id=req.id, + result=response_result + ) + await result_writer.send(SessionMessage(types.JSONRPCMessage(root=resp))) + + except Exception as e: + logger.error(f"RPC failed: {e}") + err_resp = types.JSONRPCError( + jsonrpc="2.0", + id=req.id, + error=types.ErrorData(code=-32603, message=str(e)) + ) + await result_writer.send(SessionMessage(types.JSONRPCMessage(root=err_resp))) + + async with anyio.create_task_group() as tg: + tg.start_soon(process_outgoing_messages) + yield read_stream, write_stream + tg.cancel_scope.cancel() diff --git a/examples/grpc/grpc_server.py b/examples/grpc/grpc_server.py new file mode 100644 index 0000000000..1a1ad8f016 --- /dev/null +++ b/examples/grpc/grpc_server.py @@ -0,0 +1,210 @@ + +import asyncio +import logging +import grpc +import anyio +import mcp.types as types +from mcp.shared.message import SessionMessage +from mcp.server import Server + +# Import generated gRPC code +# Assuming these are in the same directory or PYTHONPATH +try: + import mcp_pb2 + import mcp_pb2_grpc +except ImportError: + # Fallback for when running directly + import examples.grpc.mcp_pb2 as mcp_pb2 + import examples.grpc.mcp_pb2_grpc as mcp_pb2_grpc + +logger = logging.getLogger("grpc_mcp_server") + +class GrpcMcpService(mcp_pb2_grpc.McpServicer): + """ + Adapts gRPC requests to an existing MCP Server instance. + """ + def __init__(self, mcp_server: Server): + self.mcp_server = mcp_server + # Create memory streams for bridging gRPC -> MCP + self.read_stream_writer, self.read_stream = anyio.create_memory_object_stream(100) + self.write_stream, self.write_stream_reader = anyio.create_memory_object_stream(100) + self.pending_responses = {} + + async def start_background_loop(self): + """Starts the MCP server loop in the background.""" + logger.info("Initializing MCP Server...") + init_options = self.mcp_server.create_initialization_options() + + async def run_mcp(): + try: + await self.mcp_server.run( + self.read_stream, + self.write_stream, + init_options + ) + except Exception as e: + logger.error(f"MCP Server loop failed: {e}", exc_info=True) + + async def process_outgoing_messages(): + logger.info("Starting outgoing message processor...") + async with self.write_stream_reader: + async for message in self.write_stream_reader: + # Message type: SessionMessage + # Contains: JSONRPCMessage (Request, Response, Notification) + try: + json_rpc_message = message.message + logger.debug(f"SDK emitted message raw: {message}") + + # Use the Pydantic model to identify message type + if hasattr(json_rpc_message.root, 'id'): + msg_id = json_rpc_message.root.id + if msg_id in self.pending_responses: + logger.info(f"Processing response for ID: {msg_id}") + fut = self.pending_responses.pop(msg_id) + if not fut.done(): + fut.set_result(json_rpc_message.root) + else: + logger.warning(f"Received response for unknown ID: {msg_id}") + else: + # Notifications + logger.debug(f"Received notification: {json_rpc_message}") + + except Exception as e: + logger.error(f"Error processing outgoing message: {e}", exc_info=True) + logger.info("Outgoing message processor channel closed") + + # Start tasks + loop = asyncio.get_running_loop() + loop.create_task(run_mcp()) + loop.create_task(process_outgoing_messages()) + + # Perform Initialization Handshake + await self._initialize_mcp_session() + + async def _initialize_mcp_session(self): + """Performs the MCP initialization handshake (JSON-RPC over MemoryStream).""" + req_id = "init_0" + init_req = types.JSONRPCRequest( + jsonrpc="2.0", + id=req_id, + method="initialize", + params=types.InitializeRequestParams( + protocolVersion="2024-11-05", + capabilities=types.ClientCapabilities( + experimental={}, + sampling={} + ), + clientInfo=types.Implementation(name="grpc-adapter", version="1.0.0") + ).model_dump(by_alias=True) + ) + + loop = asyncio.get_running_loop() + fut = loop.create_future() + self.pending_responses[req_id] = fut + + # Send Initialize Request + await self.read_stream_writer.send(SessionMessage(types.JSONRPCMessage(root=init_req))) + + # Wait for Initialize Response + await fut + + # Send Initialized Notification + notif = types.JSONRPCNotification( + jsonrpc="2.0", + method="notifications/initialized", + params=types.NotificationParams().model_dump(by_alias=True) + ) + await self.read_stream_writer.send(SessionMessage(types.JSONRPCMessage(root=notif))) + logger.info("MCP Server Initialized") + + # --- gRPC Handlers --- + + async def ListResources(self, request, context): + return await self._unary_call( + request, context, "resources/list", mcp_pb2.ListResourcesResponse, "resources", self._map_resource + ) + + async def ListTools(self, request, context): + return await self._unary_call( + request, context, "tools/list", mcp_pb2.ListToolsResponse, "tools", self._map_tool + ) + + async def CallTool(self, request, context): + import google.protobuf.json_format + + # Access nested 'request' message + tool_req = request.request + arguments = google.protobuf.json_format.MessageToDict(tool_req.arguments) + + req_id = f"call_tool_{id(request)}" + params = types.CallToolRequestParams(name=tool_req.name, arguments=arguments).model_dump(by_alias=True) + + response = await self._send_request(req_id, "tools/call", params, context) + if not response: return # Error already handled + + try: + result = response.result + content_list = [] + for c in result.get("content", []): + c_type = c.get("type") + if c_type == "text": + content_list.append(mcp_pb2.CallToolResponse.Content( + text=mcp_pb2.TextContent(text=c.get("text", "")) + )) + elif c_type == "image": + content_list.append(mcp_pb2.CallToolResponse.Content( + image=mcp_pb2.ImageContent( + data=c.get("data", b""), + mime_type=c.get("mimeType", "") + ) + )) + # Add other types as needed + yield mcp_pb2.CallToolResponse(content=content_list, is_error=result.get("isError", False)) + except Exception as e: + logger.error(f"Error processing CallTool result: {e}", exc_info=True) + await context.abort(grpc.StatusCode.INTERNAL, str(e)) + + # --- Helper Methods --- + + async def _unary_call(self, request, context, method, response_cls, list_key, map_func): + req_id = f"{method.replace('/', '_')}_{id(request)}" + response = await self._send_request(req_id, method, {}, context) + if not response: return response_cls() + + try: + result = response.result + items_pb = [map_func(item) for item in result.get(list_key, [])] + return response_cls(**{list_key: items_pb}, common=mcp_pb2.ResponseFields()) + except Exception as e: + logger.error(f"Error mapping {method} response: {e}", exc_info=True) + await context.abort(grpc.StatusCode.INTERNAL, str(e)) + raise + + async def _send_request(self, req_id, method, params, context): + json_rpc_req = types.JSONRPCRequest(jsonrpc="2.0", id=req_id, method=method, params=params) + + loop = asyncio.get_running_loop() + fut = loop.create_future() + self.pending_responses[req_id] = fut + + await self.read_stream_writer.send(SessionMessage(types.JSONRPCMessage(root=json_rpc_req))) + + try: + return await asyncio.wait_for(fut, timeout=10.0) + except asyncio.TimeoutError: + await context.abort(grpc.StatusCode.DEADLINE_EXCEEDED, "Timeout waiting for MCP SDK") + return None + + def _map_resource(self, res): + return mcp_pb2.Resource( + uri=res.get("uri"), + name=res.get("name", ""), + description=res.get("description", ""), + mime_type=res.get("mimeType", "") + ) + + def _map_tool(self, tool): + return mcp_pb2.Tool( + name=tool.get("name"), + description=tool.get("description", "") + ) diff --git a/examples/grpc/mcp.proto b/examples/grpc/mcp.proto new file mode 100644 index 0000000000..001a949385 --- /dev/null +++ b/examples/grpc/mcp.proto @@ -0,0 +1,610 @@ +// MCP protocol schema based on the MCP specification: +// https://modelcontextprotocol.io/specification/2025-06-18/ +// Last Updated on: August 18, 2025. + +syntax = "proto3"; + +package model_context_protocol; + +import "google/protobuf/duration.proto"; +import "google/protobuf/struct.proto"; + +service Mcp { + // + // Resources + // + + // List resources. + rpc ListResources(ListResourcesRequest) returns (ListResourcesResponse); + // Read a resource. + // In order to better integrate with existing data plane mechanisms for + // request routing and authz, the client will add a header called + // mcp_resource whose value is the name of the resource being requested. + rpc ReadResource(ReadResourceRequest) returns (ReadResourceResponse); + // List resource templates. + rpc ListResourceTemplates(ListResourceTemplatesRequest) + returns (ListResourceTemplatesResponse); + + // + // Prompts + // + + // List prompts. + rpc ListPrompts(ListPromptsRequest) returns (ListPromptsResponse); + + // Get a prompt. + // In order to better integrate with existing data plane mechanisms for + // request routing and authz, the client will add a header called + // mcp_prompt whose value is the name of the prompt being requested. + rpc GetPrompt(GetPromptRequest) returns (GetPromptResponse); + + // + // Tools + // + + // List tools. + rpc ListTools(ListToolsRequest) returns (ListToolsResponse); + + // Call a tool. + // In order to better integrate with existing data plane mechanisms for + // request routing and authz, the client will add a header called + // mcp_tool whose value is the name of the tool being called. + rpc CallTool(CallToolRequest) returns (stream CallToolResponse); + + // + // Completions + // + + // Requests completions. + // In order to better integrate with existing data plane mechanisms for + // request routing and authz, the client will add a header called + // mcp_resource whose value is the name of the resource or prompt being + // used for completion. + rpc Complete(CompletionRequest) returns (CompletionResponse); + + // + // Cancellation + // + + rpc CancelTask(CancelTaskRequest) returns (CancelTaskResponse); +} + +enum ProtocolVersion { + VERSION_UNKNOWN = 0; + VERSION_20250326 = 1; + VERSION_20250618 = 2; + // More values can be added as needed. +} + +// If the request or response metadata field includes the key "progressToken" +// and the peer supports progress notifications, then the peer will send back +// the following. +// https://modelcontextprotocol.io/specification/2025-03-26/basic/utilities/progress +message ProgressNotification { + string progress_token = 1; + float progress = 2; + float total = 3; + string message = 4; +} + +// Logging, as per +// https://modelcontextprotocol.io/specification/2025-03-26/server/utilities/logging. +enum LogLevel { + LOG_LEVEL_UNKNOWN = 0; + LOG_LEVEL_DEBUG = 1; + LOG_LEVEL_INFO = 2; + LOG_LEVEL_NOTICE = 3; + LOG_LEVEL_WARNING = 4; + LOG_LEVEL_ERROR = 5; + LOG_LEVEL_CRITICAL = 6; + LOG_LEVEL_ALERT = 7; + LOG_LEVEL_EMERGENCY = 8; +} + +message LogMessage { + LogLevel log_level = 1; + string logger = 2; // Optional. + google.protobuf.Value data = 3; +} + +// Requests sent from the server to the client. +message ServerInitiatedRequest { + // Exactly one of these fields must be populated. + // (Not using "oneof", since that causes forward-compatibility problems.) + + // Sampling, as per + // https://modelcontextprotocol.io/specification/2025-03-26/client/sampling. + SamplingCreateMessageRequest sampling_create_message = 8; + + // Root list request, as per + // https://modelcontextprotocol.io/specification/2025-03-26/client/roots. + ListRootsRequest list_roots_request = 9; + bool notify_on_root_list_update = 10; + + // Elicitations, as per + // https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation. + ElicitRequest elicit_request = 11; +} + +// Responses to server-initiated requests. +message ServerInitiatedResponse { + // Exactly one of these fields must be populated. + // (Not using "oneof", since that causes forward-compatibility problems.) + + // Sampling, as per + // https://modelcontextprotocol.io/specification/2025-03-26/client/sampling. + SamplingCreateMessageResult sampling_create_message_result = 7; + + // Root list response, as per + // https://modelcontextprotocol.io/specification/2025-03-26/client/roots. + ListRootsResult root_list_result = 8; + + // Elicitation response, as per + // https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation. + ElicitResult elicit_result = 9; +} + +// Common fields used in all requests from client to server. +message RequestFields { + // Escape hatch for arbitrary side-channel data. + google.protobuf.Struct metadata = 1; + + // Pagination support, as described in + // https://modelcontextprotocol.io/specification/2025-03-26/server/utilities/pagination. + string cursor = 2; // Optional. + + ProgressNotification progress = 3; + + // Asks the server to send logs at a particular level. + LogLevel log_level = 4; // Optional. + + // The task ID supplied by the server, if any. + string task_id = 5; // Optional. + + // Responses to server-initiated requests. + map dependent_responses = 6; + + // State that the server gave us to echo back in subsequent RPCs for the + // same peristant request. + google.protobuf.Struct resume_data = 7; +} + +// Common fields used in all responses from server to client. +message ResponseFields { + // Optional. Sent only on the initial response on any RPC. + string instructions = 1; + + // Escape hatch for arbitrary side-channel data. + google.protobuf.Struct metadata = 2; + + // Pagination support, as described in + // https://modelcontextprotocol.io/specification/2025-03-26/server/utilities/pagination. + string next_cursor = 3; // Optional. + + ProgressNotification progress = 4; + + LogMessage log_message = 5; // Optional. + + // Optional. Sent only on the initial response on any RPC. + // If populated, this indicates that the server has started a + // persistent task, and the client is expected to echo back this ID + // in all subsequent RPCs related to the same request. + string task_id = 6; + + // Dependent requests. The presence of this field tells the client + // that the request is incomplete and that the client must try its + // request again with the result of these dependent requests in the + // dependent_responses field. + map dependent_requests = 7; + + // State for the client to echo back in subsequent RPCs for the + // same peristant request. + google.protobuf.Struct resume_data = 8; +} + +enum Role { + ROLE_UNKNOWN = 0; + ROLE_USER = 1; + ROLE_ASSISTANT = 2; +} + +message Annotations { + repeated Role audience = 1; + float priority = 2; // Must be in range [0,1]. +} + +message TextContent { + string text = 1; + Annotations annotations = 2; // Optional. +} + +message ImageContent { + bytes data = 1; + string mime_type = 2; + Annotations annotations = 3; // Optional. +} + +message AudioContent { + bytes data = 1; + string mime_type = 2; + Annotations annotations = 3; // Optional. +} + +message ListRootsRequest {} + +message ListRootsResult { + message Root { + string uri = 1; + string name = 2; // Optional. + } + repeated Root roots = 1; +} + +// Represents a message that contains text or binary data. +message SamplingMessage { + Role role = 1; // Which role is sending the message. + + // Exactly one of these fields must be populated. + // (Not using "oneof", since that causes forward-compatibility problems.) + TextContent text = 2; + ImageContent image = 3; + AudioContent audio = 4; +} + +message SamplingCreateMessageRequest { + repeated SamplingMessage messages = 1; + + message ModelPreferences { + message ModelHint { + string name = 1; // Optional. + } + repeated ModelHint hints = 1; + float intelligence_priority = 2; + float speed_priority = 3; + float cost_priority = 4; + } + ModelPreferences model_preferences = 2; // Optional. + + string system_prompt = 3; // Optional. + + enum IncludeContext { + NONE = 0; + THIS_SERVER = 1; + ALL_SERVERS = 2; + } + IncludeContext include_context = 4; // Optional. + + float temperature = 5; // Optional. + int32 max_tokens = 6; // Optional. + repeated string stop_sequence = 7; // Optional. +} + +message SamplingCreateMessageResult { + SamplingMessage message = 1; + string model = 2; + string stop_reason = 3; // Optional. +} + +message PrimitiveSchemaDefinition { + message StringSchema { + enum Format { + FORMAT_UNKNOWN = 0; + FORMAT_EMAIL = 1; + FORMAT_URI = 2; + FORMAT_DATE = 3; + FORMAT_DATE_TIME = 4; + } + string title = 1; + string description = 2; + uint64 min_length = 3; + uint64 max_length = 4; + Format format = 5; + } + + message NumberSchema { + string title = 1; + string description = 2; + uint64 minimum = 3; + uint64 maximum = 4; + } + + message BooleanSchema { + string title = 1; + string description = 2; + bool default = 3; + } + + message EnumSchema { + string title = 1; + string description = 2; + repeated string enum_list = 3; + repeated string enum_names = 4; + } + + // Exactly one of these fields will be present. + // (Not using "oneof", since that causes forward-compatibility problems.) + StringSchema string_schema = 1; + NumberSchema number_schema = 2; + BooleanSchema boolean_schema = 3; + EnumSchema enum_schema = 4; +} + +message ElicitRequest { + string message = 1; // Message to present to user. Required. + map requested_schema = 2; + repeated string required_fields = 3; +} + +message ElicitResult { + enum Type { + UNKNOWN = 0; + TYPE_ACCEPT = 1; + TYPE_DECLINE = 2; + TYPE_CANCEL = 3; + } + Type type = 1; + google.protobuf.Struct content = 2; +} + +// +// ListResources method +// + +// Represents a resource. +message Resource { + string uri = 1; + string name = 2; + string title = 7; + string description = 3; // Optional. + string mime_type = 4; // Optional. + Annotations annotations = 5; // Optional. + uint64 size = 6; // Optional. +} + +message ListResourcesRequest { + RequestFields common = 1; +} + +message ListResourcesResponse { + ResponseFields common = 1; + + repeated Resource resources = 2; + google.protobuf.Duration ttl = 3; +} + +// +// ReadResource method +// + +// Represents the contents of a resource. +message ResourceContents { + string uri = 1; + string mime_type = 2; + + // Exactly one of these fields must be populated. + // (Not using "oneof", since that causes forward-compatibility problems.) + string text = 3; + bytes blob = 4; +} + +message ReadResourceRequest { + RequestFields common = 1; + string uri = 2; +} + +message ReadResourceResponse { + ResponseFields common = 1; + repeated ResourceContents resource = 2; +} + +// +// ListResourceTemplates method +// + +// Represents a resource template. +message ResourceTemplate { + string uri_template = 1; + string name = 2; + string title = 6; + string description = 3; // Optional. + string mime_type = 4; // Optional. + Annotations annotations = 5; // Optional. +} + +message ListResourceTemplatesRequest { + RequestFields common = 1; +} + +message ListResourceTemplatesResponse { + ResponseFields common = 1; + repeated ResourceTemplate resource_templates = 2; + google.protobuf.Duration ttl = 3; +} + +// +// ListPrompts method +// + +// Represents a prompt. +message Prompt { + message Argument { + string name = 1; + string title = 4; + string description = 2; // Optional. + bool required = 3; // Optional. + } + + string name = 1; + string title = 4; + string description = 2; // Optional. + repeated Argument arguments = 3; // Optional. +} + +message ListPromptsRequest { + RequestFields common = 1; +} + +message ListPromptsResponse { + ResponseFields common = 1; + repeated Prompt prompts = 2; + google.protobuf.Duration ttl = 3; +} + +// +// GetPrompt method +// + +message EmbeddedResource { + ResourceContents contents = 1; + Annotations annotations = 2; // Optional. +} + +message PromptMessage { + Role role = 1; + + // Exactly one of these fields must be populated. + // (Not using "oneof", since that causes forward-compatibility problems.) + TextContent text = 2; + ImageContent image = 3; + AudioContent audio = 4; + EmbeddedResource embedded_resource = 5; + Resource resource_link = 6; +} + +message GetPromptRequest { + RequestFields common = 1; + string name = 2; + map arguments = 3; +} + +message GetPromptResponse { + ResponseFields common = 1; + string description = 2; // Optional. + repeated PromptMessage messages = 3; +} + +// +// ListTools and WatchToolList methods +// + +message ToolAnnotations { + string title = 1; // Optional. + bool read_only_hint = 2; // Optional. + bool destructive_hint = 3; // Optional. + bool idempotent_hint = 4; // Optional. + bool open_world_hint = 5; // Optional. +} + +// Represents a tool. +message Tool { + string name = 1; + string title = 6; + string description = 2; // Optional. + google.protobuf.Struct input_schema = 3; + google.protobuf.Struct output_schema = 5; // Optional. + ToolAnnotations annotations = 4; // Optional. +} + +message ListToolsRequest { + RequestFields common = 1; +} + +message ListToolsResponse { + ResponseFields common = 1; + repeated Tool tools = 2; + google.protobuf.Duration ttl = 3; +} + +// +// CallTool method +// + +message CallToolRequest { + RequestFields common = 1; + + message Request { + string name = 1; + google.protobuf.Struct arguments = 2; + } + + // Exactly one of these fields must be populated. + // (Not using "oneof", since that causes forward-compatibility problems.) + Request request = 2; // Required on initial request only. +} + +message CallToolResponse { + ResponseFields common = 1; + + message Content { + // Unstructured result contents. At least one will be populated if + // the tool does not define an output schema. If the tool does define + // an output schema, the structured_content field will be populated + // instead, but one of these fields may still be populated for backward + // compatibility. + // + // No more than one of these fields must be populated. + // (Not using "oneof", since that causes forward-compatibility problems.) + TextContent text = 1; + ImageContent image = 2; + AudioContent audio = 3; + EmbeddedResource embedded_resource = 4; + Resource resource_link = 7; + } + repeated Content content = 2; + + // If the tool defines an output schema, this field will be populated. + // Clients that support structured output should ignore the content + // field above if this field is set. + google.protobuf.Struct structured_content = 3; + + bool is_error = 4; // Optional. +} + +message ResourceReference { + string uri = 1; +} + +message PromptReference { + string name = 1; + string title = 2; +} + +message CompletionRequest { + RequestFields common = 1; + + // Exactly one of these fields must be populated. + // (Not using "oneof", since that causes forward-compatibility problems.) + ResourceReference resource_reference = 2; + PromptReference prompt_reference = 3; + + message Argument { + string name = 1; + string value = 2; + } + Argument argument = 4; + + message Context { + map arguments = 1; + } + Context context = 5; +} + +message CompletionResponse { + ResponseFields common = 1; + + repeated string values = 2; + int64 total_matches = 3; // Optional. + bool has_more = 4; // Optional. +} + +// +// Cancellation +// + +message CancelTaskRequest { + RequestFields common = 1; // task_id must be populated. +} + +message CancelTaskResponse { + ResponseFields common = 1; +} diff --git a/examples/grpc/mcp_pb2.py b/examples/grpc/mcp_pb2.py new file mode 100644 index 0000000000..12c2aecf23 --- /dev/null +++ b/examples/grpc/mcp_pb2.py @@ -0,0 +1,186 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: mcp.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'mcp.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tmcp.proto\x12\x16model_context_protocol\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\"`\n\x14ProgressNotification\x12\x16\n\x0eprogress_token\x18\x01 \x01(\t\x12\x10\n\x08progress\x18\x02 \x01(\x02\x12\r\n\x05total\x18\x03 \x01(\x02\x12\x0f\n\x07message\x18\x04 \x01(\t\"w\n\nLogMessage\x12\x33\n\tlog_level\x18\x01 \x01(\x0e\x32 .model_context_protocol.LogLevel\x12\x0e\n\x06logger\x18\x02 \x01(\t\x12$\n\x04\x64\x61ta\x18\x03 \x01(\x0b\x32\x16.google.protobuf.Value\"\x98\x02\n\x16ServerInitiatedRequest\x12U\n\x17sampling_create_message\x18\x08 \x01(\x0b\x32\x34.model_context_protocol.SamplingCreateMessageRequest\x12\x44\n\x12list_roots_request\x18\t \x01(\x0b\x32(.model_context_protocol.ListRootsRequest\x12\"\n\x1anotify_on_root_list_update\x18\n \x01(\x08\x12=\n\x0e\x65licit_request\x18\x0b \x01(\x0b\x32%.model_context_protocol.ElicitRequest\"\xf6\x01\n\x17ServerInitiatedResponse\x12[\n\x1esampling_create_message_result\x18\x07 \x01(\x0b\x32\x33.model_context_protocol.SamplingCreateMessageResult\x12\x41\n\x10root_list_result\x18\x08 \x01(\x0b\x32\'.model_context_protocol.ListRootsResult\x12;\n\relicit_result\x18\t \x01(\x0b\x32$.model_context_protocol.ElicitResult\"\xc6\x03\n\rRequestFields\x12)\n\x08metadata\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06\x63ursor\x18\x02 \x01(\t\x12>\n\x08progress\x18\x03 \x01(\x0b\x32,.model_context_protocol.ProgressNotification\x12\x33\n\tlog_level\x18\x04 \x01(\x0e\x32 .model_context_protocol.LogLevel\x12\x0f\n\x07task_id\x18\x05 \x01(\t\x12Z\n\x13\x64\x65pendent_responses\x18\x06 \x03(\x0b\x32=.model_context_protocol.RequestFields.DependentResponsesEntry\x12,\n\x0bresume_data\x18\x07 \x01(\x0b\x32\x17.google.protobuf.Struct\x1aj\n\x17\x44\x65pendentResponsesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12>\n\x05value\x18\x02 \x01(\x0b\x32/.model_context_protocol.ServerInitiatedResponse:\x02\x38\x01\"\xe3\x03\n\x0eResponseFields\x12\x14\n\x0cinstructions\x18\x01 \x01(\t\x12)\n\x08metadata\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x13\n\x0bnext_cursor\x18\x03 \x01(\t\x12>\n\x08progress\x18\x04 \x01(\x0b\x32,.model_context_protocol.ProgressNotification\x12\x37\n\x0blog_message\x18\x05 \x01(\x0b\x32\".model_context_protocol.LogMessage\x12\x0f\n\x07task_id\x18\x06 \x01(\t\x12Y\n\x12\x64\x65pendent_requests\x18\x07 \x03(\x0b\x32=.model_context_protocol.ResponseFields.DependentRequestsEntry\x12,\n\x0bresume_data\x18\x08 \x01(\x0b\x32\x17.google.protobuf.Struct\x1ah\n\x16\x44\x65pendentRequestsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12=\n\x05value\x18\x02 \x01(\x0b\x32..model_context_protocol.ServerInitiatedRequest:\x02\x38\x01\"O\n\x0b\x41nnotations\x12.\n\x08\x61udience\x18\x01 \x03(\x0e\x32\x1c.model_context_protocol.Role\x12\x10\n\x08priority\x18\x02 \x01(\x02\"U\n\x0bTextContent\x12\x0c\n\x04text\x18\x01 \x01(\t\x12\x38\n\x0b\x61nnotations\x18\x02 \x01(\x0b\x32#.model_context_protocol.Annotations\"i\n\x0cImageContent\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12\x11\n\tmime_type\x18\x02 \x01(\t\x12\x38\n\x0b\x61nnotations\x18\x03 \x01(\x0b\x32#.model_context_protocol.Annotations\"i\n\x0c\x41udioContent\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12\x11\n\tmime_type\x18\x02 \x01(\t\x12\x38\n\x0b\x61nnotations\x18\x03 \x01(\x0b\x32#.model_context_protocol.Annotations\"\x12\n\x10ListRootsRequest\"q\n\x0fListRootsResult\x12;\n\x05roots\x18\x01 \x03(\x0b\x32,.model_context_protocol.ListRootsResult.Root\x1a!\n\x04Root\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"\xda\x01\n\x0fSamplingMessage\x12*\n\x04role\x18\x01 \x01(\x0e\x32\x1c.model_context_protocol.Role\x12\x31\n\x04text\x18\x02 \x01(\x0b\x32#.model_context_protocol.TextContent\x12\x33\n\x05image\x18\x03 \x01(\x0b\x32$.model_context_protocol.ImageContent\x12\x33\n\x05\x61udio\x18\x04 \x01(\x0b\x32$.model_context_protocol.AudioContent\"\x8c\x05\n\x1cSamplingCreateMessageRequest\x12\x39\n\x08messages\x18\x01 \x03(\x0b\x32\'.model_context_protocol.SamplingMessage\x12`\n\x11model_preferences\x18\x02 \x01(\x0b\x32\x45.model_context_protocol.SamplingCreateMessageRequest.ModelPreferences\x12\x15\n\rsystem_prompt\x18\x03 \x01(\t\x12\\\n\x0finclude_context\x18\x04 \x01(\x0e\x32\x43.model_context_protocol.SamplingCreateMessageRequest.IncludeContext\x12\x13\n\x0btemperature\x18\x05 \x01(\x02\x12\x12\n\nmax_tokens\x18\x06 \x01(\x05\x12\x15\n\rstop_sequence\x18\x07 \x03(\t\x1a\xdb\x01\n\x10ModelPreferences\x12^\n\x05hints\x18\x01 \x03(\x0b\x32O.model_context_protocol.SamplingCreateMessageRequest.ModelPreferences.ModelHint\x12\x1d\n\x15intelligence_priority\x18\x02 \x01(\x02\x12\x16\n\x0espeed_priority\x18\x03 \x01(\x02\x12\x15\n\rcost_priority\x18\x04 \x01(\x02\x1a\x19\n\tModelHint\x12\x0c\n\x04name\x18\x01 \x01(\t\"<\n\x0eIncludeContext\x12\x08\n\x04NONE\x10\x00\x12\x0f\n\x0bTHIS_SERVER\x10\x01\x12\x0f\n\x0b\x41LL_SERVERS\x10\x02\"{\n\x1bSamplingCreateMessageResult\x12\x38\n\x07message\x18\x01 \x01(\x0b\x32\'.model_context_protocol.SamplingMessage\x12\r\n\x05model\x18\x02 \x01(\t\x12\x13\n\x0bstop_reason\x18\x03 \x01(\t\"\x85\x07\n\x19PrimitiveSchemaDefinition\x12U\n\rstring_schema\x18\x01 \x01(\x0b\x32>.model_context_protocol.PrimitiveSchemaDefinition.StringSchema\x12U\n\rnumber_schema\x18\x02 \x01(\x0b\x32>.model_context_protocol.PrimitiveSchemaDefinition.NumberSchema\x12W\n\x0e\x62oolean_schema\x18\x03 \x01(\x0b\x32?.model_context_protocol.PrimitiveSchemaDefinition.BooleanSchema\x12Q\n\x0b\x65num_schema\x18\x04 \x01(\x0b\x32<.model_context_protocol.PrimitiveSchemaDefinition.EnumSchema\x1a\x98\x02\n\x0cStringSchema\x12\r\n\x05title\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x12\n\nmin_length\x18\x03 \x01(\x04\x12\x12\n\nmax_length\x18\x04 \x01(\x04\x12U\n\x06\x66ormat\x18\x05 \x01(\x0e\x32\x45.model_context_protocol.PrimitiveSchemaDefinition.StringSchema.Format\"e\n\x06\x46ormat\x12\x12\n\x0e\x46ORMAT_UNKNOWN\x10\x00\x12\x10\n\x0c\x46ORMAT_EMAIL\x10\x01\x12\x0e\n\nFORMAT_URI\x10\x02\x12\x0f\n\x0b\x46ORMAT_DATE\x10\x03\x12\x14\n\x10\x46ORMAT_DATE_TIME\x10\x04\x1aT\n\x0cNumberSchema\x12\r\n\x05title\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x0f\n\x07minimum\x18\x03 \x01(\x04\x12\x0f\n\x07maximum\x18\x04 \x01(\x04\x1a\x44\n\rBooleanSchema\x12\r\n\x05title\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x0f\n\x07\x64\x65\x66\x61ult\x18\x03 \x01(\x08\x1aW\n\nEnumSchema\x12\r\n\x05title\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x11\n\tenum_list\x18\x03 \x03(\t\x12\x12\n\nenum_names\x18\x04 \x03(\t\"\xfa\x01\n\rElicitRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12T\n\x10requested_schema\x18\x02 \x03(\x0b\x32:.model_context_protocol.ElicitRequest.RequestedSchemaEntry\x12\x17\n\x0frequired_fields\x18\x03 \x03(\t\x1ai\n\x14RequestedSchemaEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12@\n\x05value\x18\x02 \x01(\x0b\x32\x31.model_context_protocol.PrimitiveSchemaDefinition:\x02\x38\x01\"\xba\x01\n\x0c\x45licitResult\x12\x37\n\x04type\x18\x01 \x01(\x0e\x32).model_context_protocol.ElicitResult.Type\x12(\n\x07\x63ontent\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"G\n\x04Type\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x0f\n\x0bTYPE_ACCEPT\x10\x01\x12\x10\n\x0cTYPE_DECLINE\x10\x02\x12\x0f\n\x0bTYPE_CANCEL\x10\x03\"\xa4\x01\n\x08Resource\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\r\n\x05title\x18\x07 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x11\n\tmime_type\x18\x04 \x01(\t\x12\x38\n\x0b\x61nnotations\x18\x05 \x01(\x0b\x32#.model_context_protocol.Annotations\x12\x0c\n\x04size\x18\x06 \x01(\x04\"M\n\x14ListResourcesRequest\x12\x35\n\x06\x63ommon\x18\x01 \x01(\x0b\x32%.model_context_protocol.RequestFields\"\xac\x01\n\x15ListResourcesResponse\x12\x36\n\x06\x63ommon\x18\x01 \x01(\x0b\x32&.model_context_protocol.ResponseFields\x12\x33\n\tresources\x18\x02 \x03(\x0b\x32 .model_context_protocol.Resource\x12&\n\x03ttl\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\"N\n\x10ResourceContents\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x11\n\tmime_type\x18\x02 \x01(\t\x12\x0c\n\x04text\x18\x03 \x01(\t\x12\x0c\n\x04\x62lob\x18\x04 \x01(\x0c\"Y\n\x13ReadResourceRequest\x12\x35\n\x06\x63ommon\x18\x01 \x01(\x0b\x32%.model_context_protocol.RequestFields\x12\x0b\n\x03uri\x18\x02 \x01(\t\"\x8a\x01\n\x14ReadResourceResponse\x12\x36\n\x06\x63ommon\x18\x01 \x01(\x0b\x32&.model_context_protocol.ResponseFields\x12:\n\x08resource\x18\x02 \x03(\x0b\x32(.model_context_protocol.ResourceContents\"\xa7\x01\n\x10ResourceTemplate\x12\x14\n\x0curi_template\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\r\n\x05title\x18\x06 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x11\n\tmime_type\x18\x04 \x01(\t\x12\x38\n\x0b\x61nnotations\x18\x05 \x01(\x0b\x32#.model_context_protocol.Annotations\"U\n\x1cListResourceTemplatesRequest\x12\x35\n\x06\x63ommon\x18\x01 \x01(\x0b\x32%.model_context_protocol.RequestFields\"\xc5\x01\n\x1dListResourceTemplatesResponse\x12\x36\n\x06\x63ommon\x18\x01 \x01(\x0b\x32&.model_context_protocol.ResponseFields\x12\x44\n\x12resource_templates\x18\x02 \x03(\x0b\x32(.model_context_protocol.ResourceTemplate\x12&\n\x03ttl\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\"\xc6\x01\n\x06Prompt\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05title\x18\x04 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12:\n\targuments\x18\x03 \x03(\x0b\x32\'.model_context_protocol.Prompt.Argument\x1aN\n\x08\x41rgument\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05title\x18\x04 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x10\n\x08required\x18\x03 \x01(\x08\"K\n\x12ListPromptsRequest\x12\x35\n\x06\x63ommon\x18\x01 \x01(\x0b\x32%.model_context_protocol.RequestFields\"\xa6\x01\n\x13ListPromptsResponse\x12\x36\n\x06\x63ommon\x18\x01 \x01(\x0b\x32&.model_context_protocol.ResponseFields\x12/\n\x07prompts\x18\x02 \x03(\x0b\x32\x1e.model_context_protocol.Prompt\x12&\n\x03ttl\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\"\x88\x01\n\x10\x45mbeddedResource\x12:\n\x08\x63ontents\x18\x01 \x01(\x0b\x32(.model_context_protocol.ResourceContents\x12\x38\n\x0b\x61nnotations\x18\x02 \x01(\x0b\x32#.model_context_protocol.Annotations\"\xd6\x02\n\rPromptMessage\x12*\n\x04role\x18\x01 \x01(\x0e\x32\x1c.model_context_protocol.Role\x12\x31\n\x04text\x18\x02 \x01(\x0b\x32#.model_context_protocol.TextContent\x12\x33\n\x05image\x18\x03 \x01(\x0b\x32$.model_context_protocol.ImageContent\x12\x33\n\x05\x61udio\x18\x04 \x01(\x0b\x32$.model_context_protocol.AudioContent\x12\x43\n\x11\x65mbedded_resource\x18\x05 \x01(\x0b\x32(.model_context_protocol.EmbeddedResource\x12\x37\n\rresource_link\x18\x06 \x01(\x0b\x32 .model_context_protocol.Resource\"\xd5\x01\n\x10GetPromptRequest\x12\x35\n\x06\x63ommon\x18\x01 \x01(\x0b\x32%.model_context_protocol.RequestFields\x12\x0c\n\x04name\x18\x02 \x01(\t\x12J\n\targuments\x18\x03 \x03(\x0b\x32\x37.model_context_protocol.GetPromptRequest.ArgumentsEntry\x1a\x30\n\x0e\x41rgumentsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x99\x01\n\x11GetPromptResponse\x12\x36\n\x06\x63ommon\x18\x01 \x01(\x0b\x32&.model_context_protocol.ResponseFields\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x37\n\x08messages\x18\x03 \x03(\x0b\x32%.model_context_protocol.PromptMessage\"\x84\x01\n\x0fToolAnnotations\x12\r\n\x05title\x18\x01 \x01(\t\x12\x16\n\x0eread_only_hint\x18\x02 \x01(\x08\x12\x18\n\x10\x64\x65structive_hint\x18\x03 \x01(\x08\x12\x17\n\x0fidempotent_hint\x18\x04 \x01(\x08\x12\x17\n\x0fopen_world_hint\x18\x05 \x01(\x08\"\xd5\x01\n\x04Tool\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05title\x18\x06 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12-\n\x0cinput_schema\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12.\n\routput_schema\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12<\n\x0b\x61nnotations\x18\x04 \x01(\x0b\x32\'.model_context_protocol.ToolAnnotations\"I\n\x10ListToolsRequest\x12\x35\n\x06\x63ommon\x18\x01 \x01(\x0b\x32%.model_context_protocol.RequestFields\"\xa0\x01\n\x11ListToolsResponse\x12\x36\n\x06\x63ommon\x18\x01 \x01(\x0b\x32&.model_context_protocol.ResponseFields\x12+\n\x05tools\x18\x02 \x03(\x0b\x32\x1c.model_context_protocol.Tool\x12&\n\x03ttl\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\"\xcf\x01\n\x0f\x43\x61llToolRequest\x12\x35\n\x06\x63ommon\x18\x01 \x01(\x0b\x32%.model_context_protocol.RequestFields\x12@\n\x07request\x18\x02 \x01(\x0b\x32/.model_context_protocol.CallToolRequest.Request\x1a\x43\n\x07Request\x12\x0c\n\x04name\x18\x01 \x01(\t\x12*\n\targuments\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\xfb\x03\n\x10\x43\x61llToolResponse\x12\x36\n\x06\x63ommon\x18\x01 \x01(\x0b\x32&.model_context_protocol.ResponseFields\x12\x41\n\x07\x63ontent\x18\x02 \x03(\x0b\x32\x30.model_context_protocol.CallToolResponse.Content\x12\x33\n\x12structured_content\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\x10\n\x08is_error\x18\x04 \x01(\x08\x1a\xa4\x02\n\x07\x43ontent\x12\x31\n\x04text\x18\x01 \x01(\x0b\x32#.model_context_protocol.TextContent\x12\x33\n\x05image\x18\x02 \x01(\x0b\x32$.model_context_protocol.ImageContent\x12\x33\n\x05\x61udio\x18\x03 \x01(\x0b\x32$.model_context_protocol.AudioContent\x12\x43\n\x11\x65mbedded_resource\x18\x04 \x01(\x0b\x32(.model_context_protocol.EmbeddedResource\x12\x37\n\rresource_link\x18\x07 \x01(\x0b\x32 .model_context_protocol.Resource\" \n\x11ResourceReference\x12\x0b\n\x03uri\x18\x01 \x01(\t\".\n\x0fPromptReference\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\"\x9a\x04\n\x11\x43ompletionRequest\x12\x35\n\x06\x63ommon\x18\x01 \x01(\x0b\x32%.model_context_protocol.RequestFields\x12\x45\n\x12resource_reference\x18\x02 \x01(\x0b\x32).model_context_protocol.ResourceReference\x12\x41\n\x10prompt_reference\x18\x03 \x01(\x0b\x32\'.model_context_protocol.PromptReference\x12\x44\n\x08\x61rgument\x18\x04 \x01(\x0b\x32\x32.model_context_protocol.CompletionRequest.Argument\x12\x42\n\x07\x63ontext\x18\x05 \x01(\x0b\x32\x31.model_context_protocol.CompletionRequest.Context\x1a\'\n\x08\x41rgument\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\x1a\x90\x01\n\x07\x43ontext\x12S\n\targuments\x18\x01 \x03(\x0b\x32@.model_context_protocol.CompletionRequest.Context.ArgumentsEntry\x1a\x30\n\x0e\x41rgumentsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x85\x01\n\x12\x43ompletionResponse\x12\x36\n\x06\x63ommon\x18\x01 \x01(\x0b\x32&.model_context_protocol.ResponseFields\x12\x0e\n\x06values\x18\x02 \x03(\t\x12\x15\n\rtotal_matches\x18\x03 \x01(\x03\x12\x10\n\x08has_more\x18\x04 \x01(\x08\"J\n\x11\x43\x61ncelTaskRequest\x12\x35\n\x06\x63ommon\x18\x01 \x01(\x0b\x32%.model_context_protocol.RequestFields\"L\n\x12\x43\x61ncelTaskResponse\x12\x36\n\x06\x63ommon\x18\x01 \x01(\x0b\x32&.model_context_protocol.ResponseFields*R\n\x0fProtocolVersion\x12\x13\n\x0fVERSION_UNKNOWN\x10\x00\x12\x14\n\x10VERSION_20250326\x10\x01\x12\x14\n\x10VERSION_20250618\x10\x02*\xd2\x01\n\x08LogLevel\x12\x15\n\x11LOG_LEVEL_UNKNOWN\x10\x00\x12\x13\n\x0fLOG_LEVEL_DEBUG\x10\x01\x12\x12\n\x0eLOG_LEVEL_INFO\x10\x02\x12\x14\n\x10LOG_LEVEL_NOTICE\x10\x03\x12\x15\n\x11LOG_LEVEL_WARNING\x10\x04\x12\x13\n\x0fLOG_LEVEL_ERROR\x10\x05\x12\x16\n\x12LOG_LEVEL_CRITICAL\x10\x06\x12\x13\n\x0fLOG_LEVEL_ALERT\x10\x07\x12\x17\n\x13LOG_LEVEL_EMERGENCY\x10\x08*;\n\x04Role\x12\x10\n\x0cROLE_UNKNOWN\x10\x00\x12\r\n\tROLE_USER\x10\x01\x12\x12\n\x0eROLE_ASSISTANT\x10\x02\x32\xba\x07\n\x03Mcp\x12l\n\rListResources\x12,.model_context_protocol.ListResourcesRequest\x1a-.model_context_protocol.ListResourcesResponse\x12i\n\x0cReadResource\x12+.model_context_protocol.ReadResourceRequest\x1a,.model_context_protocol.ReadResourceResponse\x12\x84\x01\n\x15ListResourceTemplates\x12\x34.model_context_protocol.ListResourceTemplatesRequest\x1a\x35.model_context_protocol.ListResourceTemplatesResponse\x12\x66\n\x0bListPrompts\x12*.model_context_protocol.ListPromptsRequest\x1a+.model_context_protocol.ListPromptsResponse\x12`\n\tGetPrompt\x12(.model_context_protocol.GetPromptRequest\x1a).model_context_protocol.GetPromptResponse\x12`\n\tListTools\x12(.model_context_protocol.ListToolsRequest\x1a).model_context_protocol.ListToolsResponse\x12_\n\x08\x43\x61llTool\x12\'.model_context_protocol.CallToolRequest\x1a(.model_context_protocol.CallToolResponse0\x01\x12\x61\n\x08\x43omplete\x12).model_context_protocol.CompletionRequest\x1a*.model_context_protocol.CompletionResponse\x12\x63\n\nCancelTask\x12).model_context_protocol.CancelTaskRequest\x1a*.model_context_protocol.CancelTaskResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'mcp_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_REQUESTFIELDS_DEPENDENTRESPONSESENTRY']._loaded_options = None + _globals['_REQUESTFIELDS_DEPENDENTRESPONSESENTRY']._serialized_options = b'8\001' + _globals['_RESPONSEFIELDS_DEPENDENTREQUESTSENTRY']._loaded_options = None + _globals['_RESPONSEFIELDS_DEPENDENTREQUESTSENTRY']._serialized_options = b'8\001' + _globals['_ELICITREQUEST_REQUESTEDSCHEMAENTRY']._loaded_options = None + _globals['_ELICITREQUEST_REQUESTEDSCHEMAENTRY']._serialized_options = b'8\001' + _globals['_GETPROMPTREQUEST_ARGUMENTSENTRY']._loaded_options = None + _globals['_GETPROMPTREQUEST_ARGUMENTSENTRY']._serialized_options = b'8\001' + _globals['_COMPLETIONREQUEST_CONTEXT_ARGUMENTSENTRY']._loaded_options = None + _globals['_COMPLETIONREQUEST_CONTEXT_ARGUMENTSENTRY']._serialized_options = b'8\001' + _globals['_PROTOCOLVERSION']._serialized_start=9372 + _globals['_PROTOCOLVERSION']._serialized_end=9454 + _globals['_LOGLEVEL']._serialized_start=9457 + _globals['_LOGLEVEL']._serialized_end=9667 + _globals['_ROLE']._serialized_start=9669 + _globals['_ROLE']._serialized_end=9728 + _globals['_PROGRESSNOTIFICATION']._serialized_start=99 + _globals['_PROGRESSNOTIFICATION']._serialized_end=195 + _globals['_LOGMESSAGE']._serialized_start=197 + _globals['_LOGMESSAGE']._serialized_end=316 + _globals['_SERVERINITIATEDREQUEST']._serialized_start=319 + _globals['_SERVERINITIATEDREQUEST']._serialized_end=599 + _globals['_SERVERINITIATEDRESPONSE']._serialized_start=602 + _globals['_SERVERINITIATEDRESPONSE']._serialized_end=848 + _globals['_REQUESTFIELDS']._serialized_start=851 + _globals['_REQUESTFIELDS']._serialized_end=1305 + _globals['_REQUESTFIELDS_DEPENDENTRESPONSESENTRY']._serialized_start=1199 + _globals['_REQUESTFIELDS_DEPENDENTRESPONSESENTRY']._serialized_end=1305 + _globals['_RESPONSEFIELDS']._serialized_start=1308 + _globals['_RESPONSEFIELDS']._serialized_end=1791 + _globals['_RESPONSEFIELDS_DEPENDENTREQUESTSENTRY']._serialized_start=1687 + _globals['_RESPONSEFIELDS_DEPENDENTREQUESTSENTRY']._serialized_end=1791 + _globals['_ANNOTATIONS']._serialized_start=1793 + _globals['_ANNOTATIONS']._serialized_end=1872 + _globals['_TEXTCONTENT']._serialized_start=1874 + _globals['_TEXTCONTENT']._serialized_end=1959 + _globals['_IMAGECONTENT']._serialized_start=1961 + _globals['_IMAGECONTENT']._serialized_end=2066 + _globals['_AUDIOCONTENT']._serialized_start=2068 + _globals['_AUDIOCONTENT']._serialized_end=2173 + _globals['_LISTROOTSREQUEST']._serialized_start=2175 + _globals['_LISTROOTSREQUEST']._serialized_end=2193 + _globals['_LISTROOTSRESULT']._serialized_start=2195 + _globals['_LISTROOTSRESULT']._serialized_end=2308 + _globals['_LISTROOTSRESULT_ROOT']._serialized_start=2275 + _globals['_LISTROOTSRESULT_ROOT']._serialized_end=2308 + _globals['_SAMPLINGMESSAGE']._serialized_start=2311 + _globals['_SAMPLINGMESSAGE']._serialized_end=2529 + _globals['_SAMPLINGCREATEMESSAGEREQUEST']._serialized_start=2532 + _globals['_SAMPLINGCREATEMESSAGEREQUEST']._serialized_end=3184 + _globals['_SAMPLINGCREATEMESSAGEREQUEST_MODELPREFERENCES']._serialized_start=2903 + _globals['_SAMPLINGCREATEMESSAGEREQUEST_MODELPREFERENCES']._serialized_end=3122 + _globals['_SAMPLINGCREATEMESSAGEREQUEST_MODELPREFERENCES_MODELHINT']._serialized_start=3097 + _globals['_SAMPLINGCREATEMESSAGEREQUEST_MODELPREFERENCES_MODELHINT']._serialized_end=3122 + _globals['_SAMPLINGCREATEMESSAGEREQUEST_INCLUDECONTEXT']._serialized_start=3124 + _globals['_SAMPLINGCREATEMESSAGEREQUEST_INCLUDECONTEXT']._serialized_end=3184 + _globals['_SAMPLINGCREATEMESSAGERESULT']._serialized_start=3186 + _globals['_SAMPLINGCREATEMESSAGERESULT']._serialized_end=3309 + _globals['_PRIMITIVESCHEMADEFINITION']._serialized_start=3312 + _globals['_PRIMITIVESCHEMADEFINITION']._serialized_end=4213 + _globals['_PRIMITIVESCHEMADEFINITION_STRINGSCHEMA']._serialized_start=3688 + _globals['_PRIMITIVESCHEMADEFINITION_STRINGSCHEMA']._serialized_end=3968 + _globals['_PRIMITIVESCHEMADEFINITION_STRINGSCHEMA_FORMAT']._serialized_start=3867 + _globals['_PRIMITIVESCHEMADEFINITION_STRINGSCHEMA_FORMAT']._serialized_end=3968 + _globals['_PRIMITIVESCHEMADEFINITION_NUMBERSCHEMA']._serialized_start=3970 + _globals['_PRIMITIVESCHEMADEFINITION_NUMBERSCHEMA']._serialized_end=4054 + _globals['_PRIMITIVESCHEMADEFINITION_BOOLEANSCHEMA']._serialized_start=4056 + _globals['_PRIMITIVESCHEMADEFINITION_BOOLEANSCHEMA']._serialized_end=4124 + _globals['_PRIMITIVESCHEMADEFINITION_ENUMSCHEMA']._serialized_start=4126 + _globals['_PRIMITIVESCHEMADEFINITION_ENUMSCHEMA']._serialized_end=4213 + _globals['_ELICITREQUEST']._serialized_start=4216 + _globals['_ELICITREQUEST']._serialized_end=4466 + _globals['_ELICITREQUEST_REQUESTEDSCHEMAENTRY']._serialized_start=4361 + _globals['_ELICITREQUEST_REQUESTEDSCHEMAENTRY']._serialized_end=4466 + _globals['_ELICITRESULT']._serialized_start=4469 + _globals['_ELICITRESULT']._serialized_end=4655 + _globals['_ELICITRESULT_TYPE']._serialized_start=4584 + _globals['_ELICITRESULT_TYPE']._serialized_end=4655 + _globals['_RESOURCE']._serialized_start=4658 + _globals['_RESOURCE']._serialized_end=4822 + _globals['_LISTRESOURCESREQUEST']._serialized_start=4824 + _globals['_LISTRESOURCESREQUEST']._serialized_end=4901 + _globals['_LISTRESOURCESRESPONSE']._serialized_start=4904 + _globals['_LISTRESOURCESRESPONSE']._serialized_end=5076 + _globals['_RESOURCECONTENTS']._serialized_start=5078 + _globals['_RESOURCECONTENTS']._serialized_end=5156 + _globals['_READRESOURCEREQUEST']._serialized_start=5158 + _globals['_READRESOURCEREQUEST']._serialized_end=5247 + _globals['_READRESOURCERESPONSE']._serialized_start=5250 + _globals['_READRESOURCERESPONSE']._serialized_end=5388 + _globals['_RESOURCETEMPLATE']._serialized_start=5391 + _globals['_RESOURCETEMPLATE']._serialized_end=5558 + _globals['_LISTRESOURCETEMPLATESREQUEST']._serialized_start=5560 + _globals['_LISTRESOURCETEMPLATESREQUEST']._serialized_end=5645 + _globals['_LISTRESOURCETEMPLATESRESPONSE']._serialized_start=5648 + _globals['_LISTRESOURCETEMPLATESRESPONSE']._serialized_end=5845 + _globals['_PROMPT']._serialized_start=5848 + _globals['_PROMPT']._serialized_end=6046 + _globals['_PROMPT_ARGUMENT']._serialized_start=5968 + _globals['_PROMPT_ARGUMENT']._serialized_end=6046 + _globals['_LISTPROMPTSREQUEST']._serialized_start=6048 + _globals['_LISTPROMPTSREQUEST']._serialized_end=6123 + _globals['_LISTPROMPTSRESPONSE']._serialized_start=6126 + _globals['_LISTPROMPTSRESPONSE']._serialized_end=6292 + _globals['_EMBEDDEDRESOURCE']._serialized_start=6295 + _globals['_EMBEDDEDRESOURCE']._serialized_end=6431 + _globals['_PROMPTMESSAGE']._serialized_start=6434 + _globals['_PROMPTMESSAGE']._serialized_end=6776 + _globals['_GETPROMPTREQUEST']._serialized_start=6779 + _globals['_GETPROMPTREQUEST']._serialized_end=6992 + _globals['_GETPROMPTREQUEST_ARGUMENTSENTRY']._serialized_start=6944 + _globals['_GETPROMPTREQUEST_ARGUMENTSENTRY']._serialized_end=6992 + _globals['_GETPROMPTRESPONSE']._serialized_start=6995 + _globals['_GETPROMPTRESPONSE']._serialized_end=7148 + _globals['_TOOLANNOTATIONS']._serialized_start=7151 + _globals['_TOOLANNOTATIONS']._serialized_end=7283 + _globals['_TOOL']._serialized_start=7286 + _globals['_TOOL']._serialized_end=7499 + _globals['_LISTTOOLSREQUEST']._serialized_start=7501 + _globals['_LISTTOOLSREQUEST']._serialized_end=7574 + _globals['_LISTTOOLSRESPONSE']._serialized_start=7577 + _globals['_LISTTOOLSRESPONSE']._serialized_end=7737 + _globals['_CALLTOOLREQUEST']._serialized_start=7740 + _globals['_CALLTOOLREQUEST']._serialized_end=7947 + _globals['_CALLTOOLREQUEST_REQUEST']._serialized_start=7880 + _globals['_CALLTOOLREQUEST_REQUEST']._serialized_end=7947 + _globals['_CALLTOOLRESPONSE']._serialized_start=7950 + _globals['_CALLTOOLRESPONSE']._serialized_end=8457 + _globals['_CALLTOOLRESPONSE_CONTENT']._serialized_start=8165 + _globals['_CALLTOOLRESPONSE_CONTENT']._serialized_end=8457 + _globals['_RESOURCEREFERENCE']._serialized_start=8459 + _globals['_RESOURCEREFERENCE']._serialized_end=8491 + _globals['_PROMPTREFERENCE']._serialized_start=8493 + _globals['_PROMPTREFERENCE']._serialized_end=8539 + _globals['_COMPLETIONREQUEST']._serialized_start=8542 + _globals['_COMPLETIONREQUEST']._serialized_end=9080 + _globals['_COMPLETIONREQUEST_ARGUMENT']._serialized_start=8894 + _globals['_COMPLETIONREQUEST_ARGUMENT']._serialized_end=8933 + _globals['_COMPLETIONREQUEST_CONTEXT']._serialized_start=8936 + _globals['_COMPLETIONREQUEST_CONTEXT']._serialized_end=9080 + _globals['_COMPLETIONREQUEST_CONTEXT_ARGUMENTSENTRY']._serialized_start=6944 + _globals['_COMPLETIONREQUEST_CONTEXT_ARGUMENTSENTRY']._serialized_end=6992 + _globals['_COMPLETIONRESPONSE']._serialized_start=9083 + _globals['_COMPLETIONRESPONSE']._serialized_end=9216 + _globals['_CANCELTASKREQUEST']._serialized_start=9218 + _globals['_CANCELTASKREQUEST']._serialized_end=9292 + _globals['_CANCELTASKRESPONSE']._serialized_start=9294 + _globals['_CANCELTASKRESPONSE']._serialized_end=9370 + _globals['_MCP']._serialized_start=9731 + _globals['_MCP']._serialized_end=10685 +# @@protoc_insertion_point(module_scope) diff --git a/examples/grpc/mcp_pb2_grpc.py b/examples/grpc/mcp_pb2_grpc.py new file mode 100644 index 0000000000..cbc8b5569d --- /dev/null +++ b/examples/grpc/mcp_pb2_grpc.py @@ -0,0 +1,487 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + +import mcp_pb2 as mcp__pb2 + +GRPC_GENERATED_VERSION = '1.76.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in mcp_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + + +class McpStub(object): + """ + Resources + + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.ListResources = channel.unary_unary( + '/model_context_protocol.Mcp/ListResources', + request_serializer=mcp__pb2.ListResourcesRequest.SerializeToString, + response_deserializer=mcp__pb2.ListResourcesResponse.FromString, + _registered_method=True) + self.ReadResource = channel.unary_unary( + '/model_context_protocol.Mcp/ReadResource', + request_serializer=mcp__pb2.ReadResourceRequest.SerializeToString, + response_deserializer=mcp__pb2.ReadResourceResponse.FromString, + _registered_method=True) + self.ListResourceTemplates = channel.unary_unary( + '/model_context_protocol.Mcp/ListResourceTemplates', + request_serializer=mcp__pb2.ListResourceTemplatesRequest.SerializeToString, + response_deserializer=mcp__pb2.ListResourceTemplatesResponse.FromString, + _registered_method=True) + self.ListPrompts = channel.unary_unary( + '/model_context_protocol.Mcp/ListPrompts', + request_serializer=mcp__pb2.ListPromptsRequest.SerializeToString, + response_deserializer=mcp__pb2.ListPromptsResponse.FromString, + _registered_method=True) + self.GetPrompt = channel.unary_unary( + '/model_context_protocol.Mcp/GetPrompt', + request_serializer=mcp__pb2.GetPromptRequest.SerializeToString, + response_deserializer=mcp__pb2.GetPromptResponse.FromString, + _registered_method=True) + self.ListTools = channel.unary_unary( + '/model_context_protocol.Mcp/ListTools', + request_serializer=mcp__pb2.ListToolsRequest.SerializeToString, + response_deserializer=mcp__pb2.ListToolsResponse.FromString, + _registered_method=True) + self.CallTool = channel.unary_stream( + '/model_context_protocol.Mcp/CallTool', + request_serializer=mcp__pb2.CallToolRequest.SerializeToString, + response_deserializer=mcp__pb2.CallToolResponse.FromString, + _registered_method=True) + self.Complete = channel.unary_unary( + '/model_context_protocol.Mcp/Complete', + request_serializer=mcp__pb2.CompletionRequest.SerializeToString, + response_deserializer=mcp__pb2.CompletionResponse.FromString, + _registered_method=True) + self.CancelTask = channel.unary_unary( + '/model_context_protocol.Mcp/CancelTask', + request_serializer=mcp__pb2.CancelTaskRequest.SerializeToString, + response_deserializer=mcp__pb2.CancelTaskResponse.FromString, + _registered_method=True) + + +class McpServicer(object): + """ + Resources + + """ + + def ListResources(self, request, context): + """List resources. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ReadResource(self, request, context): + """Read a resource. + In order to better integrate with existing data plane mechanisms for + request routing and authz, the client will add a header called + mcp_resource whose value is the name of the resource being requested. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListResourceTemplates(self, request, context): + """List resource templates. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListPrompts(self, request, context): + """ + Prompts + + + List prompts. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetPrompt(self, request, context): + """Get a prompt. + In order to better integrate with existing data plane mechanisms for + request routing and authz, the client will add a header called + mcp_prompt whose value is the name of the prompt being requested. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListTools(self, request, context): + """ + Tools + + + List tools. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CallTool(self, request, context): + """Call a tool. + In order to better integrate with existing data plane mechanisms for + request routing and authz, the client will add a header called + mcp_tool whose value is the name of the tool being called. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Complete(self, request, context): + """ + Completions + + + Requests completions. + In order to better integrate with existing data plane mechanisms for + request routing and authz, the client will add a header called + mcp_resource whose value is the name of the resource or prompt being + used for completion. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CancelTask(self, request, context): + """ + Cancellation + + + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_McpServicer_to_server(servicer, server): + rpc_method_handlers = { + 'ListResources': grpc.unary_unary_rpc_method_handler( + servicer.ListResources, + request_deserializer=mcp__pb2.ListResourcesRequest.FromString, + response_serializer=mcp__pb2.ListResourcesResponse.SerializeToString, + ), + 'ReadResource': grpc.unary_unary_rpc_method_handler( + servicer.ReadResource, + request_deserializer=mcp__pb2.ReadResourceRequest.FromString, + response_serializer=mcp__pb2.ReadResourceResponse.SerializeToString, + ), + 'ListResourceTemplates': grpc.unary_unary_rpc_method_handler( + servicer.ListResourceTemplates, + request_deserializer=mcp__pb2.ListResourceTemplatesRequest.FromString, + response_serializer=mcp__pb2.ListResourceTemplatesResponse.SerializeToString, + ), + 'ListPrompts': grpc.unary_unary_rpc_method_handler( + servicer.ListPrompts, + request_deserializer=mcp__pb2.ListPromptsRequest.FromString, + response_serializer=mcp__pb2.ListPromptsResponse.SerializeToString, + ), + 'GetPrompt': grpc.unary_unary_rpc_method_handler( + servicer.GetPrompt, + request_deserializer=mcp__pb2.GetPromptRequest.FromString, + response_serializer=mcp__pb2.GetPromptResponse.SerializeToString, + ), + 'ListTools': grpc.unary_unary_rpc_method_handler( + servicer.ListTools, + request_deserializer=mcp__pb2.ListToolsRequest.FromString, + response_serializer=mcp__pb2.ListToolsResponse.SerializeToString, + ), + 'CallTool': grpc.unary_stream_rpc_method_handler( + servicer.CallTool, + request_deserializer=mcp__pb2.CallToolRequest.FromString, + response_serializer=mcp__pb2.CallToolResponse.SerializeToString, + ), + 'Complete': grpc.unary_unary_rpc_method_handler( + servicer.Complete, + request_deserializer=mcp__pb2.CompletionRequest.FromString, + response_serializer=mcp__pb2.CompletionResponse.SerializeToString, + ), + 'CancelTask': grpc.unary_unary_rpc_method_handler( + servicer.CancelTask, + request_deserializer=mcp__pb2.CancelTaskRequest.FromString, + response_serializer=mcp__pb2.CancelTaskResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'model_context_protocol.Mcp', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('model_context_protocol.Mcp', rpc_method_handlers) + + + # This class is part of an EXPERIMENTAL API. +class Mcp(object): + """ + Resources + + """ + + @staticmethod + def ListResources(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/model_context_protocol.Mcp/ListResources', + mcp__pb2.ListResourcesRequest.SerializeToString, + mcp__pb2.ListResourcesResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ReadResource(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/model_context_protocol.Mcp/ReadResource', + mcp__pb2.ReadResourceRequest.SerializeToString, + mcp__pb2.ReadResourceResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ListResourceTemplates(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/model_context_protocol.Mcp/ListResourceTemplates', + mcp__pb2.ListResourceTemplatesRequest.SerializeToString, + mcp__pb2.ListResourceTemplatesResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ListPrompts(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/model_context_protocol.Mcp/ListPrompts', + mcp__pb2.ListPromptsRequest.SerializeToString, + mcp__pb2.ListPromptsResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetPrompt(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/model_context_protocol.Mcp/GetPrompt', + mcp__pb2.GetPromptRequest.SerializeToString, + mcp__pb2.GetPromptResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ListTools(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/model_context_protocol.Mcp/ListTools', + mcp__pb2.ListToolsRequest.SerializeToString, + mcp__pb2.ListToolsResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def CallTool(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_stream( + request, + target, + '/model_context_protocol.Mcp/CallTool', + mcp__pb2.CallToolRequest.SerializeToString, + mcp__pb2.CallToolResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def Complete(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/model_context_protocol.Mcp/Complete', + mcp__pb2.CompletionRequest.SerializeToString, + mcp__pb2.CompletionResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def CancelTask(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/model_context_protocol.Mcp/CancelTask', + mcp__pb2.CancelTaskRequest.SerializeToString, + mcp__pb2.CancelTaskResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/examples/grpc/run_client.sh b/examples/grpc/run_client.sh new file mode 100755 index 0000000000..1c464ad824 --- /dev/null +++ b/examples/grpc/run_client.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -e +cd "$(dirname "$0")/../.." + +# Ensure .agent_venv exists or use it +if [ ! -d ".agent_venv" ]; then + echo "Please run ./examples/grpc/run_server.sh first to set up the environment." + exit 1 +fi + +source .agent_venv/bin/activate + +echo "Running gRPC Client..." +PYTHONPATH=src:. python examples/grpc/client.py diff --git a/examples/grpc/run_server.sh b/examples/grpc/run_server.sh new file mode 100755 index 0000000000..7fafcf4a9b --- /dev/null +++ b/examples/grpc/run_server.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -e +cd "$(dirname "$0")/../.." + +# Ensure .agent_venv exists +if [ ! -d ".agent_venv" ]; then + echo "Creating virtual environment..." + python3 -m venv .agent_venv + source .agent_venv/bin/activate + pip install . + pip install grpcio-tools anyio pydantic starlette sse-starlette httpx click pydantic-settings jsonschema httpx-sse +else + source .agent_venv/bin/activate + # Ensure dependencies are present + pip install -q grpcio-tools anyio pydantic starlette sse-starlette httpx click pydantic-settings jsonschema httpx-sse +fi + +# Regenerate protos (optional but good practice) +python -m grpc_tools.protoc -I examples/grpc --python_out=examples/grpc --grpc_python_out=examples/grpc examples/grpc/mcp.proto + +echo "Starting gRPC Server on 0.0.0.0:50051..." +PYTHONPATH=src:. python examples/grpc/server.py diff --git a/examples/grpc/server.py b/examples/grpc/server.py new file mode 100644 index 0000000000..3834991264 --- /dev/null +++ b/examples/grpc/server.py @@ -0,0 +1,51 @@ + +""" +This is an example of how a USER would write a FastMCP server and expose it over gRPC +using the pre-built 'GrpcMcpService' adapter components. +""" +import logging +import anyio +import grpc +from mcp.server.fastmcp import FastMCP +from examples.grpc.grpc_server import GrpcMcpService +import examples.grpc.mcp_pb2_grpc as mcp_pb2_grpc + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("user_server") + +# 1. Define your FastMCP Server (Business Logic) +mcp = FastMCP("my-fastmcp-app") + +@mcp.tool() +def add(a: int, b: int) -> int: + """Adds two numbers""" + return a + b + +@mcp.resource("file:///example.txt") +def get_example() -> str: + return "This is an example resource" + +# 2. Infrastructure Setup (Boilerplate to run over gRPC) +async def serve(): + # Wrap the internal Low Level Server with the gRPC Adapter + # Note: FastMCP wraps the low-level server in ._mcp_server + service = GrpcMcpService(mcp._mcp_server) + + # Standard gRPC Server setup + server = grpc.aio.server() + mcp_pb2_grpc.add_McpServicer_to_server(service, server) + server.add_insecure_port('0.0.0.0:50051') + + # Start the MCP background loop + await service.start_background_loop() + + logger.info("Starting gRPC server on 0.0.0.0:50051") + await server.start() + await server.wait_for_termination() + +if __name__ == '__main__': + try: + anyio.run(serve) + except KeyboardInterrupt: + pass diff --git a/src/mcp/server/fastmcp/__init__.py b/src/mcp/server/fastmcp/__init__.py index a89902cfd7..6fad24980b 100644 --- a/src/mcp/server/fastmcp/__init__.py +++ b/src/mcp/server/fastmcp/__init__.py @@ -7,5 +7,8 @@ from .server import Context, FastMCP from .utilities.types import Audio, Image -__version__ = version("mcp") +try: + __version__ = version("mcp") +except Exception: + __version__ = "0.0.0" __all__ = ["FastMCP", "Context", "Image", "Audio", "Icon"]