mcp.ts157 lines · main
| 1 | import { |
| 2 | ApiKey, |
| 3 | ApiKeyType, |
| 4 | ApplyMigrationOptions, |
| 5 | DatabaseOperations, |
| 6 | DebuggingOperations, |
| 7 | DevelopmentOperations, |
| 8 | ExecuteSqlOptions, |
| 9 | GetLogsOptions, |
| 10 | } from '@supabase/mcp-server-supabase/platform' |
| 11 | |
| 12 | import { DEFAULT_EXPOSED_SCHEMAS } from './constants' |
| 13 | import { generateTypescriptTypes } from './generate-types' |
| 14 | import { getLints } from './lints' |
| 15 | import { getLogQuery, retrieveAnalyticsData } from './logs' |
| 16 | import { applyAndTrackMigrations, listMigrationVersions } from './migrations' |
| 17 | import { executeQuery } from './query' |
| 18 | import { getProjectSettings } from './settings' |
| 19 | import { ResponseError } from '@/types' |
| 20 | |
| 21 | export type GetDatabaseOperationsOptions = { |
| 22 | headers?: HeadersInit |
| 23 | } |
| 24 | |
| 25 | export type GetDevelopmentOperationsOptions = { |
| 26 | headers?: HeadersInit |
| 27 | } |
| 28 | |
| 29 | export type GetDebuggingOperationsOptions = { |
| 30 | headers?: HeadersInit |
| 31 | } |
| 32 | |
| 33 | export function getDatabaseOperations({ |
| 34 | headers, |
| 35 | }: GetDatabaseOperationsOptions): DatabaseOperations { |
| 36 | return { |
| 37 | async executeSql<T>(_projectRef: string, options: ExecuteSqlOptions) { |
| 38 | const { query, parameters, read_only: readOnly } = options |
| 39 | |
| 40 | const { data, error } = await executeQuery<T>({ query, parameters, headers, readOnly }) |
| 41 | |
| 42 | if (error) { |
| 43 | throw error |
| 44 | } |
| 45 | |
| 46 | return data |
| 47 | }, |
| 48 | async listMigrations() { |
| 49 | const { data, error } = await listMigrationVersions({ headers }) |
| 50 | |
| 51 | if (error) { |
| 52 | throw error |
| 53 | } |
| 54 | |
| 55 | return data |
| 56 | }, |
| 57 | async applyMigration(_projectRef: string, options: ApplyMigrationOptions) { |
| 58 | const { query, name } = options |
| 59 | const { error } = await applyAndTrackMigrations({ query, name, headers }) |
| 60 | |
| 61 | if (error) { |
| 62 | throw error |
| 63 | } |
| 64 | }, |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | export function getDevelopmentOperations({ |
| 69 | headers, |
| 70 | }: GetDevelopmentOperationsOptions): DevelopmentOperations { |
| 71 | return { |
| 72 | async getProjectUrl(_projectRef) { |
| 73 | const settings = getProjectSettings() |
| 74 | return `${settings.app_config.protocol}://${settings.app_config.endpoint}` |
| 75 | }, |
| 76 | async getPublishableKeys(_projectRef) { |
| 77 | const settings = getProjectSettings() |
| 78 | const anonKey = settings.service_api_keys.find((key) => key.name === 'anon key') |
| 79 | |
| 80 | if (!anonKey) { |
| 81 | throw new Error('Anon key not found in project settings') |
| 82 | } |
| 83 | |
| 84 | // For self-hosted, only the legacy anon key is available and returned here. |
| 85 | // There is currently no publishable key variable in self-hosted configuration, |
| 86 | // and the migration to new publishable keys requires additional Auth and service setup. |
| 87 | const publishableKeysArray: ApiKey[] = [ |
| 88 | { |
| 89 | api_key: anonKey.api_key, |
| 90 | name: anonKey.name, |
| 91 | type: 'anon' as ApiKeyType, |
| 92 | }, |
| 93 | ] |
| 94 | |
| 95 | return publishableKeysArray |
| 96 | }, |
| 97 | async generateTypescriptTypes(_projectRef) { |
| 98 | const response = await generateTypescriptTypes({ headers }) |
| 99 | |
| 100 | if (response instanceof ResponseError) { |
| 101 | throw response |
| 102 | } |
| 103 | |
| 104 | return response |
| 105 | }, |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | export function getDebuggingOperations({ |
| 110 | headers, |
| 111 | }: GetDebuggingOperationsOptions): DebuggingOperations { |
| 112 | return { |
| 113 | async getLogs(projectRef: string, options: GetLogsOptions) { |
| 114 | const sql = getLogQuery(options.service) |
| 115 | |
| 116 | const { data, error } = await retrieveAnalyticsData({ |
| 117 | name: 'logs.all', |
| 118 | projectRef, |
| 119 | params: { |
| 120 | sql, |
| 121 | iso_timestamp_start: options.iso_timestamp_start, |
| 122 | iso_timestamp_end: options.iso_timestamp_end, |
| 123 | }, |
| 124 | }) |
| 125 | |
| 126 | if (error) { |
| 127 | throw error |
| 128 | } |
| 129 | |
| 130 | return data |
| 131 | }, |
| 132 | async getSecurityAdvisors(_projectRef) { |
| 133 | const { data, error } = await getLints({ |
| 134 | headers, |
| 135 | exposedSchemas: DEFAULT_EXPOSED_SCHEMAS, |
| 136 | }) |
| 137 | |
| 138 | if (error) { |
| 139 | throw error |
| 140 | } |
| 141 | |
| 142 | return data.filter((lint) => lint.categories.includes('SECURITY')) |
| 143 | }, |
| 144 | async getPerformanceAdvisors(_projectRef) { |
| 145 | const { data, error } = await getLints({ |
| 146 | headers, |
| 147 | exposedSchemas: DEFAULT_EXPOSED_SCHEMAS, |
| 148 | }) |
| 149 | |
| 150 | if (error) { |
| 151 | throw error |
| 152 | } |
| 153 | |
| 154 | return data.filter((lint) => lint.categories.includes('PERFORMANCE')) |
| 155 | }, |
| 156 | } |
| 157 | } |