highwire-mcp 0.10.0 → 0.11.1
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 +8 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/ideas.d.ts +62 -0
- package/dist/ideas.js +174 -0
- package/dist/ideas.js.map +1 -0
- package/dist/new-cli.d.ts +14 -0
- package/dist/new-cli.js +107 -0
- package/dist/new-cli.js.map +1 -0
- package/dist/orders-cli.js +8 -0
- package/dist/orders-cli.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/tools/fees.js +7 -5
- package/dist/tools/fees.js.map +1 -1
- package/ideas/buyback-coin/PRD.md +124 -0
- package/ideas/buyback-coin/idea.json +121 -0
- package/ideas/buyback-coin/policy.example.json +12 -0
- package/ideas/community-treasury/PRD.md +144 -0
- package/ideas/community-treasury/idea.json +133 -0
- package/ideas/community-treasury/policy.example.json +12 -0
- package/ideas/dip-buyer/PRD.md +138 -0
- package/ideas/dip-buyer/idea.json +126 -0
- package/ideas/dip-buyer/policy.example.json +12 -0
- package/ideas/fee-funded-compute/PRD.md +143 -0
- package/ideas/fee-funded-compute/idea.json +124 -0
- package/ideas/fee-funded-compute/policy.example.json +12 -0
- package/ideas/launch-scout/PRD.md +134 -0
- package/ideas/launch-scout/idea.json +124 -0
- package/ideas/launch-scout/policy.example.json +11 -0
- package/ideas/starter/.env.example +5 -0
- package/ideas/starter/.mcp.json +12 -0
- package/ideas/starter/AGENTS.md +40 -0
- package/ideas/starter/CLAUDE.md +1 -0
- package/ideas/starter/README.md +85 -0
- package/ideas/starter/assets/README.md +1 -0
- package/ideas/starter/gitignore +8 -0
- package/ideas/starter/package.json +28 -0
- package/ideas/starter/scripts/env.mjs +23 -0
- package/ideas/starter/scripts/highwire.mjs +7 -0
- package/ideas/starter/scripts/service.mjs +70 -0
- package/ideas/starter/scripts/setup.mjs +65 -0
- package/ideas/starter/src/agent.ts +21 -0
- package/ideas/starter/src/highwire.ts +44 -0
- package/ideas/starter/src/start.ts +40 -0
- package/ideas/starter/src/tools.ts +43 -0
- package/ideas/starter/tsconfig.json +14 -0
- package/package.json +11 -4
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
// Highwire's command line, for the agent's own code: every command runs for this project's wallet, inside its policy.
|
|
8
|
+
|
|
9
|
+
// The project's .env, if there is one (SOLANA_RPC_URL, say): every command the agent runs inherits it.
|
|
10
|
+
const ENV_FILE = fileURLToPath(new URL("../.env", import.meta.url));
|
|
11
|
+
if (existsSync(ENV_FILE)) process.loadEnvFile(ENV_FILE);
|
|
12
|
+
|
|
13
|
+
export const HOME = process.env.HIGHWIRE_HOME || join(homedir(), ".highwire", "{{slug}}");
|
|
14
|
+
const CLI = fileURLToPath(new URL("../node_modules/highwire-mcp/dist/cli.js", import.meta.url));
|
|
15
|
+
|
|
16
|
+
export interface Result {
|
|
17
|
+
ok: boolean;
|
|
18
|
+
/** The command's output: its JSON when it was asked for (`--json`), otherwise its text. */
|
|
19
|
+
out: string;
|
|
20
|
+
err: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Runs `highwire <args>`. Never throws for a refusal: `ok` is false and `err` says why. */
|
|
24
|
+
export function highwire(args: string[]): Promise<Result> {
|
|
25
|
+
const wallet = join(HOME, "wallet.json");
|
|
26
|
+
const env = { ...process.env, HIGHWIRE_HOME: HOME, ...(existsSync(wallet) ? { HIGHWIRE_KEYPAIR_PATH: wallet } : {}) };
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
execFile(process.execPath, [CLI, ...args], { env, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
29
|
+
resolve({ ok: !error, out: stdout.trim(), err: stderr.trim() });
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Runs a command that takes `--json` and parses its result. */
|
|
35
|
+
export async function highwireJson<T = Record<string, unknown>>(args: string[]): Promise<{ ok: boolean; body: T | null; err: string }> {
|
|
36
|
+
const r = await highwire([...args, "--json"]);
|
|
37
|
+
let body: T | null = null;
|
|
38
|
+
try {
|
|
39
|
+
body = JSON.parse(r.out) as T;
|
|
40
|
+
} catch {
|
|
41
|
+
body = null;
|
|
42
|
+
}
|
|
43
|
+
return { ok: r.ok, body, err: r.err };
|
|
44
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { HOME } from "./highwire.ts";
|
|
6
|
+
|
|
7
|
+
// `npm start`: the whole agent, in one process. It runs Highwire's order runner, which fills the standing orders
|
|
8
|
+
// (fee claims, buybacks, limit buys, stop-losses), and the agent's own loop, `src/agent.ts --loop`. If either stops,
|
|
9
|
+
// both stop, so `npm run service` (or any process manager) restarts them together. The policy decides what's real:
|
|
10
|
+
// while it's a dry run everything is simulated, and once the user sets "live": true the same process acts, with a
|
|
11
|
+
// dry run's timed orders running straight away. Nothing here writes the policy.
|
|
12
|
+
|
|
13
|
+
const ROOT = fileURLToPath(new URL("..", import.meta.url));
|
|
14
|
+
const POLICY = join(HOME, "policy.json");
|
|
15
|
+
const log = (s: string) => console.log(`${new Date().toISOString().slice(0, 19)}Z ${s}`);
|
|
16
|
+
|
|
17
|
+
if (!existsSync(join(HOME, "wallet.json")) || !existsSync(POLICY)) {
|
|
18
|
+
log(`No wallet or policy in ${HOME} yet: run \`npm run setup\` first.`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
let live = false;
|
|
22
|
+
try {
|
|
23
|
+
live = (JSON.parse(readFileSync(POLICY, "utf8")) as { live?: boolean }).live === true;
|
|
24
|
+
} catch {
|
|
25
|
+
// Highwire's own commands say what's wrong with the policy.
|
|
26
|
+
}
|
|
27
|
+
log(live ? "The policy is live: orders fill and the agent acts." : `The policy is a dry run: everything is simulated until you set "live": true in ${POLICY}.`);
|
|
28
|
+
if (!process.env.SOLANA_RPC_URL) log("Using Solana's public RPC, which rate-limits busy agents. Put SOLANA_RPC_URL in .env for a steadier one.");
|
|
29
|
+
|
|
30
|
+
const children: ChildProcess[] = [
|
|
31
|
+
spawn(process.execPath, [join(ROOT, "scripts/highwire.mjs"), "orders", "run"], { cwd: ROOT, stdio: "inherit" }),
|
|
32
|
+
spawn(join(ROOT, "node_modules/.bin/tsx"), [join(ROOT, "src/agent.ts"), "--loop"], { cwd: ROOT, stdio: "inherit" }),
|
|
33
|
+
];
|
|
34
|
+
for (const c of children) {
|
|
35
|
+
c.on("exit", (code) => {
|
|
36
|
+
for (const other of children) if (other !== c && other.exitCode === null) other.kill("SIGTERM");
|
|
37
|
+
process.exitCode = code ?? 1;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
for (const s of ["SIGINT", "SIGTERM"] as const) process.on(s, () => children.forEach((c) => c.kill(s)));
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
5
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
6
|
+
import { HOME } from "./highwire.ts";
|
|
7
|
+
|
|
8
|
+
// Every Highwire tool, from code: the same MCP server a coding agent uses, started for this project's wallet. Execute
|
|
9
|
+
// tools (execute_trade, execute_claim, post_comment …) still pass the policy, and are dry runs until it's live.
|
|
10
|
+
|
|
11
|
+
const CLI = fileURLToPath(new URL("../node_modules/highwire-mcp/dist/cli.js", import.meta.url));
|
|
12
|
+
|
|
13
|
+
export interface ToolResult<T> {
|
|
14
|
+
ok: boolean;
|
|
15
|
+
/** The tool's JSON. On failure: `{error, code, next_step}`. */
|
|
16
|
+
body: T;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class Highwire {
|
|
20
|
+
private constructor(private readonly client: Client) {}
|
|
21
|
+
|
|
22
|
+
static async connect(): Promise<Highwire> {
|
|
23
|
+
const wallet = join(HOME, "wallet.json");
|
|
24
|
+
const env: Record<string, string> = {};
|
|
25
|
+
for (const [k, v] of Object.entries(process.env)) if (v !== undefined) env[k] = v;
|
|
26
|
+
env.HIGHWIRE_HOME = HOME;
|
|
27
|
+
if (existsSync(wallet)) env.HIGHWIRE_KEYPAIR_PATH = wallet;
|
|
28
|
+
const client = new Client({ name: "{{slug}}", version: "0" });
|
|
29
|
+
await client.connect(new StdioClientTransport({ command: process.execPath, args: [CLI], env, stderr: "ignore" }));
|
|
30
|
+
return new Highwire(client);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Calls one tool. A refusal or error comes back with `ok: false`, never as a throw. */
|
|
34
|
+
async call<T = Record<string, unknown>>(name: string, args: Record<string, unknown> = {}): Promise<ToolResult<T>> {
|
|
35
|
+
const res = await this.client.callTool({ name, arguments: args });
|
|
36
|
+
const text = (res.content as Array<{ type: string; text?: string }>)[0]?.text ?? "{}";
|
|
37
|
+
return { ok: !res.isError, body: JSON.parse(text) as T };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
close(): Promise<void> {
|
|
41
|
+
return this.client.close();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"types": ["node"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noUncheckedIndexedAccess": true,
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"skipLibCheck": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "highwire-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Highwire: the MCP server that lets AI agents use higher.fun, the open-source Solana launchpad",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,11 +17,17 @@
|
|
|
17
17
|
"highwire-source": "./src/index.ts",
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./ideas": {
|
|
22
|
+
"highwire-source": "./src/ideas.ts",
|
|
23
|
+
"types": "./dist/ideas.d.ts",
|
|
24
|
+
"default": "./dist/ideas.js"
|
|
20
25
|
}
|
|
21
26
|
},
|
|
22
27
|
"files": [
|
|
23
28
|
"dist",
|
|
24
29
|
"skills",
|
|
30
|
+
"ideas",
|
|
25
31
|
"README.md",
|
|
26
32
|
"LICENSE"
|
|
27
33
|
],
|
|
@@ -35,13 +41,14 @@
|
|
|
35
41
|
"test": "tsx --conditions=highwire-source --test test/*.test.ts",
|
|
36
42
|
"smoke": "tsx --conditions=highwire-source scripts/smoke.ts",
|
|
37
43
|
"simulate": "tsx --conditions=highwire-source scripts/simulate.ts",
|
|
38
|
-
"prepack": "npm run build && cp ../../LICENSE .",
|
|
44
|
+
"prepack": "npm run build && cp ../../LICENSE . && rm -rf ideas && cp -R ../../ideas ideas",
|
|
45
|
+
"postpack": "rm -rf ideas",
|
|
39
46
|
"rehearse": "tsx --conditions=highwire-source scripts/rehearse.ts"
|
|
40
47
|
},
|
|
41
48
|
"dependencies": {
|
|
42
49
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
43
|
-
"highwire-agent": "0.
|
|
44
|
-
"highwire-core": "0.
|
|
50
|
+
"highwire-agent": "0.11.1",
|
|
51
|
+
"highwire-core": "0.11.1",
|
|
45
52
|
"zod": "4.6.7"
|
|
46
53
|
},
|
|
47
54
|
"mcpName": "fun.highwire/highwire"
|