Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from bundle
Operational SendPulse skill with live-verified REST, MCP handshake, and MCP tool-call tests.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/mcp_handshake_test.py
1#!/usr/bin/env python32import argparse3import json4import sys5import urllib.request67DEFAULT_URL = "https://mcp.sendpulse.com/mcp"8DEFAULT_PROTOCOL_VERSION = "2025-03-26"91011def post_json(url: str, payload: dict, headers: dict):12data = json.dumps(payload).encode("utf-8")13req = urllib.request.Request(url, data=data, method="POST")14req.add_header("Content-Type", "application/json")15req.add_header("Accept", "application/json, text/event-stream")16for k, v in headers.items():17req.add_header(k, v)18with urllib.request.urlopen(req, timeout=30) as resp:19raw = resp.read().decode("utf-8")20return resp.status, dict(resp.headers.items()), raw212223def main():24parser = argparse.ArgumentParser(description="Test SendPulse MCP auth and basic protocol flow.")25parser.add_argument("--sp-id", help="SendPulse MCP/OpenAI ID header value")26parser.add_argument("--sp-secret", help="SendPulse MCP/OpenAI Secret header value")27parser.add_argument("--url", default=DEFAULT_URL, help="MCP URL")28parser.add_argument("--protocol-version", default=DEFAULT_PROTOCOL_VERSION, help="MCP protocol version")29parser.add_argument("--full-tools", action="store_true", help="Print full tools payload instead of summary")30args = parser.parse_args()3132sp_id = args.sp_id33sp_secret = args.sp_secret34if not sp_id or not sp_secret:35print("Missing --sp-id or --sp-secret", file=sys.stderr)36sys.exit(2)3738common_headers = {39"X-SP-ID": sp_id,40"X-SP-SECRET": sp_secret,41}4243initialize_payload = {44"jsonrpc": "2.0",45"id": 1,46"method": "initialize",47"params": {48"protocolVersion": args.protocol_version,49"capabilities": {},50"clientInfo": {"name": "sendpulse-mcp-handshake-test", "version": "1.0"},51},52}53status, headers, body = post_json(args.url, initialize_payload, common_headers)54init_data = json.loads(body)55session_id = headers.get("Mcp-Session-Id") or headers.get("mcp-session-id")56if status != 200 or not session_id:57print(json.dumps({58"ok": False,59"step": "initialize",60"status": status,61"hasSessionId": bool(session_id),62"body": init_data,63}, indent=2))64sys.exit(1)6566notify_payload = {67"jsonrpc": "2.0",68"method": "notifications/initialized",69"params": {},70}71notify_headers = dict(common_headers)72notify_headers["Mcp-Session-Id"] = session_id73notify_status, _, notify_body = post_json(args.url, notify_payload, notify_headers)74try:75notify_data = json.loads(notify_body)76except Exception:77notify_data = {"raw": notify_body}7879tools_payload = {80"jsonrpc": "2.0",81"id": 2,82"method": "tools/list",83"params": {},84}85tools_status, _, tools_body = post_json(args.url, tools_payload, notify_headers)86tools_data = json.loads(tools_body)87tools = tools_data.get("result", {}).get("tools", [])8889result = {90"ok": True,91"url": args.url,92"initialize": {93"status": status,94"sessionId": session_id,95"serverInfo": init_data.get("result", {}).get("serverInfo", {}),96"protocolVersion": init_data.get("result", {}).get("protocolVersion"),97"capabilities": sorted((init_data.get("result", {}).get("capabilities") or {}).keys()),98},99"initializedNotification": {100"status": notify_status,101"body": notify_data,102},103"tools": {104"status": tools_status,105"count": len(tools),106"sample": [tool.get("name") for tool in tools[:10]],107},108}109110if args.full_tools:111result["tools"]["full"] = tools112113print(json.dumps(result, indent=2))114115116if __name__ == "__main__":117main()118