More MCP work
This commit is contained in:
@@ -1,124 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Inlines method bodies from a .cpp file into the corresponding .h file.
|
||||
|
||||
Scans the .cpp for method definitions matching "ClassName::MethodName(...)"
|
||||
and extracts the body (from opening '{' to closing '}'). Then reads the .h
|
||||
file and replaces matching declaration-only lines (ending with ';') with the
|
||||
declaration (minus ';') followed by the body.
|
||||
|
||||
Usage: python3 tools/inline-methods.py <header.h> <source.cpp>
|
||||
|
||||
Outputs the new header to stdout.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
|
||||
|
||||
def extract_bodies(cpp_lines):
|
||||
"""Extract method bodies from cpp file.
|
||||
Returns dict of (ClassName, MethodName) -> list of body lines (from '{' to '}')."""
|
||||
bodies = {}
|
||||
i = 0
|
||||
while i < len(cpp_lines):
|
||||
# Match lines like: void UMCPHandler_Foo::Handle(...)
|
||||
m = re.match(r'^\S.*?\s+(\w+)::(\w+)\s*\(', cpp_lines[i])
|
||||
if not m:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
class_name = m.group(1)
|
||||
method_name = m.group(2)
|
||||
|
||||
# Skip forward to the line containing '{'
|
||||
while i < len(cpp_lines) and '{' not in cpp_lines[i]:
|
||||
i += 1
|
||||
if i >= len(cpp_lines):
|
||||
break
|
||||
|
||||
# Collect from '{' to matching '}'
|
||||
brace_depth = 0
|
||||
body_lines = []
|
||||
while i < len(cpp_lines):
|
||||
line = cpp_lines[i]
|
||||
brace_depth += line.count('{') - line.count('}')
|
||||
body_lines.append(line)
|
||||
i += 1
|
||||
if brace_depth == 0:
|
||||
break
|
||||
|
||||
bodies[(class_name, method_name)] = body_lines
|
||||
|
||||
return bodies
|
||||
|
||||
|
||||
def inline_into_header(h_lines, bodies):
|
||||
"""Replace declaration-only methods in header with inlined bodies."""
|
||||
output = []
|
||||
for line in h_lines:
|
||||
# Match declaration lines like:
|
||||
# \tvirtual void Handle(const FJsonObject* Json, ...) override;
|
||||
# Capture: indent, everything before method name, method name, rest up to ';'
|
||||
m = re.match(r'^(\t+)(.*?\s+)(\w+)\s*(\(.*\)\s*(?:const\s*)?(?:override\s*)?);', line)
|
||||
if m:
|
||||
indent = m.group(1)
|
||||
prefix = m.group(2) # e.g. "virtual void "
|
||||
method_name = m.group(3)
|
||||
params = m.group(4) # e.g. "(const FJsonObject* Json, FJsonObject* Result) override"
|
||||
|
||||
# Find which class we're inside
|
||||
class_name = None
|
||||
for prev in reversed(output):
|
||||
cm = re.match(r'^class\s+(\w+)\s*', prev)
|
||||
if cm:
|
||||
class_name = cm.group(1)
|
||||
break
|
||||
|
||||
if class_name and (class_name, method_name) in bodies:
|
||||
body_lines = bodies[(class_name, method_name)]
|
||||
# Emit the declaration as a definition (replace ';' with body)
|
||||
output.append(f'{indent}{prefix}{method_name}{params}')
|
||||
for bline in body_lines:
|
||||
if bline.strip():
|
||||
output.append(indent + bline)
|
||||
else:
|
||||
output.append('')
|
||||
continue
|
||||
|
||||
output.append(line)
|
||||
return output
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print(f"Usage: {sys.argv[0]} <header.h> <source.cpp>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
h_path = sys.argv[1]
|
||||
cpp_path = sys.argv[2]
|
||||
|
||||
with open(cpp_path) as f:
|
||||
cpp_lines = [line.rstrip('\n') for line in f]
|
||||
|
||||
with open(h_path) as f:
|
||||
h_lines = [line.rstrip('\n') for line in f]
|
||||
|
||||
bodies = extract_bodies(cpp_lines)
|
||||
|
||||
if not bodies:
|
||||
print("No method bodies found in cpp file.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Found {len(bodies)} method(s) to inline:", file=sys.stderr)
|
||||
for (cls, method) in bodies:
|
||||
print(f" {cls}::{method}", file=sys.stderr)
|
||||
|
||||
result = inline_into_header(h_lines, bodies)
|
||||
|
||||
for line in result:
|
||||
print(line)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -68,7 +68,7 @@ def disconnect():
|
||||
|
||||
|
||||
def send_and_receive(message):
|
||||
"""Send a JSON message to the editor and return the response."""
|
||||
"""Send a JSON message to the editor and return the null-terminated response."""
|
||||
data = json.dumps(message) + "\n"
|
||||
sock.sendall(data.encode())
|
||||
|
||||
@@ -78,10 +78,10 @@ def send_and_receive(message):
|
||||
if not chunk:
|
||||
raise ConnectionError("Connection closed")
|
||||
result += chunk
|
||||
try:
|
||||
return json.loads(result)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if b"\0" in result:
|
||||
break
|
||||
|
||||
return result[:result.index(b"\0")].decode()
|
||||
|
||||
|
||||
def forward_to_editor(arguments):
|
||||
@@ -128,10 +128,8 @@ def handle_message(msg):
|
||||
params = msg.get("params", {})
|
||||
arguments = params.get("arguments", {})
|
||||
result = forward_to_editor(arguments)
|
||||
is_error = "error" in result
|
||||
return make_jsonrpc(msg_id, {
|
||||
"content": [{"type": "text", "text": json.dumps(result)}],
|
||||
**({"isError": True} if is_error else {}),
|
||||
"content": [{"type": "text", "text": result}],
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
66
tools/mcp-test.py
Executable file
66
tools/mcp-test.py
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Human-friendly MCP test client.
|
||||
|
||||
Usage: python3 tools/mcp-test.py command=show_commands
|
||||
python3 tools/mcp-test.py command=list_blueprint_assets filter=lx
|
||||
python3 tools/mcp-test.py command=show_commands verbose=true
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
import socket
|
||||
|
||||
HOST = "localhost"
|
||||
PORT = 9847
|
||||
TIMEOUT = 120
|
||||
|
||||
def main():
|
||||
msg = {}
|
||||
for arg in sys.argv[1:]:
|
||||
key, _, value = arg.partition("=")
|
||||
if value.lower() == "true":
|
||||
value = True
|
||||
elif value.lower() == "false":
|
||||
value = False
|
||||
else:
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
msg[key] = value
|
||||
|
||||
if not msg:
|
||||
print("Usage: python3 tools/mcp-test.py command=show_commands")
|
||||
sys.exit(1)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(TIMEOUT)
|
||||
try:
|
||||
sock.connect((HOST, PORT))
|
||||
except (ConnectionRefusedError, socket.timeout, OSError) as e:
|
||||
print(f"Cannot connect to {HOST}:{PORT} — is the editor running?")
|
||||
sys.exit(1)
|
||||
|
||||
sock.sendall((json.dumps(msg) + "\n").encode())
|
||||
|
||||
result = b""
|
||||
while True:
|
||||
chunk = sock.recv(65536)
|
||||
if not chunk:
|
||||
break
|
||||
result += chunk
|
||||
if b"\0" in result:
|
||||
break
|
||||
|
||||
sock.close()
|
||||
result = result[:result.index(b"\0")].decode() if b"\0" in result else result.decode()
|
||||
|
||||
try:
|
||||
parsed = json.loads(result)
|
||||
print(json.dumps(parsed, indent=2))
|
||||
except json.JSONDecodeError:
|
||||
print(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user