@branch42-labs/netraptor-mcp 0.0.0-stage → 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 CHANGED
@@ -1,3 +1,45 @@
1
- # Temporary Holding Version
1
+ # @branch42-labs/netraptor-mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Netraptor's local, read-only MCP server lets coding agents query API traffic captured by the Netraptor Chrome extension. Captured data stays on your computer.
4
+
5
+ ## Setup
6
+
7
+ 1. In Netraptor, open **Settings → Local MCP server** and copy the pairing command.
8
+ 2. Run it once, then enable the Local MCP server switch.
9
+ 3. Configure your MCP client.
10
+
11
+ Claude Code:
12
+
13
+ ```sh
14
+ claude mcp add netraptor -- npx -y @branch42-labs/netraptor-mcp
15
+ ```
16
+
17
+ Cursor (`~/.cursor/mcp.json` or the project's `.cursor/mcp.json`):
18
+
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "netraptor": {
23
+ "command": "npx",
24
+ "args": ["-y", "@branch42-labs/netraptor-mcp"]
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ The bridge listens only on `127.0.0.1:47321`. Set `NETRAPTOR_MCP_PORT` for the server and use the same port in extension settings to change it.
31
+
32
+ ## Security model
33
+
34
+ - The listener binds to loopback only.
35
+ - WebSocket upgrades are accepted only from the paired `chrome-extension://<id>` Origin.
36
+ - Pairing stores a derived secret and extension ID in `~/.netraptor/mcp.json` with mode `0600`.
37
+ - Every connection performs mutual HMAC-SHA256 challenge/response before captured data is accepted or sent.
38
+ - Only one authenticated extension connection is active; a newer connection replaces it.
39
+ - All exposed tools are read-only, and authorization/cookie headers are redacted.
40
+
41
+ To pair again, generate a new code in Netraptor Settings and run:
42
+
43
+ ```sh
44
+ npx -y @branch42-labs/netraptor-mcp pair <code>
45
+ ```
package/dist/auth.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export declare const HANDSHAKE_TIMEOUT_MS = 10000;
2
+ export declare function nonce(): string;
3
+ export declare function hmac(secret: string, role: "server" | "extension", challenge: string): string;
4
+ export declare function validHmac(secret: string, role: "server" | "extension", challenge: string, proof: unknown): boolean;
package/dist/auth.js ADDED
@@ -0,0 +1,18 @@
1
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
2
+ export const HANDSHAKE_TIMEOUT_MS = 10_000;
3
+ export function nonce() {
4
+ return randomBytes(32).toString("base64url");
5
+ }
6
+ export function hmac(secret, role, challenge) {
7
+ return createHmac("sha256", Buffer.from(secret, "base64url"))
8
+ .update(`${role}:${challenge}`)
9
+ .digest("base64url");
10
+ }
11
+ export function validHmac(secret, role, challenge, proof) {
12
+ if (typeof proof !== "string")
13
+ return false;
14
+ const expected = Buffer.from(hmac(secret, role, challenge));
15
+ const actual = Buffer.from(proof);
16
+ return expected.length === actual.length && timingSafeEqual(expected, actual);
17
+ }
18
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvE,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAE3C,MAAM,UAAU,KAAK;IACnB,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,MAAc,EAAE,IAA4B,EAAE,SAAiB;IAClF,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;SAC1D,MAAM,CAAC,GAAG,IAAI,IAAI,SAAS,EAAE,CAAC;SAC9B,MAAM,CAAC,WAAW,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,MAAc,EACd,IAA4B,EAC5B,SAAiB,EACjB,KAAc;IAEd,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClC,OAAO,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,IAAI,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAChF,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { PairingConfig } from "./pairing.js";
2
+ export declare const NOT_CONNECTED = "Netraptor isn't connected. Open Chrome, then turn on Local MCP server in Netraptor Settings (Pro).";
3
+ export declare const NOT_PAIRED = "Run `npx -y @branch42-labs/netraptor-mcp pair <code>` with the code from Netraptor Settings \u2192 Local MCP server.";
4
+ export declare class ExtensionBridgeServer {
5
+ private pairing;
6
+ private readonly host;
7
+ private readonly port;
8
+ private readonly requestTimeoutMs;
9
+ private readonly loadPairing?;
10
+ private readonly http;
11
+ private readonly wss;
12
+ private active?;
13
+ private counter;
14
+ private readonly pending;
15
+ constructor(pairing: PairingConfig | undefined, host?: string, port?: number, requestTimeoutMs?: number, loadPairing?: (() => Promise<PairingConfig | undefined>) | undefined);
16
+ get address(): {
17
+ host: string;
18
+ port: number;
19
+ } | undefined;
20
+ get connected(): boolean;
21
+ listen(): Promise<void>;
22
+ close(): Promise<void>;
23
+ request(method: string, params: Record<string, unknown>): Promise<unknown>;
24
+ private authenticate;
25
+ private onMessage;
26
+ }
@@ -0,0 +1,161 @@
1
+ import { createServer } from "node:http";
2
+ import { WebSocketServer, WebSocket } from "ws";
3
+ import { HANDSHAKE_TIMEOUT_MS, hmac, nonce, validHmac } from "./auth.js";
4
+ import { parseMessage } from "./protocol.js";
5
+ const REQUEST_TIMEOUT_MS = 10_000;
6
+ export const NOT_CONNECTED = "Netraptor isn't connected. Open Chrome, then turn on Local MCP server in Netraptor Settings (Pro).";
7
+ export const NOT_PAIRED = "Run `npx -y @branch42-labs/netraptor-mcp pair <code>` with the code from Netraptor Settings → Local MCP server.";
8
+ export class ExtensionBridgeServer {
9
+ pairing;
10
+ host;
11
+ port;
12
+ requestTimeoutMs;
13
+ loadPairing;
14
+ http;
15
+ wss = new WebSocketServer({ noServer: true });
16
+ active;
17
+ counter = 0;
18
+ pending = new Map();
19
+ constructor(pairing, host = "127.0.0.1", port = 47321, requestTimeoutMs = REQUEST_TIMEOUT_MS,
20
+ // Re-read on each connection so pairing after the agent started the server works.
21
+ loadPairing) {
22
+ this.pairing = pairing;
23
+ this.host = host;
24
+ this.port = port;
25
+ this.requestTimeoutMs = requestTimeoutMs;
26
+ this.loadPairing = loadPairing;
27
+ this.http = createServer((_request, response) => {
28
+ response.writeHead(404).end();
29
+ });
30
+ this.http.on("upgrade", async (request, socket, head) => {
31
+ if (this.loadPairing)
32
+ this.pairing = await this.loadPairing().catch(() => this.pairing);
33
+ const expected = this.pairing ? `chrome-extension://${this.pairing.extensionId}` : undefined;
34
+ if (!expected || request.headers.origin !== expected) {
35
+ socket.write("HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n");
36
+ socket.destroy();
37
+ return;
38
+ }
39
+ this.wss.handleUpgrade(request, socket, head, (ws) => this.authenticate(ws));
40
+ });
41
+ }
42
+ get address() {
43
+ const address = this.http.address();
44
+ return address && typeof address === "object"
45
+ ? { host: this.host, port: address.port }
46
+ : undefined;
47
+ }
48
+ get connected() {
49
+ return this.active?.readyState === WebSocket.OPEN;
50
+ }
51
+ async listen() {
52
+ await new Promise((resolve, reject) => {
53
+ this.http.once("error", reject);
54
+ this.http.listen(this.port, this.host, () => {
55
+ this.http.off("error", reject);
56
+ resolve();
57
+ });
58
+ });
59
+ }
60
+ async close() {
61
+ this.active?.close(1001, "Server stopping");
62
+ for (const pending of this.pending.values()) {
63
+ clearTimeout(pending.timer);
64
+ pending.reject(new Error(NOT_CONNECTED));
65
+ }
66
+ this.pending.clear();
67
+ for (const client of this.wss.clients)
68
+ client.terminate();
69
+ await new Promise((resolve) => this.wss.close(() => resolve()));
70
+ await new Promise((resolve, reject) => {
71
+ this.http.close((error) => error ? reject(error) : resolve());
72
+ });
73
+ }
74
+ async request(method, params) {
75
+ if (!this.pairing && this.loadPairing)
76
+ this.pairing = await this.loadPairing();
77
+ if (!this.pairing)
78
+ throw new Error(NOT_PAIRED);
79
+ const ws = this.active;
80
+ if (!ws || ws.readyState !== WebSocket.OPEN)
81
+ throw new Error(NOT_CONNECTED);
82
+ const id = `${process.pid}-${++this.counter}`;
83
+ return new Promise((resolve, reject) => {
84
+ const timer = setTimeout(() => {
85
+ this.pending.delete(id);
86
+ reject(new Error(`Netraptor extension timed out after ${this.requestTimeoutMs / 1000}s.`));
87
+ }, this.requestTimeoutMs);
88
+ this.pending.set(id, { resolve, reject, timer });
89
+ ws.send(JSON.stringify({ id, method, params }), (error) => {
90
+ if (!error)
91
+ return;
92
+ clearTimeout(timer);
93
+ this.pending.delete(id);
94
+ reject(error);
95
+ });
96
+ });
97
+ }
98
+ authenticate(ws) {
99
+ const pairing = this.pairing;
100
+ if (!pairing) {
101
+ ws.close(1008, "Not paired");
102
+ return;
103
+ }
104
+ const serverChallenge = nonce();
105
+ let clientChallenge;
106
+ let serverProved = false;
107
+ const timeout = setTimeout(() => ws.close(1008, "Handshake timed out"), HANDSHAKE_TIMEOUT_MS);
108
+ const fail = () => ws.close(1008, "Authentication failed");
109
+ const onHandshake = (raw) => {
110
+ const message = parseMessage(raw);
111
+ if (message?.type === "client-challenge" && typeof message.challenge === "string" && !clientChallenge) {
112
+ clientChallenge = message.challenge;
113
+ serverProved = true;
114
+ ws.send(JSON.stringify({
115
+ type: "server-proof",
116
+ proof: hmac(pairing.secret, "server", clientChallenge),
117
+ }));
118
+ return;
119
+ }
120
+ if (message?.type === "client-proof"
121
+ && serverProved
122
+ && validHmac(pairing.secret, "extension", serverChallenge, message.proof)) {
123
+ clearTimeout(timeout);
124
+ ws.off("message", onHandshake);
125
+ const previous = this.active;
126
+ this.active = ws;
127
+ if (previous && previous !== ws)
128
+ previous.close(4001, "Replaced by newer connection");
129
+ ws.on("message", (data) => this.onMessage(ws, data));
130
+ ws.on("close", () => {
131
+ if (this.active === ws)
132
+ this.active = undefined;
133
+ });
134
+ ws.send(JSON.stringify({ type: "authenticated" }));
135
+ return;
136
+ }
137
+ fail();
138
+ };
139
+ ws.on("message", onHandshake);
140
+ ws.send(JSON.stringify({ type: "server-challenge", challenge: serverChallenge }));
141
+ }
142
+ onMessage(ws, raw) {
143
+ const message = parseMessage(raw);
144
+ if (message?.type === "ping") {
145
+ ws.send(JSON.stringify({ type: "pong" }));
146
+ return;
147
+ }
148
+ if (typeof message?.id !== "string")
149
+ return;
150
+ const pending = this.pending.get(message.id);
151
+ if (!pending)
152
+ return;
153
+ clearTimeout(pending.timer);
154
+ this.pending.delete(message.id);
155
+ if (typeof message.error === "string")
156
+ pending.reject(new Error(message.error));
157
+ else
158
+ pending.resolve(message.result);
159
+ }
160
+ }
161
+ //# sourceMappingURL=bridge-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-server.js","sourceRoot":"","sources":["../src/bridge-server.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(argv?: string[]): Promise<void>;
package/dist/cli.js ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
3
+ import { ExtensionBridgeServer } from "./bridge-server.js";
4
+ import { createMcpServer } from "./mcp-server.js";
5
+ import { readPairingConfig, writePairingConfig } from "./pairing.js";
6
+ function usage() {
7
+ return `Netraptor local MCP server
8
+
9
+ Usage:
10
+ netraptor-mcp Start the stdio MCP server and localhost bridge
11
+ netraptor-mcp pair <CODE> Pair this computer with the Netraptor extension
12
+ netraptor-mcp --help Show this help
13
+
14
+ Environment:
15
+ NETRAPTOR_MCP_PORT WebSocket port (default: 47321)
16
+ NETRAPTOR_HOME Config directory override (tests/advanced use)`;
17
+ }
18
+ export async function main(argv = process.argv.slice(2)) {
19
+ if (argv[0] === "--help" || argv[0] === "-h" || argv[0] === "help") {
20
+ process.stdout.write(`${usage()}\n`);
21
+ return;
22
+ }
23
+ if (argv[0] === "pair") {
24
+ if (!argv[1] || argv.length !== 2)
25
+ throw new Error("Usage: netraptor-mcp pair <CODE>");
26
+ const config = await writePairingConfig(argv[1]);
27
+ process.stdout.write(`Paired with Chrome extension ${config.extensionId}.\n`);
28
+ return;
29
+ }
30
+ if (argv.length > 0)
31
+ throw new Error(`Unknown command: ${argv[0]}\n\n${usage()}`);
32
+ const rawPort = process.env.NETRAPTOR_MCP_PORT ?? "47321";
33
+ const port = Number(rawPort);
34
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
35
+ throw new Error(`Invalid NETRAPTOR_MCP_PORT: ${rawPort}`);
36
+ }
37
+ const bridge = new ExtensionBridgeServer(await readPairingConfig(), "127.0.0.1", port, undefined, () => readPairingConfig());
38
+ await bridge.listen();
39
+ const mcp = createMcpServer(bridge);
40
+ await mcp.connect(new StdioServerTransport());
41
+ const shutdown = () => void bridge.close().finally(() => process.exit(0));
42
+ process.once("SIGINT", shutdown);
43
+ process.once("SIGTERM", shutdown);
44
+ }
45
+ main().catch((error) => {
46
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
47
+ process.exitCode = 1;
48
+ });
49
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAErE,SAAS,KAAK;IACZ,OAAO;;;;;;;;;+EASsE,CAAC;AAChF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACrD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC;QACrC,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACvF,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,MAAM,CAAC,WAAW,KAAK,CAAC,CAAC;QAC9E,OAAO;IACT,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,EAAE,CAAC,CAAC;IAElF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC;IAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,+BAA+B,OAAO,EAAE,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,qBAAqB,CACtC,MAAM,iBAAiB,EAAE,EACzB,WAAW,EACX,IAAI,EACJ,SAAS,EACT,GAAG,EAAE,CAAC,iBAAiB,EAAE,CAC1B,CAAC;IACF,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;IACtB,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;IAE9C,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACjC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/server";
2
+ import type { ExtensionBridgeServer } from "./bridge-server.js";
3
+ export declare function createMcpServer(bridge: Pick<ExtensionBridgeServer, "request">): McpServer;
@@ -0,0 +1,47 @@
1
+ import { McpServer } from "@modelcontextprotocol/server";
2
+ import * as z from "zod/v4";
3
+ function text(value) {
4
+ return {
5
+ content: [{
6
+ type: "text",
7
+ text: typeof value === "string" ? value : JSON.stringify(value, null, 2),
8
+ }],
9
+ };
10
+ }
11
+ export function createMcpServer(bridge) {
12
+ const server = new McpServer({ name: "netraptor", version: "0.0.1" });
13
+ const proxy = async (method, params) => {
14
+ try {
15
+ return text(await bridge.request(method, params));
16
+ }
17
+ catch (error) {
18
+ return text(error instanceof Error ? error.message : String(error));
19
+ }
20
+ };
21
+ server.registerTool("list_sessions", {
22
+ description: "List locally captured Netraptor sessions.",
23
+ inputSchema: z.object({}),
24
+ }, () => proxy("list_sessions", {}));
25
+ server.registerTool("list_endpoints", {
26
+ description: "List endpoints in a captured session (defaults to the most recent session).",
27
+ inputSchema: z.object({ sessionId: z.number().int().positive().optional() }),
28
+ }, (params) => proxy("list_endpoints", params));
29
+ server.registerTool("get_endpoint", {
30
+ description: "Get schema, parameters, examples, and pages for one endpoint.",
31
+ inputSchema: z.object({
32
+ method: z.string().min(1),
33
+ template: z.string().min(1),
34
+ sessionId: z.number().int().positive().optional(),
35
+ }),
36
+ }, (params) => proxy("get_endpoint", params));
37
+ server.registerTool("search", {
38
+ description: "Search captured endpoint templates, URLs, and GraphQL operation names.",
39
+ inputSchema: z.object({ query: z.string().min(1) }),
40
+ }, (params) => proxy("search", params));
41
+ server.registerTool("get_request", {
42
+ description: "Get a captured request with authorization and cookie headers redacted.",
43
+ inputSchema: z.object({ id: z.number().int().positive() }),
44
+ }, (params) => proxy("get_request", params));
45
+ return server;
46
+ }
47
+ //# sourceMappingURL=mcp-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface PairingConfig {
2
+ extensionId: string;
3
+ secret: string;
4
+ }
5
+ export declare function configPath(env?: NodeJS.ProcessEnv): string;
6
+ export declare function parsePairingCode(code: string): PairingConfig;
7
+ export declare function writePairingConfig(code: string, env?: NodeJS.ProcessEnv): Promise<PairingConfig>;
8
+ export declare function readPairingConfig(env?: NodeJS.ProcessEnv): Promise<PairingConfig | undefined>;
@@ -0,0 +1,59 @@
1
+ import { createHash } from "node:crypto";
2
+ import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ export function configPath(env = process.env) {
6
+ return join(env.NETRAPTOR_HOME || join(homedir(), ".netraptor"), "mcp.json");
7
+ }
8
+ export function parsePairingCode(code) {
9
+ let decoded;
10
+ try {
11
+ decoded = Buffer.from(code.trim(), "base64url").toString("utf8");
12
+ }
13
+ catch {
14
+ throw new Error("Invalid pairing code.");
15
+ }
16
+ const separator = decoded.indexOf(".");
17
+ const extensionId = decoded.slice(0, separator);
18
+ const encodedSecret = decoded.slice(separator + 1);
19
+ let secretBytes;
20
+ try {
21
+ secretBytes = Buffer.from(encodedSecret, "base64url");
22
+ }
23
+ catch {
24
+ throw new Error("Invalid pairing code.");
25
+ }
26
+ if (!/^[a-p]{32}$/.test(extensionId) || secretBytes.length !== 32) {
27
+ throw new Error("Invalid pairing code.");
28
+ }
29
+ return {
30
+ extensionId,
31
+ secret: createHash("sha256").update(secretBytes).digest("base64url"),
32
+ };
33
+ }
34
+ export async function writePairingConfig(code, env = process.env) {
35
+ const config = parsePairingCode(code);
36
+ const path = configPath(env);
37
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
38
+ const temporary = `${path}.${process.pid}.tmp`;
39
+ await writeFile(temporary, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
40
+ await chmod(temporary, 0o600);
41
+ await rename(temporary, path);
42
+ await chmod(path, 0o600);
43
+ return config;
44
+ }
45
+ export async function readPairingConfig(env = process.env) {
46
+ try {
47
+ const parsed = JSON.parse(await readFile(configPath(env), "utf8"));
48
+ if (!/^[a-p]{32}$/.test(parsed.extensionId ?? "") || typeof parsed.secret !== "string") {
49
+ return undefined;
50
+ }
51
+ return parsed;
52
+ }
53
+ catch (error) {
54
+ if (error.code === "ENOENT")
55
+ return undefined;
56
+ throw error;
57
+ }
58
+ }
59
+ //# sourceMappingURL=pairing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing.js","sourceRoot":"","sources":["../src/pairing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface BridgeRequest {
2
+ id: string;
3
+ method: string;
4
+ params: Record<string, unknown>;
5
+ }
6
+ export interface BridgeResponse {
7
+ id: string;
8
+ result?: unknown;
9
+ error?: string;
10
+ }
11
+ export declare function parseMessage(data: unknown): Record<string, unknown> | undefined;
@@ -0,0 +1,12 @@
1
+ export function parseMessage(data) {
2
+ try {
3
+ const parsed = JSON.parse(String(data));
4
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
5
+ ? parsed
6
+ : undefined;
7
+ }
8
+ catch {
9
+ return undefined;
10
+ }
11
+ }
12
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAYA,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAY,CAAC;QACnD,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YACnE,CAAC,CAAC,MAAiC;YACnC,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "name": "@branch42-labs/netraptor-mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Local read-only MCP bridge for Netraptor",
5
+ "private": false,
6
+ "type": "module",
7
+ "bin": {
8
+ "netraptor-mcp": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "engines": {
15
+ "node": ">=20"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/branch42-labs/netraptor-extension.git",
20
+ "directory": "packages/mcp"
21
+ },
22
+ "license": "MIT",
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc -p tsconfig.build.json",
28
+ "test": "vitest run",
29
+ "typecheck": "tsc --noEmit"
30
+ },
31
+ "dependencies": {
32
+ "@modelcontextprotocol/server": "^2.3.1",
33
+ "ws": "^8.22.0",
34
+ "zod": "^4.3.6"
35
+ },
36
+ "devDependencies": {
37
+ "@modelcontextprotocol/client": "^2.3.1",
38
+ "@types/node": "^24.10.0",
39
+ "@types/ws": "^8.18.1",
40
+ "typescript": "^5.9.3",
41
+ "vitest": "^4.0.7"
42
+ }
43
+ }