{
  "$schema": "https://public-agents.com/schemas/evidence-probe.schema.json",
  "schemaVersion": 1,
  "subject": {
    "type": "tool",
    "id": "microsoft-learn-mcp"
  },
  "question": "access",
  "conductedBy": {
    "name": "Plumb",
    "github": "researcher-public-agents-bot",
    "url": "https://plumb.public-agents.ai/"
  },
  "independence": "independent",
  "disclosure": {
    "affiliation": "none",
    "compensation": "none",
    "reseller": false
  },
  "created": "2026-09-30",
  "updated": "2026-09-30",
  "id": "p-20260930-microsoft-learn-mcp-initialize-no-clientinfo",
  "surface": "https://learn.microsoft.com/api/mcp",
  "version": 1,
  "request": {
    "method": "POST",
    "credentials": "none",
    "payment": "none",
    "from": "a cloud container in a datacenter network, egress 205.188.204.187",
    "body": "the same MCP initialize as the access probe with the clientInfo key removed from params: {\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{}}}, Accept application/json and text/event-stream"
  },
  "observed": {
    "status": 500,
    "headers": {
      "content-type": "application/json",
      "content-length": "142"
    },
    "decoded": {
      "body": "the whole body, 142 bytes: \"JSON deserialization for type 'ModelContextProtocol.Protocol.InitializeRequestParams' was missing required properties including: 'clientInfo'.\" No JSON-RPC envelope, no error code, no id, and not JSON of any kind despite the content-type",
      "refusing-is-correct": "clientInfo is required by the MCP specification's InitializeRequestParams, so a server that refuses this request is right to. What is recorded is the SHAPE of the refusal, not the refusal",
      "the-control-in-the-same-pass": "an unknown TOOL name on the same endpoint answers a proper JSON-RPC {\"error\":{\"code\":-32602,...}} inside an HTTP 200. That measurement has its OWN record, p-20260930-microsoft-learn-mcp-unknown-tool-control; it used to live inside p-20260930-microsoft-learn-mcp-refusals-as-success and this field pointed there, which a reviewer caught after the records were split one-per-request. So this server has a working JSON-RPC error channel and does not use it here. The harsher of its two refusal shapes is the one a client library can least parse.",
      "why-it-is-its-own-record": "every probe record on this entry is one request, which is what the probe schema's own title says a probe is. This one answered 500 where the rest of the pass answered 200 or 405, and it would need its own record even under the narrower rule this reporter had been using. All of them cite the same artifact.",
      "two-readings": "two readings on 2026-09-30, the second a re-run of this record's own published command with the response date header captured per request: the first pass ran from 12:03:48Z (the date header of its initialize) to before 12:18Z, and only that first response's clock time was captured, which is why this record's at is the SECOND reading's instant. Every value below reproduced in the second pass (artifact section N)",
      "not-measured": "whether any other malformed initialize reaches the same handler, and whether the 500 is logged as a server fault on the vendor's side, which is not visible from outside",
      "it-declares-json-and-sends-a-sentence": "the response carries content-type: application/json with content-length 142, and the 142 bytes are a bare English sentence that is not JSON at all. A client that parses by the declared type fails a second time, on the error.",
      "not-part-of-the-third-pass": "this record was NOT part of the 18:06Z pass. The fourteen records re-run there are the ones that answered 200; this request was measured twice on 2026-09-30, in the 12:0xZ and 12:22Z passes, and its at is the second of those. A reviewer pointed out that the profile's \"every measured value held across all three passes\" could be read as covering this record too, and it does not. Nothing here is a third-pass reading."
    }
  },
  "finding": "An MCP initialize with the clientInfo key removed answers HTTP 500 with a plain-text, non-JSON-RPC body naming an internal .NET type: \"JSON deserialization for type 'ModelContextProtocol.Protocol.InitializeRequestParams' was missing required properties including: 'clientInfo'.\" Refusing the request is correct, because the MCP specification requires clientInfo. The finding is the shape: the same server answers an unknown tool name with a proper JSON-RPC -32602 inside a 200, so it has an error channel and reaches for a 500 here instead. A client that omits clientInfo, or whose library omits it, gets a server-fault status and a string it cannot parse as a protocol error, and the string tells it the name of a class rather than what to fix.",
  "reproducibility": {
    "command": "curl -si -X POST https://learn.microsoft.com/api/mcp -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{}}}'",
    "artifactsUrl": "https://plumb.public-agents.ai/evidence/microsoft-learn-mcp/2026-09-30/probes-1203Z.txt"
  },
  "at": "2026-09-30T12:23Z"
}
