@wolpertingerlabs/drawlatch 1.0.0-alpha.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.
Files changed (95) hide show
  1. package/CONNECTIONS.md +197 -0
  2. package/INGESTORS.md +790 -0
  3. package/LICENSE +21 -0
  4. package/README.md +685 -0
  5. package/dist/cli/generate-keys.d.ts +15 -0
  6. package/dist/cli/generate-keys.js +107 -0
  7. package/dist/connections/anthropic.json +16 -0
  8. package/dist/connections/bluesky.json +26 -0
  9. package/dist/connections/devin.json +15 -0
  10. package/dist/connections/discord-bot.json +24 -0
  11. package/dist/connections/discord-oauth.json +16 -0
  12. package/dist/connections/github.json +25 -0
  13. package/dist/connections/google-ai.json +15 -0
  14. package/dist/connections/google.json +28 -0
  15. package/dist/connections/hex.json +14 -0
  16. package/dist/connections/lichess.json +15 -0
  17. package/dist/connections/linear.json +29 -0
  18. package/dist/connections/mastodon.json +25 -0
  19. package/dist/connections/notion.json +33 -0
  20. package/dist/connections/openai.json +16 -0
  21. package/dist/connections/openrouter.json +16 -0
  22. package/dist/connections/reddit.json +28 -0
  23. package/dist/connections/slack.json +23 -0
  24. package/dist/connections/stripe.json +25 -0
  25. package/dist/connections/telegram.json +26 -0
  26. package/dist/connections/trello.json +25 -0
  27. package/dist/connections/twitch.json +28 -0
  28. package/dist/connections/x.json +27 -0
  29. package/dist/mcp/server.d.ts +13 -0
  30. package/dist/mcp/server.js +258 -0
  31. package/dist/remote/ingestors/base-ingestor.d.ts +65 -0
  32. package/dist/remote/ingestors/base-ingestor.js +132 -0
  33. package/dist/remote/ingestors/discord/discord-gateway.d.ts +58 -0
  34. package/dist/remote/ingestors/discord/discord-gateway.js +341 -0
  35. package/dist/remote/ingestors/discord/index.d.ts +3 -0
  36. package/dist/remote/ingestors/discord/index.js +3 -0
  37. package/dist/remote/ingestors/discord/types.d.ts +56 -0
  38. package/dist/remote/ingestors/discord/types.js +68 -0
  39. package/dist/remote/ingestors/index.d.ts +16 -0
  40. package/dist/remote/ingestors/index.js +20 -0
  41. package/dist/remote/ingestors/manager.d.ts +65 -0
  42. package/dist/remote/ingestors/manager.js +201 -0
  43. package/dist/remote/ingestors/poll/index.d.ts +2 -0
  44. package/dist/remote/ingestors/poll/index.js +2 -0
  45. package/dist/remote/ingestors/poll/poll-ingestor.d.ts +78 -0
  46. package/dist/remote/ingestors/poll/poll-ingestor.js +283 -0
  47. package/dist/remote/ingestors/registry.d.ts +32 -0
  48. package/dist/remote/ingestors/registry.js +46 -0
  49. package/dist/remote/ingestors/ring-buffer.d.ts +33 -0
  50. package/dist/remote/ingestors/ring-buffer.js +62 -0
  51. package/dist/remote/ingestors/slack/index.d.ts +3 -0
  52. package/dist/remote/ingestors/slack/index.js +3 -0
  53. package/dist/remote/ingestors/slack/socket-mode.d.ts +48 -0
  54. package/dist/remote/ingestors/slack/socket-mode.js +267 -0
  55. package/dist/remote/ingestors/slack/types.d.ts +70 -0
  56. package/dist/remote/ingestors/slack/types.js +72 -0
  57. package/dist/remote/ingestors/types.d.ts +138 -0
  58. package/dist/remote/ingestors/types.js +13 -0
  59. package/dist/remote/ingestors/webhook/base-webhook-ingestor.d.ts +112 -0
  60. package/dist/remote/ingestors/webhook/base-webhook-ingestor.js +119 -0
  61. package/dist/remote/ingestors/webhook/github-types.d.ts +45 -0
  62. package/dist/remote/ingestors/webhook/github-types.js +65 -0
  63. package/dist/remote/ingestors/webhook/github-webhook-ingestor.d.ts +43 -0
  64. package/dist/remote/ingestors/webhook/github-webhook-ingestor.js +86 -0
  65. package/dist/remote/ingestors/webhook/index.d.ts +8 -0
  66. package/dist/remote/ingestors/webhook/index.js +12 -0
  67. package/dist/remote/ingestors/webhook/stripe-types.d.ts +57 -0
  68. package/dist/remote/ingestors/webhook/stripe-types.js +108 -0
  69. package/dist/remote/ingestors/webhook/stripe-webhook-ingestor.d.ts +47 -0
  70. package/dist/remote/ingestors/webhook/stripe-webhook-ingestor.js +90 -0
  71. package/dist/remote/ingestors/webhook/trello-types.d.ts +90 -0
  72. package/dist/remote/ingestors/webhook/trello-types.js +81 -0
  73. package/dist/remote/ingestors/webhook/trello-webhook-ingestor.d.ts +60 -0
  74. package/dist/remote/ingestors/webhook/trello-webhook-ingestor.js +126 -0
  75. package/dist/remote/server.d.ts +103 -0
  76. package/dist/remote/server.js +536 -0
  77. package/dist/shared/config.d.ts +213 -0
  78. package/dist/shared/config.js +269 -0
  79. package/dist/shared/connections.d.ts +72 -0
  80. package/dist/shared/connections.js +103 -0
  81. package/dist/shared/crypto/channel.d.ts +95 -0
  82. package/dist/shared/crypto/channel.js +175 -0
  83. package/dist/shared/crypto/index.d.ts +3 -0
  84. package/dist/shared/crypto/index.js +3 -0
  85. package/dist/shared/crypto/keys.d.ts +92 -0
  86. package/dist/shared/crypto/keys.js +143 -0
  87. package/dist/shared/logger.d.ts +30 -0
  88. package/dist/shared/logger.js +74 -0
  89. package/dist/shared/protocol/handshake.d.ts +116 -0
  90. package/dist/shared/protocol/handshake.js +214 -0
  91. package/dist/shared/protocol/index.d.ts +3 -0
  92. package/dist/shared/protocol/index.js +2 -0
  93. package/dist/shared/protocol/messages.d.ts +46 -0
  94. package/dist/shared/protocol/messages.js +8 -0
  95. package/package.json +105 -0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Mutual authentication handshake protocol.
3
+ *
4
+ * Inspired by the Noise NK pattern — the responder's (remote server's) public
5
+ * key is known in advance. The initiator (MCP proxy) proves its identity by
6
+ * signing a challenge, and both sides derive a shared secret via X25519 ECDH.
7
+ *
8
+ * Protocol flow:
9
+ *
10
+ * Initiator (MCP Proxy) Responder (Remote Server)
11
+ * ────────────────────── ────────────────────────
12
+ *
13
+ * 1. Generate ephemeral X25519 keypair
14
+ * Sign(ephemeral_pub || nonce_i) with Ed25519
15
+ * ──── HandshakeInit ────────────────►
16
+ * 2. Verify initiator's signature
17
+ * Check initiator's pubkey is authorized
18
+ * Generate ephemeral X25519 keypair
19
+ * Sign(ephemeral_pub || nonce_r || nonce_i)
20
+ * ◄──── HandshakeReply ──────
21
+ * 3. Verify responder's signature
22
+ * Both: ECDH(ephemeral_i, ephemeral_r) → shared secret
23
+ * Both: HKDF(shared_secret, transcript_hash) → session keys
24
+ * ──── HandshakeFinish (encrypted "ready") ──►
25
+ * 4. Decrypt and verify "ready"
26
+ * Session established ✓
27
+ *
28
+ * The handshake transcript (all messages concatenated) is hashed and used as
29
+ * HKDF salt, binding the session keys to the exact handshake that occurred.
30
+ * This prevents unknown-key-share attacks.
31
+ */
32
+ import { type KeyBundle, type PublicKeyBundle, type SessionKeys } from '../crypto/index.js';
33
+ export interface HandshakeInit {
34
+ type: 'handshake_init';
35
+ /** Initiator's static Ed25519 public key (PEM) */
36
+ signingPubKey: string;
37
+ /** Initiator's ephemeral X25519 public key (PEM) */
38
+ ephemeralPubKey: string;
39
+ /** Random nonce (32 bytes, hex) */
40
+ nonceI: string;
41
+ /** Ed25519 signature over (ephemeralPubKey || nonceI) */
42
+ signature: string;
43
+ /** Protocol version */
44
+ version: 1;
45
+ }
46
+ export interface HandshakeReply {
47
+ type: 'handshake_reply';
48
+ /** Responder's ephemeral X25519 public key (PEM) */
49
+ ephemeralPubKey: string;
50
+ /** Random nonce (32 bytes, hex) */
51
+ nonceR: string;
52
+ /** Ed25519 signature over (ephemeralPubKey || nonceR || nonceI) */
53
+ signature: string;
54
+ }
55
+ export interface HandshakeFinish {
56
+ type: 'handshake_finish';
57
+ /** Encrypted "ready" payload — proves the initiator derived the right keys */
58
+ payload: string;
59
+ }
60
+ export type HandshakeMessage = HandshakeInit | HandshakeReply | HandshakeFinish;
61
+ export declare class HandshakeInitiator {
62
+ /** Our full key bundle */
63
+ private readonly ownKeys;
64
+ /** The remote server's known public keys */
65
+ private readonly peerPublicKeys;
66
+ private ephemeral;
67
+ private nonceI;
68
+ private transcript;
69
+ constructor(
70
+ /** Our full key bundle */
71
+ ownKeys: KeyBundle,
72
+ /** The remote server's known public keys */
73
+ peerPublicKeys: PublicKeyBundle);
74
+ /**
75
+ * Step 1: Create the initial handshake message.
76
+ */
77
+ createInit(): HandshakeInit;
78
+ /**
79
+ * Step 3: Process the responder's reply and derive session keys.
80
+ */
81
+ processReply(reply: HandshakeReply): SessionKeys;
82
+ /**
83
+ * Create the finish message (encrypted with the newly derived keys).
84
+ */
85
+ createFinish(keys: SessionKeys): HandshakeFinish;
86
+ }
87
+ export declare class HandshakeResponder {
88
+ /** Our full key bundle */
89
+ private readonly ownKeys;
90
+ /** Set of authorized initiator public keys */
91
+ private readonly authorizedKeys;
92
+ private ephemeral;
93
+ private transcript;
94
+ constructor(
95
+ /** Our full key bundle */
96
+ ownKeys: KeyBundle,
97
+ /** Set of authorized initiator public keys */
98
+ authorizedKeys: PublicKeyBundle[]);
99
+ /**
100
+ * Step 2: Process the init message, verify the initiator, and create a reply.
101
+ */
102
+ processInit(init: HandshakeInit): {
103
+ reply: HandshakeReply;
104
+ initiatorPubKey: PublicKeyBundle;
105
+ };
106
+ /**
107
+ * Derive session keys after sending the reply.
108
+ * Call this after processInit() and before verifying the finish message.
109
+ */
110
+ deriveKeys(init: HandshakeInit): SessionKeys;
111
+ /**
112
+ * Step 4: Verify the finish message to confirm the initiator derived the right keys.
113
+ */
114
+ verifyFinish(finish: HandshakeFinish, keys: SessionKeys): boolean;
115
+ }
116
+ //# sourceMappingURL=handshake.d.ts.map
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Mutual authentication handshake protocol.
3
+ *
4
+ * Inspired by the Noise NK pattern — the responder's (remote server's) public
5
+ * key is known in advance. The initiator (MCP proxy) proves its identity by
6
+ * signing a challenge, and both sides derive a shared secret via X25519 ECDH.
7
+ *
8
+ * Protocol flow:
9
+ *
10
+ * Initiator (MCP Proxy) Responder (Remote Server)
11
+ * ────────────────────── ────────────────────────
12
+ *
13
+ * 1. Generate ephemeral X25519 keypair
14
+ * Sign(ephemeral_pub || nonce_i) with Ed25519
15
+ * ──── HandshakeInit ────────────────►
16
+ * 2. Verify initiator's signature
17
+ * Check initiator's pubkey is authorized
18
+ * Generate ephemeral X25519 keypair
19
+ * Sign(ephemeral_pub || nonce_r || nonce_i)
20
+ * ◄──── HandshakeReply ──────
21
+ * 3. Verify responder's signature
22
+ * Both: ECDH(ephemeral_i, ephemeral_r) → shared secret
23
+ * Both: HKDF(shared_secret, transcript_hash) → session keys
24
+ * ──── HandshakeFinish (encrypted "ready") ──►
25
+ * 4. Decrypt and verify "ready"
26
+ * Session established ✓
27
+ *
28
+ * The handshake transcript (all messages concatenated) is hashed and used as
29
+ * HKDF salt, binding the session keys to the exact handshake that occurred.
30
+ * This prevents unknown-key-share attacks.
31
+ */
32
+ import crypto from 'node:crypto';
33
+ import { deriveSessionKeys, EncryptedChannel, } from '../crypto/index.js';
34
+ // ── Helpers ────────────────────────────────────────────────────────────────
35
+ function signData(privateKey, ...parts) {
36
+ const combined = Buffer.concat(parts.map((p) => (typeof p === 'string' ? Buffer.from(p, 'utf-8') : p)));
37
+ return crypto.sign(null, combined, privateKey);
38
+ }
39
+ function verifySignature(publicKey, signature, ...parts) {
40
+ const combined = Buffer.concat(parts.map((p) => (typeof p === 'string' ? Buffer.from(p, 'utf-8') : p)));
41
+ return crypto.verify(null, combined, publicKey, signature);
42
+ }
43
+ // ── Initiator (MCP Proxy side) ─────────────────────────────────────────────
44
+ export class HandshakeInitiator {
45
+ ownKeys;
46
+ peerPublicKeys;
47
+ ephemeral;
48
+ nonceI;
49
+ transcript = [];
50
+ constructor(
51
+ /** Our full key bundle */
52
+ ownKeys,
53
+ /** The remote server's known public keys */
54
+ peerPublicKeys) {
55
+ this.ownKeys = ownKeys;
56
+ this.peerPublicKeys = peerPublicKeys;
57
+ this.ephemeral = crypto.generateKeyPairSync('x25519');
58
+ this.nonceI = crypto.randomBytes(32);
59
+ }
60
+ /**
61
+ * Step 1: Create the initial handshake message.
62
+ */
63
+ createInit() {
64
+ const ephemeralPubPem = this.ephemeral.publicKey.export({
65
+ type: 'spki',
66
+ format: 'pem',
67
+ });
68
+ const signature = signData(this.ownKeys.signing.privateKey, ephemeralPubPem, this.nonceI);
69
+ const msg = {
70
+ type: 'handshake_init',
71
+ signingPubKey: this.ownKeys.signing.publicKey.export({
72
+ type: 'spki',
73
+ format: 'pem',
74
+ }),
75
+ ephemeralPubKey: ephemeralPubPem,
76
+ nonceI: this.nonceI.toString('hex'),
77
+ signature: signature.toString('hex'),
78
+ version: 1,
79
+ };
80
+ // Record in transcript
81
+ this.transcript.push(Buffer.from(JSON.stringify(msg), 'utf-8'));
82
+ return msg;
83
+ }
84
+ /**
85
+ * Step 3: Process the responder's reply and derive session keys.
86
+ */
87
+ processReply(reply) {
88
+ // Record in transcript
89
+ this.transcript.push(Buffer.from(JSON.stringify(reply), 'utf-8'));
90
+ // Verify responder's signature over (ephemeralPubKey || nonceR || nonceI)
91
+ const sigValid = verifySignature(this.peerPublicKeys.signing, Buffer.from(reply.signature, 'hex'), reply.ephemeralPubKey, Buffer.from(reply.nonceR, 'hex'), this.nonceI);
92
+ if (!sigValid) {
93
+ throw new Error('Handshake failed: responder signature invalid');
94
+ }
95
+ // ECDH to derive shared secret
96
+ const peerEphemeral = crypto.createPublicKey(reply.ephemeralPubKey);
97
+ const sharedSecret = crypto.diffieHellman({
98
+ privateKey: this.ephemeral.privateKey,
99
+ publicKey: peerEphemeral,
100
+ });
101
+ // Hash the full transcript
102
+ const transcriptHash = crypto
103
+ .createHash('sha256')
104
+ .update(Buffer.concat(this.transcript))
105
+ .digest();
106
+ return deriveSessionKeys(sharedSecret, true, transcriptHash);
107
+ }
108
+ /**
109
+ * Create the finish message (encrypted with the newly derived keys).
110
+ */
111
+ createFinish(keys) {
112
+ const channel = new EncryptedChannel(keys);
113
+ const readyPayload = channel.encrypt(Buffer.from(JSON.stringify({ status: 'ready', timestamp: Date.now() }), 'utf-8'));
114
+ return {
115
+ type: 'handshake_finish',
116
+ payload: readyPayload.toString('hex'),
117
+ };
118
+ }
119
+ }
120
+ // ── Responder (Remote Server side) ──────────────────────────────────────────
121
+ export class HandshakeResponder {
122
+ ownKeys;
123
+ authorizedKeys;
124
+ ephemeral = null;
125
+ transcript = [];
126
+ constructor(
127
+ /** Our full key bundle */
128
+ ownKeys,
129
+ /** Set of authorized initiator public keys */
130
+ authorizedKeys) {
131
+ this.ownKeys = ownKeys;
132
+ this.authorizedKeys = authorizedKeys;
133
+ }
134
+ /**
135
+ * Step 2: Process the init message, verify the initiator, and create a reply.
136
+ */
137
+ processInit(init) {
138
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime validation for untrusted input regardless of static type
139
+ if (init.version !== 1) {
140
+ throw new Error(`Unsupported handshake version: ${String(init.version)}`);
141
+ }
142
+ // Record in transcript
143
+ this.transcript.push(Buffer.from(JSON.stringify(init), 'utf-8'));
144
+ // Parse the initiator's signing public key
145
+ const initiatorSigningKey = crypto.createPublicKey(init.signingPubKey);
146
+ // Check if this key is authorized
147
+ const authorized = this.authorizedKeys.find((ak) => {
148
+ const akPem = ak.signing.export({ type: 'spki', format: 'pem' });
149
+ return akPem === init.signingPubKey;
150
+ });
151
+ if (!authorized) {
152
+ throw new Error('Handshake failed: initiator not authorized');
153
+ }
154
+ // Verify initiator's signature over (ephemeralPubKey || nonceI)
155
+ const sigValid = verifySignature(initiatorSigningKey, Buffer.from(init.signature, 'hex'), init.ephemeralPubKey, Buffer.from(init.nonceI, 'hex'));
156
+ if (!sigValid) {
157
+ throw new Error('Handshake failed: initiator signature invalid');
158
+ }
159
+ // Generate our ephemeral keypair
160
+ this.ephemeral = crypto.generateKeyPairSync('x25519');
161
+ const ephemeralPubPem = this.ephemeral.publicKey.export({
162
+ type: 'spki',
163
+ format: 'pem',
164
+ });
165
+ const nonceR = crypto.randomBytes(32);
166
+ // Sign (ephemeralPubKey || nonceR || nonceI)
167
+ const signature = signData(this.ownKeys.signing.privateKey, ephemeralPubPem, nonceR, Buffer.from(init.nonceI, 'hex'));
168
+ const reply = {
169
+ type: 'handshake_reply',
170
+ ephemeralPubKey: ephemeralPubPem,
171
+ nonceR: nonceR.toString('hex'),
172
+ signature: signature.toString('hex'),
173
+ };
174
+ // Record in transcript
175
+ this.transcript.push(Buffer.from(JSON.stringify(reply), 'utf-8'));
176
+ return { reply, initiatorPubKey: authorized };
177
+ }
178
+ /**
179
+ * Derive session keys after sending the reply.
180
+ * Call this after processInit() and before verifying the finish message.
181
+ */
182
+ deriveKeys(init) {
183
+ if (!this.ephemeral) {
184
+ throw new Error('Must call processInit() first');
185
+ }
186
+ // ECDH with initiator's ephemeral public key
187
+ const peerEphemeral = crypto.createPublicKey(init.ephemeralPubKey);
188
+ const sharedSecret = crypto.diffieHellman({
189
+ privateKey: this.ephemeral.privateKey,
190
+ publicKey: peerEphemeral,
191
+ });
192
+ // Hash the full transcript
193
+ const transcriptHash = crypto
194
+ .createHash('sha256')
195
+ .update(Buffer.concat(this.transcript))
196
+ .digest();
197
+ return deriveSessionKeys(sharedSecret, false, transcriptHash);
198
+ }
199
+ /**
200
+ * Step 4: Verify the finish message to confirm the initiator derived the right keys.
201
+ */
202
+ verifyFinish(finish, keys) {
203
+ const channel = new EncryptedChannel(keys);
204
+ try {
205
+ const payload = channel.decrypt(Buffer.from(finish.payload, 'hex'));
206
+ const parsed = JSON.parse(payload.toString('utf-8'));
207
+ return parsed.status === 'ready';
208
+ }
209
+ catch {
210
+ return false;
211
+ }
212
+ }
213
+ }
214
+ //# sourceMappingURL=handshake.js.map
@@ -0,0 +1,3 @@
1
+ export { type HandshakeInit, type HandshakeReply, type HandshakeFinish, type HandshakeMessage, HandshakeInitiator, HandshakeResponder, } from './handshake.js';
2
+ export { type ProxyRequest, type ProxyResponse, type PingMessage, type PongMessage, type AppMessage, } from './messages.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { HandshakeInitiator, HandshakeResponder, } from './handshake.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Application-layer message types exchanged over the encrypted channel.
3
+ *
4
+ * All messages are JSON-serialized, encrypted with AES-256-GCM, and sent
5
+ * as hex-encoded payloads over the HTTP transport.
6
+ */
7
+ /** Request from MCP proxy → remote server */
8
+ export interface ProxyRequest {
9
+ type: 'proxy_request';
10
+ /** Unique request ID for correlation */
11
+ id: string;
12
+ /** The tool name the MCP client invoked */
13
+ toolName: string;
14
+ /** The tool's input parameters */
15
+ toolInput: Record<string, unknown>;
16
+ /** Timestamp (ms since epoch) */
17
+ timestamp: number;
18
+ }
19
+ /** Response from remote server → MCP proxy */
20
+ export interface ProxyResponse {
21
+ type: 'proxy_response';
22
+ /** Correlates to ProxyRequest.id */
23
+ id: string;
24
+ /** Whether the operation succeeded */
25
+ success: boolean;
26
+ /** The result payload (tool output) */
27
+ result?: unknown;
28
+ /** Error message if success=false */
29
+ error?: string;
30
+ /** Timestamp */
31
+ timestamp: number;
32
+ }
33
+ /** Ping to keep the connection alive / verify the channel */
34
+ export interface PingMessage {
35
+ type: 'ping';
36
+ timestamp: number;
37
+ }
38
+ /** Pong response */
39
+ export interface PongMessage {
40
+ type: 'pong';
41
+ timestamp: number;
42
+ /** Echo back the ping timestamp for RTT measurement */
43
+ echoTimestamp: number;
44
+ }
45
+ export type AppMessage = ProxyRequest | ProxyResponse | PingMessage | PongMessage;
46
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Application-layer message types exchanged over the encrypted channel.
3
+ *
4
+ * All messages are JSON-serialized, encrypted with AES-256-GCM, and sent
5
+ * as hex-encoded payloads over the HTTP transport.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=messages.js.map
package/package.json ADDED
@@ -0,0 +1,105 @@
1
+ {
2
+ "name": "@wolpertingerlabs/drawlatch",
3
+ "version": "1.0.0-alpha.1",
4
+ "description": "Encrypted MCP proxy with mutual authentication. Local MCP server forwards requests through an encrypted channel to a remote secrets-holding server.",
5
+ "type": "module",
6
+ "main": "./dist/mcp/server.js",
7
+ "types": "./dist/mcp/server.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "!dist/**/*.test.*",
11
+ "!dist/**/*.js.map",
12
+ "!dist/**/*.d.ts.map",
13
+ "README.md",
14
+ "CONNECTIONS.md",
15
+ "INGESTORS.md"
16
+ ],
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/mcp/server.d.ts",
20
+ "import": "./dist/mcp/server.js"
21
+ },
22
+ "./shared/crypto": {
23
+ "types": "./dist/shared/crypto/index.d.ts",
24
+ "import": "./dist/shared/crypto/index.js"
25
+ },
26
+ "./shared/protocol": {
27
+ "types": "./dist/shared/protocol/index.d.ts",
28
+ "import": "./dist/shared/protocol/index.js"
29
+ },
30
+ "./shared/config": {
31
+ "types": "./dist/shared/config.d.ts",
32
+ "import": "./dist/shared/config.js"
33
+ },
34
+ "./shared/connections": {
35
+ "types": "./dist/shared/connections.d.ts",
36
+ "import": "./dist/shared/connections.js"
37
+ },
38
+ "./remote/server": {
39
+ "types": "./dist/remote/server.d.ts",
40
+ "import": "./dist/remote/server.js"
41
+ },
42
+ "./remote/ingestors": {
43
+ "types": "./dist/remote/ingestors/index.d.ts",
44
+ "import": "./dist/remote/ingestors/index.js"
45
+ }
46
+ },
47
+ "scripts": {
48
+ "build": "tsc && rm -rf dist/connections && cp -r src/connections dist/connections",
49
+ "prepare": "npm run build",
50
+ "dev:remote": "tsx src/remote/server.ts",
51
+ "dev:mcp": "tsx src/mcp/server.ts",
52
+ "generate-keys": "tsx src/cli/generate-keys.ts",
53
+ "start:remote": "NODE_ENV=production node dist/remote/server.js",
54
+ "start:mcp": "node dist/mcp/server.js",
55
+ "redeploy:prod": "node start-server.js",
56
+ "test": "vitest run",
57
+ "test:watch": "vitest",
58
+ "test:coverage": "vitest run --coverage",
59
+ "lint": "eslint src/",
60
+ "lint:fix": "eslint src/ --fix",
61
+ "format": "prettier --write 'src/**/*.ts' '*.{json,ts,js}'",
62
+ "format:check": "prettier --check 'src/**/*.ts' '*.{json,ts,js}'",
63
+ "prepublishOnly": "npm run lint && npm test && npm run build",
64
+ "publish:dry-run": "npm publish --dry-run"
65
+ },
66
+ "engines": {
67
+ "node": ">=22"
68
+ },
69
+ "repository": {
70
+ "type": "git",
71
+ "url": "git+https://github.com/WolpertingerLabs/drawlatch.git"
72
+ },
73
+ "publishConfig": {
74
+ "access": "public"
75
+ },
76
+ "keywords": [
77
+ "mcp",
78
+ "security",
79
+ "proxy",
80
+ "encryption",
81
+ "secrets"
82
+ ],
83
+ "author": "",
84
+ "license": "MIT",
85
+ "dependencies": {
86
+ "@modelcontextprotocol/sdk": "^1.26.0",
87
+ "dotenv": "^17.3.1",
88
+ "express": "^5.2.1",
89
+ "zod": "^4.3.6"
90
+ },
91
+ "devDependencies": {
92
+ "@eslint/js": "^9.39.2",
93
+ "@types/express": "^5.0.6",
94
+ "@types/node": "^25.2.2",
95
+ "@vitest/coverage-v8": "^4.0.18",
96
+ "eslint": "^9.39.2",
97
+ "eslint-config-prettier": "^10.1.8",
98
+ "eslint-plugin-prettier": "^5.5.5",
99
+ "prettier": "^3.8.1",
100
+ "tsx": "^4.21.0",
101
+ "typescript": "^5.9.3",
102
+ "typescript-eslint": "^8.54.0",
103
+ "vitest": "^4.0.18"
104
+ }
105
+ }