feat(mcp): add streamable http server and fine-grained authorization

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-08-31 22:56:16 +08:00
1 parent f725c50b2c
commit f075caef21
47 files changed
+4960 -509

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -0,0 +1,92 @@
<script setup lang="ts">
import { computed, nextTick, ref } from "vue";
import { useI18n } from "vue-i18n";
type StepId = "capabilities" | "connections" | "databases" | "overrides";
const { t } = useI18n();
const steps = computed<Array<{ id: StepId; title: string; description: string }>>(() => [
{ id: "connections", title: t("settings.mcpAuthStepConnectionsTitle"), description: t("settings.mcpAuthStepConnectionsDescription") },
{ id: "databases", title: t("settings.mcpAuthStepDatabasesTitle"), description: t("settings.mcpAuthStepDatabasesDescription") },
{ id: "overrides", title: t("settings.mcpAuthStepOverridesTitle"), description: t("settings.mcpAuthStepOverridesDescription") },
{ id: "capabilities", title: t("settings.mcpAuthStepCapabilitiesTitle"), description: t("settings.mcpAuthStepCapabilitiesDescription") },
]);
const currentStep = ref<StepId>("connections");
const currentIndex = computed(() => steps.value.findIndex((step) => step.id === currentStep.value));
function scrollableAncestor(target: EventTarget | null): HTMLElement | null {
let element = target instanceof HTMLElement ? target.parentElement : null;
while (element) {
const overflowY = window.getComputedStyle(element).overflowY;
if (overflowY === "auto" || overflowY === "scroll") return element;
element = element.parentElement;
}
return null;
}
async function selectStep(step: StepId, event?: Event) {
if (step === currentStep.value) return;
const scroller = scrollableAncestor(event?.currentTarget ?? null);
const scrollTop = scroller?.scrollTop;
currentStep.value = step;
if (!scroller || scrollTop === undefined) return;
await nextTick();
requestAnimationFrame(() => {
if (scroller.isConnected) scroller.scrollTop = scrollTop;
});
}
function previousStep(event: Event) {
const step = steps.value[currentIndex.value - 1];
if (step) void selectStep(step.id, event);
}
function nextStep(event: Event) {
const step = steps.value[currentIndex.value + 1];
if (step) void selectStep(step.id, event);
}
</script>
<template>
<section class="space-y-4 rounded-md border bg-background p-3 sm:p-4">
<header class="space-y-1">
<p class="text-sm font-semibold">{{ t("settings.mcpAuthTitle") }}</p>
<p class="text-xs text-muted-foreground">{{ t("settings.mcpAuthDescription") }}</p>
</header>
<nav class="grid gap-1 rounded-md border bg-muted/50 p-1 sm:grid-cols-4" :aria-label="t('settings.mcpAuthStepsLabel')">
<button
v-for="(step, index) in steps"
:key="step.id"
type="button"
class="group flex min-w-0 items-center gap-2 rounded px-2.5 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50"
:class="currentStep === step.id ? 'bg-background shadow-sm' : 'text-muted-foreground hover:bg-background/70'"
:aria-current="currentStep === step.id ? 'step' : undefined"
@click="selectStep(step.id, $event)"
>
<span
class="flex h-5 w-5 shrink-0 items-center justify-center rounded-full border text-[11px] font-semibold"
:class="currentStep === step.id ? 'border-primary bg-primary text-primary-foreground' : index < currentIndex ? 'border-primary/40 bg-primary/10 text-primary' : 'bg-background text-muted-foreground'"
>{{ index + 1 }}</span
>
<span class="min-w-0">
<span class="block truncate text-xs font-medium">{{ step.title }}</span>
<span class="hidden truncate text-[10px] text-muted-foreground lg:block">{{ step.description }}</span>
</span>
</button>
</nav>
<div :class="currentStep === 'connections' ? 'block' : 'hidden'" :aria-hidden="currentStep !== 'connections'" role="region" :aria-label="t('settings.mcpAuthStepRegionLabel', { step: 1, title: t('settings.mcpAuthStepConnectionsTitle') })"><slot name="connections" /></div>
<div :class="currentStep === 'databases' ? 'block' : 'hidden'" :aria-hidden="currentStep !== 'databases'" role="region" :aria-label="t('settings.mcpAuthStepRegionLabel', { step: 2, title: t('settings.mcpAuthStepDatabasesTitle') })"><slot name="databases" /></div>
<div :class="currentStep === 'overrides' ? 'block' : 'hidden'" :aria-hidden="currentStep !== 'overrides'" role="region" :aria-label="t('settings.mcpAuthStepRegionLabel', { step: 3, title: t('settings.mcpAuthStepOverridesTitle') })"><slot name="overrides" /></div>
<div :class="currentStep === 'capabilities' ? 'block' : 'hidden'" :aria-hidden="currentStep !== 'capabilities'" role="region" :aria-label="t('settings.mcpAuthStepRegionLabel', { step: 4, title: t('settings.mcpAuthStepCapabilitiesTitle') })"><slot name="capabilities" /></div>
<footer class="flex items-center justify-between gap-3 border-t pt-3">
<button type="button" class="rounded-md border bg-background px-3 py-1.5 text-xs font-medium disabled:cursor-not-allowed disabled:opacity-50" :disabled="currentIndex === 0" @click="previousStep($event)">{{ t("settings.mcpAuthPreviousStep") }}</button>
<p class="text-center text-xs text-muted-foreground">{{ t("settings.mcpAuthStepProgress", { current: currentIndex + 1, total: steps.length }) }}</p>
<button type="button" class="rounded-md border bg-background px-3 py-1.5 text-xs font-medium disabled:cursor-not-allowed disabled:opacity-50" :disabled="currentIndex === steps.length - 1" @click="nextStep($event)">{{ t("settings.mcpAuthNextStep") }}</button>
</footer>
</section>
</template>
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { computed, nextTick, ref, useId, watch } from "vue";
import { AlertTriangle, Minus, Plus, Search } from "@lucide/vue";
import { AlertTriangle, ChevronLeft, ChevronRight, Minus, Plus, Search } from "@lucide/vue";
import { useI18n } from "vue-i18n";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@@ -12,6 +12,8 @@ import type { ConnectionConfig } from "@/types/database";
type ScopePane = "available" | "allowed";
type ScopeMode = "all" | "selected";
const PAGE_SIZE_OPTIONS = [6, 10, 20] as const;
const props = withDefaults(
defineProps<{
connections: readonly ConnectionConfig[];
@@ -34,6 +36,9 @@ const pickerId = useId();
const rootRef = ref<HTMLElement>();
const searchQuery = ref("");
const compactPane = ref<ScopePane>("allowed");
const availablePage = ref(1);
const allowedPage = ref(1);
const connectionsPerPage = ref<number>(PAGE_SIZE_OPTIONS[0]);
const announcement = ref("");
const pendingFocus = ref<{ pane: ScopePane; index: number } | null>(null);
@@ -49,6 +54,16 @@ function connectionMatchesSearch(connection: ConnectionConfig): boolean {
const filteredAvailableConnections = computed(() => groups.value.available.filter(connectionMatchesSearch));
const filteredAllowedConnections = computed(() => groups.value.allowed.filter(connectionMatchesSearch));
const filteredUnavailableAllowedIds = computed(() => groups.value.unavailableAllowedIds.filter((id) => matchesMcpSearchQuery(searchQuery.value, [id, t("settings.mcpScopeConnectionUnavailable")])));
const availablePageCount = computed(() => Math.max(1, Math.ceil(filteredAvailableConnections.value.length / connectionsPerPage.value)));
const allowedPageCount = computed(() => Math.max(1, Math.ceil(filteredAllowedConnections.value.length / connectionsPerPage.value)));
const pagedAvailableConnections = computed(() => {
const start = (availablePage.value - 1) * connectionsPerPage.value;
return filteredAvailableConnections.value.slice(start, start + connectionsPerPage.value);
});
const pagedAllowedConnections = computed(() => {
const start = (allowedPage.value - 1) * connectionsPerPage.value;
return filteredAllowedConnections.value.slice(start, start + connectionsPerPage.value);
});
const allowedVisibleCount = computed(() => filteredAllowedConnections.value.length + filteredUnavailableAllowedIds.value.length);
const allowedTotalCount = computed(() => groups.value.allowed.length + groups.value.unavailableAllowedIds.length);
const availableVisibleCount = computed(() => filteredAvailableConnections.value.length);
@@ -60,6 +75,17 @@ function paneCount(visible: number, total: number): string {
return searchActive.value ? `${visible}/${total}` : String(total);
}
function setPanePage(pane: ScopePane, page: number) {
const total = pane === "available" ? availablePageCount.value : allowedPageCount.value;
const next = Math.min(Math.max(1, page), total);
if (pane === "available") availablePage.value = next;
else allowedPage.value = next;
}
function paginationLabel(page: number, pageCount: number): string {
return `${page} / ${pageCount}`;
}
function connectionAddress(connection: ConnectionConfig): string {
const address = connection.host ? `${connection.host}:${connection.port}` : "";
return [address, connection.database].filter(Boolean).join(" · ") || connection.id;
@@ -135,10 +161,23 @@ watch(
watch([policyKey, () => groups.value.allowed.length], () => {
announcement.value = t("settings.mcpScopeUpdatedAnnouncement", { selected: groups.value.allowed.length, total: props.connections.length });
});
watch(searchQuery, () => {
availablePage.value = 1;
allowedPage.value = 1;
});
watch(connectionsPerPage, () => {
availablePage.value = 1;
allowedPage.value = 1;
});
watch(availablePageCount, () => setPanePage("available", availablePage.value));
watch(allowedPageCount, () => setPanePage("allowed", allowedPage.value));
</script>
<template>
<div ref="rootRef" tabindex="-1" class="mcp-scope-picker space-y-3 rounded-md border bg-muted/20 p-3 outline-none" :aria-busy="busy">
<div ref="rootRef" tabindex="-1" class="mcp-scope-picker space-y-3 outline-none" :aria-busy="busy">
<div class="flex flex-wrap items-start justify-between gap-3">
<div class="min-w-0 space-y-1">
<div class="flex flex-wrap items-center gap-2">
@@ -149,7 +188,7 @@ watch([policyKey, () => groups.value.allowed.length], () => {
</div>
</div>
<div class="grid grid-cols-1 p-1 sm:grid-cols-2 gap-2.5" role="radiogroup" :aria-labelledby="`${pickerId}-mode-label`">
<div class="grid grid-cols-1 gap-1 rounded-md border bg-muted/50 p-1 sm:grid-cols-2" role="radiogroup" :aria-labelledby="`${pickerId}-mode-label`">
<Button
:disabled="disabled"
type="button"
@@ -158,8 +197,8 @@ watch([policyKey, () => groups.value.allowed.length], () => {
:aria-checked="scopeMode === 'all'"
:tabindex="scopeMode === 'all' ? 0 : -1"
variant="outline"
class="settings-choice-card h-auto justify-center border p-3"
:class="[scopeMode === 'all' ? 'dbx-choice-selected' : '', disabled ? 'cursor-not-allowed opacity-50' : '']"
class="settings-choice-card h-auto justify-center border-0 p-2.5 shadow-none"
:class="[scopeMode === 'all' ? 'dbx-choice-selected bg-background shadow-sm' : 'text-muted-foreground hover:bg-background/70', disabled ? 'cursor-not-allowed opacity-50' : '']"
@click="setScopeMode('all')"
@keydown="onScopeModeKeydown($event, 'all')"
>
@@ -176,8 +215,8 @@ watch([policyKey, () => groups.value.allowed.length], () => {
:aria-checked="scopeMode === 'selected'"
:tabindex="scopeMode === 'selected' ? 0 : -1"
variant="outline"
class="settings-choice-card h-auto justify-center border p-3"
:class="[scopeMode === 'selected' ? 'dbx-choice-selected' : '', disabled ? 'cursor-not-allowed opacity-50' : '']"
class="settings-choice-card h-auto justify-center border-0 p-2.5 shadow-none"
:class="[scopeMode === 'selected' ? 'dbx-choice-selected bg-background shadow-sm' : 'text-muted-foreground hover:bg-background/70', disabled ? 'cursor-not-allowed opacity-50' : '']"
@click="setScopeMode('selected')"
@keydown="onScopeModeKeydown($event, 'selected')"
>
@@ -188,9 +227,17 @@ watch([policyKey, () => groups.value.allowed.length], () => {
</Button>
</div>
<div class="relative">
<Search class="pointer-events-none absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Input v-model="searchQuery" class="h-9 pl-8" :disabled="disabled" :aria-label="t('settings.mcpConnectionSearchPlaceholder')" :placeholder="t('settings.mcpConnectionSearchPlaceholder')" />
<div class="flex flex-wrap items-center gap-2">
<div class="relative min-w-0 flex-1">
<Search class="pointer-events-none absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Input v-model="searchQuery" class="h-9 pl-8" :disabled="disabled" :aria-label="t('settings.mcpConnectionSearchPlaceholder')" :placeholder="t('settings.mcpConnectionSearchPlaceholder')" />
</div>
<label class="flex h-9 items-center gap-1.5 rounded-md border bg-background px-2 text-xs text-muted-foreground">
{{ t("settings.mcpPerPage") }}
<select :value="connectionsPerPage" class="bg-transparent text-xs text-foreground outline-none" :disabled="disabled" @change="connectionsPerPage = Number(($event.target as HTMLSelectElement).value)">
<option v-for="size in PAGE_SIZE_OPTIONS" :key="size" :value="size">{{ size }}</option>
</select>
</label>
</div>
<div class="mcp-scope-mobile-tabs grid grid-cols-2 rounded-md bg-muted p-1" role="tablist" :aria-label="t('settings.mcpScopeConnection')">
@@ -243,11 +290,11 @@ watch([policyKey, () => groups.value.allowed.length], () => {
<span class="mcp-scope-batch-label">{{ t(searchActive ? "settings.mcpScopeAddMatches" : "settings.mcpScopeAddAll", { count: availableVisibleCount }) }}</span>
</Button>
</div>
<div class="h-64 overflow-y-auto p-1.5">
<div class="space-y-1.5 p-1.5">
<div v-if="filteredAvailableConnections.length === 0" class="flex h-full items-center justify-center px-3 text-center text-sm text-muted-foreground">
{{ searchActive ? t("settings.mcpConnectionSearchEmpty") : t("settings.mcpScopeAvailableEmpty") }}
</div>
<div v-for="connection in filteredAvailableConnections" :key="connection.id" class="mcp-scope-connection-row grid min-h-12 items-center gap-2 rounded px-2 py-1.5 hover:bg-muted/60">
<div v-for="connection in pagedAvailableConnections" :key="connection.id" class="mcp-scope-connection-row grid min-h-12 items-center gap-2 rounded px-2 py-1.5 hover:bg-muted/60">
<div class="min-w-0">
<TruncatedTextTooltip :text="connection.name" class="block text-sm font-medium" />
<TruncatedTextTooltip :text="connectionAddress(connection)" class="mt-0.5 block font-mono text-[11px] text-muted-foreground" />
@@ -272,6 +319,15 @@ watch([policyKey, () => groups.value.allowed.length], () => {
<Plus />
</Button>
</div>
<div v-if="availablePageCount > 1" class="flex items-center justify-center gap-2 border-t pt-2 text-xs text-muted-foreground">
<Button type="button" size="icon-sm" variant="ghost" :disabled="availablePage === 1" :title="t('settings.mcpPreviousPage')" :aria-label="t('settings.mcpPreviousPage')" @click="setPanePage('available', availablePage - 1)">
<ChevronLeft />
</Button>
<span class="min-w-12 text-center tabular-nums">{{ paginationLabel(availablePage, availablePageCount) }}</span>
<Button type="button" size="icon-sm" variant="ghost" :disabled="availablePage === availablePageCount" :title="t('settings.mcpNextPage')" :aria-label="t('settings.mcpNextPage')" @click="setPanePage('available', availablePage + 1)">
<ChevronRight />
</Button>
</div>
</div>
</section>
@@ -295,11 +351,11 @@ watch([policyKey, () => groups.value.allowed.length], () => {
<span class="mcp-scope-batch-label">{{ t(searchActive ? "settings.mcpScopeRemoveMatches" : "settings.mcpScopeRemoveAll", { count: allowedVisibleCount }) }}</span>
</Button>
</div>
<div class="h-64 overflow-y-auto p-1.5">
<div class="space-y-1.5 p-1.5">
<div v-if="filteredAllowedConnections.length === 0 && filteredUnavailableAllowedIds.length === 0" class="flex h-full items-center justify-center px-3 text-center text-sm text-muted-foreground">
{{ searchActive ? t("settings.mcpConnectionSearchEmpty") : t("settings.mcpScopeAllowedEmpty") }}
</div>
<div v-for="connection in filteredAllowedConnections" :key="connection.id" class="mcp-scope-connection-row grid min-h-12 items-center gap-2 rounded px-2 py-1.5 hover:bg-muted/60">
<div v-for="connection in pagedAllowedConnections" :key="connection.id" class="mcp-scope-connection-row grid min-h-12 items-center gap-2 rounded px-2 py-1.5 hover:bg-muted/60">
<div class="min-w-0">
<TruncatedTextTooltip :text="connection.name" class="block text-sm font-medium" />
<TruncatedTextTooltip :text="connectionAddress(connection)" class="mt-0.5 block font-mono text-[11px] text-muted-foreground" />
@@ -347,6 +403,15 @@ watch([policyKey, () => groups.value.allowed.length], () => {
</Button>
</div>
</div>
<div v-if="allowedPageCount > 1" class="flex items-center justify-center gap-2 border-t pt-2 text-xs text-muted-foreground">
<Button type="button" size="icon-sm" variant="ghost" :disabled="allowedPage === 1" :title="t('settings.mcpPreviousPage')" :aria-label="t('settings.mcpPreviousPage')" @click="setPanePage('allowed', allowedPage - 1)">
<ChevronLeft />
</Button>
<span class="min-w-12 text-center tabular-nums">{{ paginationLabel(allowedPage, allowedPageCount) }}</span>
<Button type="button" size="icon-sm" variant="ghost" :disabled="allowedPage === allowedPageCount" :title="t('settings.mcpNextPage')" :aria-label="t('settings.mcpNextPage')" @click="setPanePage('allowed', allowedPage + 1)">
<ChevronRight />
</Button>
</div>
</div>
</section>
</div>
@@ -0,0 +1,262 @@
<script setup lang="ts">
import { computed, ref, watch } from "vue";
import { Check, ChevronLeft, ChevronRight, Loader2, Plus, RefreshCw, Search } from "@lucide/vue";
import { useI18n } from "vue-i18n";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { listDatabases, mongoListDatabases, redisListDatabases } from "@/lib/backend/api";
import type { McpConnectionPolicy } from "@/stores/settingsStore";
import type { ConnectionConfig } from "@/types/database";
type DatabaseScope = McpConnectionPolicy["databaseScope"];
const { t } = useI18n();
const PAGE_SIZE_OPTIONS = [6, 10, 20] as const;
const props = withDefaults(
defineProps<{
connections: readonly ConnectionConfig[];
allowedConnectionIds: readonly string[] | null;
connectionPolicies: readonly McpConnectionPolicy[];
disabled?: boolean;
busy?: boolean;
}>(),
{ disabled: false, busy: false },
);
const emit = defineEmits<{
"update:connectionPolicies": [value: McpConnectionPolicy[]];
}>();
const selectedConnectionId = ref("");
const databasesByConnection = ref<Record<string, string[]>>({});
const loadingConnectionId = ref("");
const loadError = ref("");
const search = ref("");
const manualDatabase = ref("");
const pageSize = ref<number>(PAGE_SIZE_OPTIONS[0]);
const connectionPage = ref(1);
const databasePage = ref(1);
const scopedConnections = computed(() => (props.allowedConnectionIds === null ? [...props.connections] : props.connections.filter((connection) => props.allowedConnectionIds?.includes(connection.id))));
const selectedConnection = computed(() => scopedConnections.value.find((connection) => connection.id === selectedConnectionId.value));
const selectedPolicy = computed(() => props.connectionPolicies.find((policy) => policy.connectionId === selectedConnectionId.value));
const selectedScope = computed<DatabaseScope>(() => selectedPolicy.value?.databaseScope ?? "all");
const selectedDatabases = computed(() => selectedPolicy.value?.allowedDatabases ?? []);
const displayedDatabases = computed(() => {
const query = search.value.trim().toLocaleLowerCase();
return (databasesByConnection.value[selectedConnectionId.value] ?? []).filter((database) => !query || database.toLocaleLowerCase().includes(query));
});
const connectionPageCount = computed(() => Math.max(1, Math.ceil(scopedConnections.value.length / pageSize.value)));
const databasePageCount = computed(() => Math.max(1, Math.ceil(displayedDatabases.value.length / pageSize.value)));
const pagedScopedConnections = computed(() => {
const start = (connectionPage.value - 1) * pageSize.value;
return scopedConnections.value.slice(start, start + pageSize.value);
});
const pagedDatabases = computed(() => {
const start = (databasePage.value - 1) * pageSize.value;
return displayedDatabases.value.slice(start, start + pageSize.value);
});
watch(
scopedConnections,
(connections) => {
if (!connections.some((connection) => connection.id === selectedConnectionId.value)) {
selectedConnectionId.value = connections[0]?.id ?? "";
}
},
{ immediate: true },
);
function policyFor(connectionId: string): McpConnectionPolicy {
return (
props.connectionPolicies.find((policy) => policy.connectionId === connectionId) ?? {
connectionId,
readOnly: false,
allowDangerousSql: false,
executionModeConfigured: false,
databaseScope: "all",
allowedDatabases: [],
}
);
}
function updateSelectedPolicy(patch: Partial<McpConnectionPolicy>) {
if (props.disabled || !selectedConnectionId.value) return;
const next = { ...policyFor(selectedConnectionId.value), ...patch };
const policies = props.connectionPolicies.filter((policy) => policy.connectionId !== selectedConnectionId.value);
emit("update:connectionPolicies", [...policies, next]);
}
function setScope(scope: DatabaseScope) {
updateSelectedPolicy({ databaseScope: scope, allowedDatabases: scope === "selected" ? selectedDatabases.value : [] });
}
function setConnectionPage(page: number) {
connectionPage.value = Math.min(Math.max(1, page), connectionPageCount.value);
}
function setDatabasePage(page: number) {
databasePage.value = Math.min(Math.max(1, page), databasePageCount.value);
}
function selectConnection(connectionId: string) {
selectedConnectionId.value = connectionId;
loadError.value = "";
search.value = "";
databasePage.value = 1;
}
function toggleDatabase(database: string, checked: boolean) {
const databases = checked ? [...new Set([...selectedDatabases.value, database])] : selectedDatabases.value.filter((item) => item !== database);
updateSelectedPolicy({ databaseScope: "selected", allowedDatabases: databases });
}
function addManualDatabase() {
const database = manualDatabase.value.trim();
if (!database) return;
const loaded = databasesByConnection.value[selectedConnectionId.value] ?? [];
databasesByConnection.value = {
...databasesByConnection.value,
[selectedConnectionId.value]: [...new Set([...loaded, database])].sort((left, right) => left.localeCompare(right)),
};
toggleDatabase(database, true);
manualDatabase.value = "";
databasePage.value = 1;
}
async function loadConnectionDatabases() {
const connection = selectedConnection.value;
if (!connection || loadingConnectionId.value) return;
loadingConnectionId.value = connection.id;
loadError.value = "";
try {
const databases = connection.db_type === "mongodb" ? await mongoListDatabases(connection.id) : connection.db_type === "redis" ? (await redisListDatabases(connection.id)).map((database) => String(database.db)) : (await listDatabases(connection.id)).map((database) => database.name);
databasesByConnection.value = {
...databasesByConnection.value,
[connection.id]: [...new Set(databases.map((database) => database.trim()).filter(Boolean))].sort((left, right) => left.localeCompare(right)),
};
} catch (error: unknown) {
loadError.value = error instanceof Error ? error.message : String(error);
} finally {
loadingConnectionId.value = "";
}
}
function scopeSummary(connection: ConnectionConfig): string {
const policy = props.connectionPolicies.find((item) => item.connectionId === connection.id);
if (!policy || policy.databaseScope === "all") return t("settings.mcpDatabaseScopeSummaryAll");
if (policy.databaseScope === "none") return t("settings.mcpDatabaseScopeSummaryNone");
return t("settings.mcpDatabaseScopeSummarySelected", { count: policy.allowedDatabases.length });
}
watch(search, () => {
databasePage.value = 1;
});
watch(pageSize, () => {
connectionPage.value = 1;
databasePage.value = 1;
});
watch(connectionPageCount, () => setConnectionPage(connectionPage.value));
watch(databasePageCount, () => setDatabasePage(databasePage.value));
</script>
<template>
<section class="space-y-3">
<div>
<p class="text-sm font-medium">{{ t("settings.mcpDatabaseScopeTitle") }}</p>
<p class="text-xs text-muted-foreground">{{ t("settings.mcpDatabaseScopeDescription") }}</p>
</div>
<div v-if="!scopedConnections.length" class="rounded border border-dashed bg-background px-3 py-4 text-center text-xs text-muted-foreground">{{ t("settings.mcpDatabaseScopeEmptyHint") }}</div>
<div v-else class="grid min-h-72 overflow-hidden rounded-md border bg-background md:grid-cols-[minmax(14rem,0.42fr)_minmax(0,1fr)]">
<div class="border-b p-2 md:border-b-0 md:border-r">
<div class="flex items-center justify-between gap-2 px-2 pb-2">
<p class="text-xs font-medium text-muted-foreground">{{ t("settings.mcpDatabaseScopeAllowedConnections") }}</p>
<label class="flex h-7 items-center gap-1 rounded border bg-background px-1.5 text-[11px] text-muted-foreground">
{{ t("settings.mcpPerPage") }}
<select v-model.number="pageSize" class="bg-transparent text-[11px] text-foreground outline-none" :disabled="disabled">
<option v-for="size in PAGE_SIZE_OPTIONS" :key="size" :value="size">{{ size }}</option>
</select>
</label>
</div>
<div class="space-y-1">
<button v-for="connection in pagedScopedConnections" :key="connection.id" type="button" class="w-full rounded-md px-2 py-2 text-left transition-colors hover:bg-muted" :class="selectedConnectionId === connection.id ? 'bg-muted' : ''" @click="selectConnection(connection.id)">
<div class="flex items-center justify-between gap-2">
<span class="min-w-0 truncate text-sm font-medium">{{ connection.name }}</span>
<Badge variant="outline" class="shrink-0 text-[10px] font-normal">{{ scopeSummary(connection) }}</Badge>
</div>
<p class="mt-0.5 truncate font-mono text-[10px] text-muted-foreground">{{ connection.db_type }} · {{ connection.host || connection.database || connection.id }}</p>
</button>
</div>
<div v-if="connectionPageCount > 1" class="mt-2 flex items-center justify-center gap-2 border-t pt-2 text-xs text-muted-foreground">
<Button type="button" size="icon-sm" variant="ghost" :disabled="connectionPage === 1" :title="t('settings.mcpPreviousPage')" :aria-label="t('settings.mcpPreviousPage')" @click="setConnectionPage(connectionPage - 1)"><ChevronLeft /></Button>
<span class="min-w-12 text-center tabular-nums">{{ connectionPage }} / {{ connectionPageCount }}</span>
<Button type="button" size="icon-sm" variant="ghost" :disabled="connectionPage === connectionPageCount" :title="t('settings.mcpNextPage')" :aria-label="t('settings.mcpNextPage')" @click="setConnectionPage(connectionPage + 1)"><ChevronRight /></Button>
</div>
</div>
<div v-if="selectedConnection" class="space-y-3 p-3">
<div class="flex flex-wrap items-start justify-between gap-2">
<div>
<p class="text-sm font-medium">{{ selectedConnection.name }}</p>
<p class="text-xs text-muted-foreground">{{ t("settings.mcpDatabaseScopePermissionNote") }}</p>
</div>
<Button type="button" variant="outline" size="sm" :disabled="disabled || Boolean(loadingConnectionId)" @click="loadConnectionDatabases">
<Loader2 v-if="loadingConnectionId === selectedConnection.id" class="mr-1.5 h-3.5 w-3.5 animate-spin" />
<RefreshCw v-else class="mr-1.5 h-3.5 w-3.5" />
{{ t("settings.mcpDatabaseLoadButton") }}
</Button>
</div>
<div class="grid gap-2 sm:grid-cols-3" role="radiogroup" :aria-label="t('settings.mcpDatabaseScopeAriaLabel')">
<Button type="button" variant="outline" class="h-auto justify-start px-3 py-2 text-left" :class="selectedScope === 'all' ? 'dbx-choice-selected' : ''" :disabled="disabled || busy" @click="setScope('all')">
<span
><span class="block text-sm">{{ t("settings.mcpDatabaseScopeAllTitle") }}</span
><span class="block text-[11px] font-normal text-muted-foreground">{{ t("settings.mcpDatabaseScopeAllDescription") }}</span></span
>
</Button>
<Button type="button" variant="outline" class="h-auto justify-start px-3 py-2 text-left" :class="selectedScope === 'selected' ? 'dbx-choice-selected' : ''" :disabled="disabled || busy" @click="setScope('selected')">
<span
><span class="block text-sm">{{ t("settings.mcpDatabaseScopeSelectedTitle") }}</span
><span class="block text-[11px] font-normal text-muted-foreground">{{ t("settings.mcpDatabaseScopeSelectedDescription") }}</span></span
>
</Button>
<Button type="button" variant="outline" class="h-auto justify-start px-3 py-2 text-left" :class="selectedScope === 'none' ? 'dbx-choice-selected' : ''" :disabled="disabled || busy" @click="setScope('none')">
<span
><span class="block text-sm">{{ t("settings.mcpDatabaseScopeNoneTitle") }}</span
><span class="block text-[11px] font-normal text-muted-foreground">{{ t("settings.mcpDatabaseScopeNoneDescription") }}</span></span
>
</Button>
</div>
<template v-if="selectedScope === 'selected'">
<p class="text-xs text-muted-foreground">{{ t("settings.mcpDatabaseScopeSelectedSummary", { count: selectedDatabases.length }) }}</p>
<div class="flex gap-2">
<div class="relative min-w-0 flex-1"><Search class="absolute left-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground" /><Input v-model="search" class="h-8 pl-8 text-xs" :placeholder="t('settings.mcpDatabaseSearchPlaceholder')" /></div>
<Input v-model="manualDatabase" class="h-8 w-40 text-xs" :placeholder="t('settings.mcpDatabaseManualPlaceholder')" @keydown.enter.prevent="addManualDatabase" />
<Button type="button" variant="outline" size="sm" :disabled="disabled || !manualDatabase.trim()" @click="addManualDatabase"><Plus class="h-3.5 w-3.5" /></Button>
</div>
<p v-if="loadError" class="rounded border border-destructive/30 bg-destructive/5 px-2 py-1.5 text-xs text-destructive">{{ t("settings.mcpDatabaseLoadFailed", { error: loadError }) }}</p>
<div v-if="displayedDatabases.length" class="rounded border">
<label v-for="database in pagedDatabases" :key="database" class="flex cursor-pointer items-center gap-2 border-b px-3 py-2 text-xs last:border-b-0 hover:bg-muted/50">
<input type="checkbox" :checked="selectedDatabases.includes(database)" :disabled="disabled || busy" @change="toggleDatabase(database, ($event.target as HTMLInputElement).checked)" />
<span class="min-w-0 flex-1 truncate font-mono">{{ database }}</span>
<Check v-if="selectedDatabases.includes(database)" class="h-3.5 w-3.5 text-green-600" />
</label>
<div v-if="databasePageCount > 1" class="flex items-center justify-center gap-2 border-t px-3 py-2 text-xs text-muted-foreground">
<Button type="button" size="icon-sm" variant="ghost" :disabled="databasePage === 1" :title="t('settings.mcpPreviousPage')" :aria-label="t('settings.mcpPreviousPage')" @click="setDatabasePage(databasePage - 1)"><ChevronLeft /></Button>
<span class="min-w-12 text-center tabular-nums">{{ databasePage }} / {{ databasePageCount }}</span>
<Button type="button" size="icon-sm" variant="ghost" :disabled="databasePage === databasePageCount" :title="t('settings.mcpNextPage')" :aria-label="t('settings.mcpNextPage')" @click="setDatabasePage(databasePage + 1)"><ChevronRight /></Button>
</div>
</div>
<p v-else class="rounded border border-dashed px-3 py-4 text-center text-xs text-muted-foreground">{{ t("settings.mcpDatabaseEmptyHint") }}</p>
</template>
</div>
</div>
</section>
</template>
+121
View File
@@ -6971,6 +6971,127 @@ export default {
mcpNpmBoundary: "DBX checks MCP status; installation, upgrades, and uninstallation run through the detected npm or pnpm runtime.",
mcpRefresh: "Check again",
mcpGuide: "MCP guide",
mcpAuthTitle: "MCP authorization",
mcpAuthDescription: "Complete the authorization in order: choose connections first, then limit databases and connection exceptions, and finally set the available tools and the global execution level.",
mcpAuthStepsLabel: "MCP authorization steps",
mcpAuthStepConnectionsTitle: "Connection scope",
mcpAuthStepConnectionsDescription: "Decides which connections MCP can see",
mcpAuthStepDatabasesTitle: "Database scope",
mcpAuthStepDatabasesDescription: "Limit the databases each connection can access",
mcpAuthStepOverridesTitle: "Connection exceptions",
mcpAuthStepOverridesDescription: "Tighten execution permissions per connection",
mcpAuthStepCapabilitiesTitle: "Capabilities and execution",
mcpAuthStepCapabilitiesDescription: "Choose MCP tools and the global operation level",
mcpAuthStepRegionLabel: "Step {step}: {title}",
mcpAuthPreviousStep: "Previous",
mcpAuthNextStep: "Next",
mcpAuthStepProgress: "Step {current} of {total}",
mcpPerPage: "Per page",
mcpPreviousPage: "Previous page",
mcpNextPage: "Next page",
mcpDatabaseScopeTitle: "Connections and database scope",
mcpDatabaseScopeDescription: "Select a connection exposed to MCP on the left, then limit the databases that connection can access. Database names are matched exactly.",
mcpDatabaseScopeEmptyHint: "Allow at least one connection in 'Connection scope' first.",
mcpDatabaseScopeAllowedConnections: "Allowed connections",
mcpDatabaseScopeSummaryAll: "All databases",
mcpDatabaseScopeSummaryNone: "No database access",
mcpDatabaseScopeSummarySelected: "{count} selected",
mcpDatabaseScopePermissionNote: "The selected scope does not change the connection's existing database permissions in DBX.",
mcpDatabaseLoadButton: "Load from connection",
mcpDatabaseScopeAriaLabel: "Database access scope",
mcpDatabaseScopeAllTitle: "All databases",
mcpDatabaseScopeAllDescription: "Keep the current access scope",
mcpDatabaseScopeSelectedTitle: "Selected databases only",
mcpDatabaseScopeSelectedDescription: "Expose only the checked items",
mcpDatabaseScopeNoneTitle: "No access",
mcpDatabaseScopeNoneDescription: "Keep the rules but deny every database",
mcpDatabaseScopeSelectedSummary: "{count} databases allowed. Unchecked databases and cross-database requests are rejected by the MCP server.",
mcpDatabaseSearchPlaceholder: "Filter loaded databases",
mcpDatabaseManualPlaceholder: "Add a database name manually",
mcpDatabaseLoadFailed: "Failed to load databases: {error}. You can add the exact database names manually.",
mcpDatabaseEmptyHint: "Click 'Load from connection' to fetch the database list, or add allowed databases manually.",
mcpTransportLocalStdio: "Local stdio",
mcpTransportHttpService: "HTTP service",
mcpHttpWebServiceTitle: "Web Streamable HTTP service",
mcpHttpWebServiceDescription: "Web/Docker reuses the current web listening port. Enable it with deployment environment variables; the settings page never stores the token.",
mcpHttpStatusLabelEnabled: "Enabled",
mcpHttpStatusLabelDisabled: "Not enabled",
mcpHttpNotConfigured: "Not configured",
mcpHttpWebTokenSourceAndHosts: "Token source: {tokenSource}; allowed hosts: {hosts}",
mcpHttpWebAllowedOrigins: "Allowed origins: {origins}",
mcpHttpReloadStatus: "Reload status",
mcpHttpServiceTitle: "Streamable HTTP service",
mcpHttpStatusLabelRunning: "Running",
mcpHttpStatusLabelPending: "Pending configuration",
mcpHttpServiceDescription: "Off by default. When enabled, DBX Desktop hosts the service; the configuration above is applied to the actual listening service only after saving.",
mcpHttpListenerTitle: "Listener configuration",
mcpHttpListenerHintPrefix: "We recommend keeping ",
mcpHttpListenerHintSuffix: " so only MCP clients on the same computer can connect.",
mcpHttpHostLabel: "Listen address",
mcpHttpPortLabel: "Port",
mcpHttpPathLabel: "MCP path",
mcpHttpRemoteTitle: "Remote access",
mcpHttpRemoteHintPrefix: "Only needed when the listen address is not ",
mcpHttpRemoteHintMiddle: " or ",
mcpHttpRemoteHintSuffix: ". Once enabled, the Host and Origin allowlists are required.",
mcpHttpAllowedHostsLabel: "Allowed hosts",
mcpHttpHostsHint: "One per line, or separated with commas.",
mcpHttpAllowedOriginsLabel: "Allowed origins",
mcpHttpOriginsHintPrefix: "Must include the protocol, for example ",
mcpHttpOriginsHintSuffix: ".",
mcpHttpRemoteDisabledHint: "While remote access is off, the allowlists are not needed.",
mcpHttpUnsavedChangesHint: "The configuration above is not saved yet; the running service will not change immediately.",
mcpHttpSaveHint: "Saving starts or restarts the service and updates the connection details below.",
mcpHttpReload: "Reload",
mcpHttpSaveAndStart: "Save and start service",
mcpHttpDisabledHint: "Enable the service to configure the listen address, remote access allowlists, and client connection details.",
mcpHttpSaveAndStop: "Save and stop service",
mcpHttpConnectionInfoTitle: "Active client connection details",
mcpHttpServiceNotRunning: "The service is not running",
mcpHttpRunningConfigBadge: "Running config",
mcpHttpLastStatusBadge: "Last status",
mcpHttpRunningDraftDiffersHint: "Below are the connection details of the currently running service; they may differ from the unsaved draft above.",
mcpHttpEndpointLabel: "Service address",
mcpHttpRotateToken: "Rotate token",
mcpHttpRotateTokenConfirm: "After rotating the token, MCP clients using the old token are disconnected immediately. Continue?",
mcpHttpTokenHintPrefix: "Use this value as the HTTP ",
mcpHttpTokenHintSuffix: " credential in the client.",
mcpHttpClientConfigTitle: "Generic HTTP client configuration",
mcpHttpClientConfigHint: "Client field names differ; keep the URL and Authorization values.",
mcpHttpLogsTitle: "Service diagnostic logs",
mcpHttpValidationHostRequired: "Enter a listen address.",
mcpHttpValidationPortRange: "The port must be an integer between 1 and 65535.",
mcpHttpValidationPathPrefix: "The MCP path must start with /.",
mcpHttpValidationRemoteRequired: "Enable 'Remote access' to listen on a non-loopback address.",
mcpHttpValidationHostsRequired: "Remote access requires at least one allowed host.",
mcpHttpValidationOriginsRequired: "Remote access requires at least one allowed origin.",
mcpHttpErrorRemoteHostsAndOrigins: "Remote access requires both allowed hosts and allowed origins.",
mcpHttpErrorRemoteBinding: "Remote listening requires remote access plus Host and Origin allowlists.",
mcpHttpErrorPortInUse: "The port is already used by another application. DBX restarts its own MCP service when saving; close the other application using the port, or choose a different port.",
mcpConnectionPolicyTitle: "Per-connection permission ceiling",
mcpConnectionPolicyDescription: "Tighten permissions for each exposed connection; they cannot exceed the global MCP permissions.",
mcpConnectionPolicyEmptyHint: "Allow at least one connection in step 1 before setting per-connection execution ceilings.",
mcpConnectionPolicyInherit: "Inherit global",
mcpConnectionPolicyReadOnly: "Read only",
mcpConnectionPolicySafeWrite: "Allow safe writes",
mcpConnectionPolicyHighRiskWrite: "Allow high-risk operations",
mcpToolPermissionsTitle: "MCP tool permissions",
mcpToolPermissionsDescription: "Expose only the MCP tools you need. Once deselected, clients are rejected by the server even if they cached the tool definitions.",
mcpToolListConnections: "List connections",
mcpToolListDatabases: "List databases",
mcpToolListTables: "List tables",
mcpToolDescribeTable: "Describe table",
mcpToolGetSchemaContext: "Schema context",
mcpToolExecuteQuery: "Execute SQL / Mongo commands",
mcpToolOpenSession: "Open query session",
mcpToolCloseSession: "Close query session",
mcpToolExecuteRedisCommand: "Execute Redis command",
mcpToolSendMessage: "Send message queue message",
mcpToolAddConnection: "Add connection",
mcpToolDuplicateConnection: "Duplicate connection",
mcpToolRemoveConnection: "Remove connection",
mcpToolOpenTable: "Open table in DBX",
mcpToolExecuteAndShow: "Execute and show in DBX",
supportInfoTitle: "Support information",
supportInfoDescription: "Copy these environment details when filing issues or asking for help.",
supportInfoCopy: "Copy support info",
+121
View File
@@ -6593,6 +6593,127 @@ export default withEnglishFallback({
mcpNpmBoundary: "DBX solo comprueba y explica el estado de MCP; la instalación y actualización siguen usando npm.",
mcpRefresh: "Comprobar de nuevo",
mcpGuide: "Guía MCP",
mcpAuthTitle: "Autorización de MCP",
mcpAuthDescription: "Completa la autorización en orden: elige primero las conexiones, luego limita las bases de datos y las excepciones por conexión y, por último, define las herramientas disponibles y el nivel de ejecución global.",
mcpAuthStepsLabel: "Pasos de autorización de MCP",
mcpAuthStepConnectionsTitle: "Alcance de conexiones",
mcpAuthStepConnectionsDescription: "Define qué conexiones puede ver MCP",
mcpAuthStepDatabasesTitle: "Alcance de bases de datos",
mcpAuthStepDatabasesDescription: "Limita las bases de datos accesibles por conexión",
mcpAuthStepOverridesTitle: "Excepciones de conexión",
mcpAuthStepOverridesDescription: "Restringe aún más los permisos de ejecución por conexión",
mcpAuthStepCapabilitiesTitle: "Capacidades y ejecución",
mcpAuthStepCapabilitiesDescription: "Elige las herramientas MCP y el nivel operativo global",
mcpAuthStepRegionLabel: "Paso {step}: {title}",
mcpAuthPreviousStep: "Anterior",
mcpAuthNextStep: "Siguiente",
mcpAuthStepProgress: "Paso {current} de {total}",
mcpPerPage: "Por página",
mcpPreviousPage: "Página anterior",
mcpNextPage: "Página siguiente",
mcpDatabaseScopeTitle: "Conexiones y alcance de bases de datos",
mcpDatabaseScopeDescription: "Selecciona a la izquierda una conexión expuesta a MCP y limita las bases de datos a las que puede acceder. Los nombres de bases de datos se comparan de forma exacta.",
mcpDatabaseScopeEmptyHint: "Permite primero al menos una conexión en 'Alcance de conexiones'.",
mcpDatabaseScopeAllowedConnections: "Conexiones permitidas",
mcpDatabaseScopeSummaryAll: "Todas las bases de datos",
mcpDatabaseScopeSummaryNone: "Sin acceso a bases de datos",
mcpDatabaseScopeSummarySelected: "{count} seleccionadas",
mcpDatabaseScopePermissionNote: "El alcance seleccionado no cambia los permisos de bases de datos que la conexión ya tiene en DBX.",
mcpDatabaseLoadButton: "Cargar desde la conexión",
mcpDatabaseScopeAriaLabel: "Alcance de acceso a bases de datos",
mcpDatabaseScopeAllTitle: "Todas las bases de datos",
mcpDatabaseScopeAllDescription: "Mantiene el alcance de acceso actual",
mcpDatabaseScopeSelectedTitle: "Solo bases de datos especificadas",
mcpDatabaseScopeSelectedDescription: "Expone solo las opciones marcadas",
mcpDatabaseScopeNoneTitle: "Sin acceso",
mcpDatabaseScopeNoneDescription: "Conserva las reglas pero deniega todas las bases de datos",
mcpDatabaseScopeSelectedSummary: "{count} bases de datos permitidas. Las bases de datos no marcadas y las solicitudes entre bases de datos son rechazadas por el servidor MCP.",
mcpDatabaseSearchPlaceholder: "Filtrar bases de datos cargadas",
mcpDatabaseManualPlaceholder: "Añadir nombre de base de datos manualmente",
mcpDatabaseLoadFailed: "Error al cargar bases de datos: {error}. Puedes añadir manualmente los nombres exactos.",
mcpDatabaseEmptyHint: "Haz clic en 'Cargar desde la conexión' para obtener la lista de bases de datos o añade manualmente las permitidas.",
mcpTransportLocalStdio: "stdio local",
mcpTransportHttpService: "Servicio HTTP",
mcpHttpWebServiceTitle: "Servicio Web Streamable HTTP",
mcpHttpWebServiceDescription: "Web/Docker reutiliza el puerto de escucha web actual. Se activa con variables de entorno de despliegue; la página de ajustes nunca guarda el Token.",
mcpHttpStatusLabelEnabled: "Activado",
mcpHttpStatusLabelDisabled: "Desactivado",
mcpHttpNotConfigured: "Sin configurar",
mcpHttpWebTokenSourceAndHosts: "Origen del Token: {tokenSource}; Hosts permitidos: {hosts}",
mcpHttpWebAllowedOrigins: "Origins permitidos: {origins}",
mcpHttpReloadStatus: "Recargar estado",
mcpHttpServiceTitle: "Servicio Streamable HTTP",
mcpHttpStatusLabelRunning: "En ejecución",
mcpHttpStatusLabelPending: "Pendiente de aplicar",
mcpHttpServiceDescription: "Desactivado por defecto. Al activarlo, DBX Desktop aloja el servicio; la configuración de arriba se aplica al servicio de escucha real solo después de guardar.",
mcpHttpListenerTitle: "Configuración de escucha",
mcpHttpListenerHintPrefix: "Se recomienda mantener ",
mcpHttpListenerHintSuffix: " para que solo los clientes MCP del mismo equipo puedan conectarse.",
mcpHttpHostLabel: "Dirección de escucha",
mcpHttpPortLabel: "Puerto",
mcpHttpPathLabel: "Ruta MCP",
mcpHttpRemoteTitle: "Acceso remoto",
mcpHttpRemoteHintPrefix: "Solo es necesario cuando la dirección de escucha no es ",
mcpHttpRemoteHintMiddle: " ni ",
mcpHttpRemoteHintSuffix: ". Al activarlo, debes rellenar las listas blancas de Host y Origin.",
mcpHttpAllowedHostsLabel: "Hosts permitidos",
mcpHttpHostsHint: "Uno por línea, o separados por comas.",
mcpHttpAllowedOriginsLabel: "Origins permitidos",
mcpHttpOriginsHintPrefix: "Debe incluir el protocolo, por ejemplo ",
mcpHttpOriginsHintSuffix: ".",
mcpHttpRemoteDisabledHint: "Con el acceso remoto desactivado, las listas blancas no son necesarias.",
mcpHttpUnsavedChangesHint: "La configuración de arriba aún no se ha guardado; el servicio en ejecución no cambiará de inmediato.",
mcpHttpSaveHint: "Al guardar se inicia o reinicia el servicio y se actualiza la información de conexión de abajo.",
mcpHttpReload: "Recargar",
mcpHttpSaveAndStart: "Guardar e iniciar servicio",
mcpHttpDisabledHint: "Activa el servicio para configurar la dirección de escucha, las listas blancas de acceso remoto y la información de conexión del cliente.",
mcpHttpSaveAndStop: "Guardar y detener servicio",
mcpHttpConnectionInfoTitle: "Información de conexión del cliente aplicada",
mcpHttpServiceNotRunning: "El servicio no está en ejecución",
mcpHttpRunningConfigBadge: "Configuración en ejecución",
mcpHttpLastStatusBadge: "Último estado",
mcpHttpRunningDraftDiffersHint: "Abajo se muestra la información de conexión del servicio en ejecución; puede diferir del borrador sin guardar de arriba.",
mcpHttpEndpointLabel: "Dirección del servicio",
mcpHttpRotateToken: "Rotar Token",
mcpHttpRotateTokenConfirm: "Al rotar el Token, los clientes MCP que usen el Token anterior se desconectan de inmediato. ¿Continuar?",
mcpHttpTokenHintPrefix: "Usa este valor como credencial HTTP ",
mcpHttpTokenHintSuffix: " del cliente.",
mcpHttpClientConfigTitle: "Configuración genérica de cliente HTTP",
mcpHttpClientConfigHint: "Los nombres de campos varían según el cliente; conserva los valores de URL y Authorization.",
mcpHttpLogsTitle: "Registros de diagnóstico del servicio",
mcpHttpValidationHostRequired: "Introduce una dirección de escucha.",
mcpHttpValidationPortRange: "El puerto debe ser un entero entre 1 y 65535.",
mcpHttpValidationPathPrefix: "La ruta MCP debe empezar por /.",
mcpHttpValidationRemoteRequired: "Activa 'Acceso remoto' para escuchar en una dirección no local.",
mcpHttpValidationHostsRequired: "El acceso remoto necesita al menos un Host permitido.",
mcpHttpValidationOriginsRequired: "El acceso remoto necesita al menos un Origin permitido.",
mcpHttpErrorRemoteHostsAndOrigins: "El acceso remoto requiere tanto Hosts permitidos como Origins permitidos.",
mcpHttpErrorRemoteBinding: "La escucha remota requiere el acceso remoto activado y las listas blancas de Host y Origin.",
mcpHttpErrorPortInUse: "El puerto ya está ocupado por otra aplicación. DBX reinicia su propio servicio MCP al guardar; cierra la otra aplicación que usa el puerto o elige otro puerto.",
mcpConnectionPolicyTitle: "Límite de permisos por conexión",
mcpConnectionPolicyDescription: "Restringe aún más los permisos de cada conexión expuesta; no pueden superar los permisos globales de MCP.",
mcpConnectionPolicyEmptyHint: "Permite primero al menos una conexión en el paso 1 para configurar los límites de ejecución por conexión.",
mcpConnectionPolicyInherit: "Heredar global",
mcpConnectionPolicyReadOnly: "Solo lectura",
mcpConnectionPolicySafeWrite: "Permitir escrituras seguras",
mcpConnectionPolicyHighRiskWrite: "Permitir operaciones de alto riesgo",
mcpToolPermissionsTitle: "Permisos de herramientas MCP",
mcpToolPermissionsDescription: "Expón solo las herramientas MCP que necesitas. Al desmarcarlas, el servidor rechaza las solicitudes aunque el cliente haya cacheado las definiciones.",
mcpToolListConnections: "Listar conexiones",
mcpToolListDatabases: "Listar bases de datos",
mcpToolListTables: "Listar tablas",
mcpToolDescribeTable: "Estructura de tabla",
mcpToolGetSchemaContext: "Contexto de Schema",
mcpToolExecuteQuery: "Ejecutar SQL / comandos Mongo",
mcpToolOpenSession: "Abrir sesión de consulta",
mcpToolCloseSession: "Cerrar sesión de consulta",
mcpToolExecuteRedisCommand: "Ejecutar comando Redis",
mcpToolSendMessage: "Enviar mensaje de cola de mensajes",
mcpToolAddConnection: "Añadir conexión",
mcpToolDuplicateConnection: "Duplicar conexión",
mcpToolRemoveConnection: "Eliminar conexión",
mcpToolOpenTable: "Abrir tabla en DBX",
mcpToolExecuteAndShow: "Ejecutar y mostrar en DBX",
supportInfoTitle: "Información de soporte",
supportInfoDescription: "Copia estos datos del entorno al crear issues o pedir ayuda.",
supportInfoCopy: "Copiar información",
+121
View File
@@ -6593,6 +6593,127 @@ export default withEnglishFallback({
mcpNpmBoundary: "DBX verifica e spiega solo lo stato di MCP; l'installazione e gli aggiornamenti vengono comunque eseguiti tramite npm.",
mcpRefresh: "Verifica di nuovo",
mcpGuide: "Guida MCP",
mcpAuthTitle: "Autorizzazione MCP",
mcpAuthDescription: "Completa l'autorizzazione in ordine: scegli prima le connessioni, poi limita i database e le eccezioni per connessione e infine definisci gli strumenti disponibili e il livello di esecuzione globale.",
mcpAuthStepsLabel: "Passaggi di autorizzazione MCP",
mcpAuthStepConnectionsTitle: "Ambito connessioni",
mcpAuthStepConnectionsDescription: "Determina quali connessioni MCP può vedere",
mcpAuthStepDatabasesTitle: "Ambito database",
mcpAuthStepDatabasesDescription: "Limita i database accessibili per ogni connessione",
mcpAuthStepOverridesTitle: "Eccezioni per connessione",
mcpAuthStepOverridesDescription: "Restringe ulteriormente i permessi di esecuzione per connessione",
mcpAuthStepCapabilitiesTitle: "Capacità ed esecuzione",
mcpAuthStepCapabilitiesDescription: "Scegli gli strumenti MCP e il livello operativo globale",
mcpAuthStepRegionLabel: "Passaggio {step}: {title}",
mcpAuthPreviousStep: "Indietro",
mcpAuthNextStep: "Avanti",
mcpAuthStepProgress: "Passaggio {current} di {total}",
mcpPerPage: "Per pagina",
mcpPreviousPage: "Pagina precedente",
mcpNextPage: "Pagina successiva",
mcpDatabaseScopeTitle: "Connessioni e ambito database",
mcpDatabaseScopeDescription: "Seleziona a sinistra una connessione esposta a MCP, poi limita i database che quella connessione può raggiungere. I nomi dei database corrispondono in modo esatto.",
mcpDatabaseScopeEmptyHint: "Consenti prima almeno una connessione in 'Ambito connessioni'.",
mcpDatabaseScopeAllowedConnections: "Connessioni consentite",
mcpDatabaseScopeSummaryAll: "Tutti i database",
mcpDatabaseScopeSummaryNone: "Nessun accesso ai database",
mcpDatabaseScopeSummarySelected: "{count} selezionati",
mcpDatabaseScopePermissionNote: "L'ambito selezionato non modifica le autorizzazioni database già esistenti della connessione in DBX.",
mcpDatabaseLoadButton: "Carica dalla connessione",
mcpDatabaseScopeAriaLabel: "Ambito di accesso ai database",
mcpDatabaseScopeAllTitle: "Tutti i database",
mcpDatabaseScopeAllDescription: "Mantiene l'ambito di accesso attuale",
mcpDatabaseScopeSelectedTitle: "Solo database specificati",
mcpDatabaseScopeSelectedDescription: "Espone solo gli elementi selezionati",
mcpDatabaseScopeNoneTitle: "Nessun accesso",
mcpDatabaseScopeNoneDescription: "Mantiene le regole ma rifiuta tutti i database",
mcpDatabaseScopeSelectedSummary: "{count} database consentiti. I database non selezionati e le richieste tra database vengono rifiutati dal server MCP.",
mcpDatabaseSearchPlaceholder: "Filtra i database caricati",
mcpDatabaseManualPlaceholder: "Aggiungi manualmente un nome di database",
mcpDatabaseLoadFailed: "Caricamento dei database non riuscito: {error}. Puoi aggiungere manualmente i nomi esatti.",
mcpDatabaseEmptyHint: "Fai clic su 'Carica dalla connessione' per ottenere l'elenco dei database oppure aggiungi manualmente quelli consentiti.",
mcpTransportLocalStdio: "stdio locale",
mcpTransportHttpService: "Servizio HTTP",
mcpHttpWebServiceTitle: "Servizio Web Streamable HTTP",
mcpHttpWebServiceDescription: "Web/Docker riusa la porta di ascolto web attuale. Viene abilitato con variabili d'ambiente di distribuzione; la pagina delle impostazioni non salva mai il Token.",
mcpHttpStatusLabelEnabled: "Attivo",
mcpHttpStatusLabelDisabled: "Non attivo",
mcpHttpNotConfigured: "Non configurato",
mcpHttpWebTokenSourceAndHosts: "Origine Token: {tokenSource}; Host consentiti: {hosts}",
mcpHttpWebAllowedOrigins: "Origin consentiti: {origins}",
mcpHttpReloadStatus: "Ricarica stato",
mcpHttpServiceTitle: "Servizio Streamable HTTP",
mcpHttpStatusLabelRunning: "In esecuzione",
mcpHttpStatusLabelPending: "In attesa di applicazione",
mcpHttpServiceDescription: "Disattivato per impostazione predefinita. Se attivato, DBX Desktop ospita il servizio; la configurazione sopra viene applicata al servizio in ascolto solo dopo il salvataggio.",
mcpHttpListenerTitle: "Configurazione di ascolto",
mcpHttpListenerHintPrefix: "Si consiglia di mantenere ",
mcpHttpListenerHintSuffix: " per consentire la connessione solo ai client MCP dello stesso computer.",
mcpHttpHostLabel: "Indirizzo di ascolto",
mcpHttpPortLabel: "Porta",
mcpHttpPathLabel: "Percorso MCP",
mcpHttpRemoteTitle: "Accesso remoto",
mcpHttpRemoteHintPrefix: "Necessario solo quando l'indirizzo di ascolto non è ",
mcpHttpRemoteHintMiddle: " o ",
mcpHttpRemoteHintSuffix: ". Una volta attivato, vanno compilati gli elenchi consentiti di Host e Origin.",
mcpHttpAllowedHostsLabel: "Host consentiti",
mcpHttpHostsHint: "Uno per riga, separabili anche con virgole.",
mcpHttpAllowedOriginsLabel: "Origin consentiti",
mcpHttpOriginsHintPrefix: "Deve includere il protocollo, ad esempio ",
mcpHttpOriginsHintSuffix: ".",
mcpHttpRemoteDisabledHint: "Con l'accesso remoto disattivato, gli elenchi consentiti non sono necessari.",
mcpHttpUnsavedChangesHint: "La configurazione sopra non è ancora salvata; il servizio in esecuzione non cambierà subito.",
mcpHttpSaveHint: "Il salvataggio avvia o riavvia il servizio e aggiorna le informazioni di connessione sotto.",
mcpHttpReload: "Ricarica",
mcpHttpSaveAndStart: "Salva e avvia servizio",
mcpHttpDisabledHint: "Attiva il servizio per configurare indirizzo di ascolto, elenchi di accesso remoto e informazioni di connessione del client.",
mcpHttpSaveAndStop: "Salva e ferma servizio",
mcpHttpConnectionInfoTitle: "Informazioni di connessione del client applicate",
mcpHttpServiceNotRunning: "Il servizio non è in esecuzione",
mcpHttpRunningConfigBadge: "Configurazione in esecuzione",
mcpHttpLastStatusBadge: "Ultimo stato",
mcpHttpRunningDraftDiffersHint: "Qui sotto ci sono le informazioni di connessione del servizio in esecuzione; possono differire dalla bozza non salvata sopra.",
mcpHttpEndpointLabel: "Indirizzo del servizio",
mcpHttpRotateToken: "Ruota Token",
mcpHttpRotateTokenConfirm: "Dopo la rotazione del Token, i client MCP che usano il vecchio Token verranno disconnessi subito. Continuare?",
mcpHttpTokenHintPrefix: "Usa questo valore come credenziale HTTP ",
mcpHttpTokenHintSuffix: " del client.",
mcpHttpClientConfigTitle: "Configurazione generica del client HTTP",
mcpHttpClientConfigHint: "I nomi dei campi variano per client; conserva i valori di URL e Authorization.",
mcpHttpLogsTitle: "Log di diagnostica del servizio",
mcpHttpValidationHostRequired: "Inserisci un indirizzo di ascolto.",
mcpHttpValidationPortRange: "La porta deve essere un intero tra 1 e 65535.",
mcpHttpValidationPathPrefix: "Il percorso MCP deve iniziare con /.",
mcpHttpValidationRemoteRequired: "Attiva 'Accesso remoto' per ascoltare su un indirizzo non locale.",
mcpHttpValidationHostsRequired: "L'accesso remoto richiede almeno un Host consentito.",
mcpHttpValidationOriginsRequired: "L'accesso remoto richiede almeno un Origin consentito.",
mcpHttpErrorRemoteHostsAndOrigins: "L'accesso remoto richiede sia Host sia Origin consentiti.",
mcpHttpErrorRemoteBinding: "L'ascolto remoto richiede l'accesso remoto attivo e gli elenchi consentiti di Host e Origin.",
mcpHttpErrorPortInUse: "La porta è già usata da un'altra applicazione. DBX riavvia il proprio servizio MCP al salvataggio; chiudi l'altra applicazione che usa la porta oppure scegli un'altra porta.",
mcpConnectionPolicyTitle: "Limite di permessi per connessione",
mcpConnectionPolicyDescription: "Restringe ulteriormente i permessi di ogni connessione esposta; non possono superare i permessi MCP globali.",
mcpConnectionPolicyEmptyHint: "Consenti prima almeno una connessione nel passaggio 1 per impostare i limiti di esecuzione per connessione.",
mcpConnectionPolicyInherit: "Eredita globale",
mcpConnectionPolicyReadOnly: "Sola lettura",
mcpConnectionPolicySafeWrite: "Consenti scritture sicure",
mcpConnectionPolicyHighRiskWrite: "Consenti operazioni ad alto rischio",
mcpToolPermissionsTitle: "Permessi degli strumenti MCP",
mcpToolPermissionsDescription: "Esporre solo gli strumenti MCP necessari. Dopo la deselezione, le richieste vengono rifiutate dal server anche se il client ha le definizioni in cache.",
mcpToolListConnections: "Elenca connessioni",
mcpToolListDatabases: "Elenca database",
mcpToolListTables: "Elenca tabelle",
mcpToolDescribeTable: "Struttura tabella",
mcpToolGetSchemaContext: "Contesto Schema",
mcpToolExecuteQuery: "Esegui SQL / comandi Mongo",
mcpToolOpenSession: "Apri sessione di query",
mcpToolCloseSession: "Chiudi sessione di query",
mcpToolExecuteRedisCommand: "Esegui comando Redis",
mcpToolSendMessage: "Invia messaggio in coda",
mcpToolAddConnection: "Aggiungi connessione",
mcpToolDuplicateConnection: "Duplica connessione",
mcpToolRemoveConnection: "Rimuovi connessione",
mcpToolOpenTable: "Apri tabella in DBX",
mcpToolExecuteAndShow: "Esegui e mostra in DBX",
supportInfoTitle: "Informazioni di supporto",
supportInfoDescription: "Copia questi dettagli dell'ambiente quando apri issue o chiedi aiuto.",
supportInfoCopy: "Copia informazioni",
+121
View File
@@ -6600,6 +6600,127 @@ export default withEnglishFallback({
mcpNpmBoundary: "DBXはMCPステータスの確認と説明のみを行います。インストールとアップグレードはnpmを通じて実行されます。",
mcpRefresh: "再確認",
mcpGuide: "MCPガイド",
mcpAuthTitle: "MCP 権限設定",
mcpAuthDescription: "順番に設定を進めてください:まず接続を選択し、次にデータベースと接続ごとの例外を制限し、最後に利用するツールとグローバル実行レベルを確定します。",
mcpAuthStepsLabel: "MCP 権限設定の手順",
mcpAuthStepConnectionsTitle: "接続範囲",
mcpAuthStepConnectionsDescription: "MCP に公開する接続を決定します",
mcpAuthStepDatabasesTitle: "データベース範囲",
mcpAuthStepDatabasesDescription: "接続ごとにアクセス可能なデータベースを制限します",
mcpAuthStepOverridesTitle: "接続の例外",
mcpAuthStepOverridesDescription: "接続ごとに実行権限をさらに制限します",
mcpAuthStepCapabilitiesTitle: "機能と実行",
mcpAuthStepCapabilitiesDescription: "MCP ツールとグローバル操作レベルを選択します",
mcpAuthStepRegionLabel: "ステップ {step}:{title}",
mcpAuthPreviousStep: "前へ",
mcpAuthNextStep: "次へ",
mcpAuthStepProgress: "ステップ {current} / {total}",
mcpPerPage: "表示件数",
mcpPreviousPage: "前のページ",
mcpNextPage: "次のページ",
mcpDatabaseScopeTitle: "接続とデータベース範囲",
mcpDatabaseScopeDescription: "左側で MCP に公開する接続を選択してから、その接続がアクセスできるデータベースを制限します。データベース名は完全一致で判定されます。",
mcpDatabaseScopeEmptyHint: "先に「接続範囲」で少なくとも 1 つの接続を許可してください。",
mcpDatabaseScopeAllowedConnections: "許可済みの接続",
mcpDatabaseScopeSummaryAll: "すべてのデータベース",
mcpDatabaseScopeSummaryNone: "データベースアクセスなし",
mcpDatabaseScopeSummarySelected: "{count} 件を選択済み",
mcpDatabaseScopePermissionNote: "範囲を選択しても、DBX 内のこの接続の既存のデータベース権限は変更されません。",
mcpDatabaseLoadButton: "接続から読み込む",
mcpDatabaseScopeAriaLabel: "データベースアクセス範囲",
mcpDatabaseScopeAllTitle: "すべてのデータベース",
mcpDatabaseScopeAllDescription: "現在のアクセス範囲を維持",
mcpDatabaseScopeSelectedTitle: "指定したデータベースのみ",
mcpDatabaseScopeSelectedDescription: "チェックした項目のみ公開",
mcpDatabaseScopeNoneTitle: "アクセス不許可",
mcpDatabaseScopeNoneDescription: "ルールは保持しすべてのデータベースを拒否",
mcpDatabaseScopeSelectedSummary: "{count} 個のデータベースを許可済みです。未チェックのデータベースとクロスデータベースのリクエストは MCP サーバーにより拒否されます。",
mcpDatabaseSearchPlaceholder: "読み込み済みデータベースを絞り込み",
mcpDatabaseManualPlaceholder: "データベース名を手動追加",
mcpDatabaseLoadFailed: "データベースの読み込みに失敗しました:{error}。正確なデータベース名を手動で追加できます。",
mcpDatabaseEmptyHint: "「接続から読み込む」をクリックしてデータベース一覧を取得するか、許可するデータベースを手動で追加してください。",
mcpTransportLocalStdio: "ローカル stdio",
mcpTransportHttpService: "HTTP サービス",
mcpHttpWebServiceTitle: "Web Streamable HTTP サービス",
mcpHttpWebServiceDescription: "Web/Docker は現在の Web リスニングポートを再利用します。デプロイの環境変数で有効化され、設定ページに Token は保存されません。",
mcpHttpStatusLabelEnabled: "有効",
mcpHttpStatusLabelDisabled: "無効",
mcpHttpNotConfigured: "未設定",
mcpHttpWebTokenSourceAndHosts: "Token ソース:{tokenSource};許可 Host:{hosts}",
mcpHttpWebAllowedOrigins: "許可 Origin:{origins}",
mcpHttpReloadStatus: "状態を再読み込み",
mcpHttpServiceTitle: "Streamable HTTP サービス",
mcpHttpStatusLabelRunning: "実行中",
mcpHttpStatusLabelPending: "設定待ち",
mcpHttpServiceDescription: "既定では無効です。有効にすると DBX Desktop がサービスをホストし、保存後には上記の設定が実際のリスニングサービスに適用されます。",
mcpHttpListenerTitle: "リスニング設定",
mcpHttpListenerHintPrefix: "「",
mcpHttpListenerHintSuffix: "」を維持することをおすすめします。同じコンピューター上の MCP クライアントのみ接続できます。",
mcpHttpHostLabel: "リスニングアドレス",
mcpHttpPortLabel: "ポート",
mcpHttpPathLabel: "MCP パス",
mcpHttpRemoteTitle: "リモートアクセス",
mcpHttpRemoteHintPrefix: "リスニングアドレスが ",
mcpHttpRemoteHintMiddle: " または ",
mcpHttpRemoteHintSuffix: " 以外の場合にのみ必要です。有効にする場合は Host と Origin のホワイトリストを入力してください。",
mcpHttpAllowedHostsLabel: "許可する Host",
mcpHttpHostsHint: "1 行に 1 件、カンマ区切りも利用できます。",
mcpHttpAllowedOriginsLabel: "許可する Origin",
mcpHttpOriginsHintPrefix: "プロトコルを含める必要があります(例: ",
mcpHttpOriginsHintSuffix: ")。",
mcpHttpRemoteDisabledHint: "リモートアクセスが無効の場合、ホワイトリストの設定は不要です。",
mcpHttpUnsavedChangesHint: "上記の設定は未保存のため、実行中のサービスはすぐには変更されません。",
mcpHttpSaveHint: "保存するとサービスを開始または再起動し、下記の接続情報を更新します。",
mcpHttpReload: "再読み込み",
mcpHttpSaveAndStart: "保存してサービスを開始",
mcpHttpDisabledHint: "サービスを有効にすると、リスニングアドレス、リモートアクセスのホワイトリスト、クライアント接続情報を設定できます。",
mcpHttpSaveAndStop: "保存してサービスを停止",
mcpHttpConnectionInfoTitle: "適用済みのクライアント接続情報",
mcpHttpServiceNotRunning: "サービスは実行されていません",
mcpHttpRunningConfigBadge: "実行中の設定",
mcpHttpLastStatusBadge: "最後の状態",
mcpHttpRunningDraftDiffersHint: "下記は現在実行中のサービスの接続情報です。上記の未保存の下書きと異なる場合があります。",
mcpHttpEndpointLabel: "サービスアドレス",
mcpHttpRotateToken: "Token をローテーション",
mcpHttpRotateTokenConfirm: "Token をローテーションすると、古い Token を使う MCP クライアントは直ちに切断されます。続行しますか?",
mcpHttpTokenHintPrefix: "この値をクライアントの HTTP ",
mcpHttpTokenHintSuffix: " 認証情報として使用してください。",
mcpHttpClientConfigTitle: "汎用 HTTP クライアント設定",
mcpHttpClientConfigHint: "クライアントによりフィールド名が異なります。URL と Authorization の値は保持してください。",
mcpHttpLogsTitle: "サービス診断ログ",
mcpHttpValidationHostRequired: "リスニングアドレスを入力してください。",
mcpHttpValidationPortRange: "ポートは 1 ~ 65535 の整数である必要があります。",
mcpHttpValidationPathPrefix: "MCP パスは / で始める必要があります。",
mcpHttpValidationRemoteRequired: "ループバック以外のアドレスでリスニングする場合は「リモートアクセス」を有効にしてください。",
mcpHttpValidationHostsRequired: "リモートアクセスには少なくとも 1 つの許可 Host が必要です。",
mcpHttpValidationOriginsRequired: "リモートアクセスには少なくとも 1 つの許可 Origin が必要です。",
mcpHttpErrorRemoteHostsAndOrigins: "リモートアクセスには許可 Host と許可 Origin の両方が必要です。",
mcpHttpErrorRemoteBinding: "リモートリスニングにはリモートアクセスの有効化と Host・Origin のホワイトリストが必要です。",
mcpHttpErrorPortInUse: "このポートは他のアプリケーションで使用されています。DBX は保存時に自身の MCP サービスを再起動します。ポートを使用している他のアプリケーションを終了するか、別のポートを使用してください。",
mcpConnectionPolicyTitle: "接続ごとの権限上限",
mcpConnectionPolicyDescription: "公開した各接続に対して権限をさらに制限できます。グローバルの MCP 権限を超えることはできません。",
mcpConnectionPolicyEmptyHint: "接続ごとの実行上限を設定するには、まずステップ 1 で少なくとも 1 つの接続を許可してください。",
mcpConnectionPolicyInherit: "グローバルを継承",
mcpConnectionPolicyReadOnly: "読み取り専用のみ",
mcpConnectionPolicySafeWrite: "安全な書き込みを許可",
mcpConnectionPolicyHighRiskWrite: "高リスク操作を許可",
mcpToolPermissionsTitle: "MCP ツール権限",
mcpToolPermissionsDescription: "必要な MCP ツールのみ公開します。選択を解除すると、ツール定義をキャッシュしたクライアントもサーバー側で拒否されます。",
mcpToolListConnections: "接続を一覧",
mcpToolListDatabases: "データベースを一覧",
mcpToolListTables: "テーブルを一覧",
mcpToolDescribeTable: "テーブル構造",
mcpToolGetSchemaContext: "Schema コンテキスト",
mcpToolExecuteQuery: "SQL / Mongo コマンドを実行",
mcpToolOpenSession: "クエリセッションを開く",
mcpToolCloseSession: "クエリセッションを閉じる",
mcpToolExecuteRedisCommand: "Redis コマンドを実行",
mcpToolSendMessage: "メッセージキューのメッセージを送信",
mcpToolAddConnection: "接続を追加",
mcpToolDuplicateConnection: "接続を複製",
mcpToolRemoveConnection: "接続を削除",
mcpToolOpenTable: "DBX でテーブルを開く",
mcpToolExecuteAndShow: "DBX で実行して表示",
updateDownloadSource: "アップデートのダウンロード元",
updateDownloadSourceDescription: "アプリ内アップデートのインストーラーのダウンロード元を選択します。公式ソース推奨。中国本土ではCNBの方が高速な場合があります。",
updateDownloadSourceOfficial: "公式ソース(推奨)",
+121
View File
@@ -6340,6 +6340,127 @@ export default withEnglishFallback({
mcpNpmBoundary: "DBX는 MCP 상태를 확인하고 설명만 합니다. 설치와 업그레이드는 여전히 npm을 통해 실행됩니다.",
mcpRefresh: "다시 확인",
mcpGuide: "MCP 가이드",
mcpAuthTitle: "MCP 권한 설정",
mcpAuthDescription: "순서대로 권한을 설정하세요: 먼저 연결을 선택하고, 데이터베이스와 연결 예외를 제한한 뒤, 마지막으로 사용할 도구와 전역 실행 수준을 확정합니다.",
mcpAuthStepsLabel: "MCP 권한 설정 단계",
mcpAuthStepConnectionsTitle: "연결 범위",
mcpAuthStepConnectionsDescription: "MCP에 노출할 연결을 결정합니다",
mcpAuthStepDatabasesTitle: "데이터베이스 범위",
mcpAuthStepDatabasesDescription: "연결별로 접근 가능한 데이터베이스를 제한합니다",
mcpAuthStepOverridesTitle: "연결 예외",
mcpAuthStepOverridesDescription: "연결별로 실행 권한을 더 좁힙니다",
mcpAuthStepCapabilitiesTitle: "기능 및 실행",
mcpAuthStepCapabilitiesDescription: "MCP 도구와 전역 작업 수준을 선택합니다",
mcpAuthStepRegionLabel: "단계 {step}: {title}",
mcpAuthPreviousStep: "이전",
mcpAuthNextStep: "다음",
mcpAuthStepProgress: "{total}단계 중 {current}단계",
mcpPerPage: "페이지당",
mcpPreviousPage: "이전 페이지",
mcpNextPage: "다음 페이지",
mcpDatabaseScopeTitle: "연결 및 데이터베이스 범위",
mcpDatabaseScopeDescription: "왼쪽에서 MCP에 노출된 연결을 선택한 뒤, 해당 연결이 접근할 수 있는 데이터베이스를 제한합니다. 데이터베이스 이름은 정확히 일치해야 합니다.",
mcpDatabaseScopeEmptyHint: "먼저 '연결 범위'에서 하나 이상의 연결을 허용하세요.",
mcpDatabaseScopeAllowedConnections: "허용된 연결",
mcpDatabaseScopeSummaryAll: "모든 데이터베이스",
mcpDatabaseScopeSummaryNone: "데이터베이스 접근 없음",
mcpDatabaseScopeSummarySelected: "{count}개 선택됨",
mcpDatabaseScopePermissionNote: "범위를 선택해도 DBX에서 이 연결의 기존 데이터베이스 권한은 변경되지 않습니다.",
mcpDatabaseLoadButton: "연결에서 불러오기",
mcpDatabaseScopeAriaLabel: "데이터베이스 접근 범위",
mcpDatabaseScopeAllTitle: "모든 데이터베이스",
mcpDatabaseScopeAllDescription: "현재 접근 범위 유지",
mcpDatabaseScopeSelectedTitle: "지정한 데이터베이스만",
mcpDatabaseScopeSelectedDescription: "선택한 항목만 노출",
mcpDatabaseScopeNoneTitle: "접근 허용 안 함",
mcpDatabaseScopeNoneDescription: "규칙은 유지하되 모든 데이터베이스 거부",
mcpDatabaseScopeSelectedSummary: "{count}개 데이터베이스가 허용되었습니다. 선택하지 않은 데이터베이스와 크로스 데이터베이스 요청은 MCP 서버가 거부합니다.",
mcpDatabaseSearchPlaceholder: "불러온 데이터베이스 필터",
mcpDatabaseManualPlaceholder: "데이터베이스 이름 직접 추가",
mcpDatabaseLoadFailed: "데이터베이스 불러오기 실패: {error}. 정확한 데이터베이스 이름을 직접 추가할 수 있습니다.",
mcpDatabaseEmptyHint: "'연결에서 불러오기'를 클릭해 데이터베이스 목록을 가져오거나, 허용할 데이터베이스를 직접 추가하세요.",
mcpTransportLocalStdio: "로컬 stdio",
mcpTransportHttpService: "HTTP 서비스",
mcpHttpWebServiceTitle: "Web Streamable HTTP 서비스",
mcpHttpWebServiceDescription: "Web/Docker은 현재 웹 수신 포트를 재사용합니다. 배포 환경 변수로 활성화되며 설정 페이지에는 Token이 저장되지 않습니다.",
mcpHttpStatusLabelEnabled: "활성화됨",
mcpHttpStatusLabelDisabled: "비활성화됨",
mcpHttpNotConfigured: "미구성",
mcpHttpWebTokenSourceAndHosts: "Token 출처: {tokenSource}; 허용 Host: {hosts}",
mcpHttpWebAllowedOrigins: "허용 Origin: {origins}",
mcpHttpReloadStatus: "상태 다시 불러오기",
mcpHttpServiceTitle: "Streamable HTTP 서비스",
mcpHttpStatusLabelRunning: "실행 중",
mcpHttpStatusLabelPending: "적용 대기 중",
mcpHttpServiceDescription: "기본적으로 꺼져 있습니다. 활성화하면 DBX Desktop이 서비스를 호스팅하며, 저장 후에야 위 설정이 실제 수신 서비스에 적용됩니다.",
mcpHttpListenerTitle: "수신 설정",
mcpHttpListenerHintPrefix: "",
mcpHttpListenerHintSuffix: "을(를) 유지하는 것을 권장합니다. 같은 컴퓨터의 MCP 클라이언트만 연결할 수 있습니다.",
mcpHttpHostLabel: "수신 주소",
mcpHttpPortLabel: "포트",
mcpHttpPathLabel: "MCP 경로",
mcpHttpRemoteTitle: "원격 접근",
mcpHttpRemoteHintPrefix: "수신 주소가 ",
mcpHttpRemoteHintMiddle: " 또는 ",
mcpHttpRemoteHintSuffix: "이(가) 아닐 때만 필요합니다. 활성화하면 Host와 Origin 화이트리스트를 반드시 입력해야 합니다.",
mcpHttpAllowedHostsLabel: "허용할 Host",
mcpHttpHostsHint: "한 줄에 하나씩, 쉼표로 구분할 수도 있습니다.",
mcpHttpAllowedOriginsLabel: "허용할 Origin",
mcpHttpOriginsHintPrefix: "프로토콜을 포함해야 합니다(예: ",
mcpHttpOriginsHintSuffix: ").",
mcpHttpRemoteDisabledHint: "원격 접근이 꺼져 있으면 화이트리스트를 구성할 필요가 없습니다.",
mcpHttpUnsavedChangesHint: "위 설정이 아직 저장되지 않아 실행 중인 서비스는 즉시 변경되지 않습니다.",
mcpHttpSaveHint: "저장하면 서비스를 시작하거나 재시작하고 아래 접속 정보를 갱신합니다.",
mcpHttpReload: "다시 불러오기",
mcpHttpSaveAndStart: "저장하고 서비스 시작",
mcpHttpDisabledHint: "서비스를 활성화하면 수신 주소, 원격 접근 화이트리스트, 클라이언트 접속 정보를 구성할 수 있습니다.",
mcpHttpSaveAndStop: "저장하고 서비스 중지",
mcpHttpConnectionInfoTitle: "적용된 클라이언트 접속 정보",
mcpHttpServiceNotRunning: "서비스가 실행 중이 아닙니다",
mcpHttpRunningConfigBadge: "실행 설정",
mcpHttpLastStatusBadge: "최근 상태",
mcpHttpRunningDraftDiffersHint: "아래는 현재 실행 중인 서비스의 접속 정보이며, 위의 저장되지 않은 초안과 다를 수 있습니다.",
mcpHttpEndpointLabel: "서비스 주소",
mcpHttpRotateToken: "Token 교체",
mcpHttpRotateTokenConfirm: "Token을 교체하면 이전 Token을 사용하는 MCP 클라이언트가 즉시 연결 해제됩니다. 계속하시겠습니까?",
mcpHttpTokenHintPrefix: "이 값을 클라이언트 HTTP ",
mcpHttpTokenHintSuffix: " 자격 증명으로 사용하세요.",
mcpHttpClientConfigTitle: "범용 HTTP 클라이언트 설정",
mcpHttpClientConfigHint: "클라이언트마다 필드 이름이 다르므로 URL과 Authorization 값은 유지하세요.",
mcpHttpLogsTitle: "서비스 진단 로그",
mcpHttpValidationHostRequired: "수신 주소를 입력하세요.",
mcpHttpValidationPortRange: "포트는 1에서 65535 사이의 정수여야 합니다.",
mcpHttpValidationPathPrefix: "MCP 경로는 /로 시작해야 합니다.",
mcpHttpValidationRemoteRequired: "루프백이 아닌 주소로 수신하려면 '원격 접근'을 활성화하세요.",
mcpHttpValidationHostsRequired: "원격 접근에는 허용 Host를 하나 이상 입력해야 합니다.",
mcpHttpValidationOriginsRequired: "원격 접근에는 허용 Origin을 하나 이상 입력해야 합니다.",
mcpHttpErrorRemoteHostsAndOrigins: "원격 접근에는 허용 Host와 허용 Origin을 모두 입력해야 합니다.",
mcpHttpErrorRemoteBinding: "원격 수신에는 원격 접근 활성화와 Host·Origin 화이트리스트가 필요합니다.",
mcpHttpErrorPortInUse: "이 포트는 다른 애플리케이션이 사용 중입니다. DBX는 저장 시 자체 MCP 서비스를 재시작합니다. 해당 포트를 사용하는 다른 애플리케이션을 종료하거나 다른 포트를 사용하세요.",
mcpConnectionPolicyTitle: "연결별 권한 상한",
mcpConnectionPolicyDescription: "노출된 각 연결에 대해 권한을 더 좁힐 수 있습니다. 전역 MCP 권한을 초과할 수 없습니다.",
mcpConnectionPolicyEmptyHint: "연결별 실행 상한을 설정하려면 먼저 1단계에서 하나 이상의 연결을 허용하세요.",
mcpConnectionPolicyInherit: "전역 상속",
mcpConnectionPolicyReadOnly: "읽기 전용만",
mcpConnectionPolicySafeWrite: "안전한 쓰기 허용",
mcpConnectionPolicyHighRiskWrite: "고위험 작업 허용",
mcpToolPermissionsTitle: "MCP 도구 권한",
mcpToolPermissionsDescription: "필요한 MCP 도구만 노출하세요. 선택 해제하면 도구 정의를 캐시한 클라이언트도 서버에서 거부됩니다.",
mcpToolListConnections: "연결 나열",
mcpToolListDatabases: "데이터베이스 나열",
mcpToolListTables: "테이블 나열",
mcpToolDescribeTable: "테이블 구조",
mcpToolGetSchemaContext: "Schema 컨텍스트",
mcpToolExecuteQuery: "SQL / Mongo 명령 실행",
mcpToolOpenSession: "쿼리 세션 열기",
mcpToolCloseSession: "쿼리 세션 닫기",
mcpToolExecuteRedisCommand: "Redis 명령 실행",
mcpToolSendMessage: "메시지 큐 메시지 전송",
mcpToolAddConnection: "연결 추가",
mcpToolDuplicateConnection: "연결 복제",
mcpToolRemoveConnection: "연결 삭제",
mcpToolOpenTable: "DBX에서 테이블 열기",
mcpToolExecuteAndShow: "DBX에서 실행 및 표시",
supportInfoTitle: "지원 정보",
supportInfoDescription: "이슈를 등록하거나 도움을 요청할 때 이 환경 세부 정보를 복사하세요.",
supportInfoCopy: "지원 정보 복사",
+121
View File
@@ -6595,6 +6595,127 @@ export default withEnglishFallback({
mcpNpmBoundary: "O DBX apenas verifica e explica o status do MCP; a instalação e as atualizações ainda são feitas pelo npm.",
mcpRefresh: "Verificar novamente",
mcpGuide: "Guia do MCP",
mcpAuthTitle: "Autorização do MCP",
mcpAuthDescription: "Conclua a autorização em ordem: escolha as conexões primeiro, depois limite os bancos de dados e as exceções por conexão e, por fim, defina as ferramentas disponíveis e o nível de execução global.",
mcpAuthStepsLabel: "Etapas de autorização do MCP",
mcpAuthStepConnectionsTitle: "Escopo de conexões",
mcpAuthStepConnectionsDescription: "Define quais conexões o MCP pode ver",
mcpAuthStepDatabasesTitle: "Escopo de bancos de dados",
mcpAuthStepDatabasesDescription: "Limita os bancos acessíveis por conexão",
mcpAuthStepOverridesTitle: "Exceções por conexão",
mcpAuthStepOverridesDescription: "Restringe mais ainda as permissões de execução por conexão",
mcpAuthStepCapabilitiesTitle: "Capacidades e execução",
mcpAuthStepCapabilitiesDescription: "Escolha as ferramentas do MCP e o nível de operação global",
mcpAuthStepRegionLabel: "Etapa {step}: {title}",
mcpAuthPreviousStep: "Voltar",
mcpAuthNextStep: "Avançar",
mcpAuthStepProgress: "Etapa {current} de {total}",
mcpPerPage: "Por página",
mcpPreviousPage: "Página anterior",
mcpNextPage: "Próxima página",
mcpDatabaseScopeTitle: "Conexões e escopo de bancos de dados",
mcpDatabaseScopeDescription: "Selecione à esquerda uma conexão exposta ao MCP e depois limite os bancos que ela pode acessar. Os nomes dos bancos são comparados de forma exata.",
mcpDatabaseScopeEmptyHint: "Permita primeiro pelo menos uma conexão no 'Escopo de conexões'.",
mcpDatabaseScopeAllowedConnections: "Conexões permitidas",
mcpDatabaseScopeSummaryAll: "Todos os bancos",
mcpDatabaseScopeSummaryNone: "Sem acesso a bancos",
mcpDatabaseScopeSummarySelected: "{count} selecionados",
mcpDatabaseScopePermissionNote: "O escopo selecionado não altera as permissões de bancos que a conexão já tem no DBX.",
mcpDatabaseLoadButton: "Carregar da conexão",
mcpDatabaseScopeAriaLabel: "Escopo de acesso aos bancos",
mcpDatabaseScopeAllTitle: "Todos os bancos de dados",
mcpDatabaseScopeAllDescription: "Mantém o escopo de acesso atual",
mcpDatabaseScopeSelectedTitle: "Somente bancos especificados",
mcpDatabaseScopeSelectedDescription: "Expõe apenas os itens marcados",
mcpDatabaseScopeNoneTitle: "Sem acesso",
mcpDatabaseScopeNoneDescription: "Mantém as regras, mas recusa todos os bancos",
mcpDatabaseScopeSelectedSummary: "{count} bancos permitidos. Bancos não marcados e solicitações entre bancos são recusados pelo servidor MCP.",
mcpDatabaseSearchPlaceholder: "Filtrar bancos carregados",
mcpDatabaseManualPlaceholder: "Adicionar nome do banco manualmente",
mcpDatabaseLoadFailed: "Falha ao carregar bancos: {error}. Você pode adicionar manualmente os nomes exatos.",
mcpDatabaseEmptyHint: "Clique em 'Carregar da conexão' para obter a lista de bancos ou adicione manualmente os permitidos.",
mcpTransportLocalStdio: "stdio local",
mcpTransportHttpService: "Serviço HTTP",
mcpHttpWebServiceTitle: "Serviço Web Streamable HTTP",
mcpHttpWebServiceDescription: "O Web/Docker reutiliza a porta de escuta web atual. É ativado por variáveis de ambiente de implantação; a página de configurações nunca salva o Token.",
mcpHttpStatusLabelEnabled: "Ativado",
mcpHttpStatusLabelDisabled: "Desativado",
mcpHttpNotConfigured: "Não configurado",
mcpHttpWebTokenSourceAndHosts: "Origem do Token: {tokenSource}; Hosts permitidos: {hosts}",
mcpHttpWebAllowedOrigins: "Origins permitidos: {origins}",
mcpHttpReloadStatus: "Recarregar status",
mcpHttpServiceTitle: "Serviço Streamable HTTP",
mcpHttpStatusLabelRunning: "Em execução",
mcpHttpStatusLabelPending: "Pendente de aplicação",
mcpHttpServiceDescription: "Desativado por padrão. Quando ativado, o DBX Desktop hospeda o serviço; a configuração acima só é aplicada ao serviço de escuta real após salvar.",
mcpHttpListenerTitle: "Configuração de escuta",
mcpHttpListenerHintPrefix: "Recomendamos manter ",
mcpHttpListenerHintSuffix: " para permitir apenas clientes MCP do mesmo computador.",
mcpHttpHostLabel: "Endereço de escuta",
mcpHttpPortLabel: "Porta",
mcpHttpPathLabel: "Caminho MCP",
mcpHttpRemoteTitle: "Acesso remoto",
mcpHttpRemoteHintPrefix: "Necessário apenas quando o endereço de escuta não é ",
mcpHttpRemoteHintMiddle: " ou ",
mcpHttpRemoteHintSuffix: ". Ao ativar, é obrigatório preencher as listas de permissão de Host e Origin.",
mcpHttpAllowedHostsLabel: "Hosts permitidos",
mcpHttpHostsHint: "Um por linha, ou separados por vírgulas.",
mcpHttpAllowedOriginsLabel: "Origins permitidos",
mcpHttpOriginsHintPrefix: "Deve incluir o protocolo, por exemplo ",
mcpHttpOriginsHintSuffix: ".",
mcpHttpRemoteDisabledHint: "Com o acesso remoto desativado, as listas de permissão não são necessárias.",
mcpHttpUnsavedChangesHint: "A configuração acima ainda não foi salva; o serviço em execução não mudará imediatamente.",
mcpHttpSaveHint: "Ao salvar, o serviço é iniciado ou reiniciado e as informações de conexão abaixo são atualizadas.",
mcpHttpReload: "Recarregar",
mcpHttpSaveAndStart: "Salvar e iniciar serviço",
mcpHttpDisabledHint: "Ative o serviço para configurar endereço de escuta, listas de acesso remoto e informações de conexão do cliente.",
mcpHttpSaveAndStop: "Salvar e parar serviço",
mcpHttpConnectionInfoTitle: "Informações de conexão do cliente em vigor",
mcpHttpServiceNotRunning: "O serviço não está em execução",
mcpHttpRunningConfigBadge: "Configuração em execução",
mcpHttpLastStatusBadge: "Último status",
mcpHttpRunningDraftDiffersHint: "Abaixo estão as informações de conexão do serviço em execução; elas podem diferir do rascunho não salvo acima.",
mcpHttpEndpointLabel: "Endereço do serviço",
mcpHttpRotateToken: "Rotacionar Token",
mcpHttpRotateTokenConfirm: "Após rotacionar o Token, clientes MCP que usam o Token antigo serão desconectados imediatamente. Continuar?",
mcpHttpTokenHintPrefix: "Use este valor como a credencial HTTP ",
mcpHttpTokenHintSuffix: " do cliente.",
mcpHttpClientConfigTitle: "Configuração genérica de cliente HTTP",
mcpHttpClientConfigHint: "Os nomes dos campos variam por cliente; mantenha os valores de URL e Authorization.",
mcpHttpLogsTitle: "Logs de diagnóstico do serviço",
mcpHttpValidationHostRequired: "Informe um endereço de escuta.",
mcpHttpValidationPortRange: "A porta deve ser um inteiro entre 1 e 65535.",
mcpHttpValidationPathPrefix: "O caminho MCP deve começar com /.",
mcpHttpValidationRemoteRequired: "Ative o 'Acesso remoto' para escutar em um endereço não local.",
mcpHttpValidationHostsRequired: "O acesso remoto exige pelo menos um Host permitido.",
mcpHttpValidationOriginsRequired: "O acesso remoto exige pelo menos um Origin permitido.",
mcpHttpErrorRemoteHostsAndOrigins: "O acesso remoto exige Hosts e Origins permitidos preenchidos.",
mcpHttpErrorRemoteBinding: "A escuta remota exige acesso remoto ativado e listas de permissão de Host e Origin.",
mcpHttpErrorPortInUse: "A porta já está em uso por outro aplicativo. O DBX reinicia o próprio serviço MCP ao salvar; feche o outro aplicativo que usa a porta ou escolha outra porta.",
mcpConnectionPolicyTitle: "Limite de permissões por conexão",
mcpConnectionPolicyDescription: "Restringe mais ainda as permissões de cada conexão exposta; elas não podem exceder as permissões globais do MCP.",
mcpConnectionPolicyEmptyHint: "Permita primeiro pelo menos uma conexão na etapa 1 para configurar limites de execução por conexão.",
mcpConnectionPolicyInherit: "Herdar do global",
mcpConnectionPolicyReadOnly: "Somente leitura",
mcpConnectionPolicySafeWrite: "Permitir escritas seguras",
mcpConnectionPolicyHighRiskWrite: "Permitir operações de alto risco",
mcpToolPermissionsTitle: "Permissões de ferramentas MCP",
mcpToolPermissionsDescription: "Exponha apenas as ferramentas MCP necessárias. Ao desmarcá-las, o servidor recusa as solicitações mesmo que o cliente tenha as definições em cache.",
mcpToolListConnections: "Listar conexões",
mcpToolListDatabases: "Listar bancos de dados",
mcpToolListTables: "Listar tabelas",
mcpToolDescribeTable: "Estrutura da tabela",
mcpToolGetSchemaContext: "Contexto do Schema",
mcpToolExecuteQuery: "Executar SQL / comandos Mongo",
mcpToolOpenSession: "Abrir sessão de consulta",
mcpToolCloseSession: "Fechar sessão de consulta",
mcpToolExecuteRedisCommand: "Executar comando Redis",
mcpToolSendMessage: "Enviar mensagem da fila",
mcpToolAddConnection: "Adicionar conexão",
mcpToolDuplicateConnection: "Duplicar conexão",
mcpToolRemoveConnection: "Remover conexão",
mcpToolOpenTable: "Abrir tabela no DBX",
mcpToolExecuteAndShow: "Executar e mostrar no DBX",
supportInfoTitle: "Informações de suporte",
supportInfoDescription: "Copie estes detalhes do ambiente ao abrir issues ou pedir ajuda.",
supportInfoCopy: "Copiar informações",
+121
View File
@@ -6955,6 +6955,127 @@ export default withEnglishFallback({
mcpNpmBoundary: "DBX 负责检查 MCP 状态;安装、升级和卸载由检测到的 npm 或 pnpm 运行环境执行。",
mcpRefresh: "重新检查",
mcpGuide: "MCP 指南",
mcpAuthTitle: "MCP 授权设置",
mcpAuthDescription: "按顺序完成授权:先选择连接,再限定数据库和连接例外,最后确定可用工具与全局执行级别。",
mcpAuthStepsLabel: "MCP 授权步骤",
mcpAuthStepConnectionsTitle: "连接范围",
mcpAuthStepConnectionsDescription: "决定哪些连接可被 MCP 看见",
mcpAuthStepDatabasesTitle: "数据库范围",
mcpAuthStepDatabasesDescription: "为每个连接限定可访问的库",
mcpAuthStepOverridesTitle: "连接例外",
mcpAuthStepOverridesDescription: "按连接进一步收紧执行权限",
mcpAuthStepCapabilitiesTitle: "能力与执行",
mcpAuthStepCapabilitiesDescription: "选择 MCP 工具和全局操作级别",
mcpAuthStepRegionLabel: "第 {step} 步:{title}",
mcpAuthPreviousStep: "上一步",
mcpAuthNextStep: "下一步",
mcpAuthStepProgress: "第 {current} 步,共 {total} 步",
mcpPerPage: "每页",
mcpPreviousPage: "上一页",
mcpNextPage: "下一页",
mcpDatabaseScopeTitle: "连接与数据库范围",
mcpDatabaseScopeDescription: "先在左侧选择已暴露给 MCP 的连接,再限定该连接可访问的数据库。数据库名称按精确名称匹配。",
mcpDatabaseScopeEmptyHint: "请先在“连接范围”中允许至少一个连接。",
mcpDatabaseScopeAllowedConnections: "已允许的连接",
mcpDatabaseScopeSummaryAll: "全部库",
mcpDatabaseScopeSummaryNone: "无库访问",
mcpDatabaseScopeSummarySelected: "已选 {count} 个库",
mcpDatabaseScopePermissionNote: "选择范围不会改变此连接在 DBX 中原有的数据库权限。",
mcpDatabaseLoadButton: "从连接加载",
mcpDatabaseScopeAriaLabel: "数据库访问范围",
mcpDatabaseScopeAllTitle: "全部数据库",
mcpDatabaseScopeAllDescription: "保持现有访问范围",
mcpDatabaseScopeSelectedTitle: "仅指定数据库",
mcpDatabaseScopeSelectedDescription: "只开放勾选项",
mcpDatabaseScopeNoneTitle: "不允许访问",
mcpDatabaseScopeNoneDescription: "保留规则但拒绝所有库",
mcpDatabaseScopeSelectedSummary: "已允许 {count} 个数据库。未勾选数据库及跨库请求会被 MCP 服务端拒绝。",
mcpDatabaseSearchPlaceholder: "筛选已加载的数据库",
mcpDatabaseManualPlaceholder: "手工添加库名",
mcpDatabaseLoadFailed: "加载数据库失败:{error}。可手工添加准确的数据库名称。",
mcpDatabaseEmptyHint: "点击“从连接加载”获取数据库列表,或手工添加允许的数据库。",
mcpTransportLocalStdio: "本地 stdio",
mcpTransportHttpService: "HTTP 服务",
mcpHttpWebServiceTitle: "Web Streamable HTTP 服务",
mcpHttpWebServiceDescription: "Web/Docker 复用当前 Web 监听端口。通过部署环境变量启用,设置页不会保存 Token。",
mcpHttpStatusLabelEnabled: "已启用",
mcpHttpStatusLabelDisabled: "未启用",
mcpHttpNotConfigured: "未配置",
mcpHttpWebTokenSourceAndHosts: "Token 来源:{tokenSource};允许 Host:{hosts}",
mcpHttpWebAllowedOrigins: "允许 Origin:{origins}",
mcpHttpReloadStatus: "重新加载状态",
mcpHttpServiceTitle: "Streamable HTTP 服务",
mcpHttpStatusLabelRunning: "运行中",
mcpHttpStatusLabelPending: "待应用配置",
mcpHttpServiceDescription: "默认关闭。启用后由 DBX Desktop 托管服务;保存后才会将上方配置应用到实际监听服务。",
mcpHttpListenerTitle: "监听配置",
mcpHttpListenerHintPrefix: "推荐保留 ",
mcpHttpListenerHintSuffix: ",仅允许同一台电脑上的 MCP 客户端连接。",
mcpHttpHostLabel: "监听地址",
mcpHttpPortLabel: "端口",
mcpHttpPathLabel: "MCP 路径",
mcpHttpRemoteTitle: "远程访问",
mcpHttpRemoteHintPrefix: "仅当监听地址不是 ",
mcpHttpRemoteHintMiddle: " 或 ",
mcpHttpRemoteHintSuffix: " 时需要。开启后,必须填写 Host 和 Origin 白名单。",
mcpHttpAllowedHostsLabel: "允许的 Host",
mcpHttpHostsHint: "每行一个,也可用逗号分隔。",
mcpHttpAllowedOriginsLabel: "允许的 Origin",
mcpHttpOriginsHintPrefix: "必须包含协议,例如 ",
mcpHttpOriginsHintSuffix: "。",
mcpHttpRemoteDisabledHint: "未开启远程访问时,白名单无需配置。",
mcpHttpUnsavedChangesHint: "上方配置尚未保存,当前运行服务不会立即改变。",
mcpHttpSaveHint: "保存后会启动或重启服务,并更新下方接入信息。",
mcpHttpReload: "重新加载",
mcpHttpSaveAndStart: "保存并启动服务",
mcpHttpDisabledHint: "启用服务后可配置监听地址、远程访问白名单和客户端接入信息。",
mcpHttpSaveAndStop: "保存并停止服务",
mcpHttpConnectionInfoTitle: "已生效的客户端接入信息",
mcpHttpServiceNotRunning: "服务当前未运行",
mcpHttpRunningConfigBadge: "运行配置",
mcpHttpLastStatusBadge: "最近一次状态",
mcpHttpRunningDraftDiffersHint: "下方是当前运行服务的接入信息;它与上方未保存的草稿可能不同。",
mcpHttpEndpointLabel: "服务地址",
mcpHttpRotateToken: "轮换 Token",
mcpHttpRotateTokenConfirm: "轮换 Token 后,使用旧 Token 的 MCP 客户端会立即断开。是否继续?",
mcpHttpTokenHintPrefix: "将此值作为客户端的 HTTP ",
mcpHttpTokenHintSuffix: " 凭证。",
mcpHttpClientConfigTitle: "通用 HTTP 客户端配置",
mcpHttpClientConfigHint: "客户端字段名称不同,请保留 URL 与 Authorization 值。",
mcpHttpLogsTitle: "服务诊断日志",
mcpHttpValidationHostRequired: "请填写监听地址。",
mcpHttpValidationPortRange: "端口必须是 1 到 65535 之间的整数。",
mcpHttpValidationPathPrefix: "MCP 路径必须以 / 开头。",
mcpHttpValidationRemoteRequired: "监听非本机地址时,请开启“允许远程访问”。",
mcpHttpValidationHostsRequired: "远程访问需要至少填写一个允许的 Host。",
mcpHttpValidationOriginsRequired: "远程访问需要至少填写一个允许的 Origin。",
mcpHttpErrorRemoteHostsAndOrigins: "远程访问需要同时填写允许的 Host 和允许的 Origin。",
mcpHttpErrorRemoteBinding: "远程监听需要开启远程访问,并设置 Host 和 Origin 白名单。",
mcpHttpErrorPortInUse: "该端口已被其他应用占用。DBX 会在保存时自动重启自己的 MCP 服务;请关闭占用该端口的其他应用,或改用其他端口。",
mcpConnectionPolicyTitle: "连接级权限上限",
mcpConnectionPolicyDescription: "可针对每个已暴露连接进一步收紧权限;不能超过全局 MCP 权限。",
mcpConnectionPolicyEmptyHint: "请先在第 1 步中允许至少一个连接,才能设置连接级执行上限。",
mcpConnectionPolicyInherit: "继承全局",
mcpConnectionPolicyReadOnly: "仅只读",
mcpConnectionPolicySafeWrite: "允许安全写入",
mcpConnectionPolicyHighRiskWrite: "允许高风险操作",
mcpToolPermissionsTitle: "MCP 工具权限",
mcpToolPermissionsDescription: "只暴露当前需要的 MCP 工具。取消选择后,客户端即使缓存了工具定义也会被服务端拒绝。",
mcpToolListConnections: "列出连接",
mcpToolListDatabases: "列出数据库",
mcpToolListTables: "列出表",
mcpToolDescribeTable: "表结构",
mcpToolGetSchemaContext: "Schema 上下文",
mcpToolExecuteQuery: "执行 SQL / Mongo 命令",
mcpToolOpenSession: "打开查询会话",
mcpToolCloseSession: "关闭查询会话",
mcpToolExecuteRedisCommand: "执行 Redis 命令",
mcpToolSendMessage: "发送消息队列消息",
mcpToolAddConnection: "新增连接",
mcpToolDuplicateConnection: "复制连接",
mcpToolRemoveConnection: "删除连接",
mcpToolOpenTable: "在 DBX 中打开表",
mcpToolExecuteAndShow: "在 DBX 中执行并展示",
supportInfoTitle: "支持信息",
supportInfoDescription: "提交 issue 或寻求帮助时,可复制这些环境信息。",
supportInfoCopy: "复制支持信息",
+121
View File
@@ -5935,6 +5935,127 @@ export default withEnglishFallback({
mcpNpmBoundary: "DBX 只檢測和提示 MCP 狀態;安裝與升級仍由 npm 完成。",
mcpRefresh: "重新檢查",
mcpGuide: "MCP 指南",
mcpAuthTitle: "MCP 授權設定",
mcpAuthDescription: "按順序完成授權:先選擇連線,再限定資料庫和連線例外,最後確定可用工具與全域執行層級。",
mcpAuthStepsLabel: "MCP 授權步驟",
mcpAuthStepConnectionsTitle: "連線範圍",
mcpAuthStepConnectionsDescription: "決定哪些連線可被 MCP 看見",
mcpAuthStepDatabasesTitle: "資料庫範圍",
mcpAuthStepDatabasesDescription: "為每個連線限定可存取的庫",
mcpAuthStepOverridesTitle: "連線例外",
mcpAuthStepOverridesDescription: "按連線進一步收緊執行權限",
mcpAuthStepCapabilitiesTitle: "能力與執行",
mcpAuthStepCapabilitiesDescription: "選擇 MCP 工具和全域操作層級",
mcpAuthStepRegionLabel: "第 {step} 步:{title}",
mcpAuthPreviousStep: "上一步",
mcpAuthNextStep: "下一步",
mcpAuthStepProgress: "第 {current} 步,共 {total} 步",
mcpPerPage: "每頁",
mcpPreviousPage: "上一頁",
mcpNextPage: "下一頁",
mcpDatabaseScopeTitle: "連線與資料庫範圍",
mcpDatabaseScopeDescription: "先在左側選擇已暴露給 MCP 的連線,再限定該連線可存取的資料庫。資料庫名稱按精確名稱比對。",
mcpDatabaseScopeEmptyHint: "請先在「連線範圍」中允許至少一個連線。",
mcpDatabaseScopeAllowedConnections: "已允許的連線",
mcpDatabaseScopeSummaryAll: "全部庫",
mcpDatabaseScopeSummaryNone: "無庫存取",
mcpDatabaseScopeSummarySelected: "已選 {count} 個庫",
mcpDatabaseScopePermissionNote: "選擇範圍不會改變此連線在 DBX 中原有的資料庫權限。",
mcpDatabaseLoadButton: "從連線載入",
mcpDatabaseScopeAriaLabel: "資料庫存取範圍",
mcpDatabaseScopeAllTitle: "全部資料庫",
mcpDatabaseScopeAllDescription: "保持現有存取範圍",
mcpDatabaseScopeSelectedTitle: "僅指定資料庫",
mcpDatabaseScopeSelectedDescription: "只開放勾選項",
mcpDatabaseScopeNoneTitle: "不允許存取",
mcpDatabaseScopeNoneDescription: "保留規則但拒絕所有庫",
mcpDatabaseScopeSelectedSummary: "已允許 {count} 個資料庫。未勾選資料庫及跨庫請求會被 MCP 服務端拒絕。",
mcpDatabaseSearchPlaceholder: "篩選已載入的資料庫",
mcpDatabaseManualPlaceholder: "手動新增庫名",
mcpDatabaseLoadFailed: "載入資料庫失敗:{error}。可手動新增準確的資料庫名稱。",
mcpDatabaseEmptyHint: "點擊「從連線載入」取得資料庫清單,或手動新增允許的資料庫。",
mcpTransportLocalStdio: "本機 stdio",
mcpTransportHttpService: "HTTP 服務",
mcpHttpWebServiceTitle: "Web Streamable HTTP 服務",
mcpHttpWebServiceDescription: "Web/Docker 重用目前 Web 監聽埠。透過部署環境變數啟用,設定頁不會儲存 Token。",
mcpHttpStatusLabelEnabled: "已啟用",
mcpHttpStatusLabelDisabled: "未啟用",
mcpHttpNotConfigured: "未設定",
mcpHttpWebTokenSourceAndHosts: "Token 來源:{tokenSource};允許 Host:{hosts}",
mcpHttpWebAllowedOrigins: "允許 Origin:{origins}",
mcpHttpReloadStatus: "重新載入狀態",
mcpHttpServiceTitle: "Streamable HTTP 服務",
mcpHttpStatusLabelRunning: "執行中",
mcpHttpStatusLabelPending: "待套用設定",
mcpHttpServiceDescription: "預設關閉。啟用後由 DBX Desktop 託管服務;儲存後才會將上方設定套用到實際監聽服務。",
mcpHttpListenerTitle: "監聽設定",
mcpHttpListenerHintPrefix: "建議保留 ",
mcpHttpListenerHintSuffix: ",僅允許同一台電腦上的 MCP 用戶端連線。",
mcpHttpHostLabel: "監聽位址",
mcpHttpPortLabel: "埠",
mcpHttpPathLabel: "MCP 路徑",
mcpHttpRemoteTitle: "遠端存取",
mcpHttpRemoteHintPrefix: "僅當監聽位址不是 ",
mcpHttpRemoteHintMiddle: " 或 ",
mcpHttpRemoteHintSuffix: " 時需要。開啟後,必須填寫 Host 和 Origin 白名單。",
mcpHttpAllowedHostsLabel: "允許的 Host",
mcpHttpHostsHint: "每行一個,也可用逗號分隔。",
mcpHttpAllowedOriginsLabel: "允許的 Origin",
mcpHttpOriginsHintPrefix: "必須包含協定,例如 ",
mcpHttpOriginsHintSuffix: "。",
mcpHttpRemoteDisabledHint: "未開啟遠端存取時,白名單無需設定。",
mcpHttpUnsavedChangesHint: "上方設定尚未儲存,目前執行服務不會立即改變。",
mcpHttpSaveHint: "儲存後會啟動或重新啟動服務,並更新下方接入資訊。",
mcpHttpReload: "重新載入",
mcpHttpSaveAndStart: "儲存並啟動服務",
mcpHttpDisabledHint: "啟用服務後可設定監聽位址、遠端存取白名單和用戶端接入資訊。",
mcpHttpSaveAndStop: "儲存並停止服務",
mcpHttpConnectionInfoTitle: "已生效的用戶端接入資訊",
mcpHttpServiceNotRunning: "服務目前未執行",
mcpHttpRunningConfigBadge: "執行設定",
mcpHttpLastStatusBadge: "最近一次狀態",
mcpHttpRunningDraftDiffersHint: "下方是目前執行服務的接入資訊;它與上方未儲存的草稿可能不同。",
mcpHttpEndpointLabel: "服務位址",
mcpHttpRotateToken: "輪換 Token",
mcpHttpRotateTokenConfirm: "輪換 Token 後,使用舊 Token 的 MCP 用戶端會立即斷線。是否繼續?",
mcpHttpTokenHintPrefix: "將此值作為用戶端的 HTTP ",
mcpHttpTokenHintSuffix: " 憑證。",
mcpHttpClientConfigTitle: "通用 HTTP 用戶端設定",
mcpHttpClientConfigHint: "用戶端欄位名稱不同,請保留 URL 與 Authorization 值。",
mcpHttpLogsTitle: "服務診斷日誌",
mcpHttpValidationHostRequired: "請填寫監聽位址。",
mcpHttpValidationPortRange: "埠必須是 1 到 65535 之間的整數。",
mcpHttpValidationPathPrefix: "MCP 路徑必須以 / 開頭。",
mcpHttpValidationRemoteRequired: "監聽非本機位址時,請開啟「允許遠端存取」。",
mcpHttpValidationHostsRequired: "遠端存取需要至少填寫一個允許的 Host。",
mcpHttpValidationOriginsRequired: "遠端存取需要至少填寫一個允許的 Origin。",
mcpHttpErrorRemoteHostsAndOrigins: "遠端存取需要同時填寫允許的 Host 和允許的 Origin。",
mcpHttpErrorRemoteBinding: "遠端監聽需要開啟遠端存取,並設定 Host 和 Origin 白名單。",
mcpHttpErrorPortInUse: "該埠已被其他應用程式佔用。DBX 會在儲存時自動重新啟動自己的 MCP 服務;請關閉佔用該埠的其他應用程式,或改用其他埠。",
mcpConnectionPolicyTitle: "連線級權限上限",
mcpConnectionPolicyDescription: "可針對每個已暴露連線進一步收緊權限;不能超過全域 MCP 權限。",
mcpConnectionPolicyEmptyHint: "請先在第 1 步中允許至少一個連線,才能設定連線級執行上限。",
mcpConnectionPolicyInherit: "繼承全域",
mcpConnectionPolicyReadOnly: "僅唯讀",
mcpConnectionPolicySafeWrite: "允許安全寫入",
mcpConnectionPolicyHighRiskWrite: "允許高風險操作",
mcpToolPermissionsTitle: "MCP 工具權限",
mcpToolPermissionsDescription: "只暴露目前需要的 MCP 工具。取消選擇後,用戶端即使快取了工具定義也會被服務端拒絕。",
mcpToolListConnections: "列出連線",
mcpToolListDatabases: "列出資料庫",
mcpToolListTables: "列出資料表",
mcpToolDescribeTable: "資料表結構",
mcpToolGetSchemaContext: "Schema 上下文",
mcpToolExecuteQuery: "執行 SQL / Mongo 命令",
mcpToolOpenSession: "開啟查詢工作階段",
mcpToolCloseSession: "關閉查詢工作階段",
mcpToolExecuteRedisCommand: "執行 Redis 命令",
mcpToolSendMessage: "傳送訊息佇列訊息",
mcpToolAddConnection: "新增連線",
mcpToolDuplicateConnection: "複製連線",
mcpToolRemoveConnection: "刪除連線",
mcpToolOpenTable: "在 DBX 中開啟資料表",
mcpToolExecuteAndShow: "在 DBX 中執行並顯示",
updateDownloadSource: "更新下載來源",
updateDownloadSourceDescription: "選擇應用程式內更新安裝程式的下載來源。建議使用官方來源;CNB 在中國大陸網路環境下可能更快。",
updateDownloadSourceOfficial: "官方來源(建議)",
+8
View File
@@ -808,6 +808,11 @@ export const deleteHistoryEntry = forward("deleteHistoryEntry");
export const checkMcpServerStatus = forward("checkMcpServerStatus");
export const installMcpServer = forward("installMcpServer");
export const uninstallMcpServer = forward("uninstallMcpServer");
export const loadMcpHttpServerSettings = forward("loadMcpHttpServerSettings");
export const saveMcpHttpServerSettings = forward("saveMcpHttpServerSettings");
export const mcpHttpServerStatus = forward("mcpHttpServerStatus");
export const rotateMcpHttpServerToken = forward("rotateMcpHttpServerToken");
export const loadWebMcpHttpStatus = forward("loadWebMcpHttpStatus");
export const checkForUpdates = forward("checkForUpdates");
export const fetchChangelog = forward("fetchChangelog");
export const getSystemProxyUrl = forward("getSystemProxyUrl");
@@ -867,6 +872,9 @@ export type {
SnippetDownloadResult,
SnippetTokenStatus,
McpServerStatus,
McpHttpServerSettings,
McpHttpServerStatus,
WebMcpHttpStatus,
UpdateInfo,
RedisBlob,
RedisCollectionPage,
+20
View File
@@ -1808,6 +1808,26 @@ export async function saveMcpGlobalPolicy(policy: Omit<McpGlobalPolicy, "configu
if (!res.ok) throw await backendResponseError(res);
}
export async function loadMcpHttpServerSettings(): Promise<import("@/lib/backend/tauri").McpHttpServerSettings> {
return { enabled: false, host: "127.0.0.1", port: 5225, path: "/mcp", allowRemote: false, allowedHosts: [], allowedOrigins: [] };
}
export async function saveMcpHttpServerSettings(_settings: import("@/lib/backend/tauri").McpHttpServerSettings): Promise<import("@/lib/backend/tauri").McpHttpServerStatus> {
throw new Error("The MCP HTTP server is available only in DBX Desktop");
}
export async function mcpHttpServerStatus(): Promise<import("@/lib/backend/tauri").McpHttpServerStatus> {
return { enabled: false, running: false, endpoint: null, accessToken: null, lastError: null, recentLogs: [] };
}
export async function rotateMcpHttpServerToken(): Promise<import("@/lib/backend/tauri").McpHttpServerStatus> {
throw new Error("The MCP HTTP server is available only in DBX Desktop");
}
export async function loadWebMcpHttpStatus(): Promise<import("@/lib/backend/tauri").WebMcpHttpStatus> {
return get("/api/app-settings/mcp-http-status");
}
export async function loadMaxAgentTurns(): Promise<number> {
return get("/api/app-settings/max-agent-turns");
}
+58
View File
@@ -245,10 +245,21 @@ export interface McpGlobalPolicy {
readOnly: boolean;
allowDangerousSql: boolean;
allowedConnectionIds: string[] | null;
allowedToolNames: string[] | null;
connectionPolicies: McpConnectionPolicy[];
configured: boolean;
queryTimeoutSecs: number | null;
}
export interface McpConnectionPolicy {
connectionId: string;
readOnly: boolean;
allowDangerousSql: boolean;
executionModeConfigured: boolean;
databaseScope: "all" | "selected" | "none";
allowedDatabases: string[];
}
export interface SavedSqlSyncEntry {
folderName?: string;
fileName: string;
@@ -634,6 +645,53 @@ export async function saveMcpGlobalPolicy(policy: Omit<McpGlobalPolicy, "configu
return invoke("save_mcp_global_policy", { policy });
}
export interface McpHttpServerSettings {
enabled: boolean;
host: string;
port: number;
path: string;
allowRemote: boolean;
allowedHosts: string[];
allowedOrigins: string[];
}
export interface McpHttpServerStatus {
enabled: boolean;
running: boolean;
endpoint: string | null;
accessToken: string | null;
lastError: string | null;
recentLogs: string[];
}
export interface WebMcpHttpStatus {
enabled: boolean;
endpointPath: string;
tokenSource: "environment" | "file" | null;
allowedHosts: string[];
allowedOrigins: string[];
}
export async function loadMcpHttpServerSettings(): Promise<McpHttpServerSettings> {
return invoke("load_mcp_http_server_settings");
}
export async function saveMcpHttpServerSettings(settings: McpHttpServerSettings): Promise<McpHttpServerStatus> {
return invoke("save_mcp_http_server_settings", { settings });
}
export async function mcpHttpServerStatus(): Promise<McpHttpServerStatus> {
return invoke("mcp_http_server_status");
}
export async function rotateMcpHttpServerToken(): Promise<McpHttpServerStatus> {
return invoke("rotate_mcp_http_server_token");
}
export async function loadWebMcpHttpStatus(): Promise<WebMcpHttpStatus> {
return { enabled: false, endpointPath: "/mcp", tokenSource: null, allowedHosts: [], allowedOrigins: [] };
}
export async function loadMaxAgentTurns(): Promise<number> {
return invoke("load_max_agent_turns");
}
@@ -432,6 +432,8 @@ describe("normalizeMcpGlobalPolicy", () => {
readOnly: false,
allowDangerousSql: false,
allowedConnectionIds: null,
allowedToolNames: null,
connectionPolicies: [],
configured: false,
queryTimeoutSecs: null,
});
@@ -449,6 +451,8 @@ describe("normalizeMcpGlobalPolicy", () => {
readOnly: true,
allowDangerousSql: true,
allowedConnectionIds: ["connection-1", "connection-2"],
allowedToolNames: null,
connectionPolicies: [],
configured: true,
queryTimeoutSecs: null,
});
@@ -675,6 +679,8 @@ describe("settingsStore MCP policy persistence", () => {
readOnly: true,
allowDangerousSql: false,
allowedConnectionIds: ["connection-1"],
allowedToolNames: null,
connectionPolicies: [],
configured: true,
queryTimeoutSecs: null,
};
@@ -688,6 +694,8 @@ describe("settingsStore MCP policy persistence", () => {
readOnly: false,
allowDangerousSql: false,
allowedConnectionIds: [],
allowedToolNames: null,
connectionPolicies: [],
configured: true,
queryTimeoutSecs: null,
});
+32
View File
@@ -50,11 +50,22 @@ export interface McpGlobalPolicy {
readOnly: boolean;
allowDangerousSql: boolean;
allowedConnectionIds: string[] | null;
allowedToolNames: string[] | null;
connectionPolicies: McpConnectionPolicy[];
configured: boolean;
/** MCP query timeout override in seconds. null/undefined = inherit the connection; 0 = no limit. */
queryTimeoutSecs: number | null;
}
export interface McpConnectionPolicy {
connectionId: string;
readOnly: boolean;
allowDangerousSql: boolean;
executionModeConfigured: boolean;
databaseScope: "all" | "selected" | "none";
allowedDatabases: string[];
}
export type DesktopIconTheme = "default" | "black";
export type InterfaceLayout = "separated" | "classic";
@@ -91,12 +102,29 @@ export const DEFAULT_MCP_GLOBAL_POLICY: McpGlobalPolicy = {
readOnly: false,
allowDangerousSql: false,
allowedConnectionIds: null,
allowedToolNames: null,
connectionPolicies: [],
configured: false,
queryTimeoutSecs: null,
};
export function normalizeMcpGlobalPolicy(policy: Partial<McpGlobalPolicy> | null | undefined): McpGlobalPolicy {
const allowedConnectionIds = policy?.allowedConnectionIds === null || policy?.allowedConnectionIds === undefined ? null : [...new Set(policy.allowedConnectionIds.filter((id): id is string => typeof id === "string" && id.trim().length > 0).map((id) => id.trim()))];
const allowedToolNames = policy?.allowedToolNames === null || policy?.allowedToolNames === undefined ? null : [...new Set(policy.allowedToolNames.filter((name): name is string => typeof name === "string" && name.trim().length > 0).map((name) => name.trim()))];
const connectionPolicies = Object.values(
(policy?.connectionPolicies ?? []).reduce<Record<string, McpConnectionPolicy>>((rules, rule) => {
if (!rule || typeof rule.connectionId !== "string" || !rule.connectionId.trim()) return rules;
rules[rule.connectionId.trim()] = {
connectionId: rule.connectionId.trim(),
readOnly: rule.readOnly === true,
allowDangerousSql: rule.readOnly !== true && rule.allowDangerousSql === true,
executionModeConfigured: rule.executionModeConfigured !== false,
databaseScope: rule.databaseScope === "selected" || rule.databaseScope === "none" ? rule.databaseScope : "all",
allowedDatabases: rule.databaseScope === "selected" ? [...new Set((rule.allowedDatabases ?? []).filter((database): database is string => typeof database === "string" && database.trim().length > 0).map((database) => database.trim()))] : [],
};
return rules;
}, {}),
);
// null / undefined / non-positive => null (inherit connection). 0 is preserved
// as an explicit "no limit" only here; the UI maps "no limit" <=> 0 and
// "inherit" <=> null.
@@ -105,6 +133,8 @@ export function normalizeMcpGlobalPolicy(policy: Partial<McpGlobalPolicy> | null
readOnly: policy?.readOnly === true,
allowDangerousSql: policy?.allowDangerousSql === true,
allowedConnectionIds,
allowedToolNames,
connectionPolicies,
configured: policy?.configured === true,
queryTimeoutSecs,
};
@@ -1530,6 +1560,8 @@ export const useSettingsStore = defineStore("settings", () => {
readOnly: next.readOnly,
allowDangerousSql: next.allowDangerousSql,
allowedConnectionIds: next.allowedConnectionIds,
allowedToolNames: next.allowedToolNames,
connectionPolicies: next.connectionPolicies,
queryTimeoutSecs: next.queryTimeoutSecs,
});
} catch (error) {