linkedin-toolkit-mcp 2.0.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/LICENSE +21 -0
- package/README.md +80 -0
- package/dist/bridge.d.ts +95 -0
- package/dist/bridge.js +259 -0
- package/dist/bridge.js.map +1 -0
- package/dist/cli.d.ts +83 -0
- package/dist/cli.js +898 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.js +149 -0
- package/dist/config.js.map +1 -0
- package/dist/contract.d.ts +6770 -0
- package/dist/contract.js +926 -0
- package/dist/contract.js.map +1 -0
- package/dist/db.d.ts +64 -0
- package/dist/db.js +542 -0
- package/dist/db.js.map +1 -0
- package/dist/fake-data.d.ts +277 -0
- package/dist/fake-data.js +789 -0
- package/dist/fake-data.js.map +1 -0
- package/dist/fake-extension.d.ts +51 -0
- package/dist/fake-extension.js +151 -0
- package/dist/fake-extension.js.map +1 -0
- package/dist/gen.d.ts +5 -0
- package/dist/gen.js +22 -0
- package/dist/gen.js.map +1 -0
- package/dist/http.d.ts +27 -0
- package/dist/http.js +329 -0
- package/dist/http.js.map +1 -0
- package/dist/openapi.d.ts +30 -0
- package/dist/openapi.js +306 -0
- package/dist/openapi.js.map +1 -0
- package/dist/prompts.d.ts +9 -0
- package/dist/prompts.js +65 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +7 -0
- package/dist/resources.js +73 -0
- package/dist/resources.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +38 -0
- package/dist/server.js.map +1 -0
- package/dist/toolkit.d.ts +82 -0
- package/dist/toolkit.js +171 -0
- package/dist/toolkit.js.map +1 -0
- package/dist/tools.d.ts +31 -0
- package/dist/tools.js +103 -0
- package/dist/tools.js.map +1 -0
- package/dist/webhooks.d.ts +52 -0
- package/dist/webhooks.js +112 -0
- package/dist/webhooks.js.map +1 -0
- package/openapi.json +8334 -0
- package/package.json +45 -0
- package/tools.json +1205 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dominic Gonsalves
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# linkedin-toolkit-mcp
|
|
2
|
+
|
|
3
|
+
The MCP server, localhost bridge, HTTP action API, SQLite mirror and `lit` CLI for
|
|
4
|
+
[LinkedIn Toolkit](https://github.com/FormatixAI/linkedin-toolkit).
|
|
5
|
+
|
|
6
|
+
This package is the half that agents talk to. The other half is a Chrome extension that drives
|
|
7
|
+
**your own logged-in LinkedIn session** — there is no headless browser, no hosted service, no
|
|
8
|
+
account and no telemetry. This server does not touch LinkedIn itself; it hands actions to the
|
|
9
|
+
extension over a WebSocket on loopback and passes the answers back.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx linkedin-toolkit-mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Pairing
|
|
16
|
+
|
|
17
|
+
1. Install the extension: download the `linkedin-toolkit-extension-*.zip` from
|
|
18
|
+
[Releases](https://github.com/FormatixAI/linkedin-toolkit/releases), unzip it, then
|
|
19
|
+
`chrome://extensions` → **Developer mode** → **Load unpacked** → pick the folder.
|
|
20
|
+
2. Run `npx linkedin-toolkit-mcp`. The first run prints a pairing token and stores it in
|
|
21
|
+
`~/.linkedin-toolkit/config.json` (owner-only).
|
|
22
|
+
3. Paste that token into the extension popup → **Settings → Local bridge**, and enable the bridge.
|
|
23
|
+
The badge turns to "connected".
|
|
24
|
+
|
|
25
|
+
Point your agent at the server. Claude Code, `.mcp.json` in your project root:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"linkedin-toolkit": { "command": "npx", "args": ["-y", "linkedin-toolkit-mcp"] }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`lit serve --http` additionally exposes `POST /actions/{action}`, `POST /mcp` and
|
|
36
|
+
`GET /openapi.json` on `127.0.0.1:47830` for the Node and Python clients, n8n and anything else
|
|
37
|
+
that speaks HTTP. Everything binds to loopback and requires the pairing token as a bearer token.
|
|
38
|
+
|
|
39
|
+
Add `--fake` to any `serve` to run the whole surface without a LinkedIn account: real envelopes,
|
|
40
|
+
real error codes, demo data, no network calls.
|
|
41
|
+
|
|
42
|
+
## `lit` cheat-sheet
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
lit serve --http # run the bridge + HTTP API (add --fake for demo mode)
|
|
46
|
+
lit status # connection, quotas, business hours, queue depth
|
|
47
|
+
lit config get token --reveal # the pairing token
|
|
48
|
+
lit search "CTO fintech London" --json # sourcing; --csv also works
|
|
49
|
+
lit profile ada-lovelace --json
|
|
50
|
+
lit invite ada-lovelace --note "…" # queues for approval in Copilot mode
|
|
51
|
+
lit queue list # what is waiting for you
|
|
52
|
+
lit queue approve <id> # you, at your own terminal
|
|
53
|
+
lit sync && lit sql "select * from profiles limit 5"
|
|
54
|
+
lit research rows.csv --out packs/ # research packs from a CSV
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`lit --help` lists everything.
|
|
58
|
+
|
|
59
|
+
## Safety
|
|
60
|
+
|
|
61
|
+
Copilot mode is on by default: invites, messages, InMails and comments from an agent wait in an
|
|
62
|
+
approval queue until a human approves them in the popup. Views, follows and likes are metered
|
|
63
|
+
against the visit bucket and go out directly. Daily hard caps live in the extension and cannot be
|
|
64
|
+
raised by this server, the CLI, an agent or a config file.
|
|
65
|
+
|
|
66
|
+
Automating your LinkedIn account may breach LinkedIn's User Agreement, and accounts are restricted
|
|
67
|
+
and banned for it. That risk is yours. Read
|
|
68
|
+
[docs/safety.md](https://github.com/FormatixAI/linkedin-toolkit/blob/main/docs/safety.md) before
|
|
69
|
+
you point anything at an account you care about.
|
|
70
|
+
|
|
71
|
+
## Documentation
|
|
72
|
+
|
|
73
|
+
- [Action contract](https://github.com/FormatixAI/linkedin-toolkit/blob/main/docs/actions.md) —
|
|
74
|
+
every action, param, result, error code and event.
|
|
75
|
+
- [Agent setup](https://github.com/FormatixAI/linkedin-toolkit/blob/main/docs/agents/README.md) —
|
|
76
|
+
Claude Code, OpenAI Agents, LangChain, n8n and the rest.
|
|
77
|
+
- [Safety](https://github.com/FormatixAI/linkedin-toolkit/blob/main/docs/safety.md).
|
|
78
|
+
|
|
79
|
+
MIT licensed. Issues and pull requests:
|
|
80
|
+
<https://github.com/FormatixAI/linkedin-toolkit>
|
package/dist/bridge.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The localhost WebSocket bridge between this server and the Chrome extension.
|
|
3
|
+
*
|
|
4
|
+
* Protocol (docs/actions.md, "Bridge protocol"):
|
|
5
|
+
* extension -> server { type: 'hello', token, extensionVersion }
|
|
6
|
+
* server -> extension { type: 'hello_ok', serverVersion } or close 4001
|
|
7
|
+
* server -> extension { id, action, params }
|
|
8
|
+
* extension -> server { id, ok: true, data, rateLimit? } | { id, ok: false, error }
|
|
9
|
+
* extension -> server { event, payload }
|
|
10
|
+
*
|
|
11
|
+
* Exactly one extension connection is active at a time; a newer hello replaces
|
|
12
|
+
* the older socket.
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'node:events';
|
|
15
|
+
import type { ErrorCode, EventName, RequestOrigin } from './contract.js';
|
|
16
|
+
export declare const SERVER_VERSION = "2.0.0";
|
|
17
|
+
export declare const OFFLINE_HOW_TO_FIX: string;
|
|
18
|
+
export declare class BridgeError extends Error {
|
|
19
|
+
readonly code: ErrorCode;
|
|
20
|
+
readonly retryAfter?: number;
|
|
21
|
+
readonly howToFix?: string;
|
|
22
|
+
constructor(code: ErrorCode, message: string, extra?: {
|
|
23
|
+
retryAfter?: number;
|
|
24
|
+
howToFix?: string;
|
|
25
|
+
});
|
|
26
|
+
toJSON(): {
|
|
27
|
+
code: ErrorCode;
|
|
28
|
+
message: string;
|
|
29
|
+
retryAfter?: number;
|
|
30
|
+
howToFix?: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export declare function offlineError(): BridgeError;
|
|
34
|
+
/**
|
|
35
|
+
* What the extension answered: the action's data, plus the quota snapshot it
|
|
36
|
+
* attaches to an outreach write. The envelope in docs/actions.md has always
|
|
37
|
+
* carried `rateLimit`; carrying it here is what lets every surface pass it on.
|
|
38
|
+
*/
|
|
39
|
+
export type BridgeResponse = {
|
|
40
|
+
data: unknown;
|
|
41
|
+
rateLimit?: Record<string, number>;
|
|
42
|
+
};
|
|
43
|
+
export type BridgeOptions = {
|
|
44
|
+
port?: number;
|
|
45
|
+
token: string;
|
|
46
|
+
host?: string;
|
|
47
|
+
pingIntervalMs?: number;
|
|
48
|
+
defaultTimeoutMs?: number;
|
|
49
|
+
};
|
|
50
|
+
export declare class BridgeServer extends EventEmitter {
|
|
51
|
+
/** Typed listeners for the three events this server emits. */
|
|
52
|
+
on(event: 'event', fn: (name: EventName, payload: unknown) => void): this;
|
|
53
|
+
on(event: 'connected', fn: (info: {
|
|
54
|
+
extensionVersion: string;
|
|
55
|
+
}) => void): this;
|
|
56
|
+
on(event: 'disconnected', fn: () => void): this;
|
|
57
|
+
on(event: string | symbol, fn: (...args: any[]) => void): this;
|
|
58
|
+
readonly token: string;
|
|
59
|
+
private readonly host;
|
|
60
|
+
private readonly pingIntervalMs;
|
|
61
|
+
private readonly defaultTimeoutMs;
|
|
62
|
+
private requestedPort;
|
|
63
|
+
private wss;
|
|
64
|
+
private socket;
|
|
65
|
+
private pending;
|
|
66
|
+
private pingTimer;
|
|
67
|
+
private extensionVersion;
|
|
68
|
+
constructor(options: BridgeOptions);
|
|
69
|
+
/** The port actually bound (differs from the requested port when 0 was passed). */
|
|
70
|
+
get port(): number;
|
|
71
|
+
isConnected(): boolean;
|
|
72
|
+
connectedExtensionVersion(): string | null;
|
|
73
|
+
start(): Promise<void>;
|
|
74
|
+
stop(): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Send an action to the extension and wait for its whole response, quota
|
|
77
|
+
* snapshot included. Rejects with a BridgeError carrying the contract error
|
|
78
|
+
* code.
|
|
79
|
+
*/
|
|
80
|
+
requestFull(action: string, params?: unknown, options?: {
|
|
81
|
+
timeoutMs?: number;
|
|
82
|
+
origin?: RequestOrigin;
|
|
83
|
+
}): Promise<BridgeResponse>;
|
|
84
|
+
/** `requestFull` for callers that only want the data. */
|
|
85
|
+
request(action: string, params?: unknown, options?: {
|
|
86
|
+
timeoutMs?: number;
|
|
87
|
+
origin?: RequestOrigin;
|
|
88
|
+
}): Promise<unknown>;
|
|
89
|
+
private handleConnection;
|
|
90
|
+
/** A newer authenticated connection replaces the older one. */
|
|
91
|
+
private adopt;
|
|
92
|
+
/** Reject and clear every in-flight request. */
|
|
93
|
+
private failPending;
|
|
94
|
+
private handleFrame;
|
|
95
|
+
}
|
package/dist/bridge.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The localhost WebSocket bridge between this server and the Chrome extension.
|
|
3
|
+
*
|
|
4
|
+
* Protocol (docs/actions.md, "Bridge protocol"):
|
|
5
|
+
* extension -> server { type: 'hello', token, extensionVersion }
|
|
6
|
+
* server -> extension { type: 'hello_ok', serverVersion } or close 4001
|
|
7
|
+
* server -> extension { id, action, params }
|
|
8
|
+
* extension -> server { id, ok: true, data, rateLimit? } | { id, ok: false, error }
|
|
9
|
+
* extension -> server { event, payload }
|
|
10
|
+
*
|
|
11
|
+
* Exactly one extension connection is active at a time; a newer hello replaces
|
|
12
|
+
* the older socket.
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'node:events';
|
|
15
|
+
import { randomUUID } from 'node:crypto';
|
|
16
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
17
|
+
export const SERVER_VERSION = '2.0.0';
|
|
18
|
+
export const OFFLINE_HOW_TO_FIX = 'Install the extension from GitHub Releases, open the popup → Settings → Local bridge, ' +
|
|
19
|
+
'paste the pairing token printed by `lit serve`, and enable the bridge.';
|
|
20
|
+
export class BridgeError extends Error {
|
|
21
|
+
code;
|
|
22
|
+
retryAfter;
|
|
23
|
+
howToFix;
|
|
24
|
+
constructor(code, message, extra = {}) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = 'BridgeError';
|
|
27
|
+
this.code = code;
|
|
28
|
+
this.retryAfter = extra.retryAfter;
|
|
29
|
+
this.howToFix = extra.howToFix;
|
|
30
|
+
}
|
|
31
|
+
toJSON() {
|
|
32
|
+
const out = {
|
|
33
|
+
code: this.code,
|
|
34
|
+
message: this.message,
|
|
35
|
+
};
|
|
36
|
+
if (this.retryAfter !== undefined)
|
|
37
|
+
out.retryAfter = this.retryAfter;
|
|
38
|
+
if (this.howToFix !== undefined)
|
|
39
|
+
out.howToFix = this.howToFix;
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function offlineError() {
|
|
44
|
+
return new BridgeError('EXTENSION_OFFLINE', 'The LinkedIn Toolkit Chrome extension is not connected to this server.', { howToFix: OFFLINE_HOW_TO_FIX });
|
|
45
|
+
}
|
|
46
|
+
export class BridgeServer extends EventEmitter {
|
|
47
|
+
on(event, fn) {
|
|
48
|
+
return super.on(event, fn);
|
|
49
|
+
}
|
|
50
|
+
token;
|
|
51
|
+
host;
|
|
52
|
+
pingIntervalMs;
|
|
53
|
+
defaultTimeoutMs;
|
|
54
|
+
requestedPort;
|
|
55
|
+
wss = null;
|
|
56
|
+
socket = null;
|
|
57
|
+
pending = new Map();
|
|
58
|
+
pingTimer = null;
|
|
59
|
+
extensionVersion = null;
|
|
60
|
+
constructor(options) {
|
|
61
|
+
super();
|
|
62
|
+
this.requestedPort = options.port ?? 47829;
|
|
63
|
+
this.token = options.token;
|
|
64
|
+
this.host = options.host ?? '127.0.0.1';
|
|
65
|
+
this.pingIntervalMs = options.pingIntervalMs ?? 20_000;
|
|
66
|
+
this.defaultTimeoutMs = options.defaultTimeoutMs ?? 60_000;
|
|
67
|
+
}
|
|
68
|
+
/** The port actually bound (differs from the requested port when 0 was passed). */
|
|
69
|
+
get port() {
|
|
70
|
+
const address = this.wss?.address();
|
|
71
|
+
if (address && typeof address === 'object')
|
|
72
|
+
return address.port;
|
|
73
|
+
return this.requestedPort;
|
|
74
|
+
}
|
|
75
|
+
isConnected() {
|
|
76
|
+
return this.socket !== null && this.socket.readyState === WebSocket.OPEN;
|
|
77
|
+
}
|
|
78
|
+
connectedExtensionVersion() {
|
|
79
|
+
return this.isConnected() ? this.extensionVersion : null;
|
|
80
|
+
}
|
|
81
|
+
async start() {
|
|
82
|
+
if (this.wss)
|
|
83
|
+
return;
|
|
84
|
+
await new Promise((resolve, reject) => {
|
|
85
|
+
const wss = new WebSocketServer({ port: this.requestedPort, host: this.host });
|
|
86
|
+
const onError = (err) => reject(err);
|
|
87
|
+
wss.once('error', onError);
|
|
88
|
+
wss.once('listening', () => {
|
|
89
|
+
wss.off('error', onError);
|
|
90
|
+
this.wss = wss;
|
|
91
|
+
this.requestedPort = this.port;
|
|
92
|
+
resolve();
|
|
93
|
+
});
|
|
94
|
+
wss.on('connection', (ws) => this.handleConnection(ws));
|
|
95
|
+
});
|
|
96
|
+
// Two pings, on purpose. The protocol-level ping keeps the socket itself
|
|
97
|
+
// alive and is answered by the browser, invisibly to the extension. But an
|
|
98
|
+
// MV3 service worker is killed after ~30 s of no *JavaScript* activity, and
|
|
99
|
+
// a frame the browser answers for it is not activity — so the worker dies
|
|
100
|
+
// holding an open socket. The JSON ping is a message the worker has to
|
|
101
|
+
// wake up and handle; it answers `{type:'pong'}`, which resets its timer.
|
|
102
|
+
this.pingTimer = setInterval(() => {
|
|
103
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN)
|
|
104
|
+
return;
|
|
105
|
+
this.socket.ping();
|
|
106
|
+
try {
|
|
107
|
+
this.socket.send(JSON.stringify({ type: 'ping' }));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
/* the socket is going away; 'close' handles it */
|
|
111
|
+
}
|
|
112
|
+
}, this.pingIntervalMs);
|
|
113
|
+
this.pingTimer.unref?.();
|
|
114
|
+
}
|
|
115
|
+
async stop() {
|
|
116
|
+
if (this.pingTimer) {
|
|
117
|
+
clearInterval(this.pingTimer);
|
|
118
|
+
this.pingTimer = null;
|
|
119
|
+
}
|
|
120
|
+
this.failPending(() => new BridgeError('INTERNAL', 'Bridge server stopped'));
|
|
121
|
+
const socket = this.socket;
|
|
122
|
+
this.socket = null;
|
|
123
|
+
socket?.close();
|
|
124
|
+
const wss = this.wss;
|
|
125
|
+
this.wss = null;
|
|
126
|
+
if (!wss)
|
|
127
|
+
return;
|
|
128
|
+
await new Promise((resolve) => {
|
|
129
|
+
for (const client of wss.clients)
|
|
130
|
+
client.terminate();
|
|
131
|
+
wss.close(() => resolve());
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Send an action to the extension and wait for its whole response, quota
|
|
136
|
+
* snapshot included. Rejects with a BridgeError carrying the contract error
|
|
137
|
+
* code.
|
|
138
|
+
*/
|
|
139
|
+
async requestFull(action, params = {}, options = {}) {
|
|
140
|
+
const socket = this.socket;
|
|
141
|
+
if (!socket || socket.readyState !== WebSocket.OPEN)
|
|
142
|
+
throw offlineError();
|
|
143
|
+
const id = randomUUID();
|
|
144
|
+
const timeoutMs = options.timeoutMs ?? this.defaultTimeoutMs;
|
|
145
|
+
const origin = options.origin;
|
|
146
|
+
return await new Promise((resolve, reject) => {
|
|
147
|
+
const timer = setTimeout(() => {
|
|
148
|
+
this.pending.delete(id);
|
|
149
|
+
reject(new BridgeError('INTERNAL', `The extension did not answer ${action} within ${timeoutMs}ms.`, { howToFix: 'Check the extension service worker in chrome://extensions and retry.' }));
|
|
150
|
+
}, timeoutMs);
|
|
151
|
+
timer.unref?.();
|
|
152
|
+
this.pending.set(id, { resolve, reject, timer });
|
|
153
|
+
try {
|
|
154
|
+
socket.send(JSON.stringify({ id, action, params: params ?? {}, ...(origin ? { origin } : {}) }));
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
clearTimeout(timer);
|
|
158
|
+
this.pending.delete(id);
|
|
159
|
+
reject(new BridgeError('INTERNAL', `Failed to send ${action}: ${String(err)}`));
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/** `requestFull` for callers that only want the data. */
|
|
164
|
+
async request(action, params = {}, options = {}) {
|
|
165
|
+
return (await this.requestFull(action, params, options)).data;
|
|
166
|
+
}
|
|
167
|
+
/* ---------------------------------------------------------------- */
|
|
168
|
+
handleConnection(ws) {
|
|
169
|
+
let helloDone = false;
|
|
170
|
+
ws.on('message', (raw) => {
|
|
171
|
+
let frame;
|
|
172
|
+
try {
|
|
173
|
+
frame = JSON.parse(raw.toString());
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (!helloDone) {
|
|
179
|
+
if (frame?.type !== 'hello') {
|
|
180
|
+
ws.close(4001, 'UNAUTHORIZED');
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (frame.token !== this.token) {
|
|
184
|
+
ws.close(4001, 'UNAUTHORIZED');
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
helloDone = true;
|
|
188
|
+
this.extensionVersion = String(frame.extensionVersion ?? 'unknown');
|
|
189
|
+
this.adopt(ws);
|
|
190
|
+
ws.send(JSON.stringify({ type: 'hello_ok', serverVersion: SERVER_VERSION }));
|
|
191
|
+
this.emit('connected', { extensionVersion: this.extensionVersion });
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
this.handleFrame(frame);
|
|
195
|
+
});
|
|
196
|
+
ws.on('close', () => {
|
|
197
|
+
if (this.socket === ws) {
|
|
198
|
+
this.socket = null;
|
|
199
|
+
this.extensionVersion = null;
|
|
200
|
+
// Nothing can answer these any more; failing now beats a 60 s timeout.
|
|
201
|
+
this.failPending(offlineError);
|
|
202
|
+
this.emit('disconnected');
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
ws.on('error', () => {
|
|
206
|
+
/* a dead socket is handled by 'close' */
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/** A newer authenticated connection replaces the older one. */
|
|
210
|
+
adopt(ws) {
|
|
211
|
+
const previous = this.socket;
|
|
212
|
+
this.socket = ws;
|
|
213
|
+
if (previous && previous !== ws) {
|
|
214
|
+
// Requests in flight belong to the socket being replaced: the extension
|
|
215
|
+
// that is going away will never answer them, and the new one has never
|
|
216
|
+
// seen them. Fail them now rather than letting them time out.
|
|
217
|
+
this.failPending(() => new BridgeError('EXTENSION_OFFLINE', 'The extension reconnected while this request was in flight. Retry it.', { howToFix: 'Retry the call; the new extension connection is already live.' }));
|
|
218
|
+
previous.close(1000, 'replaced');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/** Reject and clear every in-flight request. */
|
|
222
|
+
failPending(makeError) {
|
|
223
|
+
if (this.pending.size === 0)
|
|
224
|
+
return;
|
|
225
|
+
const pending = [...this.pending.values()];
|
|
226
|
+
this.pending.clear();
|
|
227
|
+
for (const entry of pending) {
|
|
228
|
+
clearTimeout(entry.timer);
|
|
229
|
+
entry.reject(makeError());
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
handleFrame(frame) {
|
|
233
|
+
// The extension's answer to the JSON keepalive, and any other typed
|
|
234
|
+
// housekeeping frame. Nothing to do, and nothing worth logging.
|
|
235
|
+
if (typeof frame?.type === 'string')
|
|
236
|
+
return;
|
|
237
|
+
if (typeof frame?.event === 'string') {
|
|
238
|
+
this.emit('event', frame.event, frame.payload);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (typeof frame?.id !== 'string')
|
|
242
|
+
return;
|
|
243
|
+
const pending = this.pending.get(frame.id);
|
|
244
|
+
if (!pending)
|
|
245
|
+
return;
|
|
246
|
+
this.pending.delete(frame.id);
|
|
247
|
+
clearTimeout(pending.timer);
|
|
248
|
+
if (frame.ok === true) {
|
|
249
|
+
pending.resolve({
|
|
250
|
+
data: frame.data,
|
|
251
|
+
...(frame.rateLimit ? { rateLimit: frame.rateLimit } : {}),
|
|
252
|
+
});
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const error = frame.error ?? {};
|
|
256
|
+
pending.reject(new BridgeError((error.code ?? 'INTERNAL'), String(error.message ?? 'The extension returned an error.'), { retryAfter: error.retryAfter, howToFix: error.howToFix }));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `lit` — the command line for the LinkedIn Toolkit.
|
|
4
|
+
*
|
|
5
|
+
* `lit serve` runs the server (bridge plus MCP over stdio); `lit serve --http`
|
|
6
|
+
* adds the HTTP surface. Every other command talks to that running server over
|
|
7
|
+
* HTTP, so the CLI and an agent see exactly the same data through exactly the
|
|
8
|
+
* same code path.
|
|
9
|
+
*/
|
|
10
|
+
import { Command } from 'commander';
|
|
11
|
+
import { type ServerConfig } from './config.js';
|
|
12
|
+
import { FakeExtensionClient } from './fake-extension.js';
|
|
13
|
+
import { HttpServer } from './http.js';
|
|
14
|
+
import { Toolkit } from './toolkit.js';
|
|
15
|
+
export declare const NOT_RUNNING = "linkedin-toolkit server is not running. Start it with: lit serve --http";
|
|
16
|
+
export type Io = {
|
|
17
|
+
out: (text: string) => void;
|
|
18
|
+
err: (text: string) => void;
|
|
19
|
+
};
|
|
20
|
+
/** Thrown to end a command with a message and a non-zero exit code. */
|
|
21
|
+
export declare class CliError extends Error {
|
|
22
|
+
readonly exitCode: number;
|
|
23
|
+
constructor(message: string, exitCode?: number);
|
|
24
|
+
}
|
|
25
|
+
export declare function publicIdFrom(input: string): string;
|
|
26
|
+
export declare function universalNameFrom(input: string): string;
|
|
27
|
+
/** `24h`, `7d`, `30m`, or an epoch-milliseconds number. */
|
|
28
|
+
export declare function parseSince(input: string | undefined, now?: number): number | undefined;
|
|
29
|
+
export declare function toCsv(rows: Record<string, unknown>[], columns?: string[]): string;
|
|
30
|
+
/** A small RFC-4180-ish reader: quoted fields, doubled quotes, CRLF. */
|
|
31
|
+
export declare function fromCsv(text: string): Record<string, string>[];
|
|
32
|
+
export declare function slugify(input: string): string;
|
|
33
|
+
/** The note column of `lit endpoints check`. */
|
|
34
|
+
export declare function endpointNote(result: string, clientVersion: string, error?: string): string;
|
|
35
|
+
/** Fixed-width table for human output. */
|
|
36
|
+
export declare function table(rows: Record<string, unknown>[], columns: string[]): string;
|
|
37
|
+
/** Settable keys of the local server config. `token` is deliberately absent. */
|
|
38
|
+
export declare const SETTABLE_KEYS: readonly ["bridgePort", "httpPort", "webhookUrl", "dbPath", "researchTimeoutMs"];
|
|
39
|
+
export type SettableKey = (typeof SETTABLE_KEYS)[number];
|
|
40
|
+
/** Show only the last four characters, so a shoulder or a screen share leaks nothing. */
|
|
41
|
+
export declare function maskToken(token: string): string;
|
|
42
|
+
/** Parse and validate one `lit config set` value. Throws CliError on bad input. */
|
|
43
|
+
export declare function coerceSetting(key: string, raw: string): {
|
|
44
|
+
key: SettableKey;
|
|
45
|
+
value: unknown;
|
|
46
|
+
};
|
|
47
|
+
export declare class ServerClient {
|
|
48
|
+
readonly baseUrl: string;
|
|
49
|
+
readonly token: string;
|
|
50
|
+
constructor(baseUrl: string, token: string);
|
|
51
|
+
/**
|
|
52
|
+
* Where to find the server, most specific first:
|
|
53
|
+
* 1. `LINKEDIN_TOOLKIT_URL` / `LINKEDIN_TOOLKIT_TOKEN` — the same names the
|
|
54
|
+
* docs and the Node and Python clients use, so one export points them
|
|
55
|
+
* all at the same place;
|
|
56
|
+
* 2. the port a running `lit serve` actually bound, so
|
|
57
|
+
* `lit serve --http --port 9000` does not strand every other command;
|
|
58
|
+
* 3. the configured default.
|
|
59
|
+
*/
|
|
60
|
+
static fromConfig(config: ServerConfig): ServerClient;
|
|
61
|
+
private post;
|
|
62
|
+
action(action: string, params?: unknown): Promise<any>;
|
|
63
|
+
tool(tool: string, args?: unknown): Promise<any>;
|
|
64
|
+
health(): Promise<{
|
|
65
|
+
ok: boolean;
|
|
66
|
+
extensionConnected: boolean;
|
|
67
|
+
version: string;
|
|
68
|
+
}>;
|
|
69
|
+
}
|
|
70
|
+
export type ServeHandles = {
|
|
71
|
+
toolkit: Toolkit;
|
|
72
|
+
http?: HttpServer;
|
|
73
|
+
fake?: FakeExtensionClient;
|
|
74
|
+
stop: () => Promise<void>;
|
|
75
|
+
};
|
|
76
|
+
export declare function serve(options: {
|
|
77
|
+
http?: boolean;
|
|
78
|
+
port?: number;
|
|
79
|
+
bridgePort?: number;
|
|
80
|
+
fake?: boolean;
|
|
81
|
+
}, io: Io): Promise<ServeHandles>;
|
|
82
|
+
export declare function buildProgram(io?: Io): Command;
|
|
83
|
+
export declare function run(argv: string[], io?: Io): Promise<number>;
|