@lenne.tech/nuxt-extensions 1.21.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -40,6 +40,7 @@ src/
40
40
  | `useLtAi()` | One-shot `prompt()` + streaming `promptStream()` (POST `/ai/prompt` / `/ai/stream`) |
41
41
  | `useLtAiChat()` | Multi-turn chat: streaming, budget summary, confirmation gate, `stop()`/`clear()`, optional `maxMessages` cap, auto-stop on unmount |
42
42
  | `useLtAiConnections()` | User-facing list of available connections + `select()` |
43
+ | `useLtAiSettings()` | The user's own AI settings: MCP confirmation switch (`GET/PUT /ai/settings`, `enforced` → show disabled) |
43
44
  | `useLtAiUsage()` | Full token/prompt usage breakdown (`GET /ai/usage`) |
44
45
  | `useLtAiPrompts()` | User-facing CRUD for re-usable prompt snippets ("Vorlagen", `scope: 'user'` / `'tenant'`) |
45
46
  | `useLtAiPlaceholders()` | Loads the backend's `{{placeholder}}` registry for editor sidebars |
package/README.md CHANGED
@@ -709,6 +709,45 @@ await load();
709
709
  is private to the owner; `scope: 'tenant'` is shared across the owner's tenant. The list
710
710
  returned by `load()` is already server-filtered to what the user is allowed to see.
711
711
 
712
+ ### MCP confirmation switch
713
+
714
+ Over MCP (Claude Desktop, Claude Code and other clients), nest-server 12.3.0 or later asks the
715
+ user before a destructive tool runs, and before a mutating one if the server is configured that
716
+ way. Each user may switch the question off for themselves, like a "bypass permissions" mode.
717
+ `useLtAiSettings()` reads and changes that switch (`GET/PUT /ai/settings`):
718
+
719
+ ```vue
720
+ <script setup lang="ts">
721
+ const { enforced, load, settings, update } = useLtAiSettings();
722
+ await load();
723
+
724
+ async function toggle(value: boolean) {
725
+ try {
726
+ await update({ mcpConfirmation: value });
727
+ } catch {
728
+ // `error` holds the message; `settings` keeps the previous value, so the switch snaps back
729
+ }
730
+ }
731
+ </script>
732
+
733
+ <template>
734
+ <USwitch
735
+ :model-value="settings?.mcpConfirmation"
736
+ :disabled="enforced || !settings"
737
+ label="Ask before destructive AI actions over MCP"
738
+ @update:model-value="toggle"
739
+ />
740
+ </template>
741
+ ```
742
+
743
+ - `settings` is always the value as it takes effect, never just the stored choice. `update()`
744
+ adopts the server's answer instead of the input.
745
+ - `enforced` is true when the server fixed the value (`ai.mcp.confirmation.enforced`). Show the
746
+ switch disabled. A change is refused with 403 `#LTNS_0609`, which `update()` throws.
747
+ - Switching it off lets an MCP client run destructive tools in this user's name without asking.
748
+ Say so next to the switch. The tool's own `authorize()` check and the admin's `deny` / `ask`
749
+ rules still apply, and other users and the in-app chat are not affected.
750
+
712
751
  ### Lower-level + supporting composables
713
752
 
714
753
  | Composable | Purpose |
@@ -716,6 +755,7 @@ returned by `load()` is already server-filtered to what the user is allowed to s
716
755
  | `useLtAi()` | One-shot `prompt(input)` and streaming `promptStream(input, handlers)` (POST `/ai/stream`) |
717
756
  | `useLtAiChat()` | Multi-turn chat state, streaming, budget, confirmation, `stop()`/`clear()`, optional `maxMessages` cap, auto-stop on component unmount |
718
757
  | `useLtAiConnections()` | User self-service: available connections + `select()` (`selected`/`locked`) |
758
+ | `useLtAiSettings()` | The user's own AI settings: the MCP confirmation switch + `update()` (`enforced`) |
719
759
  | `useLtAiUsage()` | Full token/prompt usage breakdown (`GET /ai/usage`) |
720
760
  | `useLtAiPrompts()` | User-facing CRUD for re-usable prompt snippets ("Vorlagen", `scope: 'user'` / `'tenant'`) |
721
761
  | `useLtAiPlaceholders()` | Loads `{{placeholder}}` registry from the backend so slot / prompt editors render a dynamic helper sidebar |
@@ -829,6 +869,7 @@ The package works **with or without** `@nuxtjs/i18n`:
829
869
  | `useLtAi()` | One-shot `prompt()` + streaming `promptStream()` for the nest-server AI module |
830
870
  | `useLtAiChat()` | Multi-turn chat state, streaming, budget summary, confirmation gate, `maxMessages` cap |
831
871
  | `useLtAiConnections()` | User self-service connection list + `select()` |
872
+ | `useLtAiSettings()` | The user's own AI settings (MCP confirmation switch, nest-server >= 12.3.0) |
832
873
  | `useLtAiUsage()` | Token/prompt usage breakdown per user / tenant |
833
874
  | `useLtAiPrompts()` | User-facing prompt snippet CRUD (`'user'` / `'tenant'` scope) |
834
875
  | `useLtAiPlaceholders()` | Loads the backend's `{{placeholder}}` registry |
package/dist/module.d.mts CHANGED
@@ -3,7 +3,7 @@ import { LtExtensionsModuleOptions } from '../dist/runtime/types/index.js';
3
3
  export * from '../dist/runtime/types/index.js';
4
4
 
5
5
  declare const name = "@lenne.tech/nuxt-extensions";
6
- declare const version = "1.21.0";
6
+ declare const version = "1.22.0";
7
7
  declare const configKey = "ltExtensions";
8
8
  declare const DEFAULT_LT_AUTH_STATE_COOKIE = "lt-auth-state";
9
9
  declare const DEFAULT_LT_JWT_TOKEN_COOKIE = "lt-jwt-token";
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "configKey": "ltExtensions",
3
3
  "name": "@lenne.tech/nuxt-extensions",
4
- "version": "1.21.0",
4
+ "version": "1.22.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.3",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -2,7 +2,7 @@ import { defineNuxtModule, createResolver, tryResolveModule, addImports, addComp
2
2
  import { pathToFileURL } from 'node:url';
3
3
 
4
4
  const name = "@lenne.tech/nuxt-extensions";
5
- const version = "1.21.0";
5
+ const version = "1.22.0";
6
6
  const configKey = "ltExtensions";
7
7
  const DEFAULT_LT_AUTH_STATE_COOKIE = "lt-auth-state";
8
8
  const DEFAULT_LT_JWT_TOKEN_COOKIE = "lt-jwt-token";
@@ -204,6 +204,7 @@ const module$1 = defineNuxtModule({
204
204
  { name: "useLtAi", from: resolve("./runtime/composables/use-lt-ai") },
205
205
  { name: "useLtAiChat", from: resolve("./runtime/composables/use-lt-ai-chat") },
206
206
  { name: "useLtAiConnections", from: resolve("./runtime/composables/use-lt-ai-connections") },
207
+ { name: "useLtAiSettings", from: resolve("./runtime/composables/use-lt-ai-settings") },
207
208
  { name: "useLtAiUsage", from: resolve("./runtime/composables/use-lt-ai-usage") },
208
209
  { name: "useLtAiPrompts", from: resolve("./runtime/composables/use-lt-ai-prompts") },
209
210
  { name: "useLtAiPlaceholders", from: resolve("./runtime/composables/use-lt-ai-placeholders") },
@@ -8,6 +8,7 @@ export { useLtErrorTranslation } from './use-lt-error-translation.js';
8
8
  export { useLtAi } from './use-lt-ai.js';
9
9
  export { useLtAiChat } from './use-lt-ai-chat.js';
10
10
  export { useLtAiConnections } from './use-lt-ai-connections.js';
11
+ export { useLtAiSettings } from './use-lt-ai-settings.js';
11
12
  export { useLtAiUsage } from './use-lt-ai-usage.js';
12
13
  export { useLtAiAdmin } from './use-lt-ai-admin.js';
13
14
  export { useLtAiPrompts } from './use-lt-ai-prompts.js';
@@ -8,6 +8,7 @@ export { useLtErrorTranslation } from "./use-lt-error-translation.js";
8
8
  export { useLtAi } from "./use-lt-ai.js";
9
9
  export { useLtAiChat } from "./use-lt-ai-chat.js";
10
10
  export { useLtAiConnections } from "./use-lt-ai-connections.js";
11
+ export { useLtAiSettings } from "./use-lt-ai-settings.js";
11
12
  export { useLtAiUsage } from "./use-lt-ai-usage.js";
12
13
  export { useLtAiAdmin } from "./use-lt-ai-admin.js";
13
14
  export { useLtAiPrompts } from "./use-lt-ai-prompts.js";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * User self-service for the current user's own AI settings, today whether MCP
3
+ * asks before a destructive or mutating tool runs (a "bypass permissions"
4
+ * switch). Backed by `GET /ai/settings` and `PUT /ai/settings`
5
+ * (nest-server >= 12.3.0).
6
+ *
7
+ * The answer is always the setting as it takes effect. When the server fixes
8
+ * it (`ai.mcp.confirmation.enforced`), `enforced` is true, the switch should be
9
+ * shown disabled, and `update()` is refused with 403 (`#LTNS_0609`).
10
+ */
11
+ import type { UseLtAiSettingsReturn } from '../types/ai.js';
12
+ export declare function useLtAiSettings(): UseLtAiSettingsReturn;
@@ -0,0 +1,32 @@
1
+ import { computed, readonly, ref } from "#imports";
2
+ import { ltAiRequest } from "../lib/ai.js";
3
+ export function useLtAiSettings() {
4
+ const settings = ref(null);
5
+ const loading = ref(false);
6
+ const error = ref(null);
7
+ const enforced = computed(() => settings.value?.mcpConfirmationEnforced === true);
8
+ async function load() {
9
+ loading.value = true;
10
+ error.value = null;
11
+ try {
12
+ settings.value = await ltAiRequest("GET", "/settings");
13
+ } catch (err) {
14
+ error.value = err.message;
15
+ } finally {
16
+ loading.value = false;
17
+ }
18
+ }
19
+ async function update(input) {
20
+ loading.value = true;
21
+ error.value = null;
22
+ try {
23
+ settings.value = await ltAiRequest("PUT", "/settings", input);
24
+ } catch (err) {
25
+ error.value = err.message;
26
+ throw err;
27
+ } finally {
28
+ loading.value = false;
29
+ }
30
+ }
31
+ return { enforced, error: readonly(error), load, loading: readonly(loading), settings: readonly(settings), update };
32
+ }
@@ -126,6 +126,17 @@ export interface LtAiAvailableConnection {
126
126
  name?: string;
127
127
  selected?: boolean;
128
128
  }
129
+ /** The current user's AI settings as they take effect (`GET/PUT /ai/settings`, nest-server >= 12.3.0). */
130
+ export interface LtAiSettings {
131
+ /** Whether MCP asks this user before a destructive tool (and a mutating one, if configured) runs. */
132
+ mcpConfirmation: boolean;
133
+ /** Whether the server fixed `mcpConfirmation`, so the user cannot change it (a change answers 403 `#LTNS_0609`). */
134
+ mcpConfirmationEnforced: boolean;
135
+ }
136
+ /** The user's own choices for `PUT /ai/settings`; a field left out stays unchanged. */
137
+ export interface LtAiSettingsInput {
138
+ mcpConfirmation?: boolean;
139
+ }
129
140
  /** Usage for a single scope (user or tenant). */
130
141
  export interface LtAiUsageScope {
131
142
  maxPrompts?: number;
@@ -429,6 +440,16 @@ export interface UseLtAiConnectionsReturn {
429
440
  select: (connectionId: string) => Promise<void>;
430
441
  selected: ComputedRef<LtAiAvailableConnection | undefined>;
431
442
  }
443
+ export interface UseLtAiSettingsReturn {
444
+ /** True when the server fixed the MCP confirmation: show the switch disabled. */
445
+ enforced: ComputedRef<boolean>;
446
+ error: DeepReadonly<Ref<null | string>>;
447
+ load: () => Promise<void>;
448
+ loading: DeepReadonly<Ref<boolean>>;
449
+ settings: DeepReadonly<Ref<LtAiSettings | null>>;
450
+ /** Stores the user's choice and adopts the answer; throws (and sets `error`) when refused. */
451
+ update: (input: LtAiSettingsInput) => Promise<void>;
452
+ }
432
453
  export interface UseLtAiUsageReturn {
433
454
  error: DeepReadonly<Ref<null | string>>;
434
455
  loading: DeepReadonly<Ref<boolean>>;
@@ -3,4 +3,4 @@ export type { LtFileInfo, LtUploadItem, LtUploadOptions, LtUploadProgress, LtUpl
3
3
  export type { LtAiModuleOptions, LtAuthCookieNamesOptions, LtAuthModuleOptions, LtErrorTranslationModuleOptions, LtExtensionsModuleOptions, LtExtensionsPublicRuntimeConfig, LtFormLabelAssociationOptions, LtI18nModuleOptions, LtPreHydrationInputOptions, LtSystemSetupModuleOptions, LtTusModuleOptions, } from './module.js';
4
4
  export type { LtErrorTranslationResponse, LtParsedError, UseLtErrorTranslationReturn } from './error.js';
5
5
  export type { LtSyncConflict, LtSyncConflictResolution, LtSyncIdFormat, LtSyncItemStatus, LtSyncModuleOptions, LtSyncPushResult, LtSyncRecord, LtSyncResult, UseLtOfflineSyncOptions, UseLtOfflineSyncReturn, } from './sync.js';
6
- export type { LtAiAction, LtAiAvailableConnection, LtAiBudgetLimit, LtAiBudgetSummary, LtAiConnection, LtAiConnectionInput, LtAiConnectionPreference, LtAiEffectiveSlot, LtAiInteraction, LtAiMessage, LtAiMode, LtAiPlaceholder, LtAiPrompt, LtAiPromptHint, LtAiPromptHintInput, LtAiPromptInput, LtAiPromptRunInput, LtAiResponse, LtAiSlot, LtAiSlotInput, LtAiStreamEvent, LtAiStreamHandlers, LtAiUsage, LtAiUsageInfo, LtAiUsageScope, UseLtAiAdminReturn, UseLtAiChatOptions, UseLtAiChatReturn, UseLtAiConnectionsReturn, UseLtAiPlaceholdersReturn, UseLtAiPromptsReturn, UseLtAiReturn, UseLtAiUsageReturn, } from './ai.js';
6
+ export type { LtAiAction, LtAiAvailableConnection, LtAiBudgetLimit, LtAiBudgetSummary, LtAiConnection, LtAiConnectionInput, LtAiConnectionPreference, LtAiEffectiveSlot, LtAiInteraction, LtAiMessage, LtAiMode, LtAiPlaceholder, LtAiPrompt, LtAiPromptHint, LtAiPromptHintInput, LtAiPromptInput, LtAiPromptRunInput, LtAiResponse, LtAiSettings, LtAiSettingsInput, LtAiSlot, LtAiSlotInput, LtAiStreamEvent, LtAiStreamHandlers, LtAiUsage, LtAiUsageInfo, LtAiUsageScope, UseLtAiAdminReturn, UseLtAiChatOptions, UseLtAiChatReturn, UseLtAiConnectionsReturn, UseLtAiPlaceholdersReturn, UseLtAiPromptsReturn, UseLtAiReturn, UseLtAiSettingsReturn, UseLtAiUsageReturn, } from './ai.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nuxt-extensions",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.tech projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -102,24 +102,24 @@
102
102
  "devDependencies": {
103
103
  "@better-auth/passkey": "1.7.12",
104
104
  "@nuxt/module-builder": "1.0.3",
105
- "@nuxt/schema": "4.6.3",
105
+ "@nuxt/schema": "4.6.4",
106
106
  "@nuxt/ui": "4.12.0",
107
107
  "@playwright/test": "1.64.1",
108
108
  "@types/node": "26.6.6",
109
- "@vitest/coverage-v8": "5.0.5",
109
+ "@vitest/coverage-v8": "5.1.0",
110
110
  "@vue/server-renderer": "3.5.44",
111
111
  "better-auth": "1.7.12",
112
112
  "cross-spawn": "7.0.6",
113
113
  "fake-indexeddb": "6.2.7",
114
114
  "happy-dom": "21.0.4",
115
- "nuxt": "4.6.3",
115
+ "nuxt": "4.6.4",
116
116
  "oxfmt": "0.75.0",
117
117
  "oxlint": "1.90.0",
118
118
  "oxlint-tsgolint": "7.0.2005",
119
119
  "reka-ui": "2.11.0",
120
120
  "tus-js-client": "4.3.1",
121
121
  "typescript": "5.9.3",
122
- "vitest": "5.0.5",
122
+ "vitest": "5.1.0",
123
123
  "vue-tsc": "3.3.14"
124
124
  },
125
125
  "keywords": [