Skip to content
Open
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
252 changes: 252 additions & 0 deletions docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -89329,6 +89329,258 @@
}
}
},
"/api/internal_mcp_catalog/{id}/local-config-secret": {
"delete": {
"operationId": "deleteInternalMcpCatalogItemLocalConfigSecret",
"tags": [
"MCP Catalog"
],
"description": "Delete the DB-stored local config secret for a catalog item",
"parameters": [
{
"schema": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$"
},
"in": "path",
"name": "id",
"required": true
}
],
"responses": {
"200": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"required": [
"success"
],
"additionalProperties": false
}
}
}
},
"400": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"api_validation_error"
]
}
},
"required": [
"message",
"type"
],
"additionalProperties": false
}
},
"required": [
"error"
],
"additionalProperties": false
}
}
}
},
"401": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"api_authentication_error"
]
}
},
"required": [
"message",
"type"
],
"additionalProperties": false
}
},
"required": [
"error"
],
"additionalProperties": false
}
}
}
},
"403": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"api_authorization_error"
]
}
},
"required": [
"message",
"type"
],
"additionalProperties": false
}
},
"required": [
"error"
],
"additionalProperties": false
}
}
}
},
"404": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"api_not_found_error"
]
}
},
"required": [
"message",
"type"
],
"additionalProperties": false
}
},
"required": [
"error"
],
"additionalProperties": false
}
}
}
},
"409": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"api_conflict_error"
]
}
},
"required": [
"message",
"type"
],
"additionalProperties": false
}
},
"required": [
"error"
],
"additionalProperties": false
}
}
}
},
"500": {
"description": "Default Response",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"api_internal_server_error"
]
}
},
"required": [
"message",
"type"
],
"additionalProperties": false
}
},
"required": [
"error"
],
"additionalProperties": false
}
}
}
}
}
}
},
"/api/internal_mcp_catalog/by-name/{name}": {
"delete": {
"operationId": "deleteInternalMcpCatalogItemByName",
Expand Down
38 changes: 38 additions & 0 deletions platform/backend/src/routes/internal-mcp-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,44 @@ const internalMcpCatalogRoutes: FastifyPluginAsyncZod = async (fastify) => {
},
);

fastify.delete(
"/api/internal_mcp_catalog/:id/local-config-secret",
{
schema: {
operationId: RouteId.DeleteInternalMcpCatalogItemLocalConfigSecret,
description:
"Delete the DB-stored local config secret for a catalog item",
tags: ["MCP Catalog"],
params: z.object({
id: UuidIdSchema,
}),
response: constructResponseSchema(DeleteObjectResponseSchema),
},
},
async ({ params: { id } }, reply) => {
if (isBuiltInCatalogId(id)) {
throw new ApiError(403, "Built-in catalog items cannot be modified");
}

const catalogItem = await InternalMcpCatalogModel.findById(id, {
expandSecrets: false,
});

if (!catalogItem) {
throw new ApiError(404, "Catalog item not found");
}

if (catalogItem.localConfigSecretId) {
await secretManager().deleteSecret(catalogItem.localConfigSecretId);
await InternalMcpCatalogModel.update(id, {
localConfigSecretId: null,
});
}

return reply.send({ success: true });
},
);

fastify.delete(
"/api/internal_mcp_catalog/by-name/:name",
{
Expand Down
14 changes: 7 additions & 7 deletions platform/backend/src/routes/secrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { RouteId, SecretsManagerType } from "@shared";
import type { FastifyPluginAsyncZod } from "fastify-type-provider-zod";
import { z } from "zod";
import SecretModel from "@/models/secret";
import { isByosEnabled, secretManager } from "@/secrets-manager";
import { secretManager } from "@/secrets-manager";
import {
ApiError,
constructResponseSchema,
Expand Down Expand Up @@ -48,20 +48,20 @@ const secretsRoutes: FastifyPluginAsyncZod = async (fastify) => {
},
},
async ({ params: { id } }, reply) => {
// Security: Only allow access to secrets when BYOS is enabled or the secret is a BYOS secret.
// This prevents exposing actual secret values (API keys, tokens, etc.) when BYOS is not enabled.
// When BYOS is enabled, secrets contain vault references (safe to expose) rather than actual values.
// Security: Only expose BYOS secrets (which contain vault references, safe to expose).
// Non-BYOS secrets contain actual values (API keys, tokens, etc.) and must not be leaked.
const secret = await SecretModel.findById(id);

if (!secret) {
throw new ApiError(404, "Secret not found");
}

// Only allow access if BYOS is enabled globally OR the secret is a BYOS secret
if (!isByosEnabled() && !secret.isByosVault) {
// Only allow access if the secret is a BYOS secret to not leak actual values
// (we want to expose only vault references)
if (!secret.isByosVault) {
throw new ApiError(
403,
"Access to secrets is only allowed for BYOS (Bring Your Own Secrets) secrets when BYOS is enabled",
"Access to non-BYOS secrets is not allowed via this endpoint",
);
}

Expand Down
Loading
Loading