-
Notifications
You must be signed in to change notification settings - Fork 27
Open
Description
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:
- Buffering: Standard
ncor shell pipes can buffer input/output, breaking the strict line-based JSON-RPC expectation of the CogServer. - Protocol Violation: The CogServer sends a non-compliant
{"jsonrpc":"2.0","result":{}}response to theinitializednotification (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.bufferand flushessys.stdout.bufferimmediately 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
nctests and the Python script. - Functionality: Successfully executed
atomese/versionandatomese/echotools. - 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
Labels
No labels