Notifications
Add notification support and message type detection.
What is a Notification?
A notification is a JSON-RPC message that doesn't expect a response. The key difference from a request is the absence of an ID field:
// Request (has ID, expects response)
{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}
// Notification (no ID, no response expected)
{"jsonrpc": "2.0", "method": "notifications/initialized"}
MCP uses notifications for events like notifications/initialized
(client is ready), notifications/cancelled (cancel a request),
and notifications/progress (progress updates).
Edit: jsonrpc.py
Step 1: Add Notification Creator
Add this function after create_error_response:
def create_notification(method: str, params: dict | None = None) -> dict:
"""
Create a JSON-RPC notification (no ID, no response expected).
Args:
method: The notification method name
params: Optional parameters
Returns:
A properly formatted JSON-RPC notification
"""
notification: dict[str, Any] = {
"jsonrpc": "2.0",
"method": method
}
if params is not None:
notification["params"] = params
return notification
Step 2: Add Message Type Detection
Add these helper functions after create_notification:
def is_request(message: dict) -> bool:
"""Check if a message is a JSON-RPC request (has method AND id)."""
return (
message.get("jsonrpc") == "2.0"
and "method" in message
and "id" in message
)
def is_notification(message: dict) -> bool:
"""Check if a message is a notification (has method, NO id)."""
return (
message.get("jsonrpc") == "2.0"
and "method" in message
and "id" not in message
)
def is_response(message: dict) -> bool:
"""Check if a message is a response (has id and result/error, NO method)."""
return (
message.get("jsonrpc") == "2.0"
and "id" in message
and ("result" in message or "error" in message)
and "method" not in message
)
def get_message_type(message: dict) -> str:
"""
Determine the type of a JSON-RPC message.
Returns:
One of: "request", "notification", "response", or "invalid"
"""
if is_request(message):
return "request"
elif is_notification(message):
return "notification"
elif is_response(message):
return "response"
else:
return "invalid"
Step 3: Update the Test Block
Replace your test block with this expanded version:
# =============================================================================
# Try it out
# =============================================================================
if __name__ == "__main__":
print("=" * 60)
print("JSON-RPC Message Types Demo")
print("=" * 60)
# Create different message types
request = create_request("tools/list", request_id=1)
notification = create_notification("notifications/initialized")
success = create_success_response(1, {"tools": []})
error = create_error_response(1, JsonRpcErrorCode.METHOD_NOT_FOUND)
# Demonstrate each type
messages = [
("Request", request),
("Notification", notification),
("Success Response", success),
("Error Response", error),
]
for name, msg in messages:
print(f"\n{name}:")
print(json.dumps(msg, indent=2))
print(f" -> Type: {get_message_type(msg)}")
# Show the detection logic
print("\n" + "=" * 60)
print("Message Type Detection")
print("=" * 60)
test_cases = [
{"jsonrpc": "2.0", "id": 1, "method": "test"}, # request
{"jsonrpc": "2.0", "method": "test"}, # notification
{"jsonrpc": "2.0", "id": 1, "result": {}}, # response
{"jsonrpc": "2.0", "id": 1, "error": {"code": -1}}, # error response
{"method": "test"}, # invalid (no jsonrpc)
]
for msg in test_cases:
print(f"\n{json.dumps(msg)}")
print(f" -> is_request: {is_request(msg)}")
print(f" -> is_notification: {is_notification(msg)}")
print(f" -> is_response: {is_response(msg)}")
print(f" -> type: {get_message_type(msg)}")
Try It
python jsonrpc.py
Expected Output
============================================================
JSON-RPC Message Types Demo
============================================================
Request:
{
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}
-> Type: request
Notification:
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
-> Type: notification
Success Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": []
}
}
-> Type: response
Error Response:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32601,
"message": "Method not found"
}
}
-> Type: response
============================================================
Message Type Detection
============================================================
{"jsonrpc": "2.0", "id": 1, "method": "test"}
-> is_request: True
-> is_notification: False
-> is_response: False
-> type: request
{"jsonrpc": "2.0", "method": "test"}
-> is_request: False
-> is_notification: True
-> is_response: False
-> type: notification
...
Quick Reference: Message Types
| Type | Has ID? | Has Method? | Response? |
|---|---|---|---|
| Request | Yes | Yes | Yes (wait for it) |
| Notification | No | Yes | No (fire and forget) |
| Response | Yes | No | Is the response |
When your server receives a notification, process it silently. Never send a response — even if processing fails. The sender isn't waiting for one, and sending one could confuse clients.
Your File So Far
Your jsonrpc.py now includes:
create_request()— Make requestscreate_success_response()— Make success responsescreate_error_response()— Make error responsescreate_notification()— Make notificationsis_request(),is_notification(),is_response()— Detect message typesget_message_type()— Get message type as a string
What's Next
You have all the building blocks for JSON-RPC messages. In the final step, you'll create a router — a class that dispatches incoming messages to the right handler functions. This is the foundation of any MCP server.