llng-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.
Files changed (114) hide show
  1. package/.github/workflows/ci.yml +77 -0
  2. package/.prettierrc +7 -0
  3. package/LICENSE +661 -0
  4. package/README.md +502 -0
  5. package/dist/__tests__/api-transport.test.d.ts +1 -0
  6. package/dist/__tests__/api-transport.test.js +577 -0
  7. package/dist/__tests__/api-transport.test.js.map +1 -0
  8. package/dist/__tests__/config.test.d.ts +1 -0
  9. package/dist/__tests__/config.test.js +472 -0
  10. package/dist/__tests__/config.test.js.map +1 -0
  11. package/dist/__tests__/integration/api-mode.test.d.ts +1 -0
  12. package/dist/__tests__/integration/api-mode.test.js +199 -0
  13. package/dist/__tests__/integration/api-mode.test.js.map +1 -0
  14. package/dist/__tests__/integration/oidc-rp.test.d.ts +1 -0
  15. package/dist/__tests__/integration/oidc-rp.test.js +120 -0
  16. package/dist/__tests__/integration/oidc-rp.test.js.map +1 -0
  17. package/dist/__tests__/integration/ssh-mode.test.d.ts +1 -0
  18. package/dist/__tests__/integration/ssh-mode.test.js +101 -0
  19. package/dist/__tests__/integration/ssh-mode.test.js.map +1 -0
  20. package/dist/__tests__/k8s-transport.test.d.ts +1 -0
  21. package/dist/__tests__/k8s-transport.test.js +254 -0
  22. package/dist/__tests__/k8s-transport.test.js.map +1 -0
  23. package/dist/__tests__/oidc-tools.test.d.ts +1 -0
  24. package/dist/__tests__/oidc-tools.test.js +457 -0
  25. package/dist/__tests__/oidc-tools.test.js.map +1 -0
  26. package/dist/__tests__/registry.test.d.ts +1 -0
  27. package/dist/__tests__/registry.test.js +96 -0
  28. package/dist/__tests__/registry.test.js.map +1 -0
  29. package/dist/__tests__/ssh-transport.test.d.ts +1 -0
  30. package/dist/__tests__/ssh-transport.test.js +618 -0
  31. package/dist/__tests__/ssh-transport.test.js.map +1 -0
  32. package/dist/__tests__/tools.test.d.ts +1 -0
  33. package/dist/__tests__/tools.test.js +525 -0
  34. package/dist/__tests__/tools.test.js.map +1 -0
  35. package/dist/config.d.ts +65 -0
  36. package/dist/config.js +506 -0
  37. package/dist/config.js.map +1 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +42 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/resources/documentation.d.ts +5 -0
  42. package/dist/resources/documentation.js +56 -0
  43. package/dist/resources/documentation.js.map +1 -0
  44. package/dist/tools/cli-utilities.d.ts +3 -0
  45. package/dist/tools/cli-utilities.js +187 -0
  46. package/dist/tools/cli-utilities.js.map +1 -0
  47. package/dist/tools/config.d.ts +6 -0
  48. package/dist/tools/config.js +326 -0
  49. package/dist/tools/config.js.map +1 -0
  50. package/dist/tools/consents.d.ts +3 -0
  51. package/dist/tools/consents.js +39 -0
  52. package/dist/tools/consents.js.map +1 -0
  53. package/dist/tools/instances.d.ts +3 -0
  54. package/dist/tools/instances.js +14 -0
  55. package/dist/tools/instances.js.map +1 -0
  56. package/dist/tools/oidc-rp.d.ts +6 -0
  57. package/dist/tools/oidc-rp.js +246 -0
  58. package/dist/tools/oidc-rp.js.map +1 -0
  59. package/dist/tools/oidc.d.ts +3 -0
  60. package/dist/tools/oidc.js +343 -0
  61. package/dist/tools/oidc.js.map +1 -0
  62. package/dist/tools/secondfactors.d.ts +3 -0
  63. package/dist/tools/secondfactors.js +62 -0
  64. package/dist/tools/secondfactors.js.map +1 -0
  65. package/dist/tools/sessions.d.ts +6 -0
  66. package/dist/tools/sessions.js +300 -0
  67. package/dist/tools/sessions.js.map +1 -0
  68. package/dist/transport/api.d.ts +35 -0
  69. package/dist/transport/api.js +327 -0
  70. package/dist/transport/api.js.map +1 -0
  71. package/dist/transport/interface.d.ts +50 -0
  72. package/dist/transport/interface.js +2 -0
  73. package/dist/transport/interface.js.map +1 -0
  74. package/dist/transport/k8s.d.ts +41 -0
  75. package/dist/transport/k8s.js +303 -0
  76. package/dist/transport/k8s.js.map +1 -0
  77. package/dist/transport/registry.d.ts +20 -0
  78. package/dist/transport/registry.js +91 -0
  79. package/dist/transport/registry.js.map +1 -0
  80. package/dist/transport/ssh.d.ts +37 -0
  81. package/dist/transport/ssh.js +353 -0
  82. package/dist/transport/ssh.js.map +1 -0
  83. package/docker-compose.test.yml +16 -0
  84. package/eslint.config.js +21 -0
  85. package/package.json +38 -0
  86. package/src/__tests__/api-transport.test.ts +746 -0
  87. package/src/__tests__/config.test.ts +587 -0
  88. package/src/__tests__/integration/api-mode.test.ts +229 -0
  89. package/src/__tests__/integration/oidc-rp.test.ts +138 -0
  90. package/src/__tests__/integration/ssh-mode.test.ts +113 -0
  91. package/src/__tests__/k8s-transport.test.ts +342 -0
  92. package/src/__tests__/oidc-tools.test.ts +554 -0
  93. package/src/__tests__/registry.test.ts +110 -0
  94. package/src/__tests__/ssh-transport.test.ts +805 -0
  95. package/src/__tests__/tools.test.ts +735 -0
  96. package/src/config.ts +605 -0
  97. package/src/index.ts +48 -0
  98. package/src/resources/documentation.ts +65 -0
  99. package/src/tools/cli-utilities.ts +207 -0
  100. package/src/tools/config.ts +382 -0
  101. package/src/tools/consents.ts +50 -0
  102. package/src/tools/instances.ts +21 -0
  103. package/src/tools/oidc-rp.ts +299 -0
  104. package/src/tools/oidc.ts +434 -0
  105. package/src/tools/secondfactors.ts +78 -0
  106. package/src/tools/sessions.ts +342 -0
  107. package/src/transport/api.ts +429 -0
  108. package/src/transport/interface.ts +58 -0
  109. package/src/transport/k8s.ts +367 -0
  110. package/src/transport/registry.ts +105 -0
  111. package/src/transport/ssh.ts +430 -0
  112. package/tsconfig.json +16 -0
  113. package/vitest.config.ts +8 -0
  114. package/vitest.integration.config.ts +9 -0
@@ -0,0 +1,342 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { SessionFilter, SessionGetOptions, SessionDeleteOptions } from "../transport/interface.js";
4
+ import { TransportRegistry } from "../transport/registry.js";
5
+
6
+ /**
7
+ * Register LLNG session management tools
8
+ */
9
+ export function registerSessionTools(server: McpServer, registry: TransportRegistry): void {
10
+ // 1. llng_session_get - Get LLNG session by ID
11
+ server.tool(
12
+ "llng_session_get",
13
+ "Get LLNG session by ID",
14
+ {
15
+ id: z.string().describe("The session ID to retrieve"),
16
+ backend: z
17
+ .string()
18
+ .optional()
19
+ .describe("Optional backend type (persistent, oidc, saml, cas)"),
20
+ refreshTokens: z
21
+ .boolean()
22
+ .optional()
23
+ .describe("Filter for refresh token (offline) sessions only"),
24
+ persistent: z
25
+ .boolean()
26
+ .optional()
27
+ .describe("Shortcut for --backend persistent; also hashes UID for persistent session ID"),
28
+ hash: z
29
+ .boolean()
30
+ .optional()
31
+ .describe(
32
+ "Indicates the given session ID is the original cookie value (for hashed session storage)",
33
+ ),
34
+ instance: z.string().optional().describe("LLNG instance name (uses default if omitted)"),
35
+ },
36
+ async (args) => {
37
+ try {
38
+ const transport = registry.getTransport(args.instance);
39
+ const options: SessionGetOptions = {
40
+ backend: args.backend,
41
+ refreshTokens: args.refreshTokens,
42
+ persistent: args.persistent,
43
+ hash: args.hash,
44
+ };
45
+ const result = await transport.sessionGet(args.id, options);
46
+ return {
47
+ content: [
48
+ {
49
+ type: "text",
50
+ text: JSON.stringify(result, null, 2),
51
+ },
52
+ ],
53
+ };
54
+ } catch (error) {
55
+ return {
56
+ content: [
57
+ {
58
+ type: "text",
59
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
60
+ },
61
+ ],
62
+ isError: true,
63
+ };
64
+ }
65
+ },
66
+ );
67
+
68
+ // 2. llng_session_search - Search LLNG sessions
69
+ server.tool(
70
+ "llng_session_search",
71
+ "Search LLNG sessions",
72
+ {
73
+ where: z
74
+ .record(z.string(), z.string())
75
+ .optional()
76
+ .describe("Field=value pairs for filtering"),
77
+ select: z.array(z.string()).optional().describe("Fields to return in results"),
78
+ backend: z.string().optional().describe("Backend type (persistent, oidc, saml, cas)"),
79
+ count: z.boolean().optional().describe("Return only the count of matching sessions"),
80
+ refreshTokens: z
81
+ .boolean()
82
+ .optional()
83
+ .describe("Filter for refresh token (offline) sessions only"),
84
+ persistent: z.boolean().optional().describe("Shortcut for --backend persistent"),
85
+ hash: z.boolean().optional().describe("Indicates session IDs are original cookie values"),
86
+ idOnly: z.boolean().optional().describe("Only return session IDs"),
87
+ kind: z
88
+ .string()
89
+ .optional()
90
+ .describe(
91
+ "Filter by session kind: SSO, SAML, CAS, OIDC, Persistent. " +
92
+ "This is a shortcut that adds _session_kind to the where filter",
93
+ ),
94
+ instance: z.string().optional().describe("LLNG instance name (uses default if omitted)"),
95
+ },
96
+ async (args) => {
97
+ try {
98
+ const transport = registry.getTransport(args.instance);
99
+ const where = { ...args.where };
100
+ if (args.kind) {
101
+ where._session_kind = args.kind;
102
+ }
103
+ const filters: SessionFilter = {
104
+ where: Object.keys(where).length > 0 ? where : undefined,
105
+ select: args.select,
106
+ backend: args.backend,
107
+ count: args.count,
108
+ refreshTokens: args.refreshTokens,
109
+ persistent: args.persistent,
110
+ hash: args.hash,
111
+ idOnly: args.idOnly,
112
+ };
113
+ const result = await transport.sessionSearch(filters);
114
+ return {
115
+ content: [
116
+ {
117
+ type: "text",
118
+ text: JSON.stringify(result, null, 2),
119
+ },
120
+ ],
121
+ };
122
+ } catch (error) {
123
+ return {
124
+ content: [
125
+ {
126
+ type: "text",
127
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
128
+ },
129
+ ],
130
+ isError: true,
131
+ };
132
+ }
133
+ },
134
+ );
135
+
136
+ // 3. llng_session_delete - Delete LLNG session(s)
137
+ server.tool(
138
+ "llng_session_delete",
139
+ "Delete LLNG session(s)",
140
+ {
141
+ ids: z
142
+ .array(z.string())
143
+ .optional()
144
+ .describe("Array of session IDs to delete (not needed when using where)"),
145
+ backend: z
146
+ .string()
147
+ .optional()
148
+ .describe("Optional backend type (persistent, oidc, saml, cas)"),
149
+ refreshTokens: z
150
+ .boolean()
151
+ .optional()
152
+ .describe("Filter for refresh token (offline) sessions only"),
153
+ persistent: z.boolean().optional().describe("Shortcut for --backend persistent"),
154
+ hash: z.boolean().optional().describe("Indicates session IDs are original cookie values"),
155
+ where: z
156
+ .record(z.string(), z.string())
157
+ .optional()
158
+ .describe("Delete sessions matching filter instead of by ID"),
159
+ kind: z
160
+ .string()
161
+ .optional()
162
+ .describe(
163
+ "Filter by session kind: SSO, SAML, CAS, OIDC, Persistent. " +
164
+ "This is a shortcut that adds _session_kind to the where filter",
165
+ ),
166
+ instance: z.string().optional().describe("LLNG instance name (uses default if omitted)"),
167
+ },
168
+ async (args) => {
169
+ try {
170
+ const transport = registry.getTransport(args.instance);
171
+ const where = { ...args.where };
172
+ if (args.kind) {
173
+ where._session_kind = args.kind;
174
+ }
175
+ const options: SessionDeleteOptions = {
176
+ backend: args.backend,
177
+ refreshTokens: args.refreshTokens,
178
+ persistent: args.persistent,
179
+ hash: args.hash,
180
+ where: Object.keys(where).length > 0 ? where : undefined,
181
+ };
182
+ const ids = args.ids || [];
183
+ await transport.sessionDelete(ids, options);
184
+ const desc = options.where ? "matching sessions" : `${ids.length} session(s)`;
185
+ return {
186
+ content: [
187
+ {
188
+ type: "text",
189
+ text: `Successfully deleted ${desc}`,
190
+ },
191
+ ],
192
+ };
193
+ } catch (error) {
194
+ return {
195
+ content: [
196
+ {
197
+ type: "text",
198
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
199
+ },
200
+ ],
201
+ isError: true,
202
+ };
203
+ }
204
+ },
205
+ );
206
+
207
+ // 4. llng_session_setKey - Set key(s) in an LLNG session
208
+ server.tool(
209
+ "llng_session_setKey",
210
+ "Set key(s) in an LLNG session",
211
+ {
212
+ id: z.string().describe("The session ID to modify"),
213
+ keys: z.record(z.string(), z.any()).describe("Key-value pairs to set in the session"),
214
+ backend: z.string().optional().describe("Optional backend type"),
215
+ refreshTokens: z.boolean().optional().describe("Target refresh token sessions"),
216
+ persistent: z.boolean().optional().describe("Shortcut for --backend persistent"),
217
+ hash: z.boolean().optional().describe("Session ID is original cookie value"),
218
+ instance: z.string().optional().describe("LLNG instance name (uses default if omitted)"),
219
+ },
220
+ async (args) => {
221
+ try {
222
+ const transport = registry.getTransport(args.instance);
223
+ const options: SessionGetOptions = {
224
+ backend: args.backend,
225
+ refreshTokens: args.refreshTokens,
226
+ persistent: args.persistent,
227
+ hash: args.hash,
228
+ };
229
+ await transport.sessionSetKey(args.id, args.keys, options);
230
+ return {
231
+ content: [
232
+ {
233
+ type: "text",
234
+ text: `Successfully updated keys in session '${args.id}'`,
235
+ },
236
+ ],
237
+ };
238
+ } catch (error) {
239
+ return {
240
+ content: [
241
+ {
242
+ type: "text",
243
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
244
+ },
245
+ ],
246
+ isError: true,
247
+ };
248
+ }
249
+ },
250
+ );
251
+
252
+ // 5. llng_session_delKey - Delete key(s) from an LLNG session
253
+ server.tool(
254
+ "llng_session_delKey",
255
+ "Delete key(s) from an LLNG session",
256
+ {
257
+ id: z.string().describe("The session ID to modify"),
258
+ keys: z.array(z.string()).describe("Array of keys to delete from the session"),
259
+ backend: z.string().optional().describe("Optional backend type"),
260
+ refreshTokens: z.boolean().optional().describe("Target refresh token sessions"),
261
+ persistent: z.boolean().optional().describe("Shortcut for --backend persistent"),
262
+ hash: z.boolean().optional().describe("Session ID is original cookie value"),
263
+ instance: z.string().optional().describe("LLNG instance name (uses default if omitted)"),
264
+ },
265
+ async (args) => {
266
+ try {
267
+ const transport = registry.getTransport(args.instance);
268
+ const options: SessionGetOptions = {
269
+ backend: args.backend,
270
+ refreshTokens: args.refreshTokens,
271
+ persistent: args.persistent,
272
+ hash: args.hash,
273
+ };
274
+ await transport.sessionDelKey(args.id, args.keys, options);
275
+ return {
276
+ content: [
277
+ {
278
+ type: "text",
279
+ text: `Successfully deleted keys from session '${args.id}'`,
280
+ },
281
+ ],
282
+ };
283
+ } catch (error) {
284
+ return {
285
+ content: [
286
+ {
287
+ type: "text",
288
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
289
+ },
290
+ ],
291
+ isError: true,
292
+ };
293
+ }
294
+ },
295
+ );
296
+
297
+ // 6. llng_session_backup - Backup all LLNG sessions
298
+ server.tool(
299
+ "llng_session_backup",
300
+ "Backup all LLNG sessions",
301
+ {
302
+ backend: z
303
+ .string()
304
+ .optional()
305
+ .describe("Optional backend type to backup (persistent, oidc, saml, cas)"),
306
+ refreshTokens: z
307
+ .boolean()
308
+ .optional()
309
+ .describe("Filter for refresh token (offline) sessions only"),
310
+ persistent: z.boolean().optional().describe("Shortcut for --backend persistent"),
311
+ instance: z.string().optional().describe("LLNG instance name (uses default if omitted)"),
312
+ },
313
+ async (args) => {
314
+ try {
315
+ const transport = registry.getTransport(args.instance);
316
+ const result = await transport.sessionBackup(
317
+ args.backend,
318
+ args.refreshTokens,
319
+ args.persistent,
320
+ );
321
+ return {
322
+ content: [
323
+ {
324
+ type: "text",
325
+ text: result,
326
+ },
327
+ ],
328
+ };
329
+ } catch (error) {
330
+ return {
331
+ content: [
332
+ {
333
+ type: "text",
334
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
335
+ },
336
+ ],
337
+ isError: true,
338
+ };
339
+ }
340
+ },
341
+ );
342
+ }