feat: local MCP harness (tools/resources/prompts) + Harness UI page
Streamable-HTTP MCP server on POST /mcp: device.state, attack.deploy/stop/ status/deauth/capture/export_hc22000, loot.handshakes/enterprise_creds, recon.aps/isearch/devices, pineap.kick_client/set_filter tools; recon DB + bundled opencode skills resources; attack playbook prompts. Cookie or Bearer auth. Harness page shows endpoint, token, curl snippet, capability explorer and a copy-paste pi.dev prompt. scripts/harness_stdio.py for stdio-only agents.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
"""stdio bridge to the Mark VIII MCP server.
|
||||
|
||||
Agents that only support stdio transport can run:
|
||||
|
||||
MCP_URL=http://172.16.52.1:8080/mcp \
|
||||
MCP_TOKEN=<device session token> \
|
||||
python3 scripts/harness_stdio.py
|
||||
|
||||
JSON-RPC messages are read line-by-line from stdin (one JSON object per
|
||||
line, no embedded newlines) and forwarded to the Mark VIII Streamable-HTTP
|
||||
MCP endpoint. Responses are printed back on stdout as single-line JSON.
|
||||
|
||||
Get a token from the Mark VIII Harness page, or fetch one:
|
||||
|
||||
curl -s -X POST http://172.16.52.1:8080/api/login \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"username":"root","password":"<device password>"}' \
|
||||
-c cookies.txt
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
URL = os.environ.get('MCP_URL', 'http://172.16.52.1:8080/mcp')
|
||||
TOKEN = os.environ.get('MCP_TOKEN', '')
|
||||
|
||||
|
||||
def forward(msg):
|
||||
data = json.dumps(msg).encode()
|
||||
req = urllib.request.Request(URL, data=data, method='POST')
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
req.add_header('Accept', 'application/json, text/event-stream')
|
||||
if TOKEN:
|
||||
req.add_header('Authorization', 'Bearer ' + TOKEN)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=120) as resp:
|
||||
return resp.read().decode()
|
||||
except urllib.error.HTTPError as exc:
|
||||
return json.dumps({'jsonrpc': '2.0', 'id': msg.get('id'),
|
||||
'error': {'code': exc.code, 'message': exc.read().decode()[:300]}})
|
||||
except Exception as exc: # noqa: BLE001
|
||||
return json.dumps({'jsonrpc': '2.0', 'id': msg.get('id'),
|
||||
'error': {'code': -32000, 'message': str(exc)}})
|
||||
|
||||
|
||||
def main():
|
||||
if not TOKEN:
|
||||
print('warning: MCP_TOKEN not set; server will reject calls', file=sys.stderr)
|
||||
for line in sys.stdin:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
msg = json.loads(line)
|
||||
except ValueError:
|
||||
print(json.dumps({'jsonrpc': '2.0', 'id': None,
|
||||
'error': {'code': -32700, 'message': 'parse error'}}))
|
||||
continue
|
||||
print(forward(msg), flush=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user