@cloakely/mcp 0.3.0 → 0.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.
package/README.md CHANGED
@@ -6,6 +6,12 @@ An agent that trades on chain leaks its strategy with every transaction, and any
6
6
 
7
7
  **Testnet only for now** (Elysium testnet, chain `99801`). Play money only.
8
8
 
9
+ **The easy way:** tell your agent
10
+
11
+ > Read https://cloak.boblabs.tech/agent.md and set me up to trade privately.
12
+
13
+ That page is written for agents. The agent sets up its own wallet and the connection, and asks you only for what it can't do itself (free testnet HYPE from the faucet). It never needs your own wallet's key.
14
+
9
15
  There are two ways in:
10
16
 
11
17
  | | Hosted link | Local server (this package) |
@@ -25,16 +31,18 @@ Its tools: `cloak_info`, `list_launches`, `get_launch`, `quote_bid`, `quote_swap
25
31
 
26
32
  ## Set up the local server
27
33
 
28
- Create a fresh wallet for the agent, fund it from the [Elysium faucet](https://elysium.kinetiq.xyz/testnet-faucet), and put its private key in a file only you can read:
34
+ Make the agent's own wallet. It's saved in `~/.cloak-agent-key`, readable by you only, and the key is never printed:
29
35
 
30
36
  ```sh
31
- umask 077 && echo 0xYOUR_AGENT_KEY > ~/.cloak-agent-key
37
+ npx -y @cloakely/mcp setup
32
38
  ```
33
39
 
40
+ It prints the wallet's address. Fund it with free testnet HYPE from the [Elysium faucet](https://elysium.kinetiq.xyz/testnet-faucet). Running it again keeps the same wallet.
41
+
34
42
  **Claude Code:**
35
43
 
36
44
  ```sh
37
- claude mcp add cloak --env CLOAK_KEY_FILE=$HOME/.cloak-agent-key -- npx -y @cloakely/mcp
45
+ claude mcp add cloak -- npx -y @cloakely/mcp
38
46
  ```
39
47
 
40
48
  **Claude Desktop, Cursor and other clients** (`claude_desktop_config.json`, `.cursor/mcp.json`, and so on):
@@ -42,16 +50,14 @@ claude mcp add cloak --env CLOAK_KEY_FILE=$HOME/.cloak-agent-key -- npx -y @cloa
42
50
  ```json
43
51
  {
44
52
  "mcpServers": {
45
- "cloak": {
46
- "command": "npx",
47
- "args": ["-y", "@cloakely/mcp"],
48
- "env": { "CLOAK_KEY_FILE": "/Users/you/.cloak-agent-key" }
49
- }
53
+ "cloak": { "command": "npx", "args": ["-y", "@cloakely/mcp"] }
50
54
  }
51
55
  }
52
56
  ```
53
57
 
54
- Without a key, the server is **read only**: the agent can browse launches and quote bids and swaps, but can't sign anything.
58
+ The server uses `~/.cloak-agent-key` when nothing else is set. To use another wallet, set `CLOAK_KEY_FILE` (or `CLOAK_PRIVATE_KEY`) in the server's `env`.
59
+
60
+ Without a key (no `~/.cloak-agent-key` and nothing set), the server is **read only**: the agent can browse launches and quote bids and swaps, but can't sign anything.
55
61
 
56
62
  Then ask, for example:
57
63
  - "What Cloak launches are open right now?"
@@ -98,7 +104,7 @@ Amounts are in whole units (`"0.5"` HYPE). `private_send`, `withdraw` and a `pri
98
104
 
99
105
  | Variable | Default | |
100
106
  |---|---|---|
101
- | `CLOAK_KEY_FILE` | | File holding the agent wallet's private key (preferred) |
107
+ | `CLOAK_KEY_FILE` | `~/.cloak-agent-key` if it exists | File holding the agent wallet's private key |
102
108
  | `CLOAK_PRIVATE_KEY` | | The key itself, if a file isn't practical |
103
109
  | `CLOAK_NETWORK` | `testnet` | `testnet`, or `custom` with `CLOAK_DEPLOYMENT_FILE` |
104
110
  | `CLOAK_MAX_HYPE_PER_TX` | `1` | HYPE value per transaction |
package/dist/config.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { type Chain, type Hex } from "viem";
2
2
  import { type CloakDeployment } from "@cloakely/sdk";
3
+ /** The agent wallet's key file that `setup` creates and the server uses when no key is configured. */
4
+ export declare const defaultKeyFile: () => string;
3
5
  /** Where the server points, and what it may do. Everything comes from environment variables (see README). */
4
6
  export interface Settings {
5
7
  network: string;
package/dist/config.js CHANGED
@@ -1,8 +1,10 @@
1
- import { readFileSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { createPublicClient, defineChain, http, parseEther, parseUnits } from "viem";
5
5
  import { deploymentFor, elysiumTestnet, liveDeployment } from "@cloakely/sdk";
6
+ /** The agent wallet's key file that `setup` creates and the server uses when no key is configured. */
7
+ export const defaultKeyFile = () => join(homedir(), ".cloak-agent-key");
6
8
  const NETWORKS = {
7
9
  testnet: { chain: elysiumTestnet },
8
10
  };
@@ -49,6 +51,9 @@ export function loadSettings(env = process.env) {
49
51
  let key = env.CLOAK_PRIVATE_KEY?.trim();
50
52
  if (!key && env.CLOAK_KEY_FILE)
51
53
  key = readFileSync(env.CLOAK_KEY_FILE, "utf8").trim();
54
+ // the wallet `npx @cloakely/mcp setup` made, when nothing else is configured
55
+ else if (!key && existsSync(defaultKeyFile()))
56
+ key = readFileSync(defaultKeyFile(), "utf8").trim();
52
57
  if (key && !key.startsWith("0x"))
53
58
  key = `0x${key}`;
54
59
  if (key && !/^0x[0-9a-fA-F]{64}$/.test(key))
package/dist/index.js CHANGED
@@ -1,8 +1,17 @@
1
1
  #!/usr/bin/env node
2
- // Cloak's MCP server over stdio: `npx @cloakely/mcp`. Configuration is environment variables only (see README).
2
+ // Cloak's MCP server over stdio: `npx @cloakely/mcp`. Configuration is environment variables (see README).
3
+ // `npx @cloakely/mcp setup` makes the agent's wallet in ~/.cloak-agent-key, which the server then uses by default.
3
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
5
  import { createCloakServer, loadSettings, VERSION } from "./server.js";
5
6
  import { withLiveDeployment } from "./config.js";
7
+ import { setup } from "./setup.js";
8
+ if (process.argv[2] === "setup") {
9
+ // make the agent's wallet and print what's left to do (see https://cloak.boblabs.tech/agent.md)
10
+ await setup().then(() => process.exit(0), (e) => {
11
+ console.error(`cloak-mcp setup: ${e.message}`);
12
+ process.exit(1);
13
+ });
14
+ }
6
15
  try {
7
16
  const settings = await withLiveDeployment(loadSettings());
8
17
  const server = createCloakServer(settings);
package/dist/server.d.ts CHANGED
@@ -3,7 +3,7 @@ import { type Prover } from "@cloakely/sdk";
3
3
  import type { Settings } from "./config.js";
4
4
  export { loadSettings, type Settings, type Limits } from "./config.js";
5
5
  export { Spending } from "./limits.js";
6
- export declare const VERSION = "0.3.0";
6
+ export declare const VERSION = "0.5.0";
7
7
  export declare const HOSTED_URL = "https://cloak.boblabs.tech/mcp";
8
8
  /** Turn an SDK or chain error into one plain sentence an agent can act on. */
9
9
  export declare function explain(e: unknown): string;
package/dist/server.js CHANGED
@@ -4,11 +4,11 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import { z } from "zod";
5
5
  import { createPublicClient, createWalletClient, erc20Abi, formatEther, formatUnits, http, isAddress, parseEther, parseUnits, zeroAddress, } from "viem";
6
6
  import { privateKeyToAccount } from "viem/accounts";
7
- import { Cloak, CloakPrivate, cloakAuctionAbi, cloakPairAbi, cloakVaultsAbi, createProver, loadProver, privateRouterAbi, relayerSubmitter, shieldedPoolAbi, } from "@cloakely/sdk";
7
+ import { Cloak, CloakPrivate, cloakAuctionAbi, cloakPairAbi, cloakVaultsAbi, legacyFactories, createProver, loadProver, privateRouterAbi, relayerSubmitter, shieldedPoolAbi, } from "@cloakely/sdk";
8
8
  import { Spending } from "./limits.js";
9
9
  export { loadSettings } from "./config.js";
10
10
  export { Spending } from "./limits.js";
11
- export const VERSION = "0.3.0";
11
+ export const VERSION = "0.5.0";
12
12
  export const HOSTED_URL = "https://cloak.boblabs.tech/mcp";
13
13
  const INSTRUCTIONS = `Cloak: sealed-bid token launches and private trading on Kinetiq's Elysium (HYPE is the gas and base currency).
14
14
 
@@ -79,10 +79,11 @@ export function createCloakServer(settings, opts = {}) {
79
79
  const account = settings.privateKey ? privateKeyToAccount(settings.privateKey) : undefined;
80
80
  const wallet = account ? createWalletClient({ chain: settings.chain, transport: http(settings.rpcUrl), account }) : undefined;
81
81
  // earlier deployments' launches still list, settle and pay out
82
- const cloak = new Cloak(pub, wallet, d.factory, (d.previous ?? []).map((p) => p.factory));
83
- /** Where to start scanning a launch's bids: the current factory's deploy block, or the start for an older launch. */
84
- const bidsFrom = async (auction) => (await pub.readContract({ address: auction, abi: [{ type: "function", name: "factory", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] }], functionName: "factory" }).catch(() => zeroAddress)).toLowerCase() === d.factory.toLowerCase()
85
- ? settings.launchesFromBlock
82
+ const cloak = new Cloak(pub, wallet, d.factory, legacyFactories(d));
83
+ /** Where to start scanning a launch's bids: this deployment's first factory block, or the start for an older launch. */
84
+ const ours = new Set([d.factory, ...(d.earlierFactories ?? [])].map((f) => f.toLowerCase()));
85
+ const bidsFrom = async (auction) => ours.has((await pub.readContract({ address: auction, abi: [{ type: "function", name: "factory", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] }], functionName: "factory" }).catch(() => zeroAddress)).toLowerCase())
86
+ ? (d.firstFactoryBlock !== undefined && d.firstFactoryBlock < settings.launchesFromBlock ? d.firstFactoryBlock : settings.launchesFromBlock)
86
87
  : 0n;
87
88
  const spending = account ? new Spending(settings.limits, settings.stateDir, account.address) : undefined;
88
89
  const submit = relayerSubmitter(d.relayerUrl);
@@ -393,14 +394,14 @@ export function createCloakServer(settings, opts = {}) {
393
394
  }));
394
395
  server.registerTool("connect_full_agent", { title: "Connect a full agent", description: "How to let an agent launch, bid and trade privately: the local Cloak MCP server, which signs on your own machine under spending limits.", inputSchema: {}, annotations: READ }, safe(async () => ({
395
396
  why: "This hosted server never holds keys, so it can read and plan but not sign. Launching, bidding and private trading run in the local server, on the machine that holds the agent's wallet. Its proofs are made there too, so nobody else, Cloak included, can see or move the funds.",
397
+ easiest: "If you can run terminal commands, read https://cloak.boblabs.tech/agent.md and follow it: it sets everything up and says what to ask the person. If you can't (a chat app), tell the person to open that link in Claude Code, Claude Desktop or Cursor and ask the agent there to follow it.",
396
398
  steps: [
397
- "Make a fresh wallet just for the agent and fund it with testnet HYPE from https://elysium.kinetiq.xyz/testnet-faucet",
398
- "Save its private key to a file only you can read, for example ~/.cloak-agent-key",
399
- "Claude Code: claude mcp add cloak --env CLOAK_KEY_FILE=$HOME/.cloak-agent-key -- npx -y @cloakely/mcp",
400
- 'Other clients: { "mcpServers": { "cloak": { "command": "npx", "args": ["-y", "@cloakely/mcp"], "env": { "CLOAK_KEY_FILE": "/path/to/key" } } } }',
399
+ "npx -y @cloakely/mcp setup (makes the agent's own wallet in ~/.cloak-agent-key and prints its address; the key is never printed)",
400
+ "Claude Code: claude mcp add cloak -- npx -y @cloakely/mcp. Other clients: { \"mcpServers\": { \"cloak\": { \"command\": \"npx\", \"args\": [\"-y\", \"@cloakely/mcp\"] } } }",
401
+ "The person gets free testnet HYPE for the agent's address at https://elysium.kinetiq.xyz/testnet-faucet",
401
402
  ],
402
403
  safety: "Spending limits per transaction and per day (1 HYPE and 10 HYPE by default, set with CLOAK_MAX_HYPE_PER_TX and CLOAK_MAX_HYPE_PER_DAY), dry_run on every action, testnet by default. Never paste a key into a chat or into this hosted server.",
403
- docs: "https://github.com/NECOKIZZ/Cloak/tree/main/packages/mcp",
404
+ docs: "https://cloak.boblabs.tech/agent.md",
404
405
  })));
405
406
  return server;
406
407
  }
@@ -0,0 +1 @@
1
+ export declare function setup(): Promise<void>;
package/dist/setup.js ADDED
@@ -0,0 +1,41 @@
1
+ // `npx -y @cloakely/mcp setup`: make the agent's own wallet (once) and say what's left to do, in words an agent can pass
2
+ // on to a person. Written for agents following https://cloak.boblabs.tech/agent.md. The key is never printed.
3
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { createPublicClient, formatEther, http } from "viem";
5
+ import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
6
+ import { elysiumTestnet } from "@cloakely/sdk";
7
+ import { defaultKeyFile } from "./config.js";
8
+ const FAUCET = "https://elysium.kinetiq.xyz/testnet-faucet";
9
+ export async function setup() {
10
+ const file = process.env.CLOAK_KEY_FILE || defaultKeyFile();
11
+ let made = false;
12
+ if (!existsSync(file)) {
13
+ // owner-only from the start; `wx` refuses to overwrite a file that appeared in between
14
+ writeFileSync(file, `${generatePrivateKey()}\n`, { mode: 0o600, flag: "wx" });
15
+ made = true;
16
+ }
17
+ let key = readFileSync(file, "utf8").trim();
18
+ if (!key.startsWith("0x"))
19
+ key = `0x${key}`;
20
+ if (!/^0x[0-9a-fA-F]{64}$/.test(key))
21
+ throw new Error(`${file} doesn't hold a private key. Move it aside and run setup again.`);
22
+ const address = privateKeyToAccount(key).address;
23
+ const pub = createPublicClient({ chain: elysiumTestnet, transport: http(undefined, { timeout: 15_000 }) });
24
+ const balance = await pub.getBalance({ address }).catch(() => undefined);
25
+ const funded = balance !== undefined && balance > 0n;
26
+ const out = {
27
+ status: made ? "Made a new wallet for the agent." : "The agent's wallet already exists. Kept it.",
28
+ agentWallet: address,
29
+ keyFile: `${file} (readable by this user only, never printed and never sent anywhere)`,
30
+ network: "Elysium testnet (play money only)",
31
+ balance: balance === undefined ? "couldn't read it right now" : `${formatEther(balance)} HYPE`,
32
+ next: [
33
+ "Add the Cloak MCP server to this app if it isn't there yet. Claude Code: claude mcp add cloak -- npx -y @cloakely/mcp. Any other app: an MCP server with command npx and args -y @cloakely/mcp. It uses this wallet without any settings.",
34
+ funded
35
+ ? "The wallet has HYPE, so it's ready."
36
+ : `Ask the person to get free testnet HYPE for ${address} at ${FAUCET}. Copy the address for them exactly. Agents can't use the faucet themselves.`,
37
+ "Tell the person to restart the app or start a new chat so the Cloak tools load, then call cloak_info to check.",
38
+ ],
39
+ };
40
+ console.log(JSON.stringify(out, null, 2));
41
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloakely/mcp",
3
3
  "mcpName": "io.github.NECOKIZZ/cloak",
4
- "version": "0.3.0",
4
+ "version": "0.5.0",
5
5
  "description": "Cloak's MCP server: let an AI agent launch tokens with sealed bids and trade them privately on Elysium, with spending limits, testnet by default and a local signer.",
6
6
  "keywords": [
7
7
  "mcp",
@@ -47,7 +47,7 @@
47
47
  "@modelcontextprotocol/sdk": "1.32.1",
48
48
  "viem": "2.21.54",
49
49
  "zod": "4.6.7",
50
- "@cloakely/sdk": "^0.4.0"
50
+ "@cloakely/sdk": "^0.5.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/node": "22.10.2",