Skip to content

MCP elicitation has wrong case in Form and URL keys breaking some MCP servers #1574

@awschmeder

Description

@awschmeder

cagent implements the MCP elicitation but sends keys with the wrong case, breaking some servers - notably OpenMetaData MCP server (Java).

Output from cagent 1.20.3:

{
  "capabilities": {
    "elicitation": {
      "Form": {},
      "URL": {}
    }
  }
}

Expected output per https://modelcontextprotocol.io/specification/draft/client/elicitation

{
  "capabilities": {
    "elicitation": {
      "form": {},
      "url": {}
    }
  }
}

Version affected

1.20.3

How To Reproduce

Intercept output of cagent with this script, which detects and fixes the incorrect "Form" and "URL" keys, solving the issue.

import httpx
import json
import uvicorn
from fastapi import FastAPI, Request, Response
from fastapi.responses import JSONResponse

app = FastAPI()
TARGET_URL = "https://openmetadata.example.com/mcp"

@app.api_route("/mcp", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"])
async def proxy_mcp(request: Request):
    method = request.method
    headers = dict(request.headers)
    headers.pop("host", None)
    headers.pop("content-length", None)

    # 1. Safely get the body (handle empty/non-JSON)
    raw_body = await request.body()
    body = None
    if raw_body:
        try:
            body = json.loads(raw_body)
        except json.JSONDecodeError:
            print(f"[DEBUG] Received non-JSON {method} request. Forwarding raw.")

    # 2. Fix Case-Sensitivity in Cagent Request (if it's a JSON-RPC call)
    if body and "params" in body and "capabilities" in body["params"]:
        caps = body["params"]["capabilities"]
        if "elicitation" in caps:
            print("Found Elicitation Capabilities: ", caps["elicitation"])
            e = caps["elicitation"]
            caps["elicitation"] = {
                "form": e.get("Form") if "Form" in e else e.get("form"),
                "url": e.get("URL") if "URL" in e else e.get("url")
            }
            print("[!!!] FIXED: Request 'Form/URL' -> lowercase")

    # 3. Forward to Server
    async with httpx.AsyncClient() as client:
        try:
            proxy_resp = await client.request(
                method, 
                TARGET_URL, 
                content=json.dumps(body) if body else raw_body, 
                headers=headers, 
                timeout=30.0
            )
            
            # 4. Handle Empty Server Responses (HTTP 204)
            if proxy_resp.status_code == 204 or not proxy_resp.content:
                print(f"[DEBUG] Server returned empty {proxy_resp.status_code}. Forwarding as-is.")
                return Response(status_code=proxy_resp.status_code)

            # 5. Fix Server Response for Cagent
            try:
                resp_data = proxy_resp.json()
                if "result" in resp_data and "capabilities" in resp_data["result"]:
                    # Strip 'elicitation' so cagent's strict Java parser doesn't die
                    if "elicitation" in resp_data["result"]["capabilities"]:
                        print("[!!!] FIXED: Stripping 'elicitation' from response")
                        del resp_data["result"]["capabilities"]["elicitation"]
                    # Downgrade version string for compatibility
                    resp_data["result"]["protocolVersion"] = "2024-11-05"
                
                return JSONResponse(content=resp_data, status_code=proxy_resp.status_code)
            except:
                # If server response isn't JSON, just pass it through
                return Response(content=proxy_resp.content, status_code=proxy_resp.status_code)

        except Exception as e:
            print(f"[ERROR] Proxy error: {e}")
            return Response(content=str(e), status_code=500)

if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8000)


Simple cagent definition with tool:

agents:
  root:
    model: openai/gpt-5
    description: OpenMetaData Search
    instruction: |
      Use the OpenMetaData tool to search for data sets in accordance with the user instructions.
    toolsets:
      - type: mcp
        remote:
          url: http://127.0.0.1:8000/mcp
          transport_type: "streamable"
          headers:
            Authorization: "Bearer ${env.OMD_API_KEY}"
            Accept: "application/json,text/event-stream"

Results on bug-fix proxy

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Found Elicitation Capabilities:  {'Form': None, 'URL': None}
[!!!] FIXED: Request 'Form/URL' -> lowercase

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions