diff --git a/.gitignore b/.gitignore index f4b3edd2..dbccf32e 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ yarn-error.log* .cursor/ .vscode/ +.idea/ diff --git a/apps/backend/drizzle/0012_crazy_white_queen.sql b/apps/backend/drizzle/0012_crazy_white_queen.sql new file mode 100644 index 00000000..842f4f03 --- /dev/null +++ b/apps/backend/drizzle/0012_crazy_white_queen.sql @@ -0,0 +1 @@ +ALTER TABLE "mcp_servers" ADD COLUMN "headers" jsonb DEFAULT '{}'::jsonb NOT NULL; \ No newline at end of file diff --git a/apps/backend/drizzle/meta/0012_snapshot.json b/apps/backend/drizzle/meta/0012_snapshot.json new file mode 100644 index 00000000..1acbb231 --- /dev/null +++ b/apps/backend/drizzle/meta/0012_snapshot.json @@ -0,0 +1,1831 @@ +{ + "id": "3c8fdc64-07ec-40a6-b784-5989fe669cb8", + "prevId": "a9a06557-e46e-459c-80c4-2772cd50ccac", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.accounts": { + "name": "accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": { + "api_keys_user_id_idx": { + "name": "api_keys_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "api_keys_key_idx": { + "name": "api_keys_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "api_keys_is_active_idx": { + "name": "api_keys_is_active_idx", + "columns": [ + { + "expression": "is_active", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_keys_user_id_users_id_fk": { + "name": "api_keys_user_id_users_id_fk", + "tableFrom": "api_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + }, + "api_keys_name_per_user_idx": { + "name": "api_keys_name_per_user_idx", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.config": { + "name": "config", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.endpoints": { + "name": "endpoints", + "schema": "", + "columns": { + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "namespace_uuid": { + "name": "namespace_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "enable_api_key_auth": { + "name": "enable_api_key_auth", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "enable_oauth": { + "name": "enable_oauth", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "use_query_param_auth": { + "name": "use_query_param_auth", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "endpoints_namespace_uuid_idx": { + "name": "endpoints_namespace_uuid_idx", + "columns": [ + { + "expression": "namespace_uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "endpoints_user_id_idx": { + "name": "endpoints_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "endpoints_namespace_uuid_namespaces_uuid_fk": { + "name": "endpoints_namespace_uuid_namespaces_uuid_fk", + "tableFrom": "endpoints", + "tableTo": "namespaces", + "columnsFrom": [ + "namespace_uuid" + ], + "columnsTo": [ + "uuid" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "endpoints_user_id_users_id_fk": { + "name": "endpoints_user_id_users_id_fk", + "tableFrom": "endpoints", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "endpoints_name_unique": { + "name": "endpoints_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mcp_servers": { + "name": "mcp_servers", + "schema": "", + "columns": { + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "mcp_server_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'STDIO'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "args": { + "name": "args", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "env": { + "name": "env", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "error_status": { + "name": "error_status", + "type": "mcp_server_error_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'NONE'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "bearer_token": { + "name": "bearer_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "headers": { + "name": "headers", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "mcp_servers_type_idx": { + "name": "mcp_servers_type_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_servers_user_id_idx": { + "name": "mcp_servers_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "mcp_servers_error_status_idx": { + "name": "mcp_servers_error_status_idx", + "columns": [ + { + "expression": "error_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "mcp_servers_user_id_users_id_fk": { + "name": "mcp_servers_user_id_users_id_fk", + "tableFrom": "mcp_servers", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mcp_servers_name_user_unique_idx": { + "name": "mcp_servers_name_user_unique_idx", + "nullsNotDistinct": false, + "columns": [ + "name", + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.namespace_server_mappings": { + "name": "namespace_server_mappings", + "schema": "", + "columns": { + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "namespace_uuid": { + "name": "namespace_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "mcp_server_uuid": { + "name": "mcp_server_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "mcp_server_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ACTIVE'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "namespace_server_mappings_namespace_uuid_idx": { + "name": "namespace_server_mappings_namespace_uuid_idx", + "columns": [ + { + "expression": "namespace_uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "namespace_server_mappings_mcp_server_uuid_idx": { + "name": "namespace_server_mappings_mcp_server_uuid_idx", + "columns": [ + { + "expression": "mcp_server_uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "namespace_server_mappings_status_idx": { + "name": "namespace_server_mappings_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "namespace_server_mappings_namespace_uuid_namespaces_uuid_fk": { + "name": "namespace_server_mappings_namespace_uuid_namespaces_uuid_fk", + "tableFrom": "namespace_server_mappings", + "tableTo": "namespaces", + "columnsFrom": [ + "namespace_uuid" + ], + "columnsTo": [ + "uuid" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "namespace_server_mappings_mcp_server_uuid_mcp_servers_uuid_fk": { + "name": "namespace_server_mappings_mcp_server_uuid_mcp_servers_uuid_fk", + "tableFrom": "namespace_server_mappings", + "tableTo": "mcp_servers", + "columnsFrom": [ + "mcp_server_uuid" + ], + "columnsTo": [ + "uuid" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "namespace_server_mappings_unique_idx": { + "name": "namespace_server_mappings_unique_idx", + "nullsNotDistinct": false, + "columns": [ + "namespace_uuid", + "mcp_server_uuid" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.namespace_tool_mappings": { + "name": "namespace_tool_mappings", + "schema": "", + "columns": { + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "namespace_uuid": { + "name": "namespace_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tool_uuid": { + "name": "tool_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "mcp_server_uuid": { + "name": "mcp_server_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "mcp_server_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ACTIVE'" + }, + "override_name": { + "name": "override_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "override_title": { + "name": "override_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "override_description": { + "name": "override_description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "override_annotations": { + "name": "override_annotations", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "NULL" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "namespace_tool_mappings_namespace_uuid_idx": { + "name": "namespace_tool_mappings_namespace_uuid_idx", + "columns": [ + { + "expression": "namespace_uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "namespace_tool_mappings_tool_uuid_idx": { + "name": "namespace_tool_mappings_tool_uuid_idx", + "columns": [ + { + "expression": "tool_uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "namespace_tool_mappings_mcp_server_uuid_idx": { + "name": "namespace_tool_mappings_mcp_server_uuid_idx", + "columns": [ + { + "expression": "mcp_server_uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "namespace_tool_mappings_status_idx": { + "name": "namespace_tool_mappings_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "namespace_tool_mappings_namespace_uuid_namespaces_uuid_fk": { + "name": "namespace_tool_mappings_namespace_uuid_namespaces_uuid_fk", + "tableFrom": "namespace_tool_mappings", + "tableTo": "namespaces", + "columnsFrom": [ + "namespace_uuid" + ], + "columnsTo": [ + "uuid" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "namespace_tool_mappings_tool_uuid_tools_uuid_fk": { + "name": "namespace_tool_mappings_tool_uuid_tools_uuid_fk", + "tableFrom": "namespace_tool_mappings", + "tableTo": "tools", + "columnsFrom": [ + "tool_uuid" + ], + "columnsTo": [ + "uuid" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "namespace_tool_mappings_mcp_server_uuid_mcp_servers_uuid_fk": { + "name": "namespace_tool_mappings_mcp_server_uuid_mcp_servers_uuid_fk", + "tableFrom": "namespace_tool_mappings", + "tableTo": "mcp_servers", + "columnsFrom": [ + "mcp_server_uuid" + ], + "columnsTo": [ + "uuid" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "namespace_tool_mappings_unique_idx": { + "name": "namespace_tool_mappings_unique_idx", + "nullsNotDistinct": false, + "columns": [ + "namespace_uuid", + "tool_uuid" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.namespaces": { + "name": "namespaces", + "schema": "", + "columns": { + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "namespaces_user_id_idx": { + "name": "namespaces_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "namespaces_user_id_users_id_fk": { + "name": "namespaces_user_id_users_id_fk", + "tableFrom": "namespaces", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "namespaces_name_user_unique_idx": { + "name": "namespaces_name_user_unique_idx", + "nullsNotDistinct": false, + "columns": [ + "name", + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_access_tokens": { + "name": "oauth_access_tokens", + "schema": "", + "columns": { + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'admin'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "oauth_access_tokens_client_id_idx": { + "name": "oauth_access_tokens_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_access_tokens_user_id_idx": { + "name": "oauth_access_tokens_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_access_tokens_expires_at_idx": { + "name": "oauth_access_tokens_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_access_tokens_client_id_oauth_clients_client_id_fk": { + "name": "oauth_access_tokens_client_id_oauth_clients_client_id_fk", + "tableFrom": "oauth_access_tokens", + "tableTo": "oauth_clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_access_tokens_user_id_users_id_fk": { + "name": "oauth_access_tokens_user_id_users_id_fk", + "tableFrom": "oauth_access_tokens", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_authorization_codes": { + "name": "oauth_authorization_codes", + "schema": "", + "columns": { + "code": { + "name": "code", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'admin'" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code_challenge": { + "name": "code_challenge", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "code_challenge_method": { + "name": "code_challenge_method", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "oauth_authorization_codes_client_id_idx": { + "name": "oauth_authorization_codes_client_id_idx", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_authorization_codes_user_id_idx": { + "name": "oauth_authorization_codes_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "oauth_authorization_codes_expires_at_idx": { + "name": "oauth_authorization_codes_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_authorization_codes_client_id_oauth_clients_client_id_fk": { + "name": "oauth_authorization_codes_client_id_oauth_clients_client_id_fk", + "tableFrom": "oauth_authorization_codes", + "tableTo": "oauth_clients", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_authorization_codes_user_id_users_id_fk": { + "name": "oauth_authorization_codes_user_id_users_id_fk", + "tableFrom": "oauth_authorization_codes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_clients": { + "name": "oauth_clients", + "schema": "", + "columns": { + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_name": { + "name": "client_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{}'::text[]" + }, + "grant_types": { + "name": "grant_types", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{\"authorization_code\",\"refresh_token\"}'::text[]" + }, + "response_types": { + "name": "response_types", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "'{\"code\"}'::text[]" + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'admin'" + }, + "client_uri": { + "name": "client_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo_uri": { + "name": "logo_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contacts": { + "name": "contacts", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "tos_uri": { + "name": "tos_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "policy_uri": { + "name": "policy_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_id": { + "name": "software_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "software_version": { + "name": "software_version", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.oauth_sessions": { + "name": "oauth_sessions", + "schema": "", + "columns": { + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "mcp_server_uuid": { + "name": "mcp_server_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "client_information": { + "name": "client_information", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "tokens": { + "name": "tokens", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "oauth_sessions_mcp_server_uuid_idx": { + "name": "oauth_sessions_mcp_server_uuid_idx", + "columns": [ + { + "expression": "mcp_server_uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "oauth_sessions_mcp_server_uuid_mcp_servers_uuid_fk": { + "name": "oauth_sessions_mcp_server_uuid_mcp_servers_uuid_fk", + "tableFrom": "oauth_sessions", + "tableTo": "mcp_servers", + "columnsFrom": [ + "mcp_server_uuid" + ], + "columnsTo": [ + "uuid" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "oauth_sessions_unique_per_server_idx": { + "name": "oauth_sessions_unique_per_server_idx", + "nullsNotDistinct": false, + "columns": [ + "mcp_server_uuid" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tools": { + "name": "tools", + "schema": "", + "columns": { + "uuid": { + "name": "uuid", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tool_schema": { + "name": "tool_schema", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "mcp_server_uuid": { + "name": "mcp_server_uuid", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "tools_mcp_server_uuid_idx": { + "name": "tools_mcp_server_uuid_idx", + "columns": [ + { + "expression": "mcp_server_uuid", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tools_mcp_server_uuid_mcp_servers_uuid_fk": { + "name": "tools_mcp_server_uuid_mcp_servers_uuid_fk", + "tableFrom": "tools", + "tableTo": "mcp_servers", + "columnsFrom": [ + "mcp_server_uuid" + ], + "columnsTo": [ + "uuid" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "tools_unique_tool_name_per_server_idx": { + "name": "tools_unique_tool_name_per_server_idx", + "nullsNotDistinct": false, + "columns": [ + "mcp_server_uuid", + "name" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verifications": { + "name": "verifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.mcp_server_error_status": { + "name": "mcp_server_error_status", + "schema": "public", + "values": [ + "NONE", + "ERROR" + ] + }, + "public.mcp_server_status": { + "name": "mcp_server_status", + "schema": "public", + "values": [ + "ACTIVE", + "INACTIVE" + ] + }, + "public.mcp_server_type": { + "name": "mcp_server_type", + "schema": "public", + "values": [ + "STDIO", + "SSE", + "STREAMABLE_HTTP" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/backend/drizzle/meta/_journal.json b/apps/backend/drizzle/meta/_journal.json index 063e605d..a3d71868 100644 --- a/apps/backend/drizzle/meta/_journal.json +++ b/apps/backend/drizzle/meta/_journal.json @@ -85,6 +85,13 @@ "when": 1763452681793, "tag": "0011_goofy_photon", "breakpoints": true + }, + { + "idx": 12, + "version": "7", + "when": 1764122318274, + "tag": "0012_crazy_white_queen", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/backend/src/db/repositories/namespaces.repo.ts b/apps/backend/src/db/repositories/namespaces.repo.ts index 18717ddc..f36c433d 100644 --- a/apps/backend/src/db/repositories/namespaces.repo.ts +++ b/apps/backend/src/db/repositories/namespaces.repo.ts @@ -202,6 +202,7 @@ export class NamespacesRepository { url: mcpServersTable.url, env: mcpServersTable.env, bearerToken: mcpServersTable.bearerToken, + headers: mcpServersTable.headers, error_status: mcpServersTable.error_status, created_at: mcpServersTable.created_at, user_id: mcpServersTable.user_id, @@ -225,6 +226,7 @@ export class NamespacesRepository { url: server.url, env: server.env || {}, bearerToken: server.bearerToken, + headers: server.headers || {}, error_status: server.error_status, created_at: server.created_at, user_id: server.user_id, diff --git a/apps/backend/src/db/schema.ts b/apps/backend/src/db/schema.ts index 687732d1..7549a4f3 100644 --- a/apps/backend/src/db/schema.ts +++ b/apps/backend/src/db/schema.ts @@ -57,6 +57,10 @@ export const mcpServersTable = pgTable( .notNull() .defaultNow(), bearerToken: text("bearer_token"), + headers: jsonb("headers") + .$type<{ [key: string]: string }>() + .notNull() + .default(sql`'{}'::jsonb`), user_id: text("user_id").references(() => usersTable.id, { onDelete: "cascade", }), diff --git a/apps/backend/src/db/serializers/mcp-servers.serializer.ts b/apps/backend/src/db/serializers/mcp-servers.serializer.ts index 2c3b0db8..fea534b1 100644 --- a/apps/backend/src/db/serializers/mcp-servers.serializer.ts +++ b/apps/backend/src/db/serializers/mcp-servers.serializer.ts @@ -14,6 +14,7 @@ export class McpServersSerializer { error_status: dbServer.error_status, created_at: dbServer.created_at.toISOString(), bearerToken: dbServer.bearerToken, + headers: dbServer.headers, user_id: dbServer.user_id, }; } diff --git a/apps/backend/src/db/serializers/namespaces.serializer.ts b/apps/backend/src/db/serializers/namespaces.serializer.ts index c4c559f4..079bf8ef 100644 --- a/apps/backend/src/db/serializers/namespaces.serializer.ts +++ b/apps/backend/src/db/serializers/namespaces.serializer.ts @@ -45,6 +45,7 @@ export class NamespacesSerializer { url: server.url, env: server.env || {}, bearerToken: server.bearerToken, + headers: server.headers || {}, error_status: server.error_status, created_at: server.created_at.toISOString(), user_id: server.user_id, diff --git a/apps/backend/src/lib/metamcp/client.ts b/apps/backend/src/lib/metamcp/client.ts index 551606b8..8febfadb 100644 --- a/apps/backend/src/lib/metamcp/client.ts +++ b/apps/backend/src/lib/metamcp/client.ts @@ -79,20 +79,23 @@ export const createMetaMcpClient = ( // Transform the URL if TRANSFORM_LOCALHOST_TO_DOCKER_INTERNAL is set to "true" const transformedUrl = transformDockerUrl(serverParams.url); + // Build headers: start with custom headers, then add auth header + const headers: Record = { + ...(serverParams.headers || {}), + }; + // Check for authentication - prioritize OAuth tokens, fallback to bearerToken - const hasAuth = + const authToken = serverParams.oauth_tokens?.access_token || serverParams.bearerToken; + if (authToken) { + headers["Authorization"] = `Bearer ${authToken}`; + } - if (!hasAuth) { + const hasHeaders = Object.keys(headers).length > 0; + + if (!hasHeaders) { transport = new SSEClientTransport(new URL(transformedUrl)); } else { - const headers: Record = {}; - - // Use OAuth access token if available, otherwise use bearerToken - const authToken = - serverParams.oauth_tokens?.access_token || serverParams.bearerToken; - headers["Authorization"] = `Bearer ${authToken}`; - transport = new SSEClientTransport(new URL(transformedUrl), { requestInit: { headers, @@ -106,20 +109,23 @@ export const createMetaMcpClient = ( // Transform the URL if TRANSFORM_LOCALHOST_TO_DOCKER_INTERNAL is set to "true" const transformedUrl = transformDockerUrl(serverParams.url); + // Build headers: start with custom headers, then add auth header + const headers: Record = { + ...(serverParams.headers || {}), + }; + // Check for authentication - prioritize OAuth tokens, fallback to bearerToken - const hasAuth = + const authToken = serverParams.oauth_tokens?.access_token || serverParams.bearerToken; + if (authToken) { + headers["Authorization"] = `Bearer ${authToken}`; + } - if (!hasAuth) { + const hasHeaders = Object.keys(headers).length > 0; + + if (!hasHeaders) { transport = new StreamableHTTPClientTransport(new URL(transformedUrl)); } else { - const headers: Record = {}; - - // Use OAuth access token if available, otherwise use bearerToken - const authToken = - serverParams.oauth_tokens?.access_token || serverParams.bearerToken; - headers["Authorization"] = `Bearer ${authToken}`; - transport = new StreamableHTTPClientTransport(new URL(transformedUrl), { requestInit: { headers, diff --git a/apps/backend/src/lib/metamcp/fetch-metamcp.ts b/apps/backend/src/lib/metamcp/fetch-metamcp.ts index 53b937cf..af95f8f7 100644 --- a/apps/backend/src/lib/metamcp/fetch-metamcp.ts +++ b/apps/backend/src/lib/metamcp/fetch-metamcp.ts @@ -51,6 +51,7 @@ export async function getMcpServers( url: mcpServersTable.url, created_at: mcpServersTable.created_at, bearerToken: mcpServersTable.bearerToken, + headers: mcpServersTable.headers, status: namespaceServerMappingsTable.status, error_status: mcpServersTable.error_status, }) @@ -88,6 +89,7 @@ export async function getMcpServers( args: server.args || [], env: server.env || {}, url: server.url, + headers: server.headers || {}, created_at: server.created_at?.toISOString() || new Date().toISOString(), status: server.status.toLowerCase(), diff --git a/apps/backend/src/lib/metamcp/utils.ts b/apps/backend/src/lib/metamcp/utils.ts index fdc7d964..7855624d 100644 --- a/apps/backend/src/lib/metamcp/utils.ts +++ b/apps/backend/src/lib/metamcp/utils.ts @@ -114,6 +114,7 @@ export async function convertDbServerToParams( stderr: "inherit" as const, oauth_tokens: oauthTokens, bearerToken: server.bearerToken, + headers: server.headers || {}, }; // Process based on server type diff --git a/apps/backend/src/routers/mcp-proxy/server.ts b/apps/backend/src/routers/mcp-proxy/server.ts index 23bc5358..daf7e7aa 100644 --- a/apps/backend/src/routers/mcp-proxy/server.ts +++ b/apps/backend/src/routers/mcp-proxy/server.ts @@ -307,7 +307,11 @@ const createTransport = async (req: express.Request): Promise => { } } - const headers = getHttpHeaders(req, transportType); + // Merge custom headers from database with passthrough headers from request + const headers = { + ...(matchingServer?.headers || {}), + ...getHttpHeaders(req, transportType), + }; console.log( `SSE transport: url=${url}, headers=${JSON.stringify(headers)}`, @@ -340,7 +344,11 @@ const createTransport = async (req: express.Request): Promise => { } } - const headers = getHttpHeaders(req, transportType); + // Merge custom headers from database with passthrough headers from request + const headers = { + ...(matchingServer?.headers || {}), + ...getHttpHeaders(req, transportType), + }; const transport = new StreamableHTTPClientTransport(new URL(url), { requestInit: { diff --git a/apps/backend/src/trpc/mcp-servers.impl.ts b/apps/backend/src/trpc/mcp-servers.impl.ts index 26e91a67..c79120c0 100644 --- a/apps/backend/src/trpc/mcp-servers.impl.ts +++ b/apps/backend/src/trpc/mcp-servers.impl.ts @@ -131,6 +131,7 @@ export const mcpServersImplementations = { env: serverConfig.env || {}, url: serverConfig.url || null, bearerToken: undefined, + headers: serverConfig.headers || {}, user_id: userId, // Default bulk imported servers to current user }; diff --git a/apps/frontend/app/[locale]/(sidebar)/mcp-servers/[uuid]/page.tsx b/apps/frontend/app/[locale]/(sidebar)/mcp-servers/[uuid]/page.tsx index fa761970..f3ecf94f 100644 --- a/apps/frontend/app/[locale]/(sidebar)/mcp-servers/[uuid]/page.tsx +++ b/apps/frontend/app/[locale]/(sidebar)/mcp-servers/[uuid]/page.tsx @@ -56,6 +56,9 @@ export default function McpServerDetailPage({ const [revealedEnvVars, setRevealedEnvVars] = useState>( new Set(), ); + const [revealedHeaders, setRevealedHeaders] = useState>( + new Set(), + ); const [bearerTokenRevealed, setBearerTokenRevealed] = useState(false); const [showDeleteDialog, setShowDeleteDialog] = useState(false); @@ -74,6 +77,19 @@ export default function McpServerDetailPage({ }); }; + // Function to toggle header visibility + const toggleHeaderVisibility = (key: string) => { + setRevealedHeaders((prev) => { + const newSet = new Set(prev); + if (newSet.has(key)) { + newSet.delete(key); + } else { + newSet.add(key); + } + return newSet; + }); + }; + // Function to mask sensitive values const maskSensitiveValue = (value: string) => { return "•".repeat(Math.min(value.length, 12)); @@ -569,6 +585,54 @@ export default function McpServerDetailPage({ )} + {Object.keys(server.headers).length > 0 && ( +
+ + {t("mcp-servers:detail.customHeaders")}: + +
+ {Object.entries(server.headers).map(([key, value]) => { + const isRevealed = revealedHeaders.has(key); + const displayValue = isRevealed + ? value + : maskSensitiveValue(value); + + return ( +
+
+ + {key}: + + + {displayValue} + +
+ +
+ ); + })} +
+
+ )} diff --git a/apps/frontend/app/[locale]/(sidebar)/mcp-servers/export-import-buttons.tsx b/apps/frontend/app/[locale]/(sidebar)/mcp-servers/export-import-buttons.tsx index 2ef7872f..4aab97a6 100644 --- a/apps/frontend/app/[locale]/(sidebar)/mcp-servers/export-import-buttons.tsx +++ b/apps/frontend/app/[locale]/(sidebar)/mcp-servers/export-import-buttons.tsx @@ -125,6 +125,9 @@ export function ExportImportButtons() { if (server.bearerToken) { config.bearerToken = server.bearerToken; } + if (server.headers && Object.keys(server.headers).length > 0) { + config.headers = server.headers; + } } mcpServersConfig[server.name] = config; @@ -268,11 +271,18 @@ export function ExportImportButtons() { }, "UrlBasedServerName": { "url": "https://example.com/sse", + "bearerToken": "optional-bearer-token", + "headers": { + "X-Custom-Header": "value" + }, "description": "Optional description", "type": "sse" // optional, defaults to "stdio" (case-insensitive) }, "StreamableHttpServerName": { "url": "https://example.com/mcp", + "headers": { + "X-Custom-Header": "value" + }, "description": "Optional description", "type": "streamable_http" // case-insensitive } diff --git a/apps/frontend/app/[locale]/(sidebar)/mcp-servers/mcp-servers-list.tsx b/apps/frontend/app/[locale]/(sidebar)/mcp-servers/mcp-servers-list.tsx index 25e76b55..da40350d 100644 --- a/apps/frontend/app/[locale]/(sidebar)/mcp-servers/mcp-servers-list.tsx +++ b/apps/frontend/app/[locale]/(sidebar)/mcp-servers/mcp-servers-list.tsx @@ -347,6 +347,9 @@ export function McpServersList({ onRefresh }: McpServersListProps) { if (server.bearerToken) { config.bearerToken = server.bearerToken; } + if (server.headers && Object.keys(server.headers).length > 0) { + config.headers = server.headers; + } } const exportFormat = { diff --git a/apps/frontend/app/[locale]/(sidebar)/mcp-servers/page.tsx b/apps/frontend/app/[locale]/(sidebar)/mcp-servers/page.tsx index 41584fe1..a8bc3991 100644 --- a/apps/frontend/app/[locale]/(sidebar)/mcp-servers/page.tsx +++ b/apps/frontend/app/[locale]/(sidebar)/mcp-servers/page.tsx @@ -58,6 +58,7 @@ export default function McpServersPage() { env: "", url: "", bearerToken: "", + headers: "", user_id: undefined, // Default to private (current user) }, }); @@ -108,6 +109,22 @@ export default function McpServersPage() { } } + // Parse headers string into object + const headersObject: Record = {}; + if (data.headers) { + const headersLines = data.headers.trim().split("\n"); + for (const line of headersLines) { + const trimmedLine = line.trim(); + if (trimmedLine && trimmedLine.includes("=")) { + const [key, ...valueParts] = trimmedLine.split("="); + const value = valueParts.join("="); // Handle values that contain '=' + if (key?.trim()) { + headersObject[key.trim()] = value; + } + } + } + } + const request: CreateMcpServerRequest = { name: data.name, description: data.description, @@ -117,6 +134,7 @@ export default function McpServersPage() { env: envObject, url: data.url, bearerToken: data.bearerToken, + headers: headersObject, user_id: data.user_id, }; @@ -399,6 +417,27 @@ export default function McpServersPage() { )} /> + + ( + + {t("mcp-servers:headers")} + +