Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .secrets.baseline

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

106 changes: 74 additions & 32 deletions mcpgateway/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8776,9 +8776,11 @@ async def admin_tool_ops_partial(
@admin_router.get("/tools/ids", response_class=JSONResponse)
@require_permission("tools.read", allow_admin_bypass=False)
async def admin_get_all_tool_ids(
q: str = Query("", description="Search query"),
include_inactive: bool = False,
gateway_id: Optional[str] = Query(None, description="Filter by gateway ID(s), comma-separated"),
team_id: Optional[str] = Depends(_validated_team_id_param),
include_public: bool = False,
db: Session = Depends(get_db),
user=Depends(get_current_user_with_permissions),
):
Expand All @@ -8788,9 +8790,11 @@ async def admin_get_all_tool_ids(
This is used by "Select All" to get all tool IDs without loading full data.

Args:
q (str): Search query to filter tools by name, ID, or description
include_inactive (bool): Whether to include inactive tools in the results
gateway_id (Optional[str]): Filter by gateway ID(s), comma-separated. Accepts the literal value 'null' to indicate NULL gateway_id (local tools).
team_id (Optional[str]): Filter by team ID.
include_public (bool): Whether to include all platform-public tools when filtering by team.
db (Session): Database session dependency
user: Current user making the request

Expand All @@ -8807,6 +8811,21 @@ async def admin_get_all_tool_ids(
if not include_inactive:
query = query.where(DbTool.enabled.is_(True))

# Apply search filter if provided
if q:
search_query = _normalize_search_query(q)
if search_query:
search_conditions = [
_like_contains(func.lower(DbTool.id), search_query),
_like_contains(func.lower(DbTool.original_name), search_query),
_like_contains(func.lower(coalesce(DbTool.display_name, "")), search_query),
_like_contains(func.lower(coalesce(DbTool.custom_name, "")), search_query),
_like_contains(func.lower(coalesce(DbTool.description, "")), search_query),
_like_contains(func.lower(coalesce(DbTool.url, "")), search_query),
]
query = query.where(or_(*search_conditions))
LOGGER.debug(f"Filtering tool IDs by search query: {search_query}")

# Apply optional gateway/server scoping (comma-separated ids). Accepts the
# literal value 'null' to indicate NULL gateway_id (local tools).
if gateway_id:
Expand All @@ -8825,22 +8844,19 @@ async def admin_get_all_tool_ids(
LOGGER.debug(f"Filtering tools by gateway IDs: {non_null_ids}")

# Build access conditions
# When team_id is specified, show items from that team plus all platform-public tools
# (visibility="public") so the "Select All" count and payload match what is actually
# visible in the edit UI. Public visibility is platform-wide regardless of team ownership.
# When team_id is specified, show items from that team; optionally include
# platform-public items when include_public is set (mirrors the partial endpoint).
# Otherwise, show all accessible items (All Teams view).
if team_id:
if team_id in team_ids:
# Apply visibility check: team/public resources + user's own resources (including private)
# Also include all platform-public tools so they can be associated with team-owned
# virtual servers.
team_access = [
and_(DbTool.team_id == team_id, DbTool.visibility.in_(["team", "public"])),
and_(DbTool.team_id == team_id, DbTool.owner_email == user_email),
DbTool.visibility == "public",
]
if include_public:
team_access.append(DbTool.visibility == "public")
query = query.where(or_(*team_access))
LOGGER.debug(f"Filtering tool IDs by team_id: {team_id}")
LOGGER.debug(f"Filtering tool IDs by team_id: {team_id}{' (include_public)' if include_public else ''}")
else:
LOGGER.warning(f"User {user_email} attempted to filter tool IDs by team {team_id} but is not a member")
query = query.where(false())
Expand Down Expand Up @@ -10020,9 +10036,11 @@ async def admin_resources_partial_html(
@admin_router.get("/prompts/ids", response_class=JSONResponse)
@require_permission("prompts.read", allow_admin_bypass=False)
async def admin_get_all_prompt_ids(
q: str = Query("", description="Search query"),
include_inactive: bool = False,
gateway_id: Optional[str] = Query(None, description="Filter by gateway ID(s), comma-separated"),
team_id: Optional[str] = Depends(_validated_team_id_param),
include_public: bool = False,
db: Session = Depends(get_db),
user=Depends(get_current_user_with_permissions),
):
Expand All @@ -10032,9 +10050,11 @@ async def admin_get_all_prompt_ids(
of prompts the requesting user can access (owner, team, or public).

Args:
q (str): Search query to filter prompts by name or description.
include_inactive (bool): When True include prompts that are inactive.
gateway_id (Optional[str]): Filter by gateway ID(s), comma-separated. Accepts the literal value 'null' to indicate NULL gateway_id (local prompts).
team_id (Optional[str]): Filter by team ID.
include_public (bool): Whether to include all platform-public prompts when filtering by team.
db (Session): Database session (injected dependency).
user: Authenticated user object from dependency injection.

Expand All @@ -10048,6 +10068,22 @@ async def admin_get_all_prompt_ids(

query = select(DbPrompt.id)

if not include_inactive:
query = query.where(DbPrompt.enabled.is_(True))

# Apply search filter if provided
if q:
search_query = _normalize_search_query(q)
if search_query:
search_conditions = [
_like_contains(func.lower(DbPrompt.id), search_query),
_like_contains(func.lower(DbPrompt.original_name), search_query),
_like_contains(func.lower(coalesce(DbPrompt.display_name, "")), search_query),
_like_contains(func.lower(coalesce(DbPrompt.description, "")), search_query),
]
query = query.where(or_(*search_conditions))
LOGGER.debug(f"Filtering prompt IDs by search query: {search_query}")

# Apply optional gateway/server scoping
if gateway_id:
gateway_ids = [gid.strip() for gid in gateway_id.split(",") if gid.strip()]
Expand All @@ -10064,27 +10100,20 @@ async def admin_get_all_prompt_ids(
query = query.where(DbPrompt.gateway_id.in_(non_null_ids))
LOGGER.debug(f"Filtering prompts by gateway IDs: {non_null_ids}")

if not include_inactive:
query = query.where(DbPrompt.enabled.is_(True))

# Build access conditions
# When team_id is specified, show items from that team plus all platform-public prompts
# (visibility="public") so the "Select All" count and payload match what is actually
# visible in the edit UI. Public visibility is platform-wide regardless of team ownership.
# When team_id is specified, show items from that team; optionally include
# platform-public items when include_public is set (mirrors the partial endpoint).
# Otherwise, show all accessible items (All Teams view).
if team_id:
# Team-specific view: show prompts from the specified team plus platform-public prompts
if team_id in team_ids:
# Apply visibility check: team/public resources + user's own resources (including private)
# Also include all platform-public prompts so they can be associated with team-owned
# virtual servers.
team_access = [
and_(DbPrompt.team_id == team_id, DbPrompt.visibility.in_(["team", "public"])),
and_(DbPrompt.team_id == team_id, DbPrompt.owner_email == user_email),
DbPrompt.visibility == "public",
]
if include_public:
team_access.append(DbPrompt.visibility == "public")
query = query.where(or_(*team_access))
LOGGER.debug(f"Filtering prompt IDs by team_id: {team_id}")
LOGGER.debug(f"Filtering prompt IDs by team_id: {team_id}{' (include_public)' if include_public else ''}")
else:
# User is not a member of this team, return no results using SQLAlchemy's false()
LOGGER.warning(f"User {user_email} attempted to filter prompt IDs by team {team_id} but is not a member")
Expand All @@ -10105,9 +10134,11 @@ async def admin_get_all_prompt_ids(
@admin_router.get("/resources/ids", response_class=JSONResponse)
@require_permission("resources.read", allow_admin_bypass=False)
async def admin_get_all_resource_ids(
q: str = Query("", description="Search query"),
include_inactive: bool = False,
gateway_id: Optional[str] = Query(None, description="Filter by gateway ID(s), comma-separated"),
team_id: Optional[str] = Depends(_validated_team_id_param),
include_public: bool = False,
db: Session = Depends(get_db),
user=Depends(get_current_user_with_permissions),
):
Expand All @@ -10117,9 +10148,11 @@ async def admin_get_all_resource_ids(
of resources the requesting user can access (owner, team, or public).

Args:
q (str): Search query to filter resources by name, URI, or description.
include_inactive (bool): Whether to include inactive resources in the results.
gateway_id (Optional[str]): Filter by gateway ID(s), comma-separated. Accepts the literal value 'null' to indicate NULL gateway_id (local resources).
team_id (Optional[str]): Filter by team ID.
include_public (bool): Whether to include all platform-public resources when filtering by team.
db (Session): Database session dependency.
user: Authenticated user object from dependency injection.

Expand All @@ -10133,6 +10166,22 @@ async def admin_get_all_resource_ids(

query = select(DbResource.id)

if not include_inactive:
query = query.where(DbResource.enabled.is_(True))

# Apply search filter if provided
if q:
search_query = _normalize_search_query(q)
if search_query:
search_conditions = [
_like_contains(func.lower(DbResource.id), search_query),
_like_contains(func.lower(DbResource.name), search_query),
_like_contains(func.lower(coalesce(DbResource.uri, "")), search_query),
_like_contains(func.lower(coalesce(DbResource.description, "")), search_query),
]
query = query.where(or_(*search_conditions))
LOGGER.debug(f"Filtering resource IDs by search query: {search_query}")

# Apply optional gateway/server scoping
if gateway_id:
gateway_ids = [gid.strip() for gid in gateway_id.split(",") if gid.strip()]
Expand All @@ -10149,27 +10198,20 @@ async def admin_get_all_resource_ids(
query = query.where(DbResource.gateway_id.in_(non_null_ids))
LOGGER.debug(f"Filtering resources by gateway IDs: {non_null_ids}")

if not include_inactive:
query = query.where(DbResource.enabled.is_(True))

# Build access conditions
# When team_id is specified, show items from that team plus all platform-public resources
# (visibility="public") so the "Select All" count and payload match what is actually
# visible in the edit UI. Public visibility is platform-wide regardless of team ownership.
# When team_id is specified, show items from that team; optionally include
# platform-public items when include_public is set (mirrors the partial endpoint).
# Otherwise, show all accessible items (All Teams view).
if team_id:
# Team-specific view: show resources from the specified team plus platform-public resources
if team_id in team_ids:
# Apply visibility check: team/public resources + user's own resources (including private)
# Also include all platform-public resources so they can be associated with team-owned
# virtual servers.
team_access = [
and_(DbResource.team_id == team_id, DbResource.visibility.in_(["team", "public"])),
and_(DbResource.team_id == team_id, DbResource.owner_email == user_email),
DbResource.visibility == "public",
]
if include_public:
team_access.append(DbResource.visibility == "public")
query = query.where(or_(*team_access))
LOGGER.debug(f"Filtering resource IDs by team_id: {team_id}")
LOGGER.debug(f"Filtering resource IDs by team_id: {team_id}{' (include_public)' if include_public else ''}")
else:
# User is not a member of this team, return no results using SQLAlchemy's false()
LOGGER.warning(f"User {user_email} attempted to filter resource IDs by team {team_id} but is not a member")
Expand Down
17 changes: 17 additions & 0 deletions mcpgateway/admin_ui/prompts.js
Original file line number Diff line number Diff line change
Expand Up @@ -749,13 +749,30 @@ export const initPromptSelect = function (
? getSelectedGatewayIds()
: [];
const selectedTeamId = getCurrentTeamId();
const searchInputId =
selectId === "edit-server-prompts"
? "searchEditPrompts"
: "searchPrompts";
const searchInput = document.getElementById(searchInputId);
const searchTerm = searchInput ? searchInput.value.trim() : "";
const params = new URLSearchParams();
if (selectedGatewayIds && selectedGatewayIds.length) {
params.set("gateway_id", selectedGatewayIds.join(","));
}
if (selectedTeamId) {
params.set("team_id", selectedTeamId);
}
if (searchTerm) {
params.set("q", searchTerm);
}
const viewPublicId =
selectId === "edit-server-prompts"
? "edit-server-view-public"
: "add-server-view-public";
const viewPublicCb = document.getElementById(viewPublicId);
if (viewPublicCb && viewPublicCb.checked) {
params.set("include_public", "true");
}
const queryString = params.toString();
const resp = await fetch(
`${window.ROOT_PATH}/admin/prompts/ids${queryString ? `?${queryString}` : ""}`
Expand Down
17 changes: 17 additions & 0 deletions mcpgateway/admin_ui/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -996,13 +996,30 @@ export const initResourceSelect = function (
? getSelectedGatewayIds()
: [];
const selectedTeamId = getCurrentTeamId();
const searchInputId =
selectId === "edit-server-resources"
? "searchEditResources"
: "searchResources";
const searchInput = document.getElementById(searchInputId);
const searchTerm = searchInput ? searchInput.value.trim() : "";
const params = new URLSearchParams();
if (selectedGatewayIds && selectedGatewayIds.length) {
params.set("gateway_id", selectedGatewayIds.join(","));
}
if (selectedTeamId) {
params.set("team_id", selectedTeamId);
}
if (searchTerm) {
params.set("q", searchTerm);
}
const viewPublicId =
selectId === "edit-server-resources"
? "edit-server-view-public"
: "add-server-view-public";
const viewPublicCb = document.getElementById(viewPublicId);
if (viewPublicCb && viewPublicCb.checked) {
params.set("include_public", "true");
}
const queryString = params.toString();
const resp = await fetch(
`${window.ROOT_PATH}/admin/resources/ids${queryString ? `?${queryString}` : ""}`
Expand Down
17 changes: 17 additions & 0 deletions mcpgateway/admin_ui/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -1197,13 +1197,30 @@ export const initToolSelect = function (
? getSelectedGatewayIds()
: [];
const selectedTeamId = getCurrentTeamId();
const searchInputId =
selectId === "edit-server-tools"
? "searchEditTools"
: "searchTools";
const searchInput = document.getElementById(searchInputId);
const searchTerm = searchInput ? searchInput.value.trim() : "";
const params = new URLSearchParams();
if (selectedGatewayIds && selectedGatewayIds.length) {
params.set("gateway_id", selectedGatewayIds.join(","));
}
if (selectedTeamId) {
params.set("team_id", selectedTeamId);
}
if (searchTerm) {
params.set("q", searchTerm);
}
const viewPublicId =
selectId === "edit-server-tools"
? "edit-server-view-public"
: "add-server-view-public";
const viewPublicCb = document.getElementById(viewPublicId);
if (viewPublicCb && viewPublicCb.checked) {
params.set("include_public", "true");
}
const queryString = params.toString();
const response = await fetch(
`${window.ROOT_PATH}/admin/tools/ids${queryString ? `?${queryString}` : ""}`
Expand Down
Loading
Loading