Skip to content

Java: preserve MCP fields in PermissionRequest extensionData #2273

Description

@jamesmontemagno

Summary

The Java SDK drops MCP permission request fields during deserialization, so applications cannot safely approve a scoped MCP request.

Root cause

java/src/main/java/com/github/copilot/rpc/PermissionRequest.java declares:

@JsonIgnoreProperties(ignoreUnknown = true)
public class PermissionRequest {
    @JsonProperty("kind")
    private String kind;
    // ...
    private Map<String, Object> extensionData;
}

There is no @JsonAnySetter (or equivalent custom deserialization) to populate extensionData. Consequently, MCP fields outside the typed properties are discarded.

Reproduction

With copilot-sdk-java 1.0.8 and 1.0.9-preview.3, an MCP permission callback receives kind=mcp, but getExtensionData() is null:

sessionConfig.setOnPermissionRequest(request -> {
    System.out.println(request.getKind());          // mcp
    System.out.println(request.getExtensionData()); // null
    return PermissionResult.deny();
});

The incoming MCP request includes the details needed for a scoped decision:

{
  "permissionRequest": {
    "kind": "mcp",
    "serverName": "playwright",
    "toolName": "playwright-browser_navigate",
    "args": { "url": "http://127.0.0.1:8106/docs/target-app/" }
  }
}

A handler that must enforce an exact server, tool, and URL therefore rejects every request. Approving all MCP requests is not an equivalent security boundary.

Expected behavior

Preserve unknown MCP fields in PermissionRequest.extensionData, including nested args, so permission handlers can enforce exact server/tool/argument allowlists.

Suggested test

Add a Java deserialization test that passes an MCP permission payload through PermissionRequest.fromJsonValue() and verifies extensionData retains serverName, toolName, and args.url.

Affected versions

  • copilot-sdk-java 1.0.8
  • copilot-sdk-java 1.0.9-preview.3

Originally filed in github/copilot-sdk-java, but this monorepo owns the affected Java source.

Activity

  1. github-actions commented on Aug 5, 2026

    @github-actions
    Contributor

    Investigation findings

    Confirmed bug — the reported behaviour reproduces exactly as described.

    What I looked at

    • java/src/main/java/com/github/copilot/rpc/PermissionRequest.java — the class that deserializes incoming permission-request payloads
    • java/src/test/java/com/github/copilot/DataObjectCoverageTest.java — the existing setter-coverage test

    Root cause

    PermissionRequest declares extensionData but had no @JsonAnySetter method. Jackson therefore honoured @JsonIgnoreProperties(ignoreUnknown = true) and silently discarded every field that wasn't one of the three typed properties (kind, toolCallId, managedApprovalRequired). MCP-specific fields — serverName, toolName, and args — were all lost at deserialization time, leaving getExtensionData() always null.

    Fix applied (commit 3b27cb3)

    1. Added @JsonAnySetter — a new setExtensionDataEntry(String key, Object value) method now captures every unknown property into extensionData, so serverName, toolName, and args are all preserved.
    2. Initialized extensionData to a LinkedHashMap at field level so the map is never null after deserialization.
    3. Added a regression test in DataObjectCoverageTest that round-trips a realistic MCP permission JSON payload and asserts all three MCP fields (including the nested args.url) are present in getExtensionData().

    Warning

    Firewall blocked 1 domain

    The following domain was blocked by the firewall during workflow execution:

    • repo.maven.apache.org

    To allow these domains, add them to the network.allowed list in your workflow frontmatter:

    network:
      allowed:
        - defaults
        - "repo.maven.apache.org"

    See Network Configuration for more information.

    Generated by Bug Handler for #2273 · sonnet46 48.8 AIC · ⌖ 5.16 AIC · ⊞ 5.9K · ◷

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions