@pineforge/codegen-mcp 0.1.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 +104 -0
- package/dist/index.js +179 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# `@pineforge/codegen-mcp`
|
|
2
|
+
|
|
3
|
+
Local stdio MCP server: bridges an AI agent to the hosted PineForge codegen API
|
|
4
|
+
**and** the user's local Docker daemon. The OHLCV file never leaves the user's
|
|
5
|
+
machine — only the Pine source travels to the API.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| name | runs on | quota |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `transpile_pine` | remote API | counts (refunded on compile error) |
|
|
12
|
+
| `get_quota` | remote API | free |
|
|
13
|
+
| `backtest_pine` | local Docker | counts 1 (the transpile call inside) |
|
|
14
|
+
| `pull_engine_image` | local Docker | free |
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx -y @pineforge/codegen-mcp
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Requires:
|
|
23
|
+
- Node ≥ 20
|
|
24
|
+
- Docker daemon running locally
|
|
25
|
+
- A PineForge API key (`pf_…`)
|
|
26
|
+
|
|
27
|
+
## Auth
|
|
28
|
+
|
|
29
|
+
Set env vars:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
export PINEFORGE_API_KEY="pf_..."
|
|
33
|
+
export PINEFORGE_GATEWAY="https://codegen-gateway.luis-fca.workers.dev" # optional
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Client configuration
|
|
37
|
+
|
|
38
|
+
### Claude Desktop / generic JSON
|
|
39
|
+
|
|
40
|
+
```jsonc
|
|
41
|
+
{
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"pineforge-codegen": {
|
|
44
|
+
"command": "npx",
|
|
45
|
+
"args": ["-y", "@pineforge/codegen-mcp"],
|
|
46
|
+
"env": {
|
|
47
|
+
"PINEFORGE_API_KEY": "pf_..."
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Claude Code CLI
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
claude mcp add pineforge-codegen \
|
|
58
|
+
--transport stdio \
|
|
59
|
+
--env PINEFORGE_API_KEY=pf_... \
|
|
60
|
+
-- npx -y @pineforge/codegen-mcp
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Cursor
|
|
64
|
+
|
|
65
|
+
Settings → MCP → New MCP Server → paste the JSON config above.
|
|
66
|
+
|
|
67
|
+
## `backtest_pine` example
|
|
68
|
+
|
|
69
|
+
```jsonc
|
|
70
|
+
{
|
|
71
|
+
"source": "//@version=6\nstrategy(\"sma cross\")\n...",
|
|
72
|
+
"ohlcv_csv_path": "./btcusdt_15m_7d.csv"
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Returns the same JSON schema as the standalone `pineforge-engine` Docker image:
|
|
77
|
+
|
|
78
|
+
```jsonc
|
|
79
|
+
{
|
|
80
|
+
"engine": "pineforge",
|
|
81
|
+
"summary": { "total_trades": 49, "net_pnl": -190.85, ... },
|
|
82
|
+
"trades": [ ... ],
|
|
83
|
+
"elapsed_seconds": 0.0042,
|
|
84
|
+
"_meta": { "strategy_cpp_bytes": 5079, "image": "ghcr.io/.../pineforge-engine:latest" }
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Filesystem scope
|
|
89
|
+
|
|
90
|
+
By default, OHLCV paths must be inside the current working directory of the MCP
|
|
91
|
+
server process. Override with:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
export PINEFORGE_ALLOW_ANYWHERE=1
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Other env vars
|
|
98
|
+
|
|
99
|
+
| var | default | purpose |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `PINEFORGE_API_KEY` | (required) | Bearer for the codegen API |
|
|
102
|
+
| `PINEFORGE_GATEWAY` | production URL | Override the API host |
|
|
103
|
+
| `PINEFORGE_ALLOW_ANYWHERE` | `0` | Allow OHLCV paths outside cwd |
|
|
104
|
+
| `PINEFORGE_DOCKER_TIMEOUT_MS` | `120000` | Hard kill for `docker pull` / `docker run` |
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @pineforge/codegen-mcp — local stdio MCP server.
|
|
4
|
+
*
|
|
5
|
+
* Bridges an AI agent to the hosted PineForge codegen API (transpile,
|
|
6
|
+
* quota) AND to the user's local Docker daemon (backtest runner). Runs
|
|
7
|
+
* on the user's machine so OHLCV files never leave it; only the Pine
|
|
8
|
+
* source travels to the codegen API.
|
|
9
|
+
*
|
|
10
|
+
* Auth: PINEFORGE_API_KEY env var (required).
|
|
11
|
+
* Gateway override: PINEFORGE_GATEWAY env var (default: production URL).
|
|
12
|
+
*/
|
|
13
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import { spawn } from "node:child_process";
|
|
17
|
+
import { mkdtemp, writeFile, rm, stat, readFile } from "node:fs/promises";
|
|
18
|
+
import { tmpdir } from "node:os";
|
|
19
|
+
import { join, resolve, isAbsolute } from "node:path";
|
|
20
|
+
// ─── Config ───────────────────────────────────────────────────────────────
|
|
21
|
+
const GATEWAY = process.env.PINEFORGE_GATEWAY ?? "https://codegen-gateway.luis-fca.workers.dev";
|
|
22
|
+
const API_KEY = process.env.PINEFORGE_API_KEY ?? "";
|
|
23
|
+
const DEFAULT_IMAGE = "ghcr.io/fullpass-4pass/pineforge-engine:latest";
|
|
24
|
+
const ALLOW_ANYWHERE = process.env.PINEFORGE_ALLOW_ANYWHERE === "1";
|
|
25
|
+
const DOCKER_TIMEOUT_MS = Number(process.env.PINEFORGE_DOCKER_TIMEOUT_MS ?? 120_000);
|
|
26
|
+
if (!API_KEY) {
|
|
27
|
+
console.error("[pineforge-mcp] PINEFORGE_API_KEY env var is required");
|
|
28
|
+
process.exit(2);
|
|
29
|
+
}
|
|
30
|
+
async function callTranspile(source) {
|
|
31
|
+
const resp = await fetch(`${GATEWAY}/transpile`, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "x-api-key": API_KEY, "content-type": "text/plain" },
|
|
34
|
+
body: source,
|
|
35
|
+
});
|
|
36
|
+
const text = await resp.text();
|
|
37
|
+
const quota = {
|
|
38
|
+
used: Number(resp.headers.get("x-quota-used") ?? 0),
|
|
39
|
+
limit: Number(resp.headers.get("x-quota-limit") ?? 0),
|
|
40
|
+
period: resp.headers.get("x-quota-period") ?? "",
|
|
41
|
+
refunded: resp.headers.get("x-quota-refunded") === "1",
|
|
42
|
+
};
|
|
43
|
+
if (!resp.ok)
|
|
44
|
+
throw new Error(`transpile failed (${resp.status}): ${text}`);
|
|
45
|
+
return { cpp: text, quota };
|
|
46
|
+
}
|
|
47
|
+
async function callGetQuota() {
|
|
48
|
+
const resp = await fetch(`${GATEWAY}/quota`, {
|
|
49
|
+
method: "GET",
|
|
50
|
+
headers: { "x-api-key": API_KEY },
|
|
51
|
+
});
|
|
52
|
+
const text = await resp.text();
|
|
53
|
+
if (!resp.ok)
|
|
54
|
+
throw new Error(`get_quota failed (${resp.status}): ${text}`);
|
|
55
|
+
return JSON.parse(text);
|
|
56
|
+
}
|
|
57
|
+
async function runBacktest(args) {
|
|
58
|
+
const csvPath = await resolveCsvPath(args.ohlcv_csv_path);
|
|
59
|
+
const image = args.image ?? DEFAULT_IMAGE;
|
|
60
|
+
// 1. transpile via remote API
|
|
61
|
+
const { cpp } = await callTranspile(args.source);
|
|
62
|
+
// 2. write strategy.cpp into a tmp dir, mount that + the user's CSV
|
|
63
|
+
const tmp = await mkdtemp(join(tmpdir(), "pineforge-bt-"));
|
|
64
|
+
const cppPath = join(tmp, "strategy.cpp");
|
|
65
|
+
await writeFile(cppPath, cpp, "utf8");
|
|
66
|
+
try {
|
|
67
|
+
const { stdout, stderr, code } = await dockerRun(image, cppPath, csvPath);
|
|
68
|
+
if (code !== 0) {
|
|
69
|
+
throw new Error(`docker exited ${code}\n` +
|
|
70
|
+
`stderr (last 2KB):\n${stderr.slice(-2048)}`);
|
|
71
|
+
}
|
|
72
|
+
let report;
|
|
73
|
+
try {
|
|
74
|
+
report = JSON.parse(stdout);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
throw new Error(`backtest produced non-JSON output (first 500B):\n${stdout.slice(0, 500)}`);
|
|
78
|
+
}
|
|
79
|
+
return { ...report, _meta: { strategy_cpp_bytes: cpp.length, image } };
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
rm(tmp, { recursive: true, force: true }).catch(() => undefined);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function pullImage(image) {
|
|
86
|
+
const child = spawn("docker", ["pull", image], { stdio: ["ignore", "pipe", "pipe"] });
|
|
87
|
+
const { stdout, stderr, code } = await collectChild(child, DOCKER_TIMEOUT_MS);
|
|
88
|
+
if (code !== 0)
|
|
89
|
+
throw new Error(`docker pull failed (${code}): ${stderr.slice(-1024)}`);
|
|
90
|
+
return { image, pulled: true, output: (stdout + stderr).trim().slice(-2048) };
|
|
91
|
+
}
|
|
92
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────
|
|
93
|
+
async function resolveCsvPath(p) {
|
|
94
|
+
const abs = isAbsolute(p) ? p : resolve(process.cwd(), p);
|
|
95
|
+
if (!ALLOW_ANYWHERE && !abs.startsWith(process.cwd() + "/") && abs !== process.cwd()) {
|
|
96
|
+
throw new Error(`OHLCV path '${abs}' is outside cwd '${process.cwd()}'. ` +
|
|
97
|
+
`Set PINEFORGE_ALLOW_ANYWHERE=1 to override.`);
|
|
98
|
+
}
|
|
99
|
+
const st = await stat(abs).catch(() => null);
|
|
100
|
+
if (!st || !st.isFile())
|
|
101
|
+
throw new Error(`OHLCV file not found: ${abs}`);
|
|
102
|
+
// Sanity-check header line.
|
|
103
|
+
const head = (await readFile(abs, "utf8")).slice(0, 200).split(/\r?\n/)[0] ?? "";
|
|
104
|
+
const expected = ["timestamp", "open", "high", "low", "close", "volume"];
|
|
105
|
+
const cols = head.toLowerCase().split(",").map((s) => s.trim());
|
|
106
|
+
if (expected.some((c, i) => cols[i] !== c)) {
|
|
107
|
+
throw new Error(`OHLCV header mismatch. Expected: ${expected.join(",")}\nGot: ${head}`);
|
|
108
|
+
}
|
|
109
|
+
return abs;
|
|
110
|
+
}
|
|
111
|
+
async function dockerRun(image, cppPath, csvPath) {
|
|
112
|
+
const child = spawn("docker", [
|
|
113
|
+
"run", "--rm",
|
|
114
|
+
"--network=none",
|
|
115
|
+
"-v", `${cppPath}:/in/strategy.cpp:ro`,
|
|
116
|
+
"-v", `${csvPath}:/in/ohlcv.csv:ro`,
|
|
117
|
+
image,
|
|
118
|
+
], { stdio: ["ignore", "pipe", "pipe"] });
|
|
119
|
+
return collectChild(child, DOCKER_TIMEOUT_MS);
|
|
120
|
+
}
|
|
121
|
+
function collectChild(child, timeoutMs) {
|
|
122
|
+
return new Promise((resolveP, rejectP) => {
|
|
123
|
+
let stdout = "", stderr = "";
|
|
124
|
+
child.stdout?.on("data", (d) => { stdout += d.toString(); });
|
|
125
|
+
child.stderr?.on("data", (d) => { stderr += d.toString(); });
|
|
126
|
+
const timer = setTimeout(() => {
|
|
127
|
+
child.kill("SIGKILL");
|
|
128
|
+
rejectP(new Error(`docker timed out after ${timeoutMs}ms`));
|
|
129
|
+
}, timeoutMs);
|
|
130
|
+
child.on("error", (e) => { clearTimeout(timer); rejectP(e); });
|
|
131
|
+
child.on("close", (code) => {
|
|
132
|
+
clearTimeout(timer);
|
|
133
|
+
resolveP({ stdout, stderr, code: code ?? 1 });
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const asTextResult = (value) => ({
|
|
138
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
139
|
+
});
|
|
140
|
+
// ─── MCP server wiring ────────────────────────────────────────────────────
|
|
141
|
+
const server = new McpServer({ name: "pineforge-codegen-mcp", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
142
|
+
server.registerTool("transpile_pine", {
|
|
143
|
+
description: "Transpile PineScript v6 source to a C++ translation unit using the hosted " +
|
|
144
|
+
"PineForge codegen API. Compile failures DO NOT consume the monthly quota — " +
|
|
145
|
+
"only successful 200 responses count. Use backtest_pine if you also want to " +
|
|
146
|
+
"run the strategy locally.",
|
|
147
|
+
inputSchema: {
|
|
148
|
+
source: z.string().describe("PineScript v6 source (must include //@version=6)."),
|
|
149
|
+
},
|
|
150
|
+
}, async ({ source }) => asTextResult(await callTranspile(source)));
|
|
151
|
+
server.registerTool("get_quota", {
|
|
152
|
+
description: "Return the current calendar month's API quota usage for the configured key: " +
|
|
153
|
+
"{used, limit, period, expires_at, tier}. Free — does not consume quota.",
|
|
154
|
+
inputSchema: {},
|
|
155
|
+
}, async () => asTextResult(await callGetQuota()));
|
|
156
|
+
server.registerTool("backtest_pine", {
|
|
157
|
+
description: "Transpile a PineScript v6 strategy and run it against an OHLCV CSV via the " +
|
|
158
|
+
"pineforge-engine Docker image on the user's local machine. The OHLCV file " +
|
|
159
|
+
"stays on the user's box; only the Pine source travels over the network. " +
|
|
160
|
+
"Returns the parsed JSON report (summary, trades, elapsed_seconds).",
|
|
161
|
+
inputSchema: {
|
|
162
|
+
source: z.string().describe("PineScript v6 source."),
|
|
163
|
+
ohlcv_csv_path: z.string().describe("Absolute or cwd-relative path to OHLCV CSV with header " +
|
|
164
|
+
"'timestamp,open,high,low,close,volume' (timestamp = UNIX ms UTC)."),
|
|
165
|
+
image: z.string().optional().describe(`Docker image override. Defaults to ${DEFAULT_IMAGE}.`),
|
|
166
|
+
},
|
|
167
|
+
}, async ({ source, ohlcv_csv_path, image }) => asTextResult(await runBacktest({ source, ohlcv_csv_path, image })));
|
|
168
|
+
server.registerTool("pull_engine_image", {
|
|
169
|
+
description: "Run `docker pull` for the pineforge-engine runtime image on the user's " +
|
|
170
|
+
"machine. Does not consume API quota. Useful before the first backtest_pine " +
|
|
171
|
+
"call.",
|
|
172
|
+
inputSchema: {
|
|
173
|
+
image: z.string().optional().describe(`Image to pull. Defaults to ${DEFAULT_IMAGE}.`),
|
|
174
|
+
},
|
|
175
|
+
}, async ({ image }) => asTextResult(await pullImage(image ?? DEFAULT_IMAGE)));
|
|
176
|
+
const transport = new StdioServerTransport();
|
|
177
|
+
await server.connect(transport);
|
|
178
|
+
console.error("[pineforge-mcp] ready (stdio) — gateway:", GATEWAY);
|
|
179
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEtD,6EAA6E;AAE7E,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,8CAA8C,CAAC;AAChG,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC;AACpD,MAAM,aAAa,GAAG,gDAAgD,CAAC;AACvE,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,KAAK,GAAG,CAAC;AACpE,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,OAAO,CAAC,CAAC;AAErF,IAAI,CAAC,OAAO,EAAE,CAAC;IACb,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;IACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAMD,KAAK,UAAU,aAAa,CAAC,MAAc;IACzC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,YAAY,EAAE;QAC/C,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE;QAC/D,IAAI,EAAE,MAAM;KACb,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAU;QACnB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACnD,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE;QAChD,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,GAAG;KACvD,CAAC;IACF,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IAC5E,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC9B,CAAC;AAED,KAAK,UAAU,YAAY;IACzB,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE;QAC3C,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE;KAClC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAgE;IACzF,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,aAAa,CAAC;IAE1C,8BAA8B;IAC9B,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEjD,oEAAoE;IACpE,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAC1C,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAEtC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1E,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,iBAAiB,IAAI,IAAI;gBACzB,uBAAuB,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAC7C,CAAC;QACJ,CAAC;QACD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAAC,CAAC;QACpC,MAAM,CAAC;YACL,MAAM,IAAI,KAAK,CACb,oDAAoD,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC3E,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,GAAI,MAAiB,EAAE,KAAK,EAAE,EAAE,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;IACrF,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACnE,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,KAAa;IACpC,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACtF,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,YAAY,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;IAC9E,IAAI,IAAI,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;AAChF,CAAC;AAED,6EAA6E;AAE7E,KAAK,UAAU,cAAc,CAAC,CAAS;IACrC,MAAM,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IAC1D,IAAI,CAAC,cAAc,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,IAAI,GAAG,KAAK,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;QACrF,MAAM,IAAI,KAAK,CACb,eAAe,GAAG,qBAAqB,OAAO,CAAC,GAAG,EAAE,KAAK;YACzD,6CAA6C,CAC9C,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,EAAE,CAAC,CAAC;IACzE,4BAA4B;IAC5B,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjF,MAAM,QAAQ,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IACzE,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAChE,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,oCAAoC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CACvE,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAID,KAAK,UAAU,SAAS,CAAC,KAAa,EAAE,OAAe,EAAE,OAAe;IACtE,MAAM,KAAK,GAAG,KAAK,CACjB,QAAQ,EACR;QACE,KAAK,EAAE,MAAM;QACb,gBAAgB;QAChB,IAAI,EAAE,GAAG,OAAO,sBAAsB;QACtC,IAAI,EAAE,GAAG,OAAO,mBAAmB;QACnC,KAAK;KACN,EACD,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CACtC,CAAC;IACF,OAAO,YAAY,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,YAAY,CAAC,KAA+B,EAAE,SAAiB;IACtE,OAAO,IAAI,OAAO,CAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;QACrD,IAAI,MAAM,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;QAC7B,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7D,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACtB,OAAO,CAAC,IAAI,KAAK,CAAC,0BAA0B,SAAS,IAAI,CAAC,CAAC,CAAC;QAC9D,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC;IACxC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;CAC3E,CAAC,CAAC;AAEH,6EAA6E;AAE7E,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,OAAO,EAAE,EACnD,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;IACE,WAAW,EACT,4EAA4E;QAC5E,6EAA6E;QAC7E,6EAA6E;QAC7E,2BAA2B;IAC7B,WAAW,EAAE;QACX,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC;KACjF;CACF,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC,CAChE,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,WAAW,EACX;IACE,WAAW,EACT,8EAA8E;QAC9E,yEAAyE;IAC3E,WAAW,EAAE,EAAE;CAChB,EACD,KAAK,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,YAAY,EAAE,CAAC,CAC/C,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;IACE,WAAW,EACT,6EAA6E;QAC7E,4EAA4E;QAC5E,0EAA0E;QAC1E,oEAAoE;IACtE,WAAW,EAAE;QACX,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;QACpD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CACjC,yDAAyD;YACzD,mEAAmE,CACpE;QACD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CACnC,sCAAsC,aAAa,GAAG,CACvD;KACF;CACF,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,EAAE,CAC1C,YAAY,CAAC,MAAM,WAAW,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,CACrE,CAAC;AAEF,MAAM,CAAC,YAAY,CACjB,mBAAmB,EACnB;IACE,WAAW,EACT,yEAAyE;QACzE,6EAA6E;QAC7E,OAAO;IACT,WAAW,EAAE;QACX,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CACnC,8BAA8B,aAAa,GAAG,CAC/C;KACF;CACF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,SAAS,CAAC,KAAK,IAAI,aAAa,CAAC,CAAC,CAC3E,CAAC;AAEF,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAChC,OAAO,CAAC,KAAK,CAAC,0CAA0C,EAAE,OAAO,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pineforge/codegen-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local MCP server: PineScript v6 → C++ via the PineForge codegen API + local Docker backtest runner.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pineforge-codegen-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=20"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"dev": "tsx src/index.ts",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
24
|
+
"zod": "^4.4.3"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20.0.0",
|
|
28
|
+
"tsx": "^4.7.0",
|
|
29
|
+
"typescript": "^5.6.0"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"pinescript",
|
|
33
|
+
"mcp",
|
|
34
|
+
"model-context-protocol",
|
|
35
|
+
"codegen",
|
|
36
|
+
"backtest",
|
|
37
|
+
"trading"
|
|
38
|
+
],
|
|
39
|
+
"homepage": "https://github.com/fullpass-4pass/pineforge-codegen",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/fullpass-4pass/pineforge-codegen.git",
|
|
43
|
+
"directory": "cloud/mcp-local"
|
|
44
|
+
},
|
|
45
|
+
"license": "Proprietary"
|
|
46
|
+
}
|