Skip to content

Commit ad81e50

Browse files
Handle JSON-RPC notifications and responses
Detect JSON-RPC notifications (no id or methods starting with "notifications/") and avoid sending responses for them per JSON-RPC 2.0 and MCP spec. Add handling for notifications/cancelled and resources/list, and treat unknown notification methods as no-ops. Build spec-compliant responses that include either "result" or "error" (never both) and serialize while ignoring nulls. Add debug logging for notification detection and response sending.
1 parent 79e3f8b commit ad81e50

File tree

1 file changed

+45
-17
lines changed

1 file changed

+45
-17
lines changed

Testimize.MCP.Server/Program.cs

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,20 @@
8585
var method = request.GetProperty("method").GetString();
8686
var id = request.TryGetProperty("id", out var idProp) ? idProp : (JsonElement?)null;
8787
var paramsElement = request.TryGetProperty("params", out var paramsProp) ? paramsProp : (JsonElement?)null;
88-
89-
await Console.Error.WriteLineAsync($"[DEBUG] Method: {method}, ID: {id}");
90-
88+
89+
// JSON-RPC 2.0 §4.1: a Notification is a Request object without
90+
// an "id" member. The Server MUST NOT reply to a Notification.
91+
// Also: any method name starting with "notifications/" is a
92+
// notification per the MCP spec (e.g. "notifications/initialized"),
93+
// even if some clients buggily include an id. Detect both.
94+
var isNotification = id is null
95+
|| (method?.StartsWith("notifications/", StringComparison.Ordinal) == true);
96+
97+
await Console.Error.WriteLineAsync($"[DEBUG] Method: {method}, ID: {id}, Notification: {isNotification}");
98+
9199
object? result = null;
92100
object? error = null;
93-
101+
94102
try
95103
{
96104
// Handle MCP methods
@@ -99,29 +107,49 @@
99107
"initialize" => mcpHandler.Initialize(paramsElement?.Deserialize<object>()),
100108
"tools/list" => mcpHandler.ToolsList(),
101109
"tools/call" => mcpHandler.ToolsCall(paramsElement ?? new JsonElement()),
102-
"notifications/initialized" => new { }, // Handle initialization notification
103-
"prompts/list" => new { prompts = new object[] { } }, // Empty prompts list
110+
"notifications/initialized" => null, // no response for notifications
111+
"notifications/cancelled" => null,
112+
"prompts/list" => new { prompts = new object[] { } },
113+
"resources/list" => new { resources = new object[] { } },
114+
_ when isNotification => null, // any other notification: no-op
104115
_ => throw new Exception($"Unknown method: {method}")
105116
};
106-
117+
107118
await Console.Error.WriteLineAsync($"[DEBUG] Method executed successfully");
108119
}
109120
catch (Exception ex)
110121
{
111122
error = new { code = -32000, message = ex.Message };
112123
await Console.Error.WriteLineAsync($"[DEBUG] Method execution failed: {ex.Message}");
113124
}
114-
115-
// Send response
116-
var response = new
125+
126+
// Notifications never get a response (spec requirement).
127+
if (isNotification)
117128
{
118-
jsonrpc = "2.0",
119-
id = id?.Deserialize<object>(),
120-
result = error == null ? result : null,
121-
error = error
122-
};
123-
124-
var responseJson = JsonSerializer.Serialize(response);
129+
await Console.Error.WriteLineAsync("[DEBUG] Notification — not sending a response");
130+
continue;
131+
}
132+
133+
// Build a spec-compliant JSON-RPC 2.0 response: MUST contain
134+
// either "result" OR "error", never both.
135+
object response = error == null
136+
? new
137+
{
138+
jsonrpc = "2.0",
139+
id = id?.Deserialize<object>(),
140+
result = result
141+
}
142+
: new
143+
{
144+
jsonrpc = "2.0",
145+
id = id?.Deserialize<object>(),
146+
error = error
147+
};
148+
149+
var responseJson = JsonSerializer.Serialize(response, new JsonSerializerOptions
150+
{
151+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
152+
});
125153
await Console.Error.WriteLineAsync($"[DEBUG] Sending response: {responseJson}");
126154
await writer.WriteLineAsync(responseJson);
127155
}

0 commit comments

Comments
 (0)