safehands-pharos 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/.env.example +64 -26
  2. package/README.md +444 -445
  3. package/dist/cli.d.ts +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +36 -8
  6. package/dist/cli.js.map +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +208 -147
  10. package/dist/index.js.map +1 -1
  11. package/dist/lib/auditLog.d.ts +9 -0
  12. package/dist/lib/auditLog.d.ts.map +1 -0
  13. package/dist/lib/auditLog.js +30 -0
  14. package/dist/lib/auditLog.js.map +1 -0
  15. package/dist/lib/constants.d.ts +12 -0
  16. package/dist/lib/constants.d.ts.map +1 -1
  17. package/dist/lib/constants.js +10 -0
  18. package/dist/lib/constants.js.map +1 -1
  19. package/dist/lib/dodoApi.d.ts +8 -0
  20. package/dist/lib/dodoApi.d.ts.map +1 -1
  21. package/dist/lib/dodoApi.js +22 -4
  22. package/dist/lib/dodoApi.js.map +1 -1
  23. package/dist/lib/envLoader.d.ts +2 -0
  24. package/dist/lib/envLoader.d.ts.map +1 -0
  25. package/dist/lib/envLoader.js +44 -0
  26. package/dist/lib/envLoader.js.map +1 -0
  27. package/dist/lib/pharosClient.d.ts.map +1 -1
  28. package/dist/lib/pharosClient.js +13 -3
  29. package/dist/lib/pharosClient.js.map +1 -1
  30. package/dist/lib/riskEngine.d.ts.map +1 -1
  31. package/dist/lib/riskEngine.js +50 -45
  32. package/dist/lib/riskEngine.js.map +1 -1
  33. package/dist/lib/signer/index.d.ts.map +1 -1
  34. package/dist/lib/signer/index.js +3 -4
  35. package/dist/lib/signer/index.js.map +1 -1
  36. package/dist/lib/spendAccumulator.d.ts +10 -0
  37. package/dist/lib/spendAccumulator.d.ts.map +1 -0
  38. package/dist/lib/spendAccumulator.js +54 -0
  39. package/dist/lib/spendAccumulator.js.map +1 -0
  40. package/dist/lib/testRpc.d.ts +1 -1
  41. package/dist/lib/testRpc.js +29 -29
  42. package/dist/lib/testTools.d.ts +1 -1
  43. package/dist/lib/testTools.js +4 -5
  44. package/dist/lib/wallet/index.d.ts +19 -0
  45. package/dist/lib/wallet/index.d.ts.map +1 -1
  46. package/dist/lib/wallet/index.js +65 -7
  47. package/dist/lib/wallet/index.js.map +1 -1
  48. package/dist/tools/approveToken.d.ts +0 -1
  49. package/dist/tools/approveToken.d.ts.map +1 -1
  50. package/dist/tools/approveToken.js +3 -1
  51. package/dist/tools/approveToken.js.map +1 -1
  52. package/dist/tools/assessRisk.d.ts.map +1 -1
  53. package/dist/tools/assessRisk.js +18 -7
  54. package/dist/tools/assessRisk.js.map +1 -1
  55. package/dist/tools/checkAllowance.d.ts +9 -2
  56. package/dist/tools/checkAllowance.d.ts.map +1 -1
  57. package/dist/tools/checkAllowance.js +20 -6
  58. package/dist/tools/checkAllowance.js.map +1 -1
  59. package/dist/tools/checkTokenSecurity.d.ts.map +1 -1
  60. package/dist/tools/checkTokenSecurity.js +7 -0
  61. package/dist/tools/checkTokenSecurity.js.map +1 -1
  62. package/dist/tools/createAgentWallet.d.ts +2 -0
  63. package/dist/tools/createAgentWallet.d.ts.map +1 -1
  64. package/dist/tools/createAgentWallet.js +30 -7
  65. package/dist/tools/createAgentWallet.js.map +1 -1
  66. package/dist/tools/executeSwap.d.ts +3 -1
  67. package/dist/tools/executeSwap.d.ts.map +1 -1
  68. package/dist/tools/executeSwap.js +13 -1
  69. package/dist/tools/executeSwap.js.map +1 -1
  70. package/dist/tools/getExecutionHistory.d.ts +15 -17
  71. package/dist/tools/getExecutionHistory.d.ts.map +1 -1
  72. package/dist/tools/getExecutionHistory.js +125 -64
  73. package/dist/tools/getExecutionHistory.js.map +1 -1
  74. package/dist/tools/getTransactionStatus.d.ts +5 -19
  75. package/dist/tools/getTransactionStatus.d.ts.map +1 -1
  76. package/dist/tools/getTransactionStatus.js +10 -18
  77. package/dist/tools/getTransactionStatus.js.map +1 -1
  78. package/dist/tools/publishRiskScore.d.ts.map +1 -1
  79. package/dist/tools/publishRiskScore.js +3 -0
  80. package/dist/tools/publishRiskScore.js.map +1 -1
  81. package/dist/tools/queryRiskRegistry.d.ts +3 -13
  82. package/dist/tools/queryRiskRegistry.d.ts.map +1 -1
  83. package/dist/tools/queryRiskRegistry.js +6 -11
  84. package/dist/tools/queryRiskRegistry.js.map +1 -1
  85. package/dist/tools/safehandsSafeExecute.d.ts.map +1 -1
  86. package/dist/tools/safehandsSafeExecute.js +12 -6
  87. package/dist/tools/safehandsSafeExecute.js.map +1 -1
  88. package/dist/tools/sendPayment.d.ts +0 -1
  89. package/dist/tools/sendPayment.d.ts.map +1 -1
  90. package/dist/tools/sendPayment.js +10 -1
  91. package/dist/tools/sendPayment.js.map +1 -1
  92. package/dist/tools/simulateTransaction.d.ts +2 -23
  93. package/dist/tools/simulateTransaction.d.ts.map +1 -1
  94. package/dist/tools/simulateTransaction.js +10 -15
  95. package/dist/tools/simulateTransaction.js.map +1 -1
  96. package/dist/tools/x402PayAndFetch.d.ts.map +1 -1
  97. package/dist/tools/x402PayAndFetch.js +4 -1
  98. package/dist/tools/x402PayAndFetch.js.map +1 -1
  99. package/dist/x402Server.js +51 -3
  100. package/dist/x402Server.js.map +1 -1
  101. package/package.json +73 -84
  102. package/.agents/skill/safehands/SKILL.md +0 -212
  103. package/.agents/skill/safehands/assets/networks.json +0 -24
  104. package/.agents/skill/safehands/assets/tokens.json +0 -66
  105. package/.agents/wallets.json +0 -20
  106. package/docs/reports/OFFICIAL_DOCS_ALIGNMENT_REPORT.md +0 -137
  107. package/docs/reports/final_audit_report.md +0 -307
  108. package/docs/reports/live_verification_report.md +0 -147
  109. package/docs/reports/pharos_skill_engine_alignment_report.md +0 -85
  110. package/examples/pharos-skill-engine/SKILL.safehands.md +0 -85
  111. package/examples/pharos-skill-engine/assets/safehands/example-actions.json +0 -49
  112. package/examples/pharos-skill-engine/assets/safehands/policy-defaults.json +0 -11
  113. package/examples/pharos-skill-engine/references/safehands.md +0 -345
  114. package/examples/scenario-hack.ts +0 -38
  115. package/skill/SKILL.md +0 -133
  116. package/skill/assets/safehands/example-actions.json +0 -49
  117. package/skill/assets/safehands/policy-defaults.json +0 -11
  118. package/skill/references/safehands.md +0 -345
package/dist/cli.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type ToolResponse } from "./lib/toolResponse.js";
2
- export type SkillCliToolName = "safehands_preflight_check" | "safehands_x402_preflight" | "safehands_wallet_health" | "token_registry_status" | "explain_risk" | "safehands_risk_report" | "safehands_safe_execute" | "create_agent_wallet" | "get_agent_wallet" | "get_agent_wallet_balance" | "check_token_security";
2
+ export type SkillCliToolName = "safehands_preflight_check" | "safehands_x402_preflight" | "safehands_wallet_health" | "token_registry_status" | "explain_risk" | "safehands_risk_report" | "safehands_safe_execute" | "create_agent_wallet" | "get_agent_wallet" | "get_agent_wallet_balance" | "check_token_security" | "assess_risk" | "get_wallet_balance" | "simulate_transaction" | "estimate_gas" | "get_token_price" | "get_transaction_status" | "query_risk_registry" | "publish_risk_score";
3
3
  export declare function getSkillCliToolNames(): string[];
4
4
  export declare function invokeSkillCliTool(toolName: string, input: unknown): Promise<ToolResponse<unknown>>;
5
5
  export declare function runSkillCli(argv: string[]): Promise<number>;
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAMA,OAAO,EAAY,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAapE,MAAM,MAAM,gBAAgB,GACxB,2BAA2B,GAC3B,0BAA0B,GAC1B,yBAAyB,GACzB,uBAAuB,GACvB,cAAc,GACd,uBAAuB,GACvB,wBAAwB,GACxB,qBAAqB,GACrB,kBAAkB,GAClB,0BAA0B,GAC1B,sBAAsB,CAAC;AA6C3B,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAE/C;AAED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAuBzG;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BjE"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAMA,OAAO,EAAY,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAqBpE,MAAM,MAAM,gBAAgB,GACxB,2BAA2B,GAC3B,0BAA0B,GAC1B,yBAAyB,GACzB,uBAAuB,GACvB,cAAc,GACd,uBAAuB,GACvB,wBAAwB,GACxB,qBAAqB,GACrB,kBAAkB,GAClB,0BAA0B,GAC1B,sBAAsB,GACtB,aAAa,GACb,oBAAoB,GACpB,sBAAsB,GACtB,cAAc,GACd,iBAAiB,GACjB,wBAAwB,GACxB,qBAAqB,GACrB,oBAAoB,CAAC;AA+DzB,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAE/C;AAED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAuBzG;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BjE"}
package/dist/cli.js CHANGED
@@ -15,6 +15,14 @@ import { handleCreateAgentWallet } from "./tools/createAgentWallet.js";
15
15
  import { handleGetAgentWallet } from "./tools/getAgentWallet.js";
16
16
  import { handleGetAgentWalletBalance } from "./tools/getAgentWalletBalance.js";
17
17
  import { handleCheckTokenSecurity } from "./tools/checkTokenSecurity.js";
18
+ import { handleAssessRisk } from "./tools/assessRisk.js";
19
+ import { handleGetWalletBalance } from "./tools/getWalletBalance.js";
20
+ import { handleSimulateTransaction } from "./tools/simulateTransaction.js";
21
+ import { handleEstimateGas } from "./tools/estimateGas.js";
22
+ import { handleGetTokenPrice } from "./tools/getTokenPrice.js";
23
+ import { handleGetTransactionStatus } from "./tools/getTransactionStatus.js";
24
+ import { handleQueryRiskRegistry } from "./tools/queryRiskRegistry.js";
25
+ import { handlePublishRiskScore } from "./tools/publishRiskScore.js";
18
26
  const SKILL_CLI_TOOLS = {
19
27
  safehands_preflight_check: handleSafeHandsPreflightCheck,
20
28
  safehands_x402_preflight: handleSafeHandsX402Preflight,
@@ -27,6 +35,14 @@ const SKILL_CLI_TOOLS = {
27
35
  get_agent_wallet: handleGetAgentWallet,
28
36
  get_agent_wallet_balance: handleGetAgentWalletBalance,
29
37
  check_token_security: handleCheckTokenSecurity,
38
+ assess_risk: handleAssessRisk,
39
+ get_wallet_balance: handleGetWalletBalance,
40
+ simulate_transaction: handleSimulateTransaction,
41
+ estimate_gas: handleEstimateGas,
42
+ get_token_price: handleGetTokenPrice,
43
+ get_transaction_status: handleGetTransactionStatus,
44
+ query_risk_registry: handleQueryRiskRegistry,
45
+ publish_risk_score: handlePublishRiskScore,
30
46
  };
31
47
  function isStructuredResponse(value) {
32
48
  return !!value && typeof value === "object" && "success" in value && "data" in value && "error" in value && "timestamp" in value;
@@ -37,20 +53,32 @@ function printJson(response) {
37
53
  function usage() {
38
54
  const tools = Object.keys(SKILL_CLI_TOOLS).sort().join(", ");
39
55
  return [
40
- "Usage: npx safehands-pharos skill <tool_name> --input-json '<json>'",
56
+ "Usage: npx safehands-pharos skill <tool_name> [<json> | --input-json '<json>' | -i '<json>']",
41
57
  "",
42
58
  `Supported Skill Engine tools: ${tools}`,
43
59
  "",
44
- "Example:",
45
- " npx safehands-pharos skill safehands_preflight_check --input-json '{\"actionType\":\"approve_token\",\"chainId\":688689,\"amount\":\"1\"}'",
60
+ "Examples:",
61
+ " npx safehands-pharos skill assess_risk '{\"action\":\"swap\",\"amount\":\"0.01\"}'",
62
+ " npx safehands-pharos skill get_wallet_balance '{\"walletAddress\":\"0xABC...\"}'",
63
+ " npx safehands-pharos skill safehands_preflight_check -i '{\"actionType\":\"approve_token\",\"chainId\":688689,\"amount\":\"1\"}'",
46
64
  ].join("\n");
47
65
  }
48
66
  function readInputJsonArg(argv) {
49
- const positional = argv.indexOf("--input-json");
50
- if (positional >= 0)
51
- return argv[positional + 1] ?? null;
52
- const prefixed = argv.find((arg) => arg.startsWith("--input-json="));
53
- return prefixed ? prefixed.slice("--input-json=".length) : null;
67
+ // --input-json <json>
68
+ const flag = argv.indexOf("--input-json");
69
+ if (flag >= 0)
70
+ return argv[flag + 1] ?? null;
71
+ // --input-json=<json>
72
+ const inline = argv.find((a) => a.startsWith("--input-json="));
73
+ if (inline)
74
+ return inline.slice("--input-json=".length);
75
+ // -i <json>
76
+ const short = argv.indexOf("-i");
77
+ if (short >= 0)
78
+ return argv[short + 1] ?? null;
79
+ // positional: first arg that looks like JSON or is not a flag
80
+ const positional = argv.find((a) => !a.startsWith("-"));
81
+ return positional ?? null;
54
82
  }
55
83
  export function getSkillCliToolNames() {
56
84
  return Object.keys(SKILL_CLI_TOOLS).sort();
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ import "./lib/envLoader.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAMA,OAAO,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -2,6 +2,11 @@
2
2
  // ─── SafeHands MCP Server ──────────────────────────────────────────────
3
3
  // Entry point — registers 27 tools (17 legacy/core + 3 managed wallet + 7 SafeHands guardrail tools) and starts the MCP server.
4
4
  // ────────────────────────────────────────────────────────────────────────
5
+ // Must be first import — loads .env before any module reads process.env at init time
6
+ import "./lib/envLoader.js";
7
+ import { createRequire } from "module";
8
+ const require = createRequire(import.meta.url);
9
+ const PKG_VERSION = require("../package.json").version;
5
10
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
11
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
12
  import { assessRiskSchema, handleAssessRisk } from "./tools/assessRisk.js";
@@ -32,9 +37,41 @@ import { tokenRegistryStatusSchema, handleTokenRegistryStatus } from "./tools/to
32
37
  import { getAgentWalletSchema, handleGetAgentWallet } from "./tools/getAgentWallet.js";
33
38
  import { getAgentWalletBalanceSchema, handleGetAgentWalletBalance } from "./tools/getAgentWalletBalance.js";
34
39
  import { fail, ok } from "./lib/toolResponse.js";
40
+ import { auditLog } from "./lib/auditLog.js";
35
41
  import { runSkillCli } from "./cli.js";
36
42
  import { runDemo } from "./demo.js";
37
43
  import { runInit } from "./init.js";
44
+ // ─── Per-tool rate limiting ────────────────────────────────────────────
45
+ // Heavy tools (block-scan history) get a tighter limit.
46
+ // Write tools get a moderate limit to prevent agent loops.
47
+ // All other tools share a generous global limit.
48
+ const HEAVY_TOOLS = new Set(["get_execution_history"]);
49
+ const WRITE_TOOLS = new Set([
50
+ "execute_swap", "send_payment", "approve_token",
51
+ "publish_risk_score", "x402_pay_and_fetch", "safehands_safe_execute",
52
+ ]);
53
+ const RATE_LIMITS = {
54
+ heavy: parseInt(process.env.MCP_RATE_LIMIT_HEAVY || "5", 10),
55
+ write: parseInt(process.env.MCP_RATE_LIMIT_WRITE || "15", 10),
56
+ default: parseInt(process.env.MCP_RATE_LIMIT_DEFAULT || "120", 10),
57
+ };
58
+ const rateBuckets = new Map();
59
+ function checkMcpRateLimit(tool) {
60
+ const limit = HEAVY_TOOLS.has(tool) ? RATE_LIMITS.heavy
61
+ : WRITE_TOOLS.has(tool) ? RATE_LIMITS.write
62
+ : RATE_LIMITS.default;
63
+ const now = Date.now();
64
+ let bucket = rateBuckets.get(tool);
65
+ if (!bucket || bucket.resetAt <= now) {
66
+ bucket = { count: 0, resetAt: now + 60_000 };
67
+ }
68
+ bucket.count++;
69
+ rateBuckets.set(tool, bucket);
70
+ if (bucket.count > limit) {
71
+ return `Rate limit exceeded for ${tool} (${limit}/min). Slow down and retry after ${Math.ceil((bucket.resetAt - now) / 1000)}s.`;
72
+ }
73
+ return null;
74
+ }
38
75
  function isStructuredResponse(value) {
39
76
  return (!!value &&
40
77
  typeof value === "object" &&
@@ -43,16 +80,37 @@ function isStructuredResponse(value) {
43
80
  "error" in value);
44
81
  }
45
82
  async function invokeTool(handler, params, source) {
83
+ const rateLimitMsg = checkMcpRateLimit(source);
84
+ if (rateLimitMsg) {
85
+ auditLog({ ts: new Date().toISOString(), tool: source, success: false, errorCode: "RATE_LIMITED" });
86
+ return fail("RATE_LIMITED", rateLimitMsg, true, source);
87
+ }
88
+ const start = Date.now();
46
89
  try {
47
90
  const result = await handler(params);
48
- if (isStructuredResponse(result))
91
+ if (isStructuredResponse(result)) {
92
+ const r = result;
93
+ auditLog({ ts: new Date().toISOString(), tool: source, success: r.success, errorCode: r.success ? undefined : r.error?.code, durationMs: Date.now() - start });
49
94
  return result;
50
- if (result && typeof result === "object" && "error" in result && !("success" in result)) {
51
- return fail("TOOL_RETURNED_ERROR", String(result.error), false, source);
52
95
  }
96
+ if (result && typeof result === "object") {
97
+ if ("error" in result && !("success" in result)) {
98
+ const msg = String(result.error);
99
+ auditLog({ ts: new Date().toISOString(), tool: source, success: false, errorCode: "TOOL_RETURNED_ERROR", durationMs: Date.now() - start });
100
+ return fail("TOOL_RETURNED_ERROR", msg, false, source);
101
+ }
102
+ // Defense-in-depth: non-ToolResponse shapes with success:false must not surface as ok()
103
+ if ("success" in result && result.success === false) {
104
+ const msg = "error" in result ? String(result.error) : "Tool returned failure without ToolResponse shape";
105
+ auditLog({ ts: new Date().toISOString(), tool: source, success: false, errorCode: "TOOL_RETURNED_ERROR", durationMs: Date.now() - start });
106
+ return fail("TOOL_RETURNED_ERROR", msg, false, source);
107
+ }
108
+ }
109
+ auditLog({ ts: new Date().toISOString(), tool: source, success: true, durationMs: Date.now() - start });
53
110
  return ok(result);
54
111
  }
55
112
  catch (err) {
113
+ auditLog({ ts: new Date().toISOString(), tool: source, success: false, errorCode: "TOOL_EXECUTION_FAILED", durationMs: Date.now() - start });
56
114
  return fail("TOOL_EXECUTION_FAILED", err instanceof Error ? err.message : String(err), false, source);
57
115
  }
58
116
  }
@@ -76,155 +134,158 @@ if (process.argv[2] === "init") {
76
134
  process.exit(0);
77
135
  }
78
136
  // ─── Deterministic Demo CLI ───────────────────────────────────────────
79
- if (process.argv.includes("--demo")) {
137
+ const isDemoMode = process.argv.includes("--demo");
138
+ if (isDemoMode) {
80
139
  await runDemo();
81
- // Brief delay allows libuv handles (express sockets) to close cleanly on Windows
82
- setTimeout(() => process.exit(0), 100);
83
- // Wait indefinitely so the script doesn't continue down to start the MCP server
84
- await new Promise(() => { });
140
+ // Set exit code and let the event loop drain naturally.
141
+ // Avoids Windows libuv UV_HANDLE_CLOSING assertion that occurs with process.exit() while
142
+ // async handles (express close, viem HTTP keep-alive) are still winding down.
143
+ process.exitCode = 0;
85
144
  }
86
145
  // ─── CLI Help ──────────────────────────────────────────────────────────
87
146
  if (process.argv.includes("--help") || process.argv.includes("-h")) {
88
- process.stdout.write(`
89
- 🛡️ SafeHands-Pharos — Transaction Safety Firewall for AI Agents
90
- v1.2.6 | Pharos Atlantic Testnet | Chain ID 688689
91
-
92
- USAGE
93
- npx safehands-pharos
94
- Start SafeHands as an MCP server over stdio.
95
-
96
- npx safehands-pharos --help
97
- Show this help.
98
-
99
- npx safehands-pharos init
100
- Launch the interactive setup wizard to configure your .env file safely.
101
-
102
- npx safehands-pharos --demo
103
- Run the deterministic non-destructive hackathon demo.
104
-
105
- npx safehands-pharos skill <tool_name> --input-json '<json>'
106
- Run a Pharos Skill Engine-compatible SafeHands CLI tool.
107
-
108
- POSITIONING
109
- SafeHands is a guardrail layer, not a generic Web3 toolbox.
110
- It checks whether an AI agent action is safe before execution.
111
-
112
- User intent
113
- → SafeHands preflight
114
- → ALLOW / WARN / BLOCK / REQUIRE_CONFIRMATION
115
- → Pharos Skill Engine or MCP execution only if safe
116
- → SafeHands risk report
117
-
118
- SAFEHANDS BRANDED TOOLS
119
- safehands_preflight_check Policy preflight for payments, approvals, swaps, x402, and custom calls
120
- safehands_safe_execute Guarded wrapper that preflights before execution
121
- safehands_wallet_health Wallet/signer/gas/x402 readiness report
122
- safehands_x402_preflight URL, payment, token, and signer safety check before x402 payment
123
- safehands_risk_report Human-readable judge/demo risk report
124
- explain_risk Explain ALLOW/WARN/BLOCK decisions in plain English
125
- token_registry_status Classify canonical, test, custom, unknown, or invalid token address
126
-
127
- OTHER MCP TOOLS
128
- Core safety: assess_risk, check_token_security, simulate_transaction, estimate_gas
129
- Execution: execute_swap, send_payment, approve_token
130
- Market: get_token_price, get_pool_info, get_gas_price
131
- Wallet/history: get_wallet_balance, check_allowance, get_transaction_status, get_execution_history
132
- Risk registry: publish_risk_score, query_risk_registry
133
- x402: x402_pay_and_fetch
134
- Managed testnet wallet: create_agent_wallet, get_agent_wallet, get_agent_wallet_balance
135
-
136
- PHAROS ATLANTIC TESTNET
137
- Environment: atlantic-testnet
138
- Chain ID: 688689
139
- RPC: https://atlantic.dplabs-internal.com
140
- Explorer: https://atlantic.pharosscan.xyz/
141
- RiskRegistry: 0x61962a6c812ee9f57b207e1ea47c19ae70bb7141
142
-
143
- x402 BEHAVIOR
144
- Free endpoints, such as /supported and /health, do not require a private key.
145
- Paid endpoints are fetched normally first. Only after HTTP 402 does SafeHands run x402 preflight and request a signer.
146
- Signed payment payloads and payment headers are not logged or returned.
147
-
148
- DEFAULT SAFETY
149
- WALLET_MODE=none
150
- WRITE_TOOLS_ENABLED=false
151
- ALLOW_UNLIMITED_APPROVAL=false
152
- ALLOW_LOCAL_X402_FETCH=false
153
-
154
- No wallet is created on install, import, or startup.
155
- Write tools are disabled by default.
156
- Unlimited approvals are blocked by default.
157
- Mainnet actions are blocked.
158
- SSRF-sensitive x402 URLs are blocked by default.
159
-
160
- EXAMPLES
161
- npx safehands-pharos skill safehands_preflight_check --input-json '{"actionType":"send_payment","chainId":688689,"isMainnet":false,"amount":"0.001","recipient":"0x0000000000000000000000000000000000000001"}'
162
-
163
- npx safehands-pharos skill token_registry_status --input-json '{"tokenAddress":"0xE0BE08c77f415F577A1B3A9aD7a1Df1479564ec8"}'
164
-
165
- DOCS
166
- README.md
167
- skill/SKILL.md
147
+ process.stdout.write(`
148
+ 🛡️ SafeHands-Pharos — Transaction Safety Firewall for AI Agents
149
+ v${PKG_VERSION} | Pharos Atlantic Testnet | Chain ID 688689
150
+
151
+ USAGE
152
+ npx safehands-pharos
153
+ Start SafeHands as an MCP server over stdio.
154
+
155
+ npx safehands-pharos --help
156
+ Show this help.
157
+
158
+ npx safehands-pharos init
159
+ Launch the interactive setup wizard to configure your .env file safely.
160
+
161
+ npx safehands-pharos --demo
162
+ Run the deterministic non-destructive hackathon demo.
163
+
164
+ npx safehands-pharos skill <tool_name> --input-json '<json>'
165
+ Run a Pharos Skill Engine-compatible SafeHands CLI tool.
166
+
167
+ POSITIONING
168
+ SafeHands is a guardrail layer, not a generic Web3 toolbox.
169
+ It checks whether an AI agent action is safe before execution.
170
+
171
+ User intent
172
+ → SafeHands preflight
173
+ → ALLOW / WARN / BLOCK / REQUIRE_CONFIRMATION
174
+ → Pharos Skill Engine or MCP execution only if safe
175
+ → SafeHands risk report
176
+
177
+ SAFEHANDS BRANDED TOOLS
178
+ safehands_preflight_check Policy preflight for payments, approvals, swaps, x402, and custom calls
179
+ safehands_safe_execute Guarded wrapper that preflights before execution
180
+ safehands_wallet_health Wallet/signer/gas/x402 readiness report
181
+ safehands_x402_preflight URL, payment, token, and signer safety check before x402 payment
182
+ safehands_risk_report Human-readable judge/demo risk report
183
+ explain_risk Explain ALLOW/WARN/BLOCK decisions in plain English
184
+ token_registry_status Classify canonical, test, custom, unknown, or invalid token address
185
+
186
+ OTHER MCP TOOLS
187
+ Core safety: assess_risk, check_token_security, simulate_transaction, estimate_gas
188
+ Execution: execute_swap, send_payment, approve_token
189
+ Market: get_token_price, get_pool_info, get_gas_price
190
+ Wallet/history: get_wallet_balance, check_allowance, get_transaction_status, get_execution_history
191
+ Risk registry: publish_risk_score, query_risk_registry
192
+ x402: x402_pay_and_fetch
193
+ Managed testnet wallet: create_agent_wallet, get_agent_wallet, get_agent_wallet_balance
194
+
195
+ PHAROS ATLANTIC TESTNET
196
+ Environment: atlantic-testnet
197
+ Chain ID: 688689
198
+ RPC: https://atlantic.dplabs-internal.com
199
+ Explorer: https://atlantic.pharosscan.xyz/
200
+ RiskRegistry: 0x61962a6c812ee9f57b207e1ea47c19ae70bb7141
201
+
202
+ x402 BEHAVIOR
203
+ Free endpoints, such as /supported and /health, do not require a private key.
204
+ Paid endpoints are fetched normally first. Only after HTTP 402 does SafeHands run x402 preflight and request a signer.
205
+ Signed payment payloads and payment headers are not logged or returned.
206
+
207
+ DEFAULT SAFETY
208
+ WALLET_MODE=none
209
+ WRITE_TOOLS_ENABLED=false
210
+ ALLOW_UNLIMITED_APPROVAL=false
211
+ ALLOW_LOCAL_X402_FETCH=false
212
+
213
+ No wallet is created on install, import, or startup.
214
+ Write tools are disabled by default.
215
+ Unlimited approvals are blocked by default.
216
+ Mainnet actions are blocked.
217
+ SSRF-sensitive x402 URLs are blocked by default.
218
+
219
+ EXAMPLES
220
+ npx safehands-pharos skill safehands_preflight_check --input-json '{"actionType":"send_payment","chainId":688689,"isMainnet":false,"amount":"0.001","recipient":"0x0000000000000000000000000000000000000001"}'
221
+
222
+ npx safehands-pharos skill token_registry_status --input-json '{"tokenAddress":"0xE0BE08c77f415F577A1B3A9aD7a1Df1479564ec8"}'
223
+
224
+ DOCS
225
+ README.md
226
+ skill/SKILL.md
168
227
  `);
169
228
  process.exit(0);
170
229
  }
171
- // ─── Startup Validation ────────────────────────────────────────────────
172
- const walletMode = process.env.WALLET_MODE || "none";
173
- const hasManagedWalletMode = walletMode === "managed-testnet";
174
- const hasExplicitSignerMode = walletMode === "managed-testnet" || walletMode === "env";
175
- if (process.env.WRITE_TOOLS_ENABLED !== "true") {
176
- console.error("⚠️ SafeHands write tools are disabled by default.");
177
- console.error(" Preflight, risk report, token registry, read-only, and x402 free endpoint checks remain available.");
178
- console.error(" To execute trusted testnet actions, set WRITE_TOOLS_ENABLED=true and configure a signer via WALLET_MODE=managed-testnet or WALLET_MODE=env.");
179
- console.error("");
180
- }
181
- else if (!hasExplicitSignerMode) {
182
- console.error("⚠️ SafeHands write tools enabled but no signer mode detected.");
183
- console.error(" Use WALLET_MODE=managed-testnet with create_agent_wallet, or WALLET_MODE=env for testnet developer mode.");
184
- console.error("");
185
- }
186
- // ─── Server Setup ──────────────────────────────────────────────────────
187
- const server = new McpServer({
188
- name: "safehands",
189
- version: "1.2.4",
190
- });
191
- // ─── Tool Registration ─────────────────────────────────────────────────
192
- server.tool("safehands_preflight_check", "Run a SafeHands policy preflight before an AI agent sends payment, approves tokens, swaps, publishes risk data, or pays x402.", safehandsPreflightCheckSchema.shape, async (params) => mcpText(await invokeTool(handleSafeHandsPreflightCheck, params, "safehands_preflight_check")));
193
- server.tool("safehands_safe_execute", "Guarded execution wrapper: preflight first, then execute only allowed and explicitly confirmed testnet actions.", safehandsSafeExecuteSchema.shape, async (params) => mcpText(await invokeTool(handleSafeHandsSafeExecute, params, "safehands_safe_execute")));
194
- server.tool("safehands_wallet_health", "Check whether an AI agent wallet is funded, signer-ready, and safe to use on Pharos Atlantic Testnet.", safehandsWalletHealthSchema.shape, async (params) => mcpText(await invokeTool(handleSafeHandsWalletHealth, params, "safehands_wallet_health")));
195
- server.tool("safehands_x402_preflight", "Check an x402 paid resource for URL safety, payment amount, token, signer, and testnet policy before signing.", safehandsX402PreflightSchema.shape, async (params) => mcpText(await invokeTool(handleSafeHandsX402Preflight, params, "safehands_x402_preflight")));
196
- server.tool("safehands_risk_report", "Generate an audit-friendly human-readable SafeHands risk report for an AI agent action.", safehandsRiskReportSchema.shape, async (params) => mcpText(await invokeTool(handleSafeHandsRiskReport, params, "safehands_risk_report")));
197
- server.tool("explain_risk", "Explain a SafeHands risk/policy decision in human-readable language.", explainRiskSchema.shape, async (params) => mcpText(await invokeTool(handleExplainRisk, params, "explain_risk")));
198
- server.tool("token_registry_status", "Classify exact token input as canonical testnet token, test liquidity token, custom, unknown, or invalid.", tokenRegistryStatusSchema.shape, async (params) => mcpText(await invokeTool(handleTokenRegistryStatus, params, "token_registry_status")));
199
- server.tool("assess_risk", "Evaluate the risk of a planned on-chain action (swap or transfer). Returns 0-100 risk score with 5-dimension breakdown.", assessRiskSchema.shape, async (params) => mcpText(await invokeTool(handleAssessRisk, params, "assess_risk")));
200
- server.tool("execute_swap", "Swap tokens via FaroSwap with built-in risk gate. Runs risk assessment first, blocks if score > 80.", executeSwapSchema.shape, async (params) => mcpText(await invokeTool(handleExecuteSwap, params, "execute_swap")));
201
- server.tool("send_payment", "Send native PHRS with pre-flight validation. Checks address, balance, and warns on high exposure.", sendPaymentSchema.shape, async (params) => mcpText(await invokeTool(handleSendPayment, params, "send_payment")));
202
- server.tool("simulate_transaction", "Dry run a swap or transfer via eth_call no gas spent. Returns expected output and revert reasons.", simulateTransactionSchema.shape, async (params) => mcpText(await invokeTool(handleSimulateTransaction, params, "simulate_transaction")));
203
- server.tool("get_execution_history", "Pull on-chain transaction history for a wallet. Filters by swap, transfer, or all.", getExecutionHistorySchema.shape, async (params) => mcpText(await invokeTool(handleGetExecutionHistory, params, "get_execution_history")));
204
- server.tool("get_token_price", "Fetch real-time price of PHRS, USDC, or USDT on Pharos using DODO liquidity quotes.", getTokenPriceSchema.shape, async (params) => mcpText(await invokeTool(handleGetTokenPrice, params, "get_token_price")));
205
- server.tool("get_wallet_balance", "Return PHRS, USDC, USDT balances for a wallet with total USD estimate.", getWalletBalanceSchema.shape, async (params) => mcpText(await invokeTool(handleGetWalletBalance, params, "get_wallet_balance")));
206
- server.tool("check_allowance", "Check ERC-20 token allowance for DODO swap approval. Returns whether approval is needed.", checkAllowanceSchema.shape, async (params) => mcpText(await invokeTool(handleCheckAllowance, params, "check_allowance")));
207
- server.tool("get_transaction_status", "Check on-chain transaction status by hash. Returns status, block, gas, and explorer link.", getTransactionStatusSchema.shape, async (params) => mcpText(await invokeTool(handleGetTransactionStatus, params, "get_transaction_status")));
208
- server.tool("estimate_gas", "Estimate gas cost for a swap or transfer before executing. Returns cost in PHRS and USD.", estimateGasSchema.shape, async (params) => mcpText(await invokeTool(handleEstimateGas, params, "estimate_gas")));
209
- server.tool("publish_risk_score", "Run risk assessment and publish result to the on-chain RiskRegistry. Other agents can query it.", publishRiskScoreSchema.shape, async (params) => mcpText(await invokeTool(handlePublishRiskScore, params, "publish_risk_score")));
210
- server.tool("query_risk_registry", "Query the on-chain RiskRegistry for a wallet's published risk score. Read-only, no private key needed.", queryRiskRegistrySchema.shape, async (params) => mcpText(await invokeTool(handleQueryRiskRegistry, params, "query_risk_registry")));
211
- server.tool("approve_token", "Approve ERC-20 token spending for FaroSwap (DODO) router. Required before swapping non-native tokens.", approveTokenSchema.shape, async (params) => mcpText(await invokeTool(handleApproveToken, params, "approve_token")));
212
- server.tool("get_gas_price", "Get current gas price on Pharos with trend classification and cost estimates.", getGasPriceSchema.shape, async (params) => mcpText(await invokeTool(handleGetGasPrice, params, "get_gas_price")));
213
- server.tool("get_pool_info", "Fetch DODO liquidity pool info for a token pair on Pharos. Returns price ratio, impact, and fees.", getPoolInfoSchema.shape, async (params) => mcpText(await invokeTool(handleGetPoolInfo, params, "get_pool_info")));
214
- server.tool("check_token_security", "Check token contract security (honeypot, mintable, ownership privileges, tax) via GoPlus Security API.", checkTokenSecuritySchema.shape, async (params) => mcpText(await invokeTool(handleCheckTokenSecurity, params, "check_token_security")));
215
- server.tool("x402_pay_and_fetch", "Fetch resources from an HTTP x402 payment-gated server. Automatically handles HTTP 402 payment challenge.", x402PayAndFetchSchema.shape, async (params) => mcpText(await invokeTool(handleX402PayAndFetch, params, "x402_pay_and_fetch")));
216
- // ─── Managed Wallet Tools ──────────────────────────────────────────────
217
- server.tool("create_agent_wallet", "Create a new managed testnet agent wallet. Private key is encrypted and never returned. Fund the wallet with testnet PHRS before using write tools.", createAgentWalletSchema.shape, async (params) => mcpText(await invokeTool(handleCreateAgentWallet, params, "create_agent_wallet")));
218
- server.tool("get_agent_wallet", "Get public info (address, environment, chainId) for a managed agent wallet. Never returns private key.", getAgentWalletSchema.shape, async (params) => mcpText(await invokeTool(handleGetAgentWallet, params, "get_agent_wallet")));
219
- server.tool("get_agent_wallet_balance", "Get PHRS, USDC, and USDT balances for a managed agent wallet on Pharos testnet.", getAgentWalletBalanceSchema.shape, async (params) => mcpText(await invokeTool(handleGetAgentWalletBalance, params, "get_agent_wallet_balance")));
220
- // ─── Start Server ──────────────────────────────────────────────────────
221
- async function main() {
222
- const transport = new StdioServerTransport();
223
- await server.connect(transport);
224
- console.error("SafeHands-Pharos MCP Server v1.2.6 running on stdio");
225
- }
226
- main().catch((error) => {
227
- console.error("Fatal error:", error);
228
- process.exit(1);
229
- });
230
+ if (!isDemoMode) {
231
+ // ─── Startup Validation ────────────────────────────────────────────────
232
+ const walletMode = process.env.WALLET_MODE || "none";
233
+ const hasManagedWalletMode = walletMode === "managed-testnet";
234
+ const hasExplicitSignerMode = walletMode === "managed-testnet" || walletMode === "env";
235
+ if (process.env.WRITE_TOOLS_ENABLED !== "true") {
236
+ console.error("⚠️ SafeHands write tools are disabled by default.");
237
+ console.error(" Preflight, risk report, token registry, read-only, and x402 free endpoint checks remain available.");
238
+ console.error(" To execute trusted testnet actions, set WRITE_TOOLS_ENABLED=true and configure a signer via WALLET_MODE=managed-testnet or WALLET_MODE=env.");
239
+ console.error("");
240
+ }
241
+ else if (!hasExplicitSignerMode) {
242
+ console.error("⚠️ SafeHands write tools enabled but no signer mode detected.");
243
+ console.error(" Use WALLET_MODE=managed-testnet with create_agent_wallet, or WALLET_MODE=env for testnet developer mode.");
244
+ console.error("");
245
+ }
246
+ // ─── Server Setup ──────────────────────────────────────────────────────
247
+ const server = new McpServer({
248
+ name: "safehands",
249
+ version: PKG_VERSION,
250
+ });
251
+ // ─── Tool Registration ─────────────────────────────────────────────────
252
+ server.tool("safehands_preflight_check", "Run a SafeHands policy preflight before an AI agent sends payment, approves tokens, swaps, publishes risk data, or pays x402.", safehandsPreflightCheckSchema.shape, async (params) => mcpText(await invokeTool(handleSafeHandsPreflightCheck, params, "safehands_preflight_check")));
253
+ server.tool("safehands_safe_execute", "Guarded execution wrapper: preflight first, then execute only allowed and explicitly confirmed testnet actions.", safehandsSafeExecuteSchema.shape, async (params) => mcpText(await invokeTool(handleSafeHandsSafeExecute, params, "safehands_safe_execute")));
254
+ server.tool("safehands_wallet_health", "Check whether an AI agent wallet is funded, signer-ready, and safe to use on Pharos Atlantic Testnet.", safehandsWalletHealthSchema.shape, async (params) => mcpText(await invokeTool(handleSafeHandsWalletHealth, params, "safehands_wallet_health")));
255
+ server.tool("safehands_x402_preflight", "Check an x402 paid resource for URL safety, payment amount, token, signer, and testnet policy before signing.", safehandsX402PreflightSchema.shape, async (params) => mcpText(await invokeTool(handleSafeHandsX402Preflight, params, "safehands_x402_preflight")));
256
+ server.tool("safehands_risk_report", "Generate an audit-friendly human-readable SafeHands risk report for an AI agent action.", safehandsRiskReportSchema.shape, async (params) => mcpText(await invokeTool(handleSafeHandsRiskReport, params, "safehands_risk_report")));
257
+ server.tool("explain_risk", "Explain a SafeHands risk/policy decision in human-readable language.", explainRiskSchema.shape, async (params) => mcpText(await invokeTool(handleExplainRisk, params, "explain_risk")));
258
+ server.tool("token_registry_status", "Classify exact token input as canonical testnet token, test liquidity token, custom, unknown, or invalid.", tokenRegistryStatusSchema.shape, async (params) => mcpText(await invokeTool(handleTokenRegistryStatus, params, "token_registry_status")));
259
+ server.tool("assess_risk", "Evaluate the risk of a planned on-chain action (swap or transfer). Returns 0-100 risk score with 5-dimension breakdown.", assessRiskSchema.shape, async (params) => mcpText(await invokeTool(handleAssessRisk, params, "assess_risk")));
260
+ server.tool("execute_swap", "Swap tokens via FaroSwap with built-in risk gate. Runs risk assessment first, blocks if score > 80.", executeSwapSchema.shape, async (params) => mcpText(await invokeTool(handleExecuteSwap, params, "execute_swap")));
261
+ server.tool("send_payment", "Send native PHRS with pre-flight validation. Checks address, balance, and warns on high exposure.", sendPaymentSchema.shape, async (params) => mcpText(await invokeTool(handleSendPayment, params, "send_payment")));
262
+ server.tool("simulate_transaction", "Dry run a swap or transfer via eth_call — no gas spent. Returns expected output and revert reasons.", simulateTransactionSchema.shape, async (params) => mcpText(await invokeTool(handleSimulateTransaction, params, "simulate_transaction")));
263
+ server.tool("get_execution_history", "Pull on-chain transaction history for a wallet. Filters by swap, transfer, or all.", getExecutionHistorySchema.shape, async (params) => mcpText(await invokeTool(handleGetExecutionHistory, params, "get_execution_history")));
264
+ server.tool("get_token_price", "Fetch real-time price of PHRS, USDC, or USDT on Pharos using DODO liquidity quotes.", getTokenPriceSchema.shape, async (params) => mcpText(await invokeTool(handleGetTokenPrice, params, "get_token_price")));
265
+ server.tool("get_wallet_balance", "Return PHRS, USDC, USDT balances for a wallet with total USD estimate.", getWalletBalanceSchema.shape, async (params) => mcpText(await invokeTool(handleGetWalletBalance, params, "get_wallet_balance")));
266
+ server.tool("check_allowance", "Check ERC-20 token allowance for DODO swap approval. Returns whether approval is needed.", checkAllowanceSchema.shape, async (params) => mcpText(await invokeTool(handleCheckAllowance, params, "check_allowance")));
267
+ server.tool("get_transaction_status", "Check on-chain transaction status by hash. Returns status, block, gas, and explorer link.", getTransactionStatusSchema.shape, async (params) => mcpText(await invokeTool(handleGetTransactionStatus, params, "get_transaction_status")));
268
+ server.tool("estimate_gas", "Estimate gas cost for a swap or transfer before executing. Returns cost in PHRS and USD.", estimateGasSchema.shape, async (params) => mcpText(await invokeTool(handleEstimateGas, params, "estimate_gas")));
269
+ server.tool("publish_risk_score", "Run risk assessment and publish result to the on-chain RiskRegistry. Other agents can query it.", publishRiskScoreSchema.shape, async (params) => mcpText(await invokeTool(handlePublishRiskScore, params, "publish_risk_score")));
270
+ server.tool("query_risk_registry", "Query the on-chain RiskRegistry for a wallet's published risk score. Read-only, no private key needed.", queryRiskRegistrySchema.shape, async (params) => mcpText(await invokeTool(handleQueryRiskRegistry, params, "query_risk_registry")));
271
+ server.tool("approve_token", "Approve ERC-20 token spending for FaroSwap (DODO) router. Required before swapping non-native tokens.", approveTokenSchema.shape, async (params) => mcpText(await invokeTool(handleApproveToken, params, "approve_token")));
272
+ server.tool("get_gas_price", "Get current gas price on Pharos with trend classification and cost estimates.", getGasPriceSchema.shape, async (params) => mcpText(await invokeTool(handleGetGasPrice, params, "get_gas_price")));
273
+ server.tool("get_pool_info", "Fetch DODO liquidity pool info for a token pair on Pharos. Returns price ratio, impact, and fees.", getPoolInfoSchema.shape, async (params) => mcpText(await invokeTool(handleGetPoolInfo, params, "get_pool_info")));
274
+ server.tool("check_token_security", "Check token contract security (honeypot, mintable, ownership privileges, tax) via GoPlus Security API.", checkTokenSecuritySchema.shape, async (params) => mcpText(await invokeTool(handleCheckTokenSecurity, params, "check_token_security")));
275
+ server.tool("x402_pay_and_fetch", "Fetch resources from an HTTP x402 payment-gated server. Automatically handles HTTP 402 payment challenge.", x402PayAndFetchSchema.shape, async (params) => mcpText(await invokeTool(handleX402PayAndFetch, params, "x402_pay_and_fetch")));
276
+ // ─── Managed Wallet Tools ──────────────────────────────────────────────
277
+ server.tool("create_agent_wallet", "Create a new managed testnet agent wallet. Private key is encrypted and never returned. Fund the wallet with testnet PHRS before using write tools.", createAgentWalletSchema.shape, async (params) => mcpText(await invokeTool(handleCreateAgentWallet, params, "create_agent_wallet")));
278
+ server.tool("get_agent_wallet", "Get public info (address, environment, chainId) for a managed agent wallet. Never returns private key.", getAgentWalletSchema.shape, async (params) => mcpText(await invokeTool(handleGetAgentWallet, params, "get_agent_wallet")));
279
+ server.tool("get_agent_wallet_balance", "Get PHRS, USDC, and USDT balances for a managed agent wallet on Pharos testnet.", getAgentWalletBalanceSchema.shape, async (params) => mcpText(await invokeTool(handleGetAgentWalletBalance, params, "get_agent_wallet_balance")));
280
+ // ─── Start Server ──────────────────────────────────────────────────────
281
+ async function main() {
282
+ const transport = new StdioServerTransport();
283
+ await server.connect(transport);
284
+ console.error(`SafeHands-Pharos MCP Server v${PKG_VERSION} running on stdio`);
285
+ }
286
+ main().catch((error) => {
287
+ console.error("Fatal error:", error);
288
+ process.exit(1);
289
+ });
290
+ } // end: !isDemoMode
230
291
  //# sourceMappingURL=index.js.map