@homeflare/alchemy 0.43.0 → 0.45.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 (612) hide show
  1. package/README.md +10 -1
  2. package/dist/caddy/index.js +35 -1
  3. package/dist/caddy/index.js.map +3 -3
  4. package/dist/launchd/index.js +35 -1
  5. package/dist/launchd/index.js.map +3 -3
  6. package/dist/linux/index.js +35 -1
  7. package/dist/linux/index.js.map +3 -3
  8. package/dist/litellm/access-group-form.d.ts +26 -0
  9. package/dist/litellm/access-group-form.d.ts.map +1 -0
  10. package/dist/litellm/access-group-operations.d.ts +37 -0
  11. package/dist/litellm/access-group-operations.d.ts.map +1 -0
  12. package/dist/litellm/access-group-types.d.ts +49 -0
  13. package/dist/litellm/access-group-types.d.ts.map +1 -0
  14. package/dist/litellm/access-group.d.ts +55 -0
  15. package/dist/litellm/access-group.d.ts.map +1 -0
  16. package/dist/litellm/credential-errors.d.ts +131 -0
  17. package/dist/litellm/credential-errors.d.ts.map +1 -0
  18. package/dist/litellm/credential-form.d.ts +79 -0
  19. package/dist/litellm/credential-form.d.ts.map +1 -0
  20. package/dist/litellm/credential-operations.d.ts +84 -0
  21. package/dist/litellm/credential-operations.d.ts.map +1 -0
  22. package/dist/litellm/credential-rewrite.d.ts +6 -0
  23. package/dist/litellm/credential-rewrite.d.ts.map +1 -0
  24. package/dist/litellm/credential-types.d.ts +87 -0
  25. package/dist/litellm/credential-types.d.ts.map +1 -0
  26. package/dist/litellm/credential-values.d.ts +53 -0
  27. package/dist/litellm/credential-values.d.ts.map +1 -0
  28. package/dist/litellm/credential.d.ts +83 -0
  29. package/dist/litellm/credential.d.ts.map +1 -0
  30. package/dist/litellm/fake-access-group-litellm.d.ts +34 -0
  31. package/dist/litellm/fake-access-group-litellm.d.ts.map +1 -0
  32. package/dist/litellm/fake-credential-litellm.d.ts +54 -0
  33. package/dist/litellm/fake-credential-litellm.d.ts.map +1 -0
  34. package/dist/litellm/fake-keys.d.ts +53 -0
  35. package/dist/litellm/fake-keys.d.ts.map +1 -0
  36. package/dist/litellm/fake-litellm.d.ts +4 -1
  37. package/dist/litellm/fake-litellm.d.ts.map +1 -1
  38. package/dist/litellm/fake-mcp-litellm.d.ts +67 -0
  39. package/dist/litellm/fake-mcp-litellm.d.ts.map +1 -0
  40. package/dist/litellm/fake-model-litellm-post-update.d.ts +49 -0
  41. package/dist/litellm/fake-model-litellm-post-update.d.ts.map +1 -0
  42. package/dist/litellm/fake-model-litellm.d.ts +48 -0
  43. package/dist/litellm/fake-model-litellm.d.ts.map +1 -0
  44. package/dist/litellm/fake-policy-attachment-litellm.d.ts +33 -0
  45. package/dist/litellm/fake-policy-attachment-litellm.d.ts.map +1 -0
  46. package/dist/litellm/fake-policy-litellm.d.ts +32 -0
  47. package/dist/litellm/fake-policy-litellm.d.ts.map +1 -0
  48. package/dist/litellm/fake-registry-base.d.ts +42 -0
  49. package/dist/litellm/fake-registry-base.d.ts.map +1 -0
  50. package/dist/litellm/fake-stack.d.ts +8 -1
  51. package/dist/litellm/fake-stack.d.ts.map +1 -1
  52. package/dist/litellm/fake-team-litellm.d.ts +47 -0
  53. package/dist/litellm/fake-team-litellm.d.ts.map +1 -0
  54. package/dist/litellm/fake-tool-litellm.d.ts +28 -0
  55. package/dist/litellm/fake-tool-litellm.d.ts.map +1 -0
  56. package/dist/litellm/fake-toolset-litellm.d.ts +29 -0
  57. package/dist/litellm/fake-toolset-litellm.d.ts.map +1 -0
  58. package/dist/litellm/index.d.ts +18 -0
  59. package/dist/litellm/index.d.ts.map +1 -1
  60. package/dist/litellm/index.js +3078 -2
  61. package/dist/litellm/index.js.map +65 -4
  62. package/dist/litellm/key-errors.d.ts +169 -0
  63. package/dist/litellm/key-errors.d.ts.map +1 -0
  64. package/dist/litellm/key-form.d.ts +119 -0
  65. package/dist/litellm/key-form.d.ts.map +1 -0
  66. package/dist/litellm/key-harness.d.ts +41 -0
  67. package/dist/litellm/key-harness.d.ts.map +1 -0
  68. package/dist/litellm/key-metadata-errors.d.ts +30 -0
  69. package/dist/litellm/key-metadata-errors.d.ts.map +1 -0
  70. package/dist/litellm/key-metadata.d.ts +22 -0
  71. package/dist/litellm/key-metadata.d.ts.map +1 -0
  72. package/dist/litellm/key-operations.d.ts +63 -0
  73. package/dist/litellm/key-operations.d.ts.map +1 -0
  74. package/dist/litellm/key-secret.d.ts +33 -0
  75. package/dist/litellm/key-secret.d.ts.map +1 -0
  76. package/dist/litellm/key.d.ts +78 -0
  77. package/dist/litellm/key.d.ts.map +1 -0
  78. package/dist/litellm/mcp-server-credential.d.ts +50 -0
  79. package/dist/litellm/mcp-server-credential.d.ts.map +1 -0
  80. package/dist/litellm/mcp-server-errors.d.ts +101 -0
  81. package/dist/litellm/mcp-server-errors.d.ts.map +1 -0
  82. package/dist/litellm/mcp-server-form.d.ts +56 -0
  83. package/dist/litellm/mcp-server-form.d.ts.map +1 -0
  84. package/dist/litellm/mcp-server-locate.d.ts +26 -0
  85. package/dist/litellm/mcp-server-locate.d.ts.map +1 -0
  86. package/dist/litellm/mcp-server-operations.d.ts +63 -0
  87. package/dist/litellm/mcp-server-operations.d.ts.map +1 -0
  88. package/dist/litellm/mcp-server-shadow.d.ts +24 -0
  89. package/dist/litellm/mcp-server-shadow.d.ts.map +1 -0
  90. package/dist/litellm/mcp-server-types.d.ts +118 -0
  91. package/dist/litellm/mcp-server-types.d.ts.map +1 -0
  92. package/dist/litellm/mcp-server-url.d.ts +11 -0
  93. package/dist/litellm/mcp-server-url.d.ts.map +1 -0
  94. package/dist/litellm/mcp-server.d.ts +95 -0
  95. package/dist/litellm/mcp-server.d.ts.map +1 -0
  96. package/dist/litellm/model-claim.d.ts +24 -0
  97. package/dist/litellm/model-claim.d.ts.map +1 -0
  98. package/dist/litellm/model-credential.d.ts +12 -0
  99. package/dist/litellm/model-credential.d.ts.map +1 -0
  100. package/dist/litellm/model-errors.d.ts +119 -0
  101. package/dist/litellm/model-errors.d.ts.map +1 -0
  102. package/dist/litellm/model-form.d.ts +78 -0
  103. package/dist/litellm/model-form.d.ts.map +1 -0
  104. package/dist/litellm/model-locate.d.ts +13 -0
  105. package/dist/litellm/model-locate.d.ts.map +1 -0
  106. package/dist/litellm/model-operations.d.ts +59 -0
  107. package/dist/litellm/model-operations.d.ts.map +1 -0
  108. package/dist/litellm/model-types.d.ts +91 -0
  109. package/dist/litellm/model-types.d.ts.map +1 -0
  110. package/dist/litellm/model.d.ts +88 -0
  111. package/dist/litellm/model.d.ts.map +1 -0
  112. package/dist/litellm/policy-attachment-form.d.ts +19 -0
  113. package/dist/litellm/policy-attachment-form.d.ts.map +1 -0
  114. package/dist/litellm/policy-attachment-operations.d.ts +31 -0
  115. package/dist/litellm/policy-attachment-operations.d.ts.map +1 -0
  116. package/dist/litellm/policy-attachment-types.d.ts +48 -0
  117. package/dist/litellm/policy-attachment-types.d.ts.map +1 -0
  118. package/dist/litellm/policy-attachment.d.ts +61 -0
  119. package/dist/litellm/policy-attachment.d.ts.map +1 -0
  120. package/dist/litellm/policy-form.d.ts +22 -0
  121. package/dist/litellm/policy-form.d.ts.map +1 -0
  122. package/dist/litellm/policy-operations.d.ts +42 -0
  123. package/dist/litellm/policy-operations.d.ts.map +1 -0
  124. package/dist/litellm/policy-types.d.ts +57 -0
  125. package/dist/litellm/policy-types.d.ts.map +1 -0
  126. package/dist/litellm/policy.d.ts +65 -0
  127. package/dist/litellm/policy.d.ts.map +1 -0
  128. package/dist/litellm/providers.d.ts +1 -1
  129. package/dist/litellm/providers.d.ts.map +1 -1
  130. package/dist/litellm/registry-errors.d.ts +62 -0
  131. package/dist/litellm/registry-errors.d.ts.map +1 -0
  132. package/dist/litellm/registry-support.d.ts +42 -0
  133. package/dist/litellm/registry-support.d.ts.map +1 -0
  134. package/dist/litellm/team-form.d.ts +26 -0
  135. package/dist/litellm/team-form.d.ts.map +1 -0
  136. package/dist/litellm/team-members.d.ts +10 -0
  137. package/dist/litellm/team-members.d.ts.map +1 -0
  138. package/dist/litellm/team-operations.d.ts +38 -0
  139. package/dist/litellm/team-operations.d.ts.map +1 -0
  140. package/dist/litellm/team-permission.d.ts +35 -0
  141. package/dist/litellm/team-permission.d.ts.map +1 -0
  142. package/dist/litellm/team-types.d.ts +101 -0
  143. package/dist/litellm/team-types.d.ts.map +1 -0
  144. package/dist/litellm/team.d.ts +67 -0
  145. package/dist/litellm/team.d.ts.map +1 -0
  146. package/dist/litellm/tool-policy-form.d.ts +21 -0
  147. package/dist/litellm/tool-policy-form.d.ts.map +1 -0
  148. package/dist/litellm/tool-policy-operations.d.ts +30 -0
  149. package/dist/litellm/tool-policy-operations.d.ts.map +1 -0
  150. package/dist/litellm/tool-policy-types.d.ts +46 -0
  151. package/dist/litellm/tool-policy-types.d.ts.map +1 -0
  152. package/dist/litellm/tool-policy.d.ts +62 -0
  153. package/dist/litellm/tool-policy.d.ts.map +1 -0
  154. package/dist/litellm/toolset-form.d.ts +20 -0
  155. package/dist/litellm/toolset-form.d.ts.map +1 -0
  156. package/dist/litellm/toolset-operations.d.ts +39 -0
  157. package/dist/litellm/toolset-operations.d.ts.map +1 -0
  158. package/dist/litellm/toolset-types.d.ts +46 -0
  159. package/dist/litellm/toolset-types.d.ts.map +1 -0
  160. package/dist/litellm/toolset.d.ts +55 -0
  161. package/dist/litellm/toolset.d.ts.map +1 -0
  162. package/dist/openbao/index.js +35 -1
  163. package/dist/openbao/index.js.map +3 -3
  164. package/dist/ownership/rows.d.ts +11 -0
  165. package/dist/ownership/rows.d.ts.map +1 -1
  166. package/dist/postgres/connection.d.ts +14 -2
  167. package/dist/postgres/connection.d.ts.map +1 -1
  168. package/dist/postgres/database-sql.d.ts +27 -8
  169. package/dist/postgres/database-sql.d.ts.map +1 -1
  170. package/dist/postgres/fake-grants-parse.d.ts +37 -0
  171. package/dist/postgres/fake-grants-parse.d.ts.map +1 -0
  172. package/dist/postgres/fake-grants-read.d.ts +61 -0
  173. package/dist/postgres/fake-grants-read.d.ts.map +1 -0
  174. package/dist/postgres/fake-grants-sql.d.ts +46 -0
  175. package/dist/postgres/fake-grants-sql.d.ts.map +1 -0
  176. package/dist/postgres/fake-grants-write.d.ts +29 -0
  177. package/dist/postgres/fake-grants-write.d.ts.map +1 -0
  178. package/dist/postgres/fake-role-sql.d.ts +41 -0
  179. package/dist/postgres/fake-role-sql.d.ts.map +1 -0
  180. package/dist/postgres/fake-schema-sql.d.ts +42 -0
  181. package/dist/postgres/fake-schema-sql.d.ts.map +1 -0
  182. package/dist/postgres/fake-sql-parse.d.ts +23 -0
  183. package/dist/postgres/fake-sql-parse.d.ts.map +1 -0
  184. package/dist/postgres/fake-sql-quote.d.ts +12 -0
  185. package/dist/postgres/fake-sql-quote.d.ts.map +1 -0
  186. package/dist/postgres/fake-sql.d.ts +42 -0
  187. package/dist/postgres/fake-sql.d.ts.map +1 -1
  188. package/dist/postgres/grants-attrs.d.ts +115 -0
  189. package/dist/postgres/grants-attrs.d.ts.map +1 -0
  190. package/dist/postgres/grants-declare.d.ts +88 -0
  191. package/dist/postgres/grants-declare.d.ts.map +1 -0
  192. package/dist/postgres/grants-diff.d.ts +25 -0
  193. package/dist/postgres/grants-diff.d.ts.map +1 -0
  194. package/dist/postgres/grants-errors.d.ts +137 -0
  195. package/dist/postgres/grants-errors.d.ts.map +1 -0
  196. package/dist/postgres/grants-existence.d.ts +14 -0
  197. package/dist/postgres/grants-existence.d.ts.map +1 -0
  198. package/dist/postgres/grants-ops.d.ts +65 -0
  199. package/dist/postgres/grants-ops.d.ts.map +1 -0
  200. package/dist/postgres/grants-ownership.d.ts +18 -0
  201. package/dist/postgres/grants-ownership.d.ts.map +1 -0
  202. package/dist/postgres/grants-plan-restore.d.ts +22 -0
  203. package/dist/postgres/grants-plan-restore.d.ts.map +1 -0
  204. package/dist/postgres/grants-plan.d.ts +65 -0
  205. package/dist/postgres/grants-plan.d.ts.map +1 -0
  206. package/dist/postgres/grants-read-defaults.d.ts +17 -0
  207. package/dist/postgres/grants-read-defaults.d.ts.map +1 -0
  208. package/dist/postgres/grants-read.d.ts +77 -0
  209. package/dist/postgres/grants-read.d.ts.map +1 -0
  210. package/dist/postgres/grants-refuse.d.ts +25 -0
  211. package/dist/postgres/grants-refuse.d.ts.map +1 -0
  212. package/dist/postgres/grants-sql.d.ts +42 -0
  213. package/dist/postgres/grants-sql.d.ts.map +1 -0
  214. package/dist/postgres/grants-words.d.ts +29 -0
  215. package/dist/postgres/grants-words.d.ts.map +1 -0
  216. package/dist/postgres/grants.d.ts +37 -0
  217. package/dist/postgres/grants.d.ts.map +1 -0
  218. package/dist/postgres/index.d.ts +31 -6
  219. package/dist/postgres/index.d.ts.map +1 -1
  220. package/dist/postgres/index.js +1998 -37
  221. package/dist/postgres/index.js.map +46 -7
  222. package/dist/postgres/installed-classifier.d.ts +13 -0
  223. package/dist/postgres/installed-classifier.d.ts.map +1 -0
  224. package/dist/postgres/providers.d.ts +16 -8
  225. package/dist/postgres/providers.d.ts.map +1 -1
  226. package/dist/postgres/psql-executor.d.ts +4 -0
  227. package/dist/postgres/psql-executor.d.ts.map +1 -1
  228. package/dist/postgres/role-attrs.d.ts +112 -0
  229. package/dist/postgres/role-attrs.d.ts.map +1 -0
  230. package/dist/postgres/role-diff.d.ts +22 -0
  231. package/dist/postgres/role-diff.d.ts.map +1 -0
  232. package/dist/postgres/role-errors.d.ts +132 -0
  233. package/dist/postgres/role-errors.d.ts.map +1 -0
  234. package/dist/postgres/role-membership-sql.d.ts +78 -0
  235. package/dist/postgres/role-membership-sql.d.ts.map +1 -0
  236. package/dist/postgres/role-provider.d.ts +11 -0
  237. package/dist/postgres/role-provider.d.ts.map +1 -0
  238. package/dist/postgres/role-scram.d.ts +8 -0
  239. package/dist/postgres/role-scram.d.ts.map +1 -0
  240. package/dist/postgres/role-secrets.d.ts +38 -0
  241. package/dist/postgres/role-secrets.d.ts.map +1 -0
  242. package/dist/postgres/role-sql.d.ts +68 -0
  243. package/dist/postgres/role-sql.d.ts.map +1 -0
  244. package/dist/postgres/role.d.ts +57 -0
  245. package/dist/postgres/role.d.ts.map +1 -0
  246. package/dist/postgres/schema-assert.d.ts +50 -0
  247. package/dist/postgres/schema-assert.d.ts.map +1 -0
  248. package/dist/postgres/schema-attrs.d.ts +51 -0
  249. package/dist/postgres/schema-attrs.d.ts.map +1 -0
  250. package/dist/postgres/schema-delete.d.ts +28 -0
  251. package/dist/postgres/schema-delete.d.ts.map +1 -0
  252. package/dist/postgres/schema-diff.d.ts +14 -0
  253. package/dist/postgres/schema-diff.d.ts.map +1 -0
  254. package/dist/postgres/schema-errors.d.ts +139 -0
  255. package/dist/postgres/schema-errors.d.ts.map +1 -0
  256. package/dist/postgres/schema-identity.d.ts +8 -0
  257. package/dist/postgres/schema-identity.d.ts.map +1 -0
  258. package/dist/postgres/schema-reconcile.d.ts +12 -0
  259. package/dist/postgres/schema-reconcile.d.ts.map +1 -0
  260. package/dist/postgres/schema-sql.d.ts +45 -0
  261. package/dist/postgres/schema-sql.d.ts.map +1 -0
  262. package/dist/postgres/schema-test-kit.d.ts +67 -0
  263. package/dist/postgres/schema-test-kit.d.ts.map +1 -0
  264. package/dist/postgres/schema.d.ts +54 -0
  265. package/dist/postgres/schema.d.ts.map +1 -0
  266. package/dist/proxmox/index.js +35 -1
  267. package/dist/proxmox/index.js.map +3 -3
  268. package/dist/release/fake-engine.d.ts +10 -0
  269. package/dist/release/fake-engine.d.ts.map +1 -1
  270. package/dist/release/index.js +35 -1
  271. package/dist/release/index.js.map +3 -3
  272. package/dist/valkey/acl-attrs.d.ts +94 -0
  273. package/dist/valkey/acl-attrs.d.ts.map +1 -0
  274. package/dist/valkey/acl-compare.d.ts +28 -0
  275. package/dist/valkey/acl-compare.d.ts.map +1 -0
  276. package/dist/valkey/acl-connection.d.ts +6 -0
  277. package/dist/valkey/acl-connection.d.ts.map +1 -0
  278. package/dist/valkey/acl-form.d.ts +62 -0
  279. package/dist/valkey/acl-form.d.ts.map +1 -0
  280. package/dist/valkey/acl-managed.d.ts +4 -0
  281. package/dist/valkey/acl-managed.d.ts.map +1 -0
  282. package/dist/valkey/acl-ops.d.ts +37 -0
  283. package/dist/valkey/acl-ops.d.ts.map +1 -0
  284. package/dist/valkey/acl-profiles.d.ts +53 -0
  285. package/dist/valkey/acl-profiles.d.ts.map +1 -0
  286. package/dist/valkey/acl-reconcile.d.ts +26 -0
  287. package/dist/valkey/acl-reconcile.d.ts.map +1 -0
  288. package/dist/valkey/acl.d.ts +24 -0
  289. package/dist/valkey/acl.d.ts.map +1 -0
  290. package/dist/valkey/connection-service.d.ts +22 -0
  291. package/dist/valkey/connection-service.d.ts.map +1 -0
  292. package/dist/valkey/connection.d.ts +59 -0
  293. package/dist/valkey/connection.d.ts.map +1 -0
  294. package/dist/valkey/errors.d.ts +165 -0
  295. package/dist/valkey/errors.d.ts.map +1 -0
  296. package/dist/valkey/fake-acl.d.ts +29 -0
  297. package/dist/valkey/fake-acl.d.ts.map +1 -0
  298. package/dist/valkey/fake-valkey.d.ts +35 -0
  299. package/dist/valkey/fake-valkey.d.ts.map +1 -0
  300. package/dist/valkey/handler-test-fixture.d.ts +20 -0
  301. package/dist/valkey/handler-test-fixture.d.ts.map +1 -0
  302. package/dist/valkey/index.d.ts +18 -0
  303. package/dist/valkey/index.d.ts.map +1 -0
  304. package/dist/valkey/index.js +964 -0
  305. package/dist/valkey/index.js.map +27 -0
  306. package/dist/valkey/instance-attrs.d.ts +46 -0
  307. package/dist/valkey/instance-attrs.d.ts.map +1 -0
  308. package/dist/valkey/instance-form.d.ts +22 -0
  309. package/dist/valkey/instance-form.d.ts.map +1 -0
  310. package/dist/valkey/instance.d.ts +48 -0
  311. package/dist/valkey/instance.d.ts.map +1 -0
  312. package/dist/valkey/provider-socket-fixture.d.ts +8 -0
  313. package/dist/valkey/provider-socket-fixture.d.ts.map +1 -0
  314. package/dist/valkey/providers.d.ts +16 -0
  315. package/dist/valkey/providers.d.ts.map +1 -0
  316. package/dist/valkey/resp.d.ts +38 -0
  317. package/dist/valkey/resp.d.ts.map +1 -0
  318. package/dist/valkey/transport-error.d.ts +36 -0
  319. package/dist/valkey/transport-error.d.ts.map +1 -0
  320. package/dist/valkey/transport.d.ts +50 -0
  321. package/dist/valkey/transport.d.ts.map +1 -0
  322. package/docs/litellm-access-group.md +105 -0
  323. package/docs/litellm-credential.md +60 -0
  324. package/docs/litellm-key-metadata.md +56 -0
  325. package/docs/litellm-key.md +194 -0
  326. package/docs/litellm-mcp.md +187 -0
  327. package/docs/litellm-model.md +111 -0
  328. package/docs/litellm-policy-attachment.md +86 -0
  329. package/docs/litellm-policy.md +103 -0
  330. package/docs/litellm-team.md +117 -0
  331. package/docs/litellm-tool-policy.md +90 -0
  332. package/docs/litellm-toolset.md +92 -0
  333. package/docs/litellm.md +16 -9
  334. package/docs/peers.md +15 -6
  335. package/docs/postgres-grants-example.md +55 -0
  336. package/docs/postgres-grants.md +157 -0
  337. package/docs/postgres-role.md +126 -0
  338. package/docs/postgres-schema.md +139 -0
  339. package/docs/postgres.md +20 -3
  340. package/docs/valkey-transport.md +29 -0
  341. package/docs/valkey.md +187 -0
  342. package/package.json +6 -2
  343. package/src/ceph/ceph-argv.test.ts +1 -1
  344. package/src/launchd/sudo-allowlist.test.ts +2 -2
  345. package/src/linux/sudo-allowlist.test.ts +3 -3
  346. package/src/linux/sudo-runner.test.ts +2 -2
  347. package/src/litellm/access-group-form.ts +105 -0
  348. package/src/litellm/access-group-operations.ts +92 -0
  349. package/src/litellm/access-group-types.ts +49 -0
  350. package/src/litellm/access-group.test.ts +197 -0
  351. package/src/litellm/access-group.ts +151 -0
  352. package/src/litellm/credential-errors.ts +183 -0
  353. package/src/litellm/credential-form.test.ts +151 -0
  354. package/src/litellm/credential-form.ts +211 -0
  355. package/src/litellm/credential-guards.test.ts +241 -0
  356. package/src/litellm/credential-operations.ts +208 -0
  357. package/src/litellm/credential-recovery.test.ts +93 -0
  358. package/src/litellm/credential-rewrite.test.ts +164 -0
  359. package/src/litellm/credential-rewrite.ts +50 -0
  360. package/src/litellm/credential-types.ts +117 -0
  361. package/src/litellm/credential-values.ts +94 -0
  362. package/src/litellm/credential.test.ts +218 -0
  363. package/src/litellm/credential.ts +227 -0
  364. package/src/litellm/fake-access-group-litellm.ts +127 -0
  365. package/src/litellm/fake-credential-litellm.ts +216 -0
  366. package/src/litellm/fake-keys.ts +190 -0
  367. package/src/litellm/fake-litellm.ts +19 -9
  368. package/src/litellm/fake-mcp-litellm.ts +214 -0
  369. package/src/litellm/fake-model-litellm-post-update.ts +103 -0
  370. package/src/litellm/fake-model-litellm.ts +233 -0
  371. package/src/litellm/fake-policy-attachment-litellm.ts +97 -0
  372. package/src/litellm/fake-policy-litellm.ts +186 -0
  373. package/src/litellm/fake-registry-base.ts +69 -0
  374. package/src/litellm/fake-stack.ts +17 -1
  375. package/src/litellm/fake-team-litellm.ts +229 -0
  376. package/src/litellm/fake-tool-litellm.ts +88 -0
  377. package/src/litellm/fake-toolset-litellm.ts +110 -0
  378. package/src/litellm/index.ts +140 -0
  379. package/src/litellm/key-delete.test.ts +212 -0
  380. package/src/litellm/key-errors.ts +241 -0
  381. package/src/litellm/key-form.ts +200 -0
  382. package/src/litellm/key-harness.ts +121 -0
  383. package/src/litellm/key-metadata-errors.ts +49 -0
  384. package/src/litellm/key-metadata.test.ts +156 -0
  385. package/src/litellm/key-metadata.ts +74 -0
  386. package/src/litellm/key-operations.ts +184 -0
  387. package/src/litellm/key-rotation.test.ts +110 -0
  388. package/src/litellm/key-scope.test.ts +135 -0
  389. package/src/litellm/key-secret.test.ts +248 -0
  390. package/src/litellm/key-secret.ts +137 -0
  391. package/src/litellm/key-transport.test.ts +166 -0
  392. package/src/litellm/key.test.ts +231 -0
  393. package/src/litellm/key.ts +213 -0
  394. package/src/litellm/mcp-server-adopt.test.ts +86 -0
  395. package/src/litellm/mcp-server-alias.test.ts +184 -0
  396. package/src/litellm/mcp-server-credential.ts +85 -0
  397. package/src/litellm/mcp-server-errors.ts +141 -0
  398. package/src/litellm/mcp-server-exports.test.ts +15 -0
  399. package/src/litellm/mcp-server-form.test.ts +241 -0
  400. package/src/litellm/mcp-server-form.ts +233 -0
  401. package/src/litellm/mcp-server-handlers.test.ts +150 -0
  402. package/src/litellm/mcp-server-locate.ts +66 -0
  403. package/src/litellm/mcp-server-operations.ts +111 -0
  404. package/src/litellm/mcp-server-registry.test.ts +146 -0
  405. package/src/litellm/mcp-server-retype.test.ts +114 -0
  406. package/src/litellm/mcp-server-shadow.test.ts +91 -0
  407. package/src/litellm/mcp-server-shadow.ts +45 -0
  408. package/src/litellm/mcp-server-tools.test.ts +85 -0
  409. package/src/litellm/mcp-server-types.ts +143 -0
  410. package/src/litellm/mcp-server-url.ts +66 -0
  411. package/src/litellm/mcp-server.test.ts +213 -0
  412. package/src/litellm/mcp-server.ts +225 -0
  413. package/src/litellm/model-claim.ts +69 -0
  414. package/src/litellm/model-credential.ts +45 -0
  415. package/src/litellm/model-errors.ts +168 -0
  416. package/src/litellm/model-foreign-row.test.ts +173 -0
  417. package/src/litellm/model-form.test.ts +228 -0
  418. package/src/litellm/model-form.ts +223 -0
  419. package/src/litellm/model-locate.test.ts +128 -0
  420. package/src/litellm/model-locate.ts +67 -0
  421. package/src/litellm/model-operations.test.ts +93 -0
  422. package/src/litellm/model-operations.ts +146 -0
  423. package/src/litellm/model-params-preserve.test.ts +119 -0
  424. package/src/litellm/model-recovery.test.ts +57 -0
  425. package/src/litellm/model-types.ts +112 -0
  426. package/src/litellm/model.test.ts +222 -0
  427. package/src/litellm/model.ts +237 -0
  428. package/src/litellm/policy-attachment-form.ts +103 -0
  429. package/src/litellm/policy-attachment-operations.ts +74 -0
  430. package/src/litellm/policy-attachment-types.ts +48 -0
  431. package/src/litellm/policy-attachment.test.ts +198 -0
  432. package/src/litellm/policy-attachment.ts +178 -0
  433. package/src/litellm/policy-form.ts +122 -0
  434. package/src/litellm/policy-operations.ts +112 -0
  435. package/src/litellm/policy-types.ts +58 -0
  436. package/src/litellm/policy.test.ts +214 -0
  437. package/src/litellm/policy.ts +146 -0
  438. package/src/litellm/providers.ts +24 -3
  439. package/src/litellm/registry-errors.ts +88 -0
  440. package/src/litellm/registry-exports.test.ts +66 -0
  441. package/src/litellm/registry-support.ts +109 -0
  442. package/src/litellm/team-form.ts +140 -0
  443. package/src/litellm/team-members.test.ts +150 -0
  444. package/src/litellm/team-members.ts +58 -0
  445. package/src/litellm/team-operations.ts +90 -0
  446. package/src/litellm/team-permission.ts +159 -0
  447. package/src/litellm/team-read.test.ts +89 -0
  448. package/src/litellm/team-types.ts +106 -0
  449. package/src/litellm/team.test.ts +206 -0
  450. package/src/litellm/team.ts +159 -0
  451. package/src/litellm/tool-policy-form.ts +94 -0
  452. package/src/litellm/tool-policy-operations.ts +58 -0
  453. package/src/litellm/tool-policy-types.ts +48 -0
  454. package/src/litellm/tool-policy.test.ts +172 -0
  455. package/src/litellm/tool-policy.ts +125 -0
  456. package/src/litellm/toolset-form.ts +104 -0
  457. package/src/litellm/toolset-operations.ts +90 -0
  458. package/src/litellm/toolset-types.ts +47 -0
  459. package/src/litellm/toolset.test.ts +198 -0
  460. package/src/litellm/toolset.ts +151 -0
  461. package/src/ownership/ownership.test.ts +33 -1
  462. package/src/ownership/rows.ts +53 -0
  463. package/src/postgres/connection.ts +42 -7
  464. package/src/postgres/database-bigint-serialization.test.ts +1 -0
  465. package/src/postgres/database-refusals.test.ts +2 -2
  466. package/src/postgres/database-sql.ts +36 -9
  467. package/src/postgres/fake-grants-parse.ts +189 -0
  468. package/src/postgres/fake-grants-read.ts +250 -0
  469. package/src/postgres/fake-grants-sql.ts +207 -0
  470. package/src/postgres/fake-grants-write.ts +90 -0
  471. package/src/postgres/fake-role-sql.ts +240 -0
  472. package/src/postgres/fake-schema-semantics.test.ts +36 -0
  473. package/src/postgres/fake-schema-sql.ts +146 -0
  474. package/src/postgres/fake-sql-parse.ts +86 -0
  475. package/src/postgres/fake-sql-quote.ts +36 -0
  476. package/src/postgres/fake-sql.ts +166 -46
  477. package/src/postgres/fixtures/pg-REL_18_6-acl.h.txt +147 -0
  478. package/src/postgres/fixtures/pg-REL_18_6-aclexplode-func.txt +31 -0
  479. package/src/postgres/fixtures/pg-REL_18_6-alter-default-privileges-synopsis.txt +82 -0
  480. package/src/postgres/fixtures/pg-REL_18_6-alter_role.sgml.txt +44 -0
  481. package/src/postgres/fixtures/pg-REL_18_6-create_role.sgml.txt +26 -0
  482. package/src/postgres/fixtures/pg-REL_18_6-grant-synopsis.txt +95 -0
  483. package/src/postgres/fixtures/pg-REL_18_6-pg_attribute.h.txt +24 -0
  484. package/src/postgres/fixtures/pg-REL_18_6-pg_auth_members.h.txt +62 -0
  485. package/src/postgres/fixtures/pg-REL_18_6-pg_authid.h.txt +70 -0
  486. package/src/postgres/fixtures/pg-REL_18_6-pg_class.h.txt +33 -0
  487. package/src/postgres/fixtures/pg-REL_18_6-pg_default_acl.h.txt +81 -0
  488. package/src/postgres/fixtures/pg-REL_18_6-pg_namespace.h.txt +28 -0
  489. package/src/postgres/fixtures/pg-REL_18_6-revoke-synopsis.txt +124 -0
  490. package/src/postgres/grants-acl.test.ts +61 -0
  491. package/src/postgres/grants-attrs.ts +185 -0
  492. package/src/postgres/grants-database.test.ts +158 -0
  493. package/src/postgres/grants-declare.ts +172 -0
  494. package/src/postgres/grants-delete-proof.test.ts +154 -0
  495. package/src/postgres/grants-diff.ts +101 -0
  496. package/src/postgres/grants-errors.ts +215 -0
  497. package/src/postgres/grants-existence-guards.test.ts +113 -0
  498. package/src/postgres/grants-existence.ts +101 -0
  499. package/src/postgres/grants-lifecycle-guards.test.ts +208 -0
  500. package/src/postgres/grants-lifecycle-repair.test.ts +208 -0
  501. package/src/postgres/grants-lifecycle.test.ts +181 -0
  502. package/src/postgres/grants-ops.ts +229 -0
  503. package/src/postgres/grants-owner-revoke.test.ts +121 -0
  504. package/src/postgres/grants-ownership.ts +49 -0
  505. package/src/postgres/grants-plan-collateral.test.ts +202 -0
  506. package/src/postgres/grants-plan-diff.test.ts +204 -0
  507. package/src/postgres/grants-plan-restore.ts +43 -0
  508. package/src/postgres/grants-plan-revocations.test.ts +69 -0
  509. package/src/postgres/grants-plan.test.ts +148 -0
  510. package/src/postgres/grants-plan.ts +245 -0
  511. package/src/postgres/grants-provenance.test.ts +134 -0
  512. package/src/postgres/grants-psql-lifecycle.test.ts +105 -0
  513. package/src/postgres/grants-read-absent.test.ts +91 -0
  514. package/src/postgres/grants-read-defaults.ts +51 -0
  515. package/src/postgres/grants-read.ts +250 -0
  516. package/src/postgres/grants-refusals.test.ts +136 -0
  517. package/src/postgres/grants-refuse.ts +119 -0
  518. package/src/postgres/grants-relkind-wire.test.ts +111 -0
  519. package/src/postgres/grants-repair-plan.test.ts +201 -0
  520. package/src/postgres/grants-sql.test.ts +128 -0
  521. package/src/postgres/grants-sql.ts +144 -0
  522. package/src/postgres/grants-words.ts +34 -0
  523. package/src/postgres/grants.ts +110 -0
  524. package/src/postgres/index.ts +129 -5
  525. package/src/postgres/installed-classifier.ts +36 -0
  526. package/src/postgres/providers.ts +33 -9
  527. package/src/postgres/psql-executor-escaping.test.ts +67 -0
  528. package/src/postgres/psql-executor.test.ts +63 -2
  529. package/src/postgres/psql-executor.ts +74 -34
  530. package/src/postgres/role-adopt.test.ts +98 -0
  531. package/src/postgres/role-attrs.ts +137 -0
  532. package/src/postgres/role-diff.ts +93 -0
  533. package/src/postgres/role-errors.ts +206 -0
  534. package/src/postgres/role-guard.test.ts +60 -0
  535. package/src/postgres/role-identity.test.ts +197 -0
  536. package/src/postgres/role-lifecycle.test.ts +228 -0
  537. package/src/postgres/role-membership-options.test.ts +73 -0
  538. package/src/postgres/role-membership-repair.test.ts +153 -0
  539. package/src/postgres/role-membership-sql.ts +223 -0
  540. package/src/postgres/role-password-ascii.test.ts +53 -0
  541. package/src/postgres/role-password-transports.test.ts +123 -0
  542. package/src/postgres/role-password.test.ts +158 -0
  543. package/src/postgres/role-privilege.test.ts +107 -0
  544. package/src/postgres/role-provenance.test.ts +200 -0
  545. package/src/postgres/role-provider.ts +105 -0
  546. package/src/postgres/role-refusals.test.ts +225 -0
  547. package/src/postgres/role-scram.test.ts +38 -0
  548. package/src/postgres/role-scram.ts +40 -0
  549. package/src/postgres/role-secrets.ts +61 -0
  550. package/src/postgres/role-sql.ts +213 -0
  551. package/src/postgres/role-valid-until.test.ts +68 -0
  552. package/src/postgres/role.ts +226 -0
  553. package/src/postgres/schema-assert.ts +139 -0
  554. package/src/postgres/schema-attrs.ts +71 -0
  555. package/src/postgres/schema-delete-proofs.test.ts +192 -0
  556. package/src/postgres/schema-delete.ts +96 -0
  557. package/src/postgres/schema-diff.ts +44 -0
  558. package/src/postgres/schema-drop-2bp01.test.ts +146 -0
  559. package/src/postgres/schema-errors.ts +209 -0
  560. package/src/postgres/schema-handlers.test.ts +193 -0
  561. package/src/postgres/schema-identity.ts +39 -0
  562. package/src/postgres/schema-lifecycle.test.ts +246 -0
  563. package/src/postgres/schema-race.test.ts +70 -0
  564. package/src/postgres/schema-reconcile.ts +100 -0
  565. package/src/postgres/schema-refusals.test.ts +144 -0
  566. package/src/postgres/schema-socket.test.ts +162 -0
  567. package/src/postgres/schema-sql.ts +130 -0
  568. package/src/postgres/schema-takeover.test.ts +149 -0
  569. package/src/postgres/schema-test-kit.ts +85 -0
  570. package/src/postgres/schema.ts +130 -0
  571. package/src/release/binary-alias.test.ts +3 -3
  572. package/src/release/binary-claims.test.ts +3 -3
  573. package/src/release/binary-directory-umask.test.ts +65 -0
  574. package/src/release/fake-engine.ts +15 -0
  575. package/src/valkey/acl-attrs.ts +98 -0
  576. package/src/valkey/acl-compare.ts +57 -0
  577. package/src/valkey/acl-connection.ts +13 -0
  578. package/src/valkey/acl-delete.test.ts +127 -0
  579. package/src/valkey/acl-form.ts +239 -0
  580. package/src/valkey/acl-managed.ts +13 -0
  581. package/src/valkey/acl-ops.ts +122 -0
  582. package/src/valkey/acl-ownership.test.ts +118 -0
  583. package/src/valkey/acl-prefix.test.ts +38 -0
  584. package/src/valkey/acl-profiles.ts +81 -0
  585. package/src/valkey/acl-reconcile.ts +180 -0
  586. package/src/valkey/acl-reserved.test.ts +69 -0
  587. package/src/valkey/acl-revocation.test.ts +94 -0
  588. package/src/valkey/acl-seat-prefix.test.ts +111 -0
  589. package/src/valkey/acl-validation.test.ts +54 -0
  590. package/src/valkey/acl.test.ts +215 -0
  591. package/src/valkey/acl.ts +169 -0
  592. package/src/valkey/connection-service.ts +46 -0
  593. package/src/valkey/connection.ts +180 -0
  594. package/src/valkey/errors.ts +239 -0
  595. package/src/valkey/fake-acl.ts +128 -0
  596. package/src/valkey/fake-valkey.ts +224 -0
  597. package/src/valkey/handler-test-fixture.ts +43 -0
  598. package/src/valkey/handlers.test.ts +205 -0
  599. package/src/valkey/index.ts +54 -0
  600. package/src/valkey/instance-attrs.ts +48 -0
  601. package/src/valkey/instance-form.ts +104 -0
  602. package/src/valkey/instance.test.ts +108 -0
  603. package/src/valkey/instance.ts +160 -0
  604. package/src/valkey/provider-socket-fixture.ts +71 -0
  605. package/src/valkey/providers.test.ts +129 -0
  606. package/src/valkey/providers.ts +21 -0
  607. package/src/valkey/resp.ts +212 -0
  608. package/src/valkey/transport-bounds.test.ts +150 -0
  609. package/src/valkey/transport-error.ts +37 -0
  610. package/src/valkey/transport.test.ts +158 -0
  611. package/src/valkey/transport.ts +109 -0
  612. package/src/valkey/upstream-bounds.test.ts +14 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * `LiteLLM.MCPServer`: the list is the proxy's in-memory REGISTRY, not the table, so a row missing
3
+ * from it can still be live.
4
+ *
5
+ * ⛔ MEASURED 2026-09-29 by reading the live 1.103.0 container: `GET /v1/mcp/server` answers
6
+ * `global_mcp_server_manager`'s registry, which `reload_servers_from_database` fills only from rows
7
+ * whose approval status is null, active or approved and skips a row whose build throws; the caller's
8
+ * key can narrow it too; and `add_mcp_server` only LOGS a failed registry refresh after the row is
9
+ * committed. The by-id `GET` reads the table first and registers what it finds; `DELETE` acts on
10
+ * the table. So absence from the list proves nothing, and these tests hold the resource to that.
11
+ * ★ EVERY VALUE IS `FAKE-*` or an RFC 2606 host.
12
+ */
13
+ import { describe, expect, test } from 'bun:test';
14
+ import { credentials } from '@distilled.cloud/litellm/Credentials';
15
+ import * as Effect from 'effect/Effect';
16
+ import * as Layer from 'effect/Layer';
17
+ import * as FetchHttpClient from 'effect/unstable/http/FetchHttpClient';
18
+ import { FAKE_BASE } from './fake-litellm.ts';
19
+ import {
20
+ type FakeMcpLitellm,
21
+ type FakeMcpOptions,
22
+ serverRow,
23
+ startFakeMcpLitellm,
24
+ } from './fake-mcp-litellm.ts';
25
+ import { fakeStack, writesOf } from './fake-stack.ts';
26
+ import { LiteLLMMCPServer, mcpServerHandlers } from './mcp-server.ts';
27
+ import { deleteMcpServer, readMcpServer } from './mcp-server-operations.ts';
28
+ import type { McpServerProps } from './mcp-server-types.ts';
29
+
30
+ const KEY = 'sk-test-master';
31
+
32
+ const run = <A, E>(fetchFn: typeof globalThis.fetch, effect: Effect.Effect<A, E, unknown>) =>
33
+ Effect.runPromise(
34
+ effect.pipe(
35
+ Effect.provide(FetchHttpClient.layer),
36
+ Effect.provide(Layer.succeed(FetchHttpClient.Fetch, fetchFn)),
37
+ Effect.provide(credentials({ apiKey: KEY, baseUrl: FAKE_BASE })),
38
+ ) as Effect.Effect<A, E, never>,
39
+ );
40
+
41
+ const stack = (fake: FakeMcpLitellm) => fakeStack({ apiKey: KEY, baseUrl: FAKE_BASE }, fake.fetch);
42
+ const start = (options: FakeMcpOptions = {}) => startFakeMcpLitellm({ masterKey: KEY, ...options });
43
+ const props: McpServerProps = {
44
+ authType: 'none',
45
+ serverName: 'FAKE_docs',
46
+ transport: 'http',
47
+ url: 'https://mcp.example.com/mcp',
48
+ };
49
+ const row = serverRow({
50
+ server_id: 'FAKE-uuid-1',
51
+ server_name: 'FAKE_docs',
52
+ url: 'https://mcp.example.com/mcp',
53
+ });
54
+
55
+ describe('delete does not trust the list', () => {
56
+ test('a live row the registry lacks is still deleted', async () => {
57
+ const fake = start({ registryMisses: ['FAKE-uuid-1'], seed: [row] });
58
+ expect(await run(fake.fetch, deleteMcpServer('FAKE-uuid-1'))).toBeUndefined();
59
+ expect(fake.servers()).toEqual([]);
60
+ expect(writesOf(fake.requests())).toEqual(['DELETE /v1/mcp/server/FAKE-uuid-1']);
61
+ });
62
+
63
+ test('a row already gone is done: the DELETE is sent, its 404 is confirmed by the table read', async () => {
64
+ const fake = start();
65
+ await run(fake.fetch, deleteMcpServer('FAKE-never'));
66
+ expect(fake.requests().map((call) => `${call.method} ${call.path}`)).toEqual([
67
+ 'DELETE /v1/mcp/server/FAKE-never',
68
+ 'GET /v1/mcp/server/FAKE-never',
69
+ ]);
70
+ });
71
+
72
+ test('a refused delete of a row the registry lacks fails, and the row stays live', async () => {
73
+ const fake = start({ forbidDelete: true, registryMisses: ['FAKE-uuid-1'], seed: [row] });
74
+ await expect(run(fake.fetch, deleteMcpServer('FAKE-uuid-1'))).rejects.toThrow();
75
+ expect(fake.servers()).toHaveLength(1);
76
+ });
77
+
78
+ test('a refused delete whose confirming read also fails re-raises the original, never done', async () => {
79
+ const fake = start({ forbidDelete: true, readFails: true, seed: [row] });
80
+ await expect(run(fake.fetch, deleteMcpServer('FAKE-uuid-1'))).rejects.toThrow();
81
+ // a read that cannot say is not absence, even for an id the proxy does not have
82
+ const gone = start({ forbidDelete: true, readFails: true });
83
+ await expect(run(gone.fetch, deleteMcpServer('FAKE-never'))).rejects.toThrow();
84
+ });
85
+ });
86
+
87
+ describe('the by-id read', () => {
88
+ test('answers the row, and undefined for a missing id', async () => {
89
+ const fake = start({ seed: [row] });
90
+ expect((await run(fake.fetch, readMcpServer('FAKE-uuid-1')))?.server_id).toBe('FAKE-uuid-1');
91
+ expect(await run(fake.fetch, readMcpServer('FAKE-never'))).toBeUndefined();
92
+ });
93
+
94
+ test('an id that is another server’s NAME reads as absent, never as that server', async () => {
95
+ // ⚠️ measured: with no table row, `fetch_mcp_server` falls back to a lookup by name or alias
96
+ const fake = start({ seed: [row] });
97
+ expect(await run(fake.fetch, readMcpServer('FAKE_docs'))).toBeUndefined();
98
+ });
99
+
100
+ test('any failure but "not found" propagates instead of reading as absence', async () => {
101
+ const fake = start({ readFails: true, seed: [row] });
102
+ await expect(run(fake.fetch, readMcpServer('FAKE-uuid-1'))).rejects.toThrow();
103
+ });
104
+ });
105
+
106
+ describe('a row the registry lacks is found by id, so it is never created again', () => {
107
+ test('a create whose registry refresh failed reads back from the table and records the row', async () => {
108
+ const fake = start({ hideCreated: true });
109
+ const engine = stack(fake);
110
+ const declare = Effect.asVoid(LiteLLMMCPServer('Docs', props));
111
+ expect(await engine.deploy(declare)).toEqual({ Docs: 'create' });
112
+ expect(writesOf(fake.requests())).toHaveLength(1);
113
+ // the by-id read registered the row, and the next deploy neither POSTs nor updates
114
+ expect(await engine.deploy(declare)).toEqual({ Docs: 'noop' });
115
+ expect(writesOf(fake.requests())).toHaveLength(1);
116
+ });
117
+
118
+ test('adopting a pinned row the registry lacks writes nothing, where a POST would answer 400 "already exists"', async () => {
119
+ const fake = start({ registryMisses: ['FAKE-uuid-1'], seed: [row] });
120
+ const declare = Effect.asVoid(LiteLLMMCPServer('Docs', { ...props, serverId: 'FAKE-uuid-1' }));
121
+ expect(await stack(fake).deploy(declare, { adopt: true })).toEqual({ Docs: 'adopted' });
122
+ expect(writesOf(fake.requests())).toEqual([]);
123
+ });
124
+
125
+ test('a refresh read of an owned row the registry lacks returns it, not undefined', async () => {
126
+ const fake = start({ registryMisses: ['FAKE-uuid-1'], seed: [row] });
127
+ const output = {
128
+ alias: null,
129
+ allowAllKeys: false,
130
+ allowedTools: [],
131
+ authType: 'none',
132
+ credentialSeal: '',
133
+ description: null,
134
+ mcpAccessGroups: [],
135
+ serverId: 'FAKE-uuid-1',
136
+ serverName: 'FAKE_docs',
137
+ transport: 'http',
138
+ url: 'https://mcp.example.com/mcp',
139
+ };
140
+ const read = await run(
141
+ fake.fetch,
142
+ mcpServerHandlers.read({ id: 'Docs', instanceId: 'i', olds: props, output }),
143
+ );
144
+ expect(read).toMatchObject({ serverId: 'FAKE-uuid-1' });
145
+ });
146
+ });
@@ -0,0 +1,114 @@
1
+ /**
2
+ * `LiteLLM.MCPServer`: changing a server's auth type, through Alchemy's real Plan and Apply.
3
+ *
4
+ * ⛔ LITELLM WIPES THE STORED CREDENTIAL ON AN AUTH-CLASS CHANGE THAT SENDS NONE. Read from the live
5
+ * 1.103.0 container (`mcp_server/db.py` lines 1013-1014; `_credential_auth_class` keeps every
6
+ * static type in its own class) and modelled by the fake as its one MEASURED rule. A resource that
7
+ * let such an edit through with the variable unset would leave the row with no credential while
8
+ * its seal still matched the old value, so the next deploy would call it converged and stay so.
9
+ * ★ EVERY VALUE IS `FAKE-*`, set and removed through the environment like a deploying process.
10
+ */
11
+ import { afterEach, beforeEach, expect, test } from 'bun:test';
12
+ import * as Effect from 'effect/Effect';
13
+ import { FAKE_BASE } from './fake-litellm.ts';
14
+ import { type FakeMcpLitellm, serverRow, startFakeMcpLitellm } from './fake-mcp-litellm.ts';
15
+ import { fakeStack, writesOf } from './fake-stack.ts';
16
+ import { LiteLLMMCPServer } from './mcp-server.ts';
17
+ import type { McpServerProps } from './mcp-server-types.ts';
18
+
19
+ const KEY = 'sk-test-master';
20
+ const VARIABLE = 'FAKE_MCP_RETYPE_TOKEN';
21
+
22
+ const stack = (fake: FakeMcpLitellm) => fakeStack({ apiKey: KEY, baseUrl: FAKE_BASE }, fake.fetch);
23
+ const declare = (props: McpServerProps) => Effect.asVoid(LiteLLMMCPServer('Keyed', props));
24
+
25
+ const bearer: McpServerProps = {
26
+ authType: 'bearer_token',
27
+ authValue: { fromEnv: VARIABLE },
28
+ serverName: 'FAKE_keyed',
29
+ transport: 'http',
30
+ url: 'https://keyed.example.com/mcp',
31
+ };
32
+ const keyed: McpServerProps = { ...bearer, authType: 'api_key' };
33
+
34
+ const message = (failure: Promise<unknown>) =>
35
+ failure.then(
36
+ () => undefined,
37
+ (error: unknown) => String(error),
38
+ );
39
+
40
+ beforeEach(() => {
41
+ process.env[VARIABLE] = 'FAKE-token-one';
42
+ });
43
+ afterEach(() => {
44
+ delete process.env[VARIABLE];
45
+ });
46
+
47
+ test('between two static types with the variable unset: refused, names it, writes nothing, keeps the credential', async () => {
48
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
49
+ const engine = stack(fake);
50
+ await engine.deploy(declare(bearer));
51
+ delete process.env[VARIABLE];
52
+ const before = fake.requests().length;
53
+ const failure = await message(engine.deploy(declare(keyed)));
54
+ expect(failure).toContain(VARIABLE);
55
+ expect(writesOf(fake.requests().slice(before))).toEqual([]);
56
+ expect(fake.servers()[0]).toMatchObject({
57
+ auth_type: 'bearer_token',
58
+ credentials: { auth_value: 'FAKE-token-one' },
59
+ });
60
+ });
61
+
62
+ test('the refused switch is recoverable: with the variable set again the credential is written, then a no-op', async () => {
63
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
64
+ const engine = stack(fake);
65
+ await engine.deploy(declare(bearer));
66
+ delete process.env[VARIABLE];
67
+ await message(engine.deploy(declare(keyed)));
68
+ process.env[VARIABLE] = 'FAKE-token-two';
69
+ expect(await engine.deploy(declare(keyed))).toEqual({ Keyed: 'update' });
70
+ expect(fake.servers()[0]).toMatchObject({
71
+ auth_type: 'api_key',
72
+ credentials: { auth_value: 'FAKE-token-two' },
73
+ });
74
+ expect(await engine.deploy(declare(keyed))).toEqual({ Keyed: 'noop' });
75
+ });
76
+
77
+ test('between two static types with the variable set: the credential rides the same write, so nothing is wiped', async () => {
78
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
79
+ const engine = stack(fake);
80
+ await engine.deploy(declare(bearer));
81
+ expect(await engine.deploy(declare(keyed))).toEqual({ Keyed: 'update' });
82
+ expect(fake.bodies().at(-1)).toMatchObject({
83
+ auth_type: 'api_key',
84
+ credentials: { auth_value: 'FAKE-token-one' },
85
+ });
86
+ expect(fake.servers()[0]?.['credentials']).toEqual({ auth_value: 'FAKE-token-one' });
87
+ expect(await engine.deploy(declare(keyed))).toEqual({ Keyed: 'noop' });
88
+ });
89
+
90
+ test('adopting a row with no auth type as a static one demands the credential too', async () => {
91
+ const bare = serverRow({
92
+ server_id: 'FAKE-uuid-5',
93
+ server_name: 'FAKE_keyed',
94
+ url: 'https://keyed.example.com/mcp',
95
+ });
96
+ const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [bare] });
97
+ delete process.env[VARIABLE];
98
+ const failure = await message(stack(fake).deploy(declare(bearer), { adopt: true }));
99
+ expect(failure).toContain(VARIABLE);
100
+ expect(writesOf(fake.requests())).toEqual([]);
101
+ });
102
+
103
+ test('an unchanged auth type with the variable unset is still updated, without touching the credential', async () => {
104
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
105
+ const engine = stack(fake);
106
+ await engine.deploy(declare(bearer));
107
+ delete process.env[VARIABLE];
108
+ const before = fake.requests().length;
109
+ const described = { ...bearer, description: 'FAKE description' };
110
+ expect(await engine.deploy(declare(described))).toEqual({ Keyed: 'update' });
111
+ expect(fake.bodies().at(-1)).not.toHaveProperty('credentials');
112
+ expect(fake.servers()[0]?.['credentials']).toEqual({ auth_value: 'FAKE-token-one' });
113
+ expect(writesOf(fake.requests().slice(before))).toHaveLength(1);
114
+ });
@@ -0,0 +1,91 @@
1
+ /**
2
+ * `LiteLLM.MCPServer`: a `description` that `mcp_info.description` would hide is refused before any
3
+ * write (mcp-server-shadow.ts says why).
4
+ *
5
+ * ⛔ MEASURED 2026-09-29, read only: the list answers `mcp_info.description` when that KEY exists,
6
+ * else the description column, and the resource writes only the column. Live, `Memos_CF` has the key
7
+ * holding a JSON null with a NULL column, and `linear` has both as strings. The fake models the list
8
+ * rule, so a write to such a row lands in the column and the list keeps answering the old value.
9
+ * ★ EVERY VALUE IS `FAKE-*` or an RFC 2606 host.
10
+ */
11
+ import { describe, expect, test } from 'bun:test';
12
+ import * as Effect from 'effect/Effect';
13
+ import { FAKE_BASE } from './fake-litellm.ts';
14
+ import { type FakeMcpLitellm, serverRow, startFakeMcpLitellm } from './fake-mcp-litellm.ts';
15
+ import { fakeStack, writesOf } from './fake-stack.ts';
16
+ import { LiteLLMMCPServer } from './mcp-server.ts';
17
+ import type { McpServerProps } from './mcp-server-types.ts';
18
+
19
+ const KEY = 'sk-test-master';
20
+
21
+ const stack = (fake: FakeMcpLitellm) => fakeStack({ apiKey: KEY, baseUrl: FAKE_BASE }, fake.fetch);
22
+ const declare = (props: McpServerProps) => Effect.asVoid(LiteLLMMCPServer('Notes', props));
23
+ const base: McpServerProps = {
24
+ authType: 'none',
25
+ serverName: 'FAKE_notes',
26
+ transport: 'http',
27
+ url: 'https://notes.example.com/mcp',
28
+ };
29
+ const held = (over: Record<string, unknown>) =>
30
+ serverRow({
31
+ server_id: 'FAKE-notes-id',
32
+ server_name: 'FAKE_notes',
33
+ url: 'https://notes.example.com/mcp',
34
+ ...over,
35
+ });
36
+ const failure = (promise: Promise<unknown>) =>
37
+ promise.then(
38
+ () => undefined,
39
+ (error: unknown) => String(error),
40
+ );
41
+
42
+ describe('a row whose mcp_info has a description key', () => {
43
+ test.each([
44
+ ['a null one (the Memos_CF shape)', { description: null, mcp_info: { description: null } }],
45
+ [
46
+ 'a different string (the linear shape)',
47
+ { description: 'FAKE old', mcp_info: { description: 'FAKE old' } },
48
+ ],
49
+ ])('%s: the declared description is refused, and nothing is written', async (_label, fields) => {
50
+ const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [held(fields)] });
51
+ const message = await failure(
52
+ stack(fake).deploy(declare({ ...base, description: 'FAKE new' }), { adopt: true }),
53
+ );
54
+ expect(message).toContain('LitellmMcpServerDescriptionShadowedError');
55
+ expect(writesOf(fake.requests())).toEqual([]);
56
+ });
57
+
58
+ test('the same description is not a difference, so nothing is read or written', async () => {
59
+ const fake = startFakeMcpLitellm({
60
+ masterKey: KEY,
61
+ seed: [held({ description: 'FAKE same', mcp_info: { description: 'FAKE same' } })],
62
+ });
63
+ const engine = stack(fake);
64
+ await engine.deploy(declare({ ...base, description: 'FAKE same' }), { adopt: true });
65
+ expect(writesOf(fake.requests())).toEqual([]);
66
+ });
67
+
68
+ test('no description declared: the row is adopted and its description left alone', async () => {
69
+ const fake = startFakeMcpLitellm({
70
+ masterKey: KEY,
71
+ seed: [held({ mcp_info: { description: null } })],
72
+ });
73
+ await stack(fake).deploy(declare(base), { adopt: true });
74
+ expect(writesOf(fake.requests())).toEqual([]);
75
+ });
76
+ });
77
+
78
+ describe('a row without one', () => {
79
+ test('a declared description is written to the column and read back', async () => {
80
+ const fake = startFakeMcpLitellm({
81
+ masterKey: KEY,
82
+ seed: [held({ description: 'FAKE old', mcp_info: { server_name: 'FAKE_notes' } })],
83
+ });
84
+ const engine = stack(fake);
85
+ await engine.deploy(declare({ ...base, description: 'FAKE new' }), { adopt: true });
86
+ expect(fake.servers()[0]).toMatchObject({ description: 'FAKE new' });
87
+ expect(await engine.deploy(declare({ ...base, description: 'FAKE new' }))).toEqual({
88
+ Notes: 'noop',
89
+ });
90
+ });
91
+ });
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The one description this resource cannot write: a row whose `mcp_info` already has a
3
+ * `description` key.
4
+ *
5
+ * ⛔ MEASURED 2026-09-29 on the live 1.103.0 container and its database (read only): the list route
6
+ * builds each row's `description` from `mcp_info.get("description")` (`_build_mcp_server_table`),
7
+ * and registering a row copies the `description` COLUMN into `mcp_info` only when the KEY IS ABSENT
8
+ * from `mcp_info` (`build_mcp_server_from_table`, `"description" not in mcp_info`). This resource
9
+ * writes the column and models no `mcp_info`, so on a row whose `mcp_info` has the key, a declared
10
+ * description lands in the column and is never read back, and every deploy would fail
11
+ * `LitellmMcpServerNotConvergedError` after the write. Live: 2 of the 9 rows in
12
+ * `LiteLLM_MCPServerTable` have the key: `linear` (a string, with the same length as its column)
13
+ * and `Memos_CF` (a JSON null, with a NULL column). A key holding null shadows the column too.
14
+ * ★ THE ROW IS READ BY ID FROM THE TABLE, because the list's `mcp_info` already has the column
15
+ * folded in: a description in it could be either, and only the table row tells them apart.
16
+ * ⚠️ Not `mcp_info` merging: a sent `mcp_info` REPLACES the stored one, and the list's copy carries
17
+ * fields the proxy adds on the way out (`is_public`), which would be persisted.
18
+ */
19
+ import * as Effect from 'effect/Effect';
20
+ import { LitellmMcpServerDescriptionShadowedError } from './mcp-server-errors.ts';
21
+ import { readMcpServer } from './mcp-server-operations.ts';
22
+ import type { McpServerProps } from './mcp-server-types.ts';
23
+
24
+ /** Fails BEFORE any write when the declared description would be hidden by the row's `mcp_info`. */
25
+ export const refuseShadowedDescription = (props: McpServerProps, serverId: string) =>
26
+ props.description === undefined
27
+ ? Effect.void
28
+ : readMcpServer(serverId).pipe(
29
+ Effect.flatMap((row) => {
30
+ const info = row?.mcp_info;
31
+ const shadowed =
32
+ info !== undefined &&
33
+ info !== null &&
34
+ 'description' in info &&
35
+ info['description'] !== props.description;
36
+ return shadowed
37
+ ? Effect.fail(
38
+ new LitellmMcpServerDescriptionShadowedError({
39
+ serverId,
40
+ serverName: props.serverName,
41
+ }),
42
+ )
43
+ : Effect.void;
44
+ }),
45
+ );
@@ -0,0 +1,85 @@
1
+ /**
2
+ * `LiteLLM.MCPServer`: the tool whitelist (`allowedTools`) through Alchemy's real Plan and Apply.
3
+ *
4
+ * ⛔ AN EMPTY LIST IS THE OPEN STATE, NOT A CLOSED ONE. On the live 1.103.0 container a row with no
5
+ * `mcp_info` enforce flag and an empty `allowed_tools` returns every tool (`server.py` lines
6
+ * 1692-1698, `mcp_server/utils.py`'s `server_applies_tool_allowlist`), so a resource that sent `[]`
7
+ * for an undeclared list would switch a live whitelist off when the row is adopted. Undeclared
8
+ * means "leave it"; only a declared list, `[]` included, is compared and sent.
9
+ * ★ EVERY VALUE IS `FAKE-*` or an RFC 2606 host.
10
+ */
11
+ import { expect, test } from 'bun:test';
12
+ import * as Effect from 'effect/Effect';
13
+ import { FAKE_BASE } from './fake-litellm.ts';
14
+ import { type FakeMcpLitellm, serverRow, startFakeMcpLitellm } from './fake-mcp-litellm.ts';
15
+ import { fakeStack, writesOf } from './fake-stack.ts';
16
+ import { LiteLLMMCPServer } from './mcp-server.ts';
17
+ import type { McpServerProps } from './mcp-server-types.ts';
18
+
19
+ const KEY = 'sk-test-master';
20
+ const TOOLS = ['FAKE_read', 'FAKE_write', 'FAKE_search'];
21
+
22
+ const stack = (fake: FakeMcpLitellm) => fakeStack({ apiKey: KEY, baseUrl: FAKE_BASE }, fake.fetch);
23
+ const declare = (props: McpServerProps) => Effect.asVoid(LiteLLMMCPServer('Bindings', props));
24
+
25
+ /** A live row with a whitelist, like an adopted `Cloudflare_Bindings`. */
26
+ const whitelisted = serverRow({
27
+ allowed_tools: TOOLS,
28
+ auth_type: 'oauth2',
29
+ server_id: 'FAKE-uuid-3',
30
+ server_name: 'FAKE_bindings',
31
+ transport: 'http',
32
+ url: 'https://bindings.example.com/mcp',
33
+ });
34
+ const withoutList: McpServerProps = {
35
+ authType: 'oauth2',
36
+ serverName: 'FAKE_bindings',
37
+ transport: 'http',
38
+ url: 'https://bindings.example.com/mcp',
39
+ };
40
+
41
+ test('adopting a whitelisted row without restating its list keeps the whitelist and writes nothing', async () => {
42
+ const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [whitelisted] });
43
+ const planned = await stack(fake).deploy(declare(withoutList), { adopt: true });
44
+ expect(Object.values(planned)).toEqual(['adopted']);
45
+ expect(writesOf(fake.requests())).toEqual([]);
46
+ expect(fake.servers()[0]?.['allowed_tools']).toEqual(TOOLS);
47
+ });
48
+
49
+ test('a grant that must change on such a row is corrected without sending the list', async () => {
50
+ const open = serverRow({ ...whitelisted, allow_all_keys: true });
51
+ const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [open] });
52
+ const planned = await stack(fake).deploy(declare(withoutList), { adopt: true });
53
+ // ⚠️ Alchemy's adoption branch plans `adopted` even when reconcile then writes: no `update` shows
54
+ expect(Object.values(planned)).toEqual(['adopted']);
55
+ expect(fake.bodies()).toHaveLength(1);
56
+ expect(fake.bodies()[0]).toMatchObject({ allow_all_keys: false });
57
+ expect(fake.bodies()[0]).not.toHaveProperty('allowed_tools');
58
+ expect(fake.servers()[0]).toMatchObject({ allow_all_keys: false, allowed_tools: TOOLS });
59
+ });
60
+
61
+ test('a declared list is authoritative: it replaces the live one, order and repeats aside', async () => {
62
+ const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [whitelisted] });
63
+ const engine = stack(fake);
64
+ const same = { ...withoutList, allowedTools: [...TOOLS].reverse() };
65
+ await engine.deploy(declare(same), { adopt: true });
66
+ expect(writesOf(fake.requests())).toEqual([]);
67
+
68
+ const narrower = { ...withoutList, allowedTools: ['FAKE_read'] };
69
+ expect(await engine.deploy(declare(narrower))).toEqual({ Bindings: 'update' });
70
+ expect(fake.bodies().at(-1)).toMatchObject({ allowed_tools: ['FAKE_read'] });
71
+ expect(fake.servers()[0]?.['allowed_tools']).toEqual(['FAKE_read']);
72
+ });
73
+
74
+ test('a declared empty list is sent as it is: an explicit "no restriction", never an accident', async () => {
75
+ const fake = startFakeMcpLitellm({ masterKey: KEY, seed: [whitelisted] });
76
+ await stack(fake).deploy(declare({ ...withoutList, allowedTools: [] }), { adopt: true });
77
+ expect(fake.bodies()[0]).toMatchObject({ allowed_tools: [] });
78
+ expect(fake.servers()[0]?.['allowed_tools']).toEqual([]);
79
+ });
80
+
81
+ test('a create that declares no list sends none', async () => {
82
+ const fake = startFakeMcpLitellm({ masterKey: KEY });
83
+ expect(await stack(fake).deploy(declare(withoutList))).toEqual({ Bindings: 'create' });
84
+ expect(fake.bodies()[0]).not.toHaveProperty('allowed_tools');
85
+ });
@@ -0,0 +1,143 @@
1
+ /**
2
+ * What a `LiteLLM.MCPServer` declares (props) and what is remembered about it (attributes).
3
+ *
4
+ * ⛔ NEITHER TYPE HOLDS A CREDENTIAL (S25). Alchemy persists props AND attributes in its state
5
+ * store, and it does not encrypt them: `StateEncoding.ts` TAGS a `Redacted` value and writes the
6
+ * inner value beside the tag (measured on alchemy 2.0.0-beta.79, `State/StateEncoding.ts` line
7
+ * 87-89). So a `Redacted<string>` prop would still land in state as plaintext. The credential is
8
+ * declared as `{ fromEnv: 'NAME' }`; the NAME is what is stored, and the value lives only in the
9
+ * deploying process (wrapped in `Redacted` there, `mcp-server-credential.ts`) and on the wire.
10
+ * ★ WHAT LITELLM STORES IS NEVER MIRRORED BACK. The list route carries `credentials`,
11
+ * `static_headers`, `env` and `env_vars`; `toAttributes` (mcp-server-form.ts) copies none of them.
12
+ * ⚠️ NOT MODELLED, on purpose: `stdio` (it runs a command on the proxy host), the OAuth client
13
+ * registration fields, `static_headers`/`extra_headers`, `mcp_info`, tool renames, BYOK and
14
+ * the AWS / token-exchange auth types (each needs credential fields this resource does not
15
+ * model). A row that uses them can still be adopted; what is not declared is left alone.
16
+ * ⚠️ NO `teams`. The create and edit requests of `@distilled.cloud/litellm` 0.3.0 (LiteLLM 1.103.0)
17
+ * have no team field; `teams` exists only on the row a read returns. Team access is granted on
18
+ * the team side (its `object_permission`), which is a different resource.
19
+ */
20
+ import type { FromEnv } from '../secrets/write-only.ts';
21
+
22
+ /** `stdio` is refused by the type: it would run a declared command on the proxy host. */
23
+ export type McpTransport = 'sse' | 'http';
24
+
25
+ /**
26
+ * Auth types that carry ONE static credential (`credentials.auth_value` on the wire). Which header
27
+ * each one becomes is LiteLLM's, not this package's (unmeasured here).
28
+ */
29
+ export type McpStaticAuthType = 'api_key' | 'bearer_token' | 'basic' | 'authorization' | 'token';
30
+
31
+ /** `oauth2` is a per-user flow LiteLLM runs itself: no static credential is declared for it. */
32
+ export type McpAuthType = 'none' | 'oauth2' | McpStaticAuthType;
33
+
34
+ export interface McpServerProps {
35
+ /**
36
+ * The server's name, and the way an existing row is ADOPTED: the live row whose `server_name`
37
+ * equals it. Changing it is an update of the same row, never a replace. The update re-sends the
38
+ * live alias, so the tool prefix stays. Declare `alias` to change the prefix.
39
+ */
40
+ readonly serverName: string;
41
+ /**
42
+ * Pins the live row (adopts by id) or chooses the id a create asks for. Without it, a live row
43
+ * is found by `serverName` and a new one gets a deterministic physical name. ⛔ A DIFFERENT
44
+ * DECLARED ID IS A REPLACE (create-first, so the old row is only removed by an opt-in
45
+ * `RemovalPolicy.destroy()`).
46
+ */
47
+ readonly serverId?: string;
48
+ /**
49
+ * The tool prefix LiteLLM shows (`<alias>-<tool>`). Compared only when declared, and an
50
+ * undeclared live alias is KEPT: an update that sends a `serverName` with no alias would make
51
+ * LiteLLM rewrite the alias to the name, so the live one is re-sent (mcp-server-form.ts).
52
+ * Declare this to change the prefix; a changed `serverName` does not. ⛔ Must match
53
+ * `^[A-Za-z0-9._]{1,128}$` (same rule as `serverName`): LiteLLM 1.103 runs `validate_tool_name`
54
+ * on it, so a space, `/`, `:`, `@` or more than 128 characters is a 400 only at apply.
55
+ */
56
+ readonly alias?: string;
57
+ /**
58
+ * The upstream MCP endpoint, `http` or `https`. ⛔ Refused if it carries userinfo, a fragment or
59
+ * a secret-looking query parameter (`?token=…`): the URL is a prop, so it lands in state. Declare
60
+ * the credential as `authValue` instead.
61
+ */
62
+ readonly url: string;
63
+ /** `sse` or `http`. Required: LiteLLM's own default is not this resource's to fix silently. */
64
+ readonly transport: McpTransport;
65
+ /** Required: a default would silently choose "no authentication". */
66
+ readonly authType: McpAuthType;
67
+ /**
68
+ * The static credential for a `McpStaticAuthType`, by the NAME of the environment variable that
69
+ * holds it. Required for those types and refused for the others.
70
+ */
71
+ readonly authValue?: FromEnv;
72
+ /**
73
+ * Compared only when declared. ⚠️ A blank one (`''` or whitespace) is refused: LiteLLM copies the
74
+ * column into `mcp_info` only when the value is truthy, so it would never converge. LiteLLM's
75
+ * list answers `mcp_info.description` in preference to this column, and this resource writes only
76
+ * the column. A row whose `mcp_info` has a `description` key (even a null one) that differs from
77
+ * the declaration is refused (`LitellmMcpServerDescriptionShadowedError`) rather than written and
78
+ * failed on the read back.
79
+ */
80
+ readonly description?: string;
81
+ /**
82
+ * Whether EVERY virtual key may call this server. Always compared, default `false`: it is an
83
+ * access grant, so an adopted row that has it on is corrected, never left open silently.
84
+ */
85
+ readonly allowAllKeys?: boolean;
86
+ /**
87
+ * The tools a caller may use, compared as a set. ⛔ COMPARED AND SENT ONLY WHEN DECLARED: omitted
88
+ * leaves the live whitelist exactly as it is. ⚠️ `[]` is NOT "closed" — at 1.103.0 an empty list
89
+ * turns the whitelist off (every tool is returned) unless the row's `mcp_info` carries the enforce
90
+ * flag, which this resource does not model. Declare `[]` only to mean "no restriction".
91
+ */
92
+ readonly allowedTools?: readonly string[];
93
+ /** Access groups this server belongs to. Always compared (as a set). */
94
+ readonly mcpAccessGroups?: readonly string[];
95
+ }
96
+
97
+ export interface McpServerAttributes {
98
+ readonly serverId: string;
99
+ readonly serverName: string | null;
100
+ readonly alias: string | null;
101
+ /** ⛔ REDACTED on the way in (userinfo and secret-looking query values become `REDACTED`). */
102
+ readonly url: string | null;
103
+ readonly transport: string;
104
+ /** `none` when the live row carries no auth type. */
105
+ readonly authType: string;
106
+ readonly description: string | null;
107
+ readonly allowAllKeys: boolean;
108
+ readonly allowedTools: readonly string[];
109
+ readonly mcpAccessGroups: readonly string[];
110
+ /**
111
+ * `scrypt:<salt>:<digest>` of the credential last written, or `''`. ⛔ Never the value. LiteLLM
112
+ * does not return a credential to compare with, so this is how a plan notices a rotated one
113
+ * (secrets/write-only.ts). It is carried from the previous state, never read from the row.
114
+ */
115
+ readonly credentialSeal: string;
116
+ }
117
+
118
+ /** Whether a value is not a string with something in it. Takes `unknown`: a declaration can say anything. */
119
+ export const isBlank = (value: unknown): boolean =>
120
+ typeof value !== 'string' || value.trim() === '';
121
+
122
+ const STATIC: ReadonlySet<string> = new Set<McpStaticAuthType>([
123
+ 'api_key',
124
+ 'bearer_token',
125
+ 'basic',
126
+ 'authorization',
127
+ 'token',
128
+ ]);
129
+
130
+ /** Whether an auth type carries one static credential. Takes a string: a live row can say anything. */
131
+ export const isStaticAuthType = (authType: string): boolean => STATIC.has(authType);
132
+
133
+ export const AUTH_TYPES: readonly McpAuthType[] = [
134
+ 'none',
135
+ 'oauth2',
136
+ 'api_key',
137
+ 'bearer_token',
138
+ 'basic',
139
+ 'authorization',
140
+ 'token',
141
+ ];
142
+
143
+ export const TRANSPORTS: readonly McpTransport[] = ['sse', 'http'];