Skip to content

[Bug] MCP tools silently fail to register when a tool inputSchema uses a nullable type union ("type": ["string","null"]) #325

Description

@marianfoo

Environment

  • OS: macOS 15 (Apple Silicon / aarch64)
  • Eclipse Version: 2026-06 (4.40.0, build 20260604-0652), JDK 21.0.8 (Temurin)
  • Plugin Version: 0.19.0.202606240339 (GitHub Copilot Language Server 1.502.5)

Describe the bug

Copilot for Eclipse silently fails to register all tools from an MCP server if any tool's inputSchema declares a property with a nullable JSON-Schema type union, i.e. "type": ["string", "null"].

The server connects normally and the Error Log even reports [CopilotMCP] Refreshed N tools for server <name> with the correct count, and [mcpGateway] registered mount … — but in Agent Mode → Configure Tools the server shows as a dead, unexpandable checkbox with no tools, and the agent cannot call any of them. Changing the single union "type": ["string","null"]"type": "string" makes all tools register and work.

This is really two bugs:

  1. Valid schema is rejected. Type-array unions like ["string","null"] are valid JSON Schema (all drafts) and are the exact nullable form [OpenAI's Structured Outputs guidance](https://platform.openai.com/docs/guides/structured-outputs) prescribes for optional fields. Other MCP clients (e.g., Claude Desktop/Code) accept them; Copilot-Eclipse's schema conversion does not.
  2. The failure is silent and server-wide. One offending property in one tool drops the entire server's tool list, with no error in the Error Log or MCP console — Refreshed N tools is logged as if it succeeded. This makes it nearly impossible to diagnose (it took ~12 iterations to bisect).

To Reproduce

  1. Save this minimal MCP server as repro-mcp.js (Node ≥ 18, no dependencies):
// repro-mcp.js — minimal MCP stdio server, no dependencies.
const send = (m) => process.stdout.write(JSON.stringify(m) + "\n");

const TOOL = {
  name: "demo_tool",
  description: "Demo tool with one optional, nullable parameter.",
  inputSchema: {
    type: "object",
    properties: {
      required_arg: { type: "string", description: "A required argument." },
      // 🔴 TRIGGER — valid JSON-Schema / OpenAI nullable union.
      //    With this line present, NO tools from this server register (silently).
      optional_arg: { type: ["string", "null"], description: "Optional, nullable." },
      // ✅ Replace the line above with:
      //    optional_arg: { type: "string", description: "Optional, nullable." }
      //    …and the tool registers and works.
    },
    required: ["required_arg"],
  },
};

let buf = "";
process.stdin.on("data", (chunk) => {
  buf += chunk;
  let nl;
  while ((nl = buf.indexOf("\n")) >= 0) {
    const line = buf.slice(0, nl); buf = buf.slice(nl + 1);
    if (!line.trim()) continue;
    let msg; try { msg = JSON.parse(line); } catch { continue; }
    if (msg.method === "initialize") {
      send({ jsonrpc: "2.0", id: msg.id, result: {
        protocolVersion: "2024-11-05",
        capabilities: { tools: {} },
        serverInfo: { name: "nullable-repro", version: "1.0.0" },
      }});
    } else if (msg.method === "tools/list") {
      send({ jsonrpc: "2.0", id: msg.id, result: { tools: [TOOL] } });
    } else if (msg.method === "ping") {
      send({ jsonrpc: "2.0", id: msg.id, result: {} });
    } else if (msg.id !== undefined) {
      send({ jsonrpc: "2.0", id: msg.id, error: { code: -32601, message: "Method not found" } });
    }
    // notifications (no id, e.g. notifications/initialized) are ignored
  }
});
  1. GitHub Copilot icon → Edit Preferences → Model Context Protocol (MCP) → set:
{
  "servers": {
    "nullable-repro": {
      "type": "stdio",
      "command": "node",
      "args": ["/absolute/path/to/repro-mcp.js"]
    }
  }
}
  1. Apply and Close, restart Eclipse. Open Agent Mode → Configure Tools: the nullable-repro server appears but its tool does not (dead checkbox, can't expand). The Error Log shows [CopilotMCP] Refreshed 1 tools for server nullable-reprono error.
  2. Edit repro-mcp.js: switch optional_arg to { type: "string", … } (the ✅ line). Restart Eclipse.
  3. The tool now registers and is callable. ✅

Expected behavior

Copilot for Eclipse should accept tool input schemas that use nullable type unions ("type": ["string","null"]) — they are valid JSON Schema and the OpenAI-recommended nullable form, and are accepted by other MCP clients. At minimum, if a schema genuinely cannot be converted, Copilot should (a) surface a clear error naming the offending tool/property, and (b) still register the other valid tools — rather than silently dropping the entire server while logging Refreshed N tools.

Screenshots

Configure Tools panel: the MCP server present but its tools missing (dead checkbox).

Image

Error Log: [CopilotMCP] Refreshed N tools for server … with the empty picker.

Image

Additional context

  • Confirmed with the minimal repro-mcp.js above (standalone, no other software): the nullable-repro server registers 0 tools with the union present, and registers/works once it's removed.
  • Originally found while debugging a real MCP server (an SAP/ABAP tool server). All tools loaded in read-only mode but vanished once a write tool with optional, nullable fields was added; bisected to the "type": ["string","null"] unions emitted for optional properties (added for OpenAI strict-mode compatibility).
  • A/B confirmed: identical server, nullable unions on → tools don't register; off → tools register and work; toggling reproduces both ways every time.
  • Not a size/depth/tool-count issue — verified: a tiny schema with the nullable union still fails, and a large schema (~86 KB, 12 tools) without it works.
  • Affected build: GitHub Copilot Language Server 1.502.5. Likely in the MCP-tool → model-function-calling schema conversion.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions