Skip to content

Reasons for Google Antigravity MCP call failure and temporary solutions #65

@oy3o

Description

@oy3o

Problem

The initial configuration using nc to bridge the MCP stdio transport to the CogServer's TCP port (18888) failed with invalid request errors.
This was caused by two issues:

  1. Buffering: Standard nc or shell pipes can buffer input/output, breaking the strict line-based JSON-RPC expectation of the CogServer.
  2. Protocol Violation: The CogServer sends a non-compliant {"jsonrpc":"2.0","result":{}} response to the initialized notification (which should not have a response). This caused strict MCP clients to reject the connection.

Temporary Solution

We created a custom Python adapter script to replace nc.

stdio_tcp_client.py
#!/usr/bin/env python3
import socket
import sys
import threading
import argparse
import time

BUFFER_SIZE = 65536
# LOG_FILE = "/tmp/mcp_debug.log"

# def log(msg):
#     with open(LOG_FILE, "a") as f:
#         timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
#         f.write(f"[{timestamp}] {msg}\n")


def stdin_to_socket(sock):
    """Reads from stdin and writes to the socket."""
    # log("Starting stdin_to_socket thread")
    try:
        while True:
            # Read from stdin
            try:
                # Use read1 to get available bytes immediately
                chunk = sys.stdin.buffer.read1(4096)
            except AttributeError:
                chunk = sys.stdin.buffer.read(4096)

            if not chunk:
                # log("Stdin EOF received")
                break

            # log(f"Stdin -> Socket ({len(chunk)} bytes): {chunk}")
            try:
                sock.sendall(chunk)
            except Exception as e:
                # log(f"Socket send error: {e}")
                break

    except Exception as e:
        pass  # log(f"Stdin loop error: {e}")
    finally:
        # log("Shutting down socket WR")
        try:
            sock.shutdown(socket.SHUT_WR)
        except:
            pass


def socket_to_stdout(sock):
    """Reads from the socket and writes to stdout, filtering bad protocol messages."""
    # log("Starting socket_to_stdout thread")
    buffer = b""
    try:
        while True:
            data = sock.recv(BUFFER_SIZE)
            if not data:
                # log("Socket EOF received")
                break

            # log(f"Socket -> Buffer ({len(data)} bytes): {data}")
            buffer += data

            while b"\n" in buffer:
                line_end = buffer.find(b"\n") + 1
                line = buffer[:line_end]
                buffer = buffer[line_end:]

                # Check for the specific malformed response to 'notifications/initialized'
                # CogServer sends: {"jsonrpc":"2.0","result":{}}
                # This response has no 'id', which violates JSON-RPC 2.0 for a Response object,
                # and it shouldn't exist because 'initialized' is a Notification.
                if b'"result":{}' in line and b'"id"' not in line:
                    # log(f"DROPPING MALFORMED LINE: {line}")
                    continue

                # log(f"Forwarding Line: {line}")
                sys.stdout.buffer.write(line)
                sys.stdout.buffer.flush()

    except Exception as e:
        sys.stderr.write(f"Socket loop error: {e}\n")


def main():
    parser = argparse.ArgumentParser(description="Bridge Stdio to TCP for MCP")
    parser.add_argument("host", help="Hostname", nargs="?", default="localhost")
    parser.add_argument("port", help="Port", type=int, nargs="?", default=18888)
    args = parser.parse_args()

    # log(f"=== Starting Session connecting to {args.host}:{args.port} ===")

    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((args.host, args.port))
        # log("Socket connected successfully")
    except Exception as e:
        # log(f"Failed to connect: {e}")
        sys.stderr.write(f"Failed to connect: {e}\n")
        sys.exit(1)

    t = threading.Thread(target=socket_to_stdout, args=(sock,), daemon=True)
    t.start()

    stdin_to_socket(sock)

    t.join(timeout=2.0)
    # log("=== Session Ended ===")


if __name__ == "__main__":
    main()

Key Features:

  • Unbuffered I/O: Reads directly from sys.stdin.buffer and flushes sys.stdout.buffer immediately after every message.
  • Protocol Filtering: Intercepts and drops the malformed {"jsonrpc":"2.0","result":{}} message from CogServer, preventing client errors.
  • Robustness: Handles socket closures and thread synchronization cleanly.

Configuration

The mcp_config.json was updated to use this adapter:

{
  "mcpServers": {
    "atomese": {
      "command": "path/to/cogserver/examples/mcp/stdio_tcp_client.py",
      "args": [
        "localhost",
        "18888"
      ],
      "env": {}
    }
  }
}

Verification

  • Connectivity: Verified via manual nc tests and the Python script.
  • Functionality: Successfully executed atomese/version and atomese/echo tools.
  • Result:
    • CogServer Version: 5.2.0
    • Echo Test: Passed

While temporary solutions exist, modifying the server to satisfy more demanding clients might be a better option.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions