From aa5c6485fab6128a7beee4dd7ae559c7f20fb497 Mon Sep 17 00:00:00 2001
From: hieptl
Date: Mon, 2 Nov 2026 10:11:02 +0700
Subject: [PATCH] feat(settings): list organization MCP servers in the Agent
Profile editors (OHE-3863)
Behind enable_org_mcp_servers, both Agent Profile editors see the
organization's MCP server listings:
- The personal editor's MCP picker lists the organization's listings
after the member's own servers, with the Organization badge. "All
servers" now means every personal and organization server, as the
launch merge does; "Choose servers" can toggle organization entries
individually, and dangling references include organization names.
- The org editor shows the MCP section it used to hide, listing the
organization-managed listings only as toggles with no "All servers"
mode (that would mean every launching member's own servers). The
save body sends the chosen names, [] when none, as before; the
stored list is kept when editing instead of being forced to [].
- The org section description, the org editor hint and the
legacy-profile warning no longer say "no MCP servers" once the flag
is on.
Flag off, the editors and their save bodies are unchanged.
---
.../agent-profile-form-state.test.ts | 64 ++++++
.../agent-profile-form.test.tsx | 182 ++++++++++++++++++
.../agent-profile-form-state.ts | 19 +-
.../agent-profiles/agent-profile-form.tsx | 116 ++++++-----
.../agent-profiles/agent-profiles-manager.tsx | 18 +-
.../agent-profiles/agent-profiles-view.tsx | 84 ++++++--
frontend/src/i18n/declaration.ts | 5 +
frontend/src/i18n/translation.json | 85 ++++++++
8 files changed, 509 insertions(+), 64 deletions(-)
create mode 100644 frontend/__tests__/components/features/settings/agent-profiles/agent-profile-form-state.test.ts
create mode 100644 frontend/__tests__/components/features/settings/agent-profiles/agent-profile-form.test.tsx
diff --git a/frontend/__tests__/components/features/settings/agent-profiles/agent-profile-form-state.test.ts b/frontend/__tests__/components/features/settings/agent-profiles/agent-profile-form-state.test.ts
new file mode 100644
index 0000000000..6b27ebf32e
--- /dev/null
+++ b/frontend/__tests__/components/features/settings/agent-profiles/agent-profile-form-state.test.ts
@@ -0,0 +1,64 @@
+import { describe, expect, it } from "vitest";
+import {
+ AgentProfileFormDeps,
+ buildAgentProfileSaveInput,
+ newAgentProfileFormState,
+} from "#/components/features/settings/agent-profiles/agent-profile-form-state";
+
+const deps: AgentProfileFormDeps = {
+ acpProviders: [],
+ defaultLlmProfileRef: "Default",
+ schema: null,
+};
+
+describe("buildAgentProfileSaveInput MCP server references", () => {
+ it("sends no MCP servers for an org profile while org MCP listings are off", () => {
+ const orgDeps = { ...deps, profileScope: "org" as const };
+ const state = {
+ ...newAgentProfileFormState(orgDeps),
+ mcpMode: "custom" as const,
+ mcpSelected: ["datadog-shared"],
+ };
+
+ const input = buildAgentProfileSaveInput(state, orgDeps);
+
+ expect(input.mcp_server_refs).toEqual([]);
+ });
+
+ it("sends the chosen organization-managed servers for an org profile when on", () => {
+ const orgDeps = {
+ ...deps,
+ profileScope: "org" as const,
+ orgMcpServersEnabled: true,
+ };
+ const state = {
+ ...newAgentProfileFormState(orgDeps),
+ mcpMode: "custom" as const,
+ mcpSelected: ["datadog-shared"],
+ };
+
+ const input = buildAgentProfileSaveInput(state, orgDeps);
+
+ expect(input.mcp_server_refs).toEqual(["datadog-shared"]);
+ // Nothing chosen stays an explicit "none", never "all".
+ expect(
+ buildAgentProfileSaveInput({ ...state, mcpSelected: [] }, orgDeps)
+ .mcp_server_refs,
+ ).toEqual([]);
+ });
+
+ it("keeps the personal semantics: null for all servers, the list when chosen", () => {
+ const personalDeps = { ...deps, orgMcpServersEnabled: true };
+ const standard = newAgentProfileFormState(personalDeps);
+
+ expect(
+ buildAgentProfileSaveInput(standard, personalDeps).mcp_server_refs,
+ ).toBeNull();
+ expect(
+ buildAgentProfileSaveInput(
+ { ...standard, mcpMode: "custom", mcpSelected: ["a", "bifrost"] },
+ personalDeps,
+ ).mcp_server_refs,
+ ).toEqual(["a", "bifrost"]);
+ });
+});
diff --git a/frontend/__tests__/components/features/settings/agent-profiles/agent-profile-form.test.tsx b/frontend/__tests__/components/features/settings/agent-profiles/agent-profile-form.test.tsx
new file mode 100644
index 0000000000..8a4e2f573d
--- /dev/null
+++ b/frontend/__tests__/components/features/settings/agent-profiles/agent-profile-form.test.tsx
@@ -0,0 +1,182 @@
+import { fireEvent, render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import { AgentProfileForm } from "#/components/features/settings/agent-profiles/agent-profile-form";
+import {
+ AgentProfileFormDeps,
+ AgentProfileFormState,
+ newAgentProfileFormState,
+} from "#/components/features/settings/agent-profiles/agent-profile-form-state";
+import type { ProfileScopeItem } from "#/components/features/settings/agent-profiles/profile-scope-list";
+import type { AcpCredentialForm } from "#/hooks/use-acp-credential-form";
+
+vi.mock("react-i18next", () => ({
+ useTranslation: () => ({
+ t: (key: string, options?: Record) =>
+ options?.names ? `${key}:${options.names}` : key,
+ }),
+}));
+
+const deps: AgentProfileFormDeps = {
+ acpProviders: [],
+ defaultLlmProfileRef: "Default",
+ schema: null,
+};
+
+const credentialForm: AcpCredentialForm = {
+ fields: [],
+ values: {},
+ setValue: () => {},
+ secretExists: () => false,
+ hasValueFor: () => false,
+ conflicts: [],
+ isDirty: false,
+ save: async () => true,
+ reset: () => {},
+ isSaving: false,
+};
+
+const ORG_MCP_ITEMS: ProfileScopeItem[] = [
+ {
+ name: "datadog-shared",
+ description: "https://dd.example/mcp",
+ scope: "organization",
+ },
+];
+
+function renderForm({
+ state,
+ scope,
+ mcpServers,
+ orgMcpServersEnabled,
+ onChange = vi.fn(),
+}: {
+ state: AgentProfileFormState;
+ scope: "personal" | "org";
+ mcpServers: ProfileScopeItem[];
+ orgMcpServersEnabled?: boolean;
+ onChange?: (patch: Partial) => void;
+}) {
+ render(
+ ,
+ );
+ return onChange;
+}
+
+describe("AgentProfileForm MCP servers", () => {
+ it("still leaves the MCP section out of an org profile while the flag is off", () => {
+ renderForm({
+ state: newAgentProfileFormState({ ...deps, profileScope: "org" }),
+ scope: "org",
+ mcpServers: ORG_MCP_ITEMS,
+ });
+
+ expect(
+ screen.queryByTestId("agent-profile-mcp-list"),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByTestId("agent-profile-mcp-mode"),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByTestId("agent-profile-mcp-hint"),
+ ).not.toBeInTheDocument();
+ });
+
+ it("lets an org profile toggle the organization-managed servers, with no 'all' mode", () => {
+ const onChange = renderForm({
+ state: {
+ ...newAgentProfileFormState({ ...deps, profileScope: "org" }),
+ mcpMode: "custom",
+ mcpSelected: [],
+ },
+ scope: "org",
+ mcpServers: ORG_MCP_ITEMS,
+ orgMcpServersEnabled: true,
+ });
+
+ expect(
+ screen.queryByTestId("agent-profile-mcp-mode"),
+ ).not.toBeInTheDocument();
+ expect(screen.getByTestId("agent-profile-mcp-hint")).toHaveTextContent(
+ "SETTINGS$AGENT_PROFILE_ORG_MCP_HINT",
+ );
+ const toggle = screen.getByTestId("agent-profile-mcp-datadog-shared");
+ expect(toggle).not.toBeChecked();
+ expect(toggle).not.toBeDisabled();
+
+ fireEvent.click(toggle);
+
+ expect(onChange).toHaveBeenCalledWith({ mcpSelected: ["datadog-shared"] });
+ });
+
+ it("tells an org admin when there is nothing to pick yet", () => {
+ renderForm({
+ state: {
+ ...newAgentProfileFormState({ ...deps, profileScope: "org" }),
+ mcpMode: "custom",
+ },
+ scope: "org",
+ mcpServers: [],
+ orgMcpServersEnabled: true,
+ });
+
+ expect(screen.getByTestId("agent-profile-mcp-none")).toHaveTextContent(
+ "SETTINGS$AGENT_PROFILE_ORG_MCP_NONE",
+ );
+ });
+
+ it("flags an org profile's reference to a listing that no longer exists", () => {
+ renderForm({
+ state: {
+ ...newAgentProfileFormState({ ...deps, profileScope: "org" }),
+ mcpMode: "custom",
+ mcpSelected: ["gone"],
+ },
+ scope: "org",
+ mcpServers: ORG_MCP_ITEMS,
+ orgMcpServersEnabled: true,
+ });
+
+ expect(screen.getByTestId("agent-profile-mcp-dangling")).toHaveTextContent(
+ "SETTINGS$AGENT_PROFILE_MCP_DANGLING:gone",
+ );
+ });
+
+ it("badges the organization's listings in a personal profile's picker", () => {
+ renderForm({
+ state: {
+ ...newAgentProfileFormState(deps),
+ mcpMode: "custom",
+ mcpSelected: ["mine"],
+ },
+ scope: "personal",
+ mcpServers: [{ name: "mine", description: null }, ...ORG_MCP_ITEMS],
+ orgMcpServersEnabled: true,
+ });
+
+ expect(screen.getByTestId("agent-profile-mcp-mode")).toBeInTheDocument();
+ expect(
+ screen.getByTestId("agent-profile-mcp-datadog-shared-org-badge"),
+ ).toBeInTheDocument();
+ expect(
+ screen.queryByTestId("agent-profile-mcp-mine-org-badge"),
+ ).not.toBeInTheDocument();
+ expect(screen.getByTestId("agent-profile-mcp-hint")).toHaveTextContent(
+ "SETTINGS$AGENT_PROFILE_MCP_CHOOSE_HINT",
+ );
+ });
+});
diff --git a/frontend/src/components/features/settings/agent-profiles/agent-profile-form-state.ts b/frontend/src/components/features/settings/agent-profiles/agent-profile-form-state.ts
index b67c6c8671..fadd0b2d1e 100644
--- a/frontend/src/components/features/settings/agent-profiles/agent-profile-form-state.ts
+++ b/frontend/src/components/features/settings/agent-profiles/agent-profile-form-state.ts
@@ -97,6 +97,12 @@ export interface AgentProfileFormDeps {
* its secrets explicitly (OHE-3088). Defaults to ``personal``.
*/
profileScope?: AgentProfileScope;
+ /**
+ * ``feature_flags.enable_org_mcp_servers`` (OHE-3091): an org profile may
+ * then name the organization-managed MCP server listings it uses. Off, an
+ * org save keeps sending ``mcp_server_refs: []``.
+ */
+ orgMcpServersEnabled?: boolean;
}
export function newAgentProfileFormState(
@@ -261,9 +267,16 @@ export function buildAgentProfileSaveInput(
stored?: AgentProfile | null,
): AgentProfileSaveInput {
const isOrg = deps.profileScope === "org";
- const mcpServerRefs = isOrg
- ? []
- : scopeRefs(state.mcpMode, state.mcpSelected);
+ // An org profile names the organization-managed servers it uses, or none;
+ // never "all", which would mean every launching member's own servers.
+ let mcpServerRefs: string[] | null;
+ if (!isOrg) {
+ mcpServerRefs = scopeRefs(state.mcpMode, state.mcpSelected);
+ } else if (deps.orgMcpServersEnabled) {
+ mcpServerRefs = state.mcpSelected;
+ } else {
+ mcpServerRefs = [];
+ }
const secretRefs = isOrg
? state.secretsSelected
: scopeRefs(state.secretsMode, state.secretsSelected);
diff --git a/frontend/src/components/features/settings/agent-profiles/agent-profile-form.tsx b/frontend/src/components/features/settings/agent-profiles/agent-profile-form.tsx
index 45562aaa9b..f1b7f5c012 100644
--- a/frontend/src/components/features/settings/agent-profiles/agent-profile-form.tsx
+++ b/frontend/src/components/features/settings/agent-profiles/agent-profile-form.tsx
@@ -56,7 +56,11 @@ interface AgentProfileFormProps {
toolsLoading?: boolean;
/** True when the tool catalog could not be loaded. */
toolsLoadFailed?: boolean;
- /** The member's configured MCP servers (user-scoped). */
+ /**
+ * The MCP servers the profile may pick: the member's own plus the
+ * organization's listings for a personal profile, the organization-managed
+ * listings for an org profile (OHE-3091).
+ */
mcpServers: ProfileScopeItem[];
/** The member's saved secrets (user-scoped). */
secrets: ProfileScopeItem[];
@@ -67,11 +71,14 @@ interface AgentProfileFormProps {
/** Credentials for the selected ACP preset; saved as global secrets. */
credentialForm: AcpCredentialForm;
/**
- * The collection being edited. An org profile is OpenHands-only, has no
- * MCP servers and always names its secrets, so the agent-kind selector, the
- * MCP section and the secrets mode switch are left out (OHE-3088).
+ * The collection being edited. An org profile is OpenHands-only and always
+ * names its secrets, so the agent-kind selector and the secrets mode switch
+ * are left out (OHE-3088); its MCP section lists the organization-managed
+ * servers only once ``orgMcpServersEnabled`` is on.
*/
profileScope?: AgentProfileScope;
+ /** ``feature_flags.enable_org_mcp_servers``: shows the org profile's MCP section. */
+ orgMcpServersEnabled?: boolean;
}
function toggleName(list: string[], name: string, checked: boolean): string[] {
@@ -104,6 +111,7 @@ export function AgentProfileForm({
skillsLoadFailed = false,
credentialForm,
profileScope = "personal",
+ orgMcpServersEnabled = false,
}: AgentProfileFormProps) {
const { t } = useTranslation();
const isAcp = state.agentKind === "acp";
@@ -132,13 +140,19 @@ export function AgentProfileForm({
];
const knownMcp = new Set(mcpServers.map(({ name }) => name));
const danglingMcpRefs =
- state.mcpMode === "custom"
+ isOrg || state.mcpMode === "custom"
? state.mcpSelected.filter((name) => !knownMcp.has(name))
: [];
const mcpCatalog: ProfileScopeItem[] = [
...mcpServers,
...danglingMcpRefs.map((name) => ({ name, description: null })),
];
+ let mcpHintKey = I18nKey.SETTINGS$AGENT_PROFILE_MCP_ALL_HINT;
+ if (isOrg) {
+ mcpHintKey = I18nKey.SETTINGS$AGENT_PROFILE_ORG_MCP_HINT;
+ } else if (state.mcpMode === "custom") {
+ mcpHintKey = I18nKey.SETTINGS$AGENT_PROFILE_MCP_CHOOSE_HINT;
+ }
const knownSecrets = new Set(secrets.map(({ name }) => name));
// An org profile always names its secrets; a personal one only when narrowed.
const hasSecretList = isOrg || state.secretsMode === "custom";
@@ -549,51 +563,57 @@ export function AgentProfileForm({
>
)}
- {/* MCP servers (both kinds): all configured, or an explicit subset.
- Not for org profiles: MCP servers are per member, so an org profile
+ {/* MCP servers: a personal profile uses all configured servers (the
+ member's own and the organization's listings) or an explicit subset;
+ an org profile names the organization-managed listings it uses, and
+ only once org MCP listings are on (OHE-3091) -- before that it
launches with none. */}
- {!isOrg && (
+ {(!isOrg || orgMcpServersEnabled) && (
{t(I18nKey.SETTINGS$AGENT_PROFILE_MCP)}
-
{
- if (!key) return;
- const mcpMode = key as ProfileScopeMode;
- const patch: Partial = { mcpMode };
- // Seed a first switch to "choose" with every configured server,
- // so narrowing starts from what the agent had.
- if (mcpMode === "custom" && state.mcpSelected.length === 0) {
- patch.mcpSelected = mcpServers.map(({ name }) => name);
- }
- onChange(patch);
- }}
- />
+ {!isOrg ? (
+ {
+ if (!key) return;
+ const mcpMode = key as ProfileScopeMode;
+ const patch: Partial = { mcpMode };
+ // Seed a first switch to "choose" with every configured server,
+ // so narrowing starts from what the agent had.
+ if (mcpMode === "custom" && state.mcpSelected.length === 0) {
+ patch.mcpSelected = mcpServers.map(({ name }) => name);
+ }
+ onChange(patch);
+ }}
+ />
+ ) : null}
{mcpCatalog.length > 0 ? (
name)
}
- isDisabled={isDisabled || state.mcpMode === "standard"}
+ isDisabled={
+ isDisabled || (!isOrg && state.mcpMode === "standard")
+ }
onToggle={(name, checked) =>
onChange({
mcpSelected: toggleName(state.mcpSelected, name, checked),
@@ -601,8 +621,15 @@ export function AgentProfileForm({
}
/>
) : (
-
- {t(I18nKey.SETTINGS$AGENT_PROFILE_MCP_NONE)}
+
+ {t(
+ isOrg
+ ? I18nKey.SETTINGS$AGENT_PROFILE_ORG_MCP_NONE
+ : I18nKey.SETTINGS$AGENT_PROFILE_MCP_NONE,
+ )}
)}
{danglingMcpRefs.length > 0 ? (
@@ -615,12 +642,11 @@ export function AgentProfileForm({
})}
) : null}
-
- {t(
- state.mcpMode === "custom"
- ? I18nKey.SETTINGS$AGENT_PROFILE_MCP_CHOOSE_HINT
- : I18nKey.SETTINGS$AGENT_PROFILE_MCP_ALL_HINT,
- )}
+
+ {t(mcpHintKey)}
)}
diff --git a/frontend/src/components/features/settings/agent-profiles/agent-profiles-manager.tsx b/frontend/src/components/features/settings/agent-profiles/agent-profiles-manager.tsx
index add14d6722..72f7a90283 100644
--- a/frontend/src/components/features/settings/agent-profiles/agent-profiles-manager.tsx
+++ b/frontend/src/components/features/settings/agent-profiles/agent-profiles-manager.tsx
@@ -23,6 +23,9 @@ interface AgentProfilesManagerProps {
isAddOrgDisabled?: boolean;
/** True when the viewer may add, edit and delete the organization's profiles. */
canManageOrg?: boolean;
+ /** ``feature_flags.enable_org_mcp_servers``: org profiles may name
+ * organization-managed MCP servers, so the admin copy says so (OHE-3091). */
+ orgMcpServersEnabled?: boolean;
}
/**
@@ -38,6 +41,7 @@ export function AgentProfilesManager({
isAddDisabled = false,
isAddOrgDisabled = isAddDisabled,
canManageOrg = false,
+ orgMcpServersEnabled = false,
}: AgentProfilesManagerProps) {
const { t } = useTranslation();
const { data, isLoading, error } = useAgentProfiles();
@@ -56,6 +60,14 @@ export function AgentProfilesManager({
);
const activeId = data?.active_agent_profile_id ?? null;
+ let orgSectionDescriptionKey =
+ I18nKey.SETTINGS$ORG_AGENT_PROFILES_DESCRIPTION;
+ if (canManageOrg) {
+ orgSectionDescriptionKey = orgMcpServersEnabled
+ ? I18nKey.SETTINGS$ORG_AGENT_PROFILES_ADMIN_DESCRIPTION_WITH_MCP
+ : I18nKey.SETTINGS$ORG_AGENT_PROFILES_ADMIN_DESCRIPTION;
+ }
+
const handleActivate = async (profile: AgentProfileSummary) => {
if (!profile.id) return;
await mutateWithToast(activateProfile, profile.id, {
@@ -117,11 +129,7 @@ export function AgentProfilesManager({
{t(I18nKey.SETTINGS$ORG_AGENT_PROFILES_TITLE)}
- {t(
- canManageOrg
- ? I18nKey.SETTINGS$ORG_AGENT_PROFILES_ADMIN_DESCRIPTION
- : I18nKey.SETTINGS$ORG_AGENT_PROFILES_DESCRIPTION,
- )}
+ {t(orgSectionDescriptionKey)}
{canManageOrg ? (
diff --git a/frontend/src/components/features/settings/agent-profiles/agent-profiles-view.tsx b/frontend/src/components/features/settings/agent-profiles/agent-profiles-view.tsx
index 3df7cfaa60..28239b69a7 100644
--- a/frontend/src/components/features/settings/agent-profiles/agent-profiles-view.tsx
+++ b/frontend/src/components/features/settings/agent-profiles/agent-profiles-view.tsx
@@ -18,6 +18,7 @@ import { useAgentProfiles } from "#/hooks/query/use-agent-profiles";
import { useAgentSettingsSchema } from "#/hooks/query/use-agent-settings-schema";
import { useConfig } from "#/hooks/query/use-config";
import { useOrgSecrets, useSearchSecrets } from "#/hooks/query/use-get-secrets";
+import { useOrgMcpServers } from "#/hooks/query/use-org-mcp-servers";
import { useMe } from "#/hooks/query/use-me";
import { usePermission } from "#/hooks/organizations/use-permissions";
import { useSkills } from "#/hooks/query/use-skills";
@@ -36,6 +37,7 @@ import {
} from "#/utils/custom-toast-handlers";
import { formControlBackNavButtonClassName } from "#/utils/form-control-classes";
import { parseMcpConfig } from "#/utils/mcp-config";
+import { toOrgMcpServerView } from "#/utils/org-mcp-servers";
import { retrieveAxiosErrorMessage } from "#/utils/retrieve-axios-error-message";
import { AgentProfileForm } from "./agent-profile-form";
import {
@@ -78,19 +80,24 @@ function configuredMcpServers(raw: unknown): ProfileScopeItem[] {
}
/**
- * The org editor's view of a profile's scopes: no MCP servers, and an explicit
- * secrets list -- every org-shared secret when the stored profile had no list
- * (OHE-3088). The save body enforces the same shape.
+ * The org editor's view of a profile's scopes: an explicit MCP list (the
+ * stored one once org MCP listings are on, OHE-3091; otherwise none) and an
+ * explicit secrets list -- every org-shared secret when the stored profile had
+ * no list (OHE-3088). The save body enforces the same shape.
*/
function withOrgScopes(
state: AgentProfileFormState,
stored: AgentProfile | null,
orgSecretNames: string[],
+ orgMcpServersEnabled: boolean,
): AgentProfileFormState {
return {
...state,
mcpMode: "custom",
- mcpSelected: [],
+ mcpSelected:
+ orgMcpServersEnabled && Array.isArray(stored?.mcp_server_refs)
+ ? state.mcpSelected
+ : [],
secretsMode: "custom",
secretsSelected: Array.isArray(stored?.secret_refs)
? state.secretsSelected
@@ -180,6 +187,15 @@ export function AgentProfilesView() {
const acpProviders = config?.acp_providers ?? EMPTY_ACP_PROVIDERS;
const isAcpEnabled = !!config?.feature_flags?.enable_acp;
+ // Organization MCP server listings (OHE-3091): a personal profile may pick
+ // them next to the member's own servers, an org profile the organization-
+ // managed ones. The query stays idle while the flag is off.
+ const orgMcpEnabled = !!config?.feature_flags?.enable_org_mcp_servers;
+ const orgMcpServersQuery = useOrgMcpServers();
+ const orgMcpListings = useMemo(
+ () => orgMcpServersQuery.data.map(toOrgMcpServerView),
+ [orgMcpServersQuery.data],
+ );
// Org admins manage the organization's shared profiles here when the
// org-profiles flag is on; everyone else only sets one as their default.
const canManageOrg =
@@ -207,6 +223,7 @@ export function AgentProfilesView() {
schema: schema ?? null,
defaultLlmProfileRef,
profileScope: editingScope,
+ orgMcpServersEnabled: orgMcpEnabled,
}),
[
acpProviders,
@@ -214,13 +231,45 @@ export function AgentProfilesView() {
schema,
defaultLlmProfileRef,
editingScope,
+ orgMcpEnabled,
],
);
const mcpConfigRaw = settings?.agent_settings?.mcp_config;
- const mcpServers = useMemo(
+ const memberMcpServers = useMemo(
() => configuredMcpServers(mcpConfigRaw),
[mcpConfigRaw],
);
+ // The personal picker lists the organization's listings after the member's
+ // own. A member's server of the same name wins at launch, so it shows once.
+ const mcpServers = useMemo(() => {
+ if (!orgMcpEnabled) return memberMcpServers;
+ const own = new Set(memberMcpServers.map(({ name }) => name));
+ return [
+ ...memberMcpServers,
+ ...orgMcpListings
+ .filter((listing) => !own.has(listing.name))
+ .map((listing) => ({
+ name: listing.name,
+ description: listing.url,
+ scope: "organization" as const,
+ })),
+ ];
+ }, [memberMcpServers, orgMcpListings, orgMcpEnabled]);
+ // An org profile may only name organization-managed listings: a
+ // user-managed one resolves against a member's personal secret.
+ const orgMcpItems = useMemo(
+ () =>
+ orgMcpEnabled
+ ? orgMcpListings
+ .filter((listing) => listing.managedBy === "org")
+ .map((listing) => ({
+ name: listing.name,
+ description: listing.url,
+ scope: "organization" as const,
+ }))
+ : [],
+ [orgMcpListings, orgMcpEnabled],
+ );
// The search list carries personal and organization-shared secrets under
// the names the runtime exposes, so ``secret_refs`` can reference either.
const secretItems = useMemo(
@@ -374,7 +423,9 @@ export function AgentProfilesView() {
if (scope === "org" ? !isOrgEditorReady : !isEditorReady) return;
const scopedDeps = { ...deps, profileScope: scope };
let state = newAgentProfileFormState(scopedDeps);
- if (scope === "org") state = withOrgScopes(state, null, orgSecretNames);
+ if (scope === "org") {
+ state = withOrgScopes(state, null, orgSecretNames, orgMcpEnabled);
+ }
setEditingScope(scope);
setEditingProfile(null);
setProfileName("");
@@ -382,7 +433,7 @@ export function AgentProfilesView() {
setInitialInput(null);
setViewMode("create");
},
- [deps, isEditorReady, isOrgEditorReady, orgSecretNames],
+ [deps, isEditorReady, isOrgEditorReady, orgSecretNames, orgMcpEnabled],
);
const handleEditProfile = useCallback(
@@ -404,7 +455,7 @@ export function AgentProfilesView() {
);
let state = storedState;
if (scope === "org") {
- state = withOrgScopes(state, profile, orgSecretNames);
+ state = withOrgScopes(state, profile, orgSecretNames, orgMcpEnabled);
}
if (profile.agent_kind === "openhands") {
// A profile can reference an LLM profile that has since been
@@ -448,6 +499,7 @@ export function AgentProfilesView() {
llmProfiles,
defaultLlmProfileRef,
orgSecretNames,
+ orgMcpEnabled,
t,
],
);
@@ -530,6 +582,7 @@ export function AgentProfilesView() {
isAddDisabled={!isEditorReady}
isAddOrgDisabled={!isOrgEditorReady}
canManageOrg={canManageOrg}
+ orgMcpServersEnabled={orgMcpEnabled}
/>
);
}
@@ -577,7 +630,11 @@ export function AgentProfilesView() {
data-testid="org-agent-profile-editor-hint"
className="text-sm leading-5 text-muted"
>
- {t(I18nKey.SETTINGS$ORG_AGENT_PROFILE_EDITOR_HINT)}
+ {t(
+ orgMcpEnabled
+ ? I18nKey.SETTINGS$ORG_AGENT_PROFILE_EDITOR_HINT_WITH_MCP
+ : I18nKey.SETTINGS$ORG_AGENT_PROFILE_EDITOR_HINT,
+ )}
) : null}
{isLegacyOrgProfile ? (
@@ -585,7 +642,11 @@ export function AgentProfilesView() {
data-testid="org-agent-profile-legacy-warning"
className="text-sm leading-5 text-yellow-400"
>
- {t(I18nKey.SETTINGS$ORG_AGENT_PROFILE_LEGACY_WARNING)}
+ {t(
+ orgMcpEnabled
+ ? I18nKey.SETTINGS$ORG_AGENT_PROFILE_LEGACY_WARNING_WITH_MCP
+ : I18nKey.SETTINGS$ORG_AGENT_PROFILE_LEGACY_WARNING,
+ )}
) : null}
@@ -622,12 +683,13 @@ export function AgentProfilesView() {
tools={toolItems}
toolsLoading={isToolCatalogPending}
toolsLoadFailed={isToolCatalogError}
- mcpServers={mcpServers}
+ mcpServers={isOrgEditor ? orgMcpItems : mcpServers}
secrets={isOrgEditor ? orgSecretItems : secretItems}
skills={skillItems}
skillsLoadFailed={skillsLoadFailed || isMarketplaceSkillsError}
credentialForm={credentialForm}
profileScope={editingScope}
+ orgMcpServersEnabled={orgMcpEnabled}
/>
diff --git a/frontend/src/i18n/declaration.ts b/frontend/src/i18n/declaration.ts
index f514342859..b9bb3770d0 100644
--- a/frontend/src/i18n/declaration.ts
+++ b/frontend/src/i18n/declaration.ts
@@ -2301,6 +2301,11 @@ export enum I18nKey {
SETTINGS$ORG_AGENT_PROFILES_ADMIN_DESCRIPTION = "SETTINGS$ORG_AGENT_PROFILES_ADMIN_DESCRIPTION",
SETTINGS$ORG_AGENT_PROFILE_EDITOR_HINT = "SETTINGS$ORG_AGENT_PROFILE_EDITOR_HINT",
SETTINGS$ORG_AGENT_PROFILE_LEGACY_WARNING = "SETTINGS$ORG_AGENT_PROFILE_LEGACY_WARNING",
+ SETTINGS$AGENT_PROFILE_ORG_MCP_HINT = "SETTINGS$AGENT_PROFILE_ORG_MCP_HINT",
+ SETTINGS$AGENT_PROFILE_ORG_MCP_NONE = "SETTINGS$AGENT_PROFILE_ORG_MCP_NONE",
+ SETTINGS$ORG_AGENT_PROFILES_ADMIN_DESCRIPTION_WITH_MCP = "SETTINGS$ORG_AGENT_PROFILES_ADMIN_DESCRIPTION_WITH_MCP",
+ SETTINGS$ORG_AGENT_PROFILE_EDITOR_HINT_WITH_MCP = "SETTINGS$ORG_AGENT_PROFILE_EDITOR_HINT_WITH_MCP",
+ SETTINGS$ORG_AGENT_PROFILE_LEGACY_WARNING_WITH_MCP = "SETTINGS$ORG_AGENT_PROFILE_LEGACY_WARNING_WITH_MCP",
SETTINGS$AGENT_PROFILE_ORG_SECRETS_HINT = "SETTINGS$AGENT_PROFILE_ORG_SECRETS_HINT",
SETTINGS$AGENT_PROFILE_ORG_SECRETS_NONE = "SETTINGS$AGENT_PROFILE_ORG_SECRETS_NONE",
SETTINGS$AGENT_PROFILE_ORG_SKILLS_HINT = "SETTINGS$AGENT_PROFILE_ORG_SKILLS_HINT",
diff --git a/frontend/src/i18n/translation.json b/frontend/src/i18n/translation.json
index c6ff185590..71178e5e10 100644
--- a/frontend/src/i18n/translation.json
+++ b/frontend/src/i18n/translation.json
@@ -39116,6 +39116,91 @@
"uk": "Цей профіль досі дозволяє учасникам використовувати власні секрети та сервери MCP. Збереження тут робить його лише організаційним: учасники, які його запускають, отримують лише вибрані нижче спільні секрети організації та жодних серверів MCP.",
"ca": "Aquest perfil encara permet que els membres facin servir els seus propis secrets i servidors MCP. Desar-lo aquí el fa exclusiu de l'organització: els membres que l'iniciïn només rebran els secrets compartits de l'organització triats a sota i cap servidor MCP."
},
+ "SETTINGS$AGENT_PROFILE_ORG_MCP_HINT": {
+ "en": "Switch on the organization-managed MCP servers this profile may use. Members launching it get only those; their own servers stay out.",
+ "ja": "このプロファイルが使用できる組織管理の MCP サーバーをオンにします。起動するメンバーはそれらのみを利用でき、各自のサーバーは含まれません。",
+ "zh-CN": "开启此配置文件可以使用的组织管理 MCP 服务器。启动它的成员只会获得这些服务器,不包含他们自己的服务器。",
+ "zh-TW": "開啟此設定檔可以使用的組織管理 MCP 伺服器。啟動它的成員只會取得這些伺服器,不包含他們自己的伺服器。",
+ "ko-KR": "이 프로필이 사용할 수 있는 조직 관리 MCP 서버를 켜세요. 이 프로필로 시작하는 구성원은 해당 서버만 사용하며 본인 서버는 포함되지 않습니다.",
+ "no": "Slå på de organisasjonsstyrte MCP-serverne denne profilen kan bruke. Medlemmer som starter den får bare disse; deres egne servere holdes utenfor.",
+ "it": "Attiva i server MCP gestiti dall'organizzazione che questo profilo può usare. I membri che lo avviano ottengono solo quelli; i loro server restano esclusi.",
+ "pt": "Ativa os servidores MCP geridos pela organização que este perfil pode usar. Os membros que o iniciam recebem apenas esses; os seus próprios servidores ficam de fora.",
+ "es": "Activa los servidores MCP gestionados por la organización que este perfil puede usar. Los miembros que lo inicien solo obtienen esos; sus propios servidores quedan fuera.",
+ "ar": "فعّل خوادم MCP التي تديرها المؤسسة والتي يمكن لهذا الملف استخدامها. يحصل الأعضاء الذين يشغّلونه على هذه الخوادم فقط؛ وتبقى خوادمهم الخاصة خارجه.",
+ "fr": "Activez les serveurs MCP gérés par l'organisation que ce profil peut utiliser. Les membres qui le lancent n'obtiennent que ceux-là ; leurs propres serveurs restent exclus.",
+ "tr": "Bu profilin kullanabileceği kuruluş tarafından yönetilen MCP sunucularını açın. Profili başlatan üyeler yalnızca bunları alır; kendi sunucuları dışarıda kalır.",
+ "de": "Schalten Sie die von der Organisation verwalteten MCP-Server ein, die dieses Profil nutzen darf. Mitglieder, die es starten, erhalten nur diese; ihre eigenen Server bleiben außen vor.",
+ "uk": "Увімкніть керовані організацією MCP-сервери, які може використовувати цей профіль. Учасники, що запускають його, отримують лише їх; власні сервери не долучаються.",
+ "ca": "Activa els servidors MCP gestionats per l'organització que aquest perfil pot utilitzar. Els membres que l'iniciïn només obtindran aquests; els seus propis servidors en queden fora."
+ },
+ "SETTINGS$AGENT_PROFILE_ORG_MCP_NONE": {
+ "en": "Your organization has no organization-managed MCP servers yet. Add them under Settings > MCP.",
+ "ja": "組織にはまだ組織管理の MCP サーバーがありません。「設定 > MCP」で追加してください。",
+ "zh-CN": "你的组织还没有组织管理的 MCP 服务器。请在“设置 > MCP”中添加。",
+ "zh-TW": "你的組織還沒有組織管理的 MCP 伺服器。請在「設定 > MCP」中新增。",
+ "ko-KR": "조직에 아직 조직 관리 MCP 서버가 없습니다. 설정 > MCP에서 추가하세요.",
+ "no": "Organisasjonen har ingen organisasjonsstyrte MCP-servere ennå. Legg dem til under Innstillinger > MCP.",
+ "it": "L'organizzazione non ha ancora server MCP gestiti dall'organizzazione. Aggiungili in Impostazioni > MCP.",
+ "pt": "A organização ainda não tem servidores MCP geridos pela organização. Adiciona-os em Definições > MCP.",
+ "es": "La organización todavía no tiene servidores MCP gestionados por la organización. Agrégalos en Configuración > MCP.",
+ "ar": "لا تملك مؤسستك خوادم MCP تديرها المؤسسة بعد. أضفها ضمن الإعدادات > MCP.",
+ "fr": "Votre organisation n'a pas encore de serveur MCP géré par l'organisation. Ajoutez-les dans Paramètres > MCP.",
+ "tr": "Kuruluşunuzun henüz kuruluş tarafından yönetilen MCP sunucusu yok. Ayarlar > MCP altından ekleyin.",
+ "de": "Ihre Organisation hat noch keine von der Organisation verwalteten MCP-Server. Fügen Sie sie unter Einstellungen > MCP hinzu.",
+ "uk": "У вашої організації ще немає керованих організацією MCP-серверів. Додайте їх у Налаштування > MCP.",
+ "ca": "L'organització encara no té servidors MCP gestionats per l'organització. Afegeix-los a Configuració > MCP."
+ },
+ "SETTINGS$ORG_AGENT_PROFILES_ADMIN_DESCRIPTION_WITH_MCP": {
+ "en": "Shared with everyone in your organization. They run on organization settings only: organization-shared secrets, organization LLM profiles, organization or instance skills, and the organization-managed MCP servers you choose.",
+ "ja": "組織の全員と共有されます。組織の設定のみで動作します: 組織共有シークレット、組織の LLM プロファイル、組織またはインスタンスのスキル、そして選択した組織管理の MCP サーバー。",
+ "zh-CN": "与组织中的所有人共享。仅使用组织设置运行:组织共享密钥、组织 LLM 配置文件、组织或实例技能,以及你选择的组织管理 MCP 服务器。",
+ "zh-TW": "與組織中的所有人共用。僅使用組織設定執行:組織共用密鑰、組織 LLM 設定檔、組織或執行個體技能,以及你選擇的組織管理 MCP 伺服器。",
+ "ko-KR": "조직의 모든 구성원과 공유됩니다. 조직 설정만으로 실행됩니다: 조직 공유 비밀 값, 조직 LLM 프로필, 조직 또는 인스턴스 스킬, 그리고 선택한 조직 관리 MCP 서버.",
+ "no": "Delt med alle i organisasjonen. De kjører kun på organisasjonsinnstillinger: organisasjonsdelte hemmeligheter, organisasjonens LLM-profiler, organisasjons- eller instansferdigheter og de organisasjonsstyrte MCP-serverne du velger.",
+ "it": "Condivisi con tutti nell'organizzazione. Usano solo impostazioni dell'organizzazione: segreti condivisi, profili LLM dell'organizzazione, skill dell'organizzazione o dell'istanza e i server MCP gestiti dall'organizzazione che scegli.",
+ "pt": "Partilhados com todos na organização. Funcionam apenas com definições da organização: segredos partilhados, perfis LLM da organização, skills da organização ou da instância e os servidores MCP geridos pela organização que escolheres.",
+ "es": "Compartidos con todos en la organización. Funcionan solo con configuración de la organización: secretos compartidos, perfiles LLM de la organización, skills de la organización o de la instancia y los servidores MCP gestionados por la organización que elijas.",
+ "ar": "مشتركة مع الجميع في مؤسستك. تعمل بإعدادات المؤسسة فقط: الأسرار المشتركة للمؤسسة، وملفات LLM الخاصة بالمؤسسة، ومهارات المؤسسة أو المثيل، وخوادم MCP التي تديرها المؤسسة والتي تختارها.",
+ "fr": "Partagés avec toute votre organisation. Ils s'exécutent uniquement avec les paramètres de l'organisation : secrets partagés, profils LLM de l'organisation, compétences de l'organisation ou de l'instance, et les serveurs MCP gérés par l'organisation que vous choisissez.",
+ "tr": "Kuruluşunuzdaki herkesle paylaşılır. Yalnızca kuruluş ayarlarıyla çalışır: kuruluşça paylaşılan gizli anahtarlar, kuruluş LLM profilleri, kuruluş veya örnek becerileri ve seçtiğiniz kuruluş tarafından yönetilen MCP sunucuları.",
+ "de": "Mit allen in Ihrer Organisation geteilt. Sie laufen ausschließlich mit Organisationseinstellungen: organisationsweit geteilte Geheimnisse, LLM-Profile der Organisation, Organisations- oder Instanz-Skills und die von der Organisation verwalteten MCP-Server, die Sie auswählen.",
+ "uk": "Спільні для всіх в організації. Працюють лише з налаштуваннями організації: спільні секрети, LLM-профілі організації, навички організації чи інстансу та обрані вами керовані організацією MCP-сервери.",
+ "ca": "Compartits amb tothom a l'organització. Funcionen només amb configuració de l'organització: secrets compartits, perfils LLM de l'organització, habilitats de l'organització o de la instància i els servidors MCP gestionats per l'organització que triïs."
+ },
+ "SETTINGS$ORG_AGENT_PROFILE_EDITOR_HINT_WITH_MCP": {
+ "en": "An organization profile uses organization settings only, never a member's personal ones: organization-shared secrets, organization LLM profiles, organization or instance skills, and the organization-managed MCP servers chosen below. Any member can start conversations and automations with it.",
+ "ja": "組織プロファイルは組織の設定のみを使用し、メンバー個人の設定は使用しません: 組織共有シークレット、組織の LLM プロファイル、組織またはインスタンスのスキル、そして以下で選択した組織管理の MCP サーバー。どのメンバーもこれで会話や自動化を開始できます。",
+ "zh-CN": "组织配置文件仅使用组织设置,绝不使用成员的个人设置:组织共享密钥、组织 LLM 配置文件、组织或实例技能,以及下方选择的组织管理 MCP 服务器。任何成员都可以用它启动对话和自动化。",
+ "zh-TW": "組織設定檔僅使用組織設定,絕不使用成員的個人設定:組織共用密鑰、組織 LLM 設定檔、組織或執行個體技能,以及下方選擇的組織管理 MCP 伺服器。任何成員都可以用它啟動對話和自動化。",
+ "ko-KR": "조직 프로필은 구성원의 개인 설정이 아닌 조직 설정만 사용합니다: 조직 공유 비밀 값, 조직 LLM 프로필, 조직 또는 인스턴스 스킬, 그리고 아래에서 선택한 조직 관리 MCP 서버. 모든 구성원이 이 프로필로 대화와 자동화를 시작할 수 있습니다.",
+ "no": "En organisasjonsprofil bruker kun organisasjonsinnstillinger, aldri et medlems personlige: organisasjonsdelte hemmeligheter, organisasjonens LLM-profiler, organisasjons- eller instansferdigheter og de organisasjonsstyrte MCP-serverne valgt nedenfor. Alle medlemmer kan starte samtaler og automatiseringer med den.",
+ "it": "Un profilo dell'organizzazione usa solo impostazioni dell'organizzazione, mai quelle personali di un membro: segreti condivisi, profili LLM dell'organizzazione, skill dell'organizzazione o dell'istanza e i server MCP gestiti dall'organizzazione scelti qui sotto. Qualsiasi membro può avviare conversazioni e automazioni con esso.",
+ "pt": "Um perfil da organização usa apenas definições da organização, nunca as pessoais de um membro: segredos partilhados, perfis LLM da organização, skills da organização ou da instância e os servidores MCP geridos pela organização escolhidos abaixo. Qualquer membro pode iniciar conversas e automações com ele.",
+ "es": "Un perfil de la organización usa solo configuración de la organización, nunca la personal de un miembro: secretos compartidos, perfiles LLM de la organización, skills de la organización o de la instancia y los servidores MCP gestionados por la organización elegidos abajo. Cualquier miembro puede iniciar conversaciones y automatizaciones con él.",
+ "ar": "يستخدم ملف المؤسسة إعدادات المؤسسة فقط، وليس الإعدادات الشخصية لأي عضو: الأسرار المشتركة للمؤسسة، وملفات LLM الخاصة بالمؤسسة، ومهارات المؤسسة أو المثيل، وخوادم MCP التي تديرها المؤسسة والمختارة أدناه. يمكن لأي عضو بدء محادثات وأتمتة به.",
+ "fr": "Un profil d'organisation n'utilise que les paramètres de l'organisation, jamais ceux d'un membre : secrets partagés, profils LLM de l'organisation, compétences de l'organisation ou de l'instance, et les serveurs MCP gérés par l'organisation choisis ci-dessous. Tout membre peut lancer des conversations et des automatisations avec lui.",
+ "tr": "Bir kuruluş profili yalnızca kuruluş ayarlarını kullanır, asla bir üyenin kişisel ayarlarını değil: kuruluşça paylaşılan gizli anahtarlar, kuruluş LLM profilleri, kuruluş veya örnek becerileri ve aşağıda seçilen kuruluş tarafından yönetilen MCP sunucuları. Her üye onunla konuşma ve otomasyon başlatabilir.",
+ "de": "Ein Organisationsprofil verwendet ausschließlich Organisationseinstellungen, nie die persönlichen eines Mitglieds: organisationsweit geteilte Geheimnisse, LLM-Profile der Organisation, Organisations- oder Instanz-Skills und die unten gewählten, von der Organisation verwalteten MCP-Server. Jedes Mitglied kann damit Unterhaltungen und Automatisierungen starten.",
+ "uk": "Профіль організації використовує лише налаштування організації, а не особисті налаштування учасника: спільні секрети, LLM-профілі організації, навички організації чи інстансу та обрані нижче керовані організацією MCP-сервери. Будь-який учасник може запускати з ним розмови та автоматизації.",
+ "ca": "Un perfil d'organització només utilitza la configuració de l'organització, mai la personal d'un membre: secrets compartits, perfils LLM de l'organització, habilitats de l'organització o de la instància i els servidors MCP gestionats per l'organització triats a sota. Qualsevol membre pot iniciar-hi converses i automatitzacions."
+ },
+ "SETTINGS$ORG_AGENT_PROFILE_LEGACY_WARNING_WITH_MCP": {
+ "en": "This profile still lets members use their own secrets and MCP servers. Saving it here makes it organization-only: members launching it get only the organization-shared secrets and organization-managed MCP servers chosen below.",
+ "ja": "このプロファイルは現在もメンバー自身のシークレットと MCP サーバーの使用を許可しています。ここで保存すると組織専用になります: 起動するメンバーは以下で選択した組織共有シークレットと組織管理の MCP サーバーのみを利用できます。",
+ "zh-CN": "此配置文件仍允许成员使用自己的密钥和 MCP 服务器。在此保存后将变为仅限组织:启动它的成员只会获得下方选择的组织共享密钥和组织管理 MCP 服务器。",
+ "zh-TW": "此設定檔仍允許成員使用自己的密鑰和 MCP 伺服器。在此儲存後將變為僅限組織:啟動它的成員只會取得下方選擇的組織共用密鑰和組織管理 MCP 伺服器。",
+ "ko-KR": "이 프로필은 여전히 구성원이 본인의 비밀 값과 MCP 서버를 사용하도록 허용합니다. 여기서 저장하면 조직 전용이 됩니다: 이 프로필로 시작하는 구성원은 아래에서 선택한 조직 공유 비밀 값과 조직 관리 MCP 서버만 사용합니다.",
+ "no": "Denne profilen lar fortsatt medlemmer bruke sine egne hemmeligheter og MCP-servere. Lagres den her, blir den kun for organisasjonen: medlemmer som starter den får bare de organisasjonsdelte hemmelighetene og organisasjonsstyrte MCP-serverne valgt nedenfor.",
+ "it": "Questo profilo consente ancora ai membri di usare i propri segreti e server MCP. Salvandolo qui diventa solo dell'organizzazione: i membri che lo avviano ottengono solo i segreti condivisi e i server MCP gestiti dall'organizzazione scelti qui sotto.",
+ "pt": "Este perfil ainda permite que os membros usem os seus próprios segredos e servidores MCP. Guardá-lo aqui torna-o exclusivo da organização: os membros que o iniciam recebem apenas os segredos partilhados e os servidores MCP geridos pela organização escolhidos abaixo.",
+ "es": "Este perfil todavía permite que los miembros usen sus propios secretos y servidores MCP. Guardarlo aquí lo convierte en exclusivo de la organización: los miembros que lo inicien solo obtienen los secretos compartidos y los servidores MCP gestionados por la organización elegidos abajo.",
+ "ar": "لا يزال هذا الملف يسمح للأعضاء باستخدام أسرارهم وخوادم MCP الخاصة بهم. حفظه هنا يجعله خاصًا بالمؤسسة فقط: يحصل الأعضاء الذين يشغّلونه على الأسرار المشتركة للمؤسسة وخوادم MCP التي تديرها المؤسسة المختارة أدناه فقط.",
+ "fr": "Ce profil laisse encore les membres utiliser leurs propres secrets et serveurs MCP. L'enregistrer ici le rend réservé à l'organisation : les membres qui le lancent n'obtiennent que les secrets partagés et les serveurs MCP gérés par l'organisation choisis ci-dessous.",
+ "tr": "Bu profil üyelerin hâlâ kendi gizli anahtarlarını ve MCP sunucularını kullanmasına izin veriyor. Burada kaydetmek onu yalnızca kuruluşa özel yapar: profili başlatan üyeler yalnızca aşağıda seçilen kuruluşça paylaşılan gizli anahtarları ve kuruluş tarafından yönetilen MCP sunucularını alır.",
+ "de": "Dieses Profil erlaubt Mitgliedern noch die eigenen Geheimnisse und MCP-Server. Wird es hier gespeichert, gilt es nur noch für die Organisation: Mitglieder, die es starten, erhalten nur die unten gewählten organisationsweit geteilten Geheimnisse und von der Organisation verwalteten MCP-Server.",
+ "uk": "Цей профіль досі дозволяє учасникам використовувати власні секрети та MCP-сервери. Збереження тут робить його лише організаційним: учасники, що запускають його, отримують лише обрані нижче спільні секрети організації та керовані організацією MCP-сервери.",
+ "ca": "Aquest perfil encara permet que els membres facin servir els seus propis secrets i servidors MCP. Desar-lo aquí el fa exclusiu de l'organització: els membres que l'iniciïn només obtindran els secrets compartits i els servidors MCP gestionats per l'organització triats a sota."
+ },
"SETTINGS$AGENT_PROFILE_ORG_SECRETS_HINT": {
"en": "Organization-shared secrets this agent may use. Members' personal secrets are never passed to an organization profile.",
"ja": "このエージェントが使用できる組織共有のシークレット。メンバーの個人シークレットが組織プロファイルに渡されることはありません。",