Skip to main content
Glama
FreePeak

Multi Database MCP Server

timescale_completion.go25.7 kB
package mcp import ( "context" "fmt" ) // CompletionItem represents a code completion item type CompletionItem struct { Name string `json:"name"` Type string `json:"type"` Documentation string `json:"documentation"` InsertText string `json:"insertText"` Parameters []string `json:"parameters,omitempty"` ReturnType string `json:"returnType,omitempty"` Category string `json:"category,omitempty"` SortText string `json:"sortText,omitempty"` FilterText string `json:"filterText,omitempty"` CommitCharacters []string `json:"commitCharacters,omitempty"` } // QuerySuggestion represents a suggested query template for TimescaleDB type QuerySuggestion struct { Title string `json:"title"` Description string `json:"description"` Query string `json:"query"` Category string `json:"category"` } // TimescaleDBCompletionProvider provides code completion for TimescaleDB functions type TimescaleDBCompletionProvider struct { contextProvider *TimescaleDBContextProvider } // NewTimescaleDBCompletionProvider creates a new TimescaleDB completion provider func NewTimescaleDBCompletionProvider() *TimescaleDBCompletionProvider { return &TimescaleDBCompletionProvider{ contextProvider: NewTimescaleDBContextProvider(), } } // GetTimeBucketCompletions returns completions for time_bucket functions func (p *TimescaleDBCompletionProvider) GetTimeBucketCompletions(ctx context.Context, dbID string, useCase UseCaseProvider) ([]CompletionItem, error) { // First check if TimescaleDB is available tsdbContext, err := p.contextProvider.DetectTimescaleDB(ctx, dbID, useCase) if err != nil { return nil, fmt.Errorf("failed to detect TimescaleDB: %w", err) } if !tsdbContext.IsTimescaleDB { return nil, fmt.Errorf("TimescaleDB is not available in the database %s", dbID) } // Define time bucket function completions completions := []CompletionItem{ { Name: "time_bucket", Type: "function", Documentation: "TimescaleDB function that groups time into buckets. Useful for downsampling time-series data.", InsertText: "time_bucket($1, $2)", Parameters: []string{"interval", "timestamp"}, ReturnType: "timestamp", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "time_bucket_gapfill", Type: "function", Documentation: "TimescaleDB function similar to time_bucket but fills in missing values (gaps) in the result.", InsertText: "time_bucket_gapfill($1, $2)", Parameters: []string{"interval", "timestamp"}, ReturnType: "timestamp", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "time_bucket_ng", Type: "function", Documentation: "TimescaleDB next-generation time bucket function that supports timezone-aware bucketing.", InsertText: "time_bucket_ng('$1', $2)", Parameters: []string{"interval", "timestamp", "timezone"}, ReturnType: "timestamp with time zone", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "time_bucket", Type: "function", Documentation: "TimescaleDB function that groups time into buckets with an offset.", InsertText: "time_bucket($1, $2, $3)", Parameters: []string{"interval", "timestamp", "offset"}, ReturnType: "timestamp", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, } // Add version information to documentation for i := range completions { completions[i].Documentation = fmt.Sprintf("TimescaleDB v%s: %s", tsdbContext.Version, completions[i].Documentation) } return completions, nil } // GetHypertableFunctionCompletions returns completions for hypertable management functions func (p *TimescaleDBCompletionProvider) GetHypertableFunctionCompletions(ctx context.Context, dbID string, useCase UseCaseProvider) ([]CompletionItem, error) { // First check if TimescaleDB is available tsdbContext, err := p.contextProvider.DetectTimescaleDB(ctx, dbID, useCase) if err != nil { return nil, fmt.Errorf("failed to detect TimescaleDB: %w", err) } if !tsdbContext.IsTimescaleDB { return nil, fmt.Errorf("TimescaleDB is not available in the database %s", dbID) } // Define hypertable function completions completions := []CompletionItem{ { Name: "create_hypertable", Type: "function", Documentation: "TimescaleDB function that converts a standard PostgreSQL table into a hypertable partitioned by time.", InsertText: "create_hypertable('$1', '$2')", Parameters: []string{"table_name", "time_column_name"}, ReturnType: "void", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "add_dimension", Type: "function", Documentation: "TimescaleDB function that adds another dimension to a hypertable for partitioning.", InsertText: "add_dimension('$1', '$2')", Parameters: []string{"hypertable", "column_name"}, ReturnType: "void", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "add_compression_policy", Type: "function", Documentation: "TimescaleDB function that adds an automatic compression policy to a hypertable.", InsertText: "add_compression_policy('$1', INTERVAL '$2')", Parameters: []string{"hypertable", "older_than"}, ReturnType: "integer", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "add_retention_policy", Type: "function", Documentation: "TimescaleDB function that adds an automatic data retention policy to a hypertable.", InsertText: "add_retention_policy('$1', INTERVAL '$2')", Parameters: []string{"hypertable", "drop_after"}, ReturnType: "integer", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "alter_job", Type: "function", Documentation: "TimescaleDB function that alters a policy job's schedule or configuration.", InsertText: "alter_job($1, scheduled => true)", Parameters: []string{"job_id"}, ReturnType: "integer", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "hypertable_size", Type: "function", Documentation: "TimescaleDB function that shows the size of a hypertable, including all chunks.", InsertText: "hypertable_size('$1')", Parameters: []string{"hypertable"}, ReturnType: "bigint", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "hypertable_detailed_size", Type: "function", Documentation: "TimescaleDB function that shows detailed size information for a hypertable.", InsertText: "hypertable_detailed_size('$1')", Parameters: []string{"hypertable"}, ReturnType: "table", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, } // Add version information to documentation for i := range completions { completions[i].Documentation = fmt.Sprintf("TimescaleDB v%s: %s", tsdbContext.Version, completions[i].Documentation) } return completions, nil } // GetContinuousAggregateFunctionCompletions returns completions for continuous aggregate functions func (p *TimescaleDBCompletionProvider) GetContinuousAggregateFunctionCompletions(ctx context.Context, dbID string, useCase UseCaseProvider) ([]CompletionItem, error) { // First check if TimescaleDB is available tsdbContext, err := p.contextProvider.DetectTimescaleDB(ctx, dbID, useCase) if err != nil { return nil, fmt.Errorf("failed to detect TimescaleDB: %w", err) } if !tsdbContext.IsTimescaleDB { return nil, fmt.Errorf("TimescaleDB is not available in the database %s", dbID) } // Define continuous aggregate function completions completions := []CompletionItem{ { Name: "create_materialized_view", Type: "function", Documentation: "TimescaleDB function that creates a continuous aggregate view.", InsertText: "CREATE MATERIALIZED VIEW $1 WITH (timescaledb.continuous) AS SELECT $2 FROM $3 GROUP BY $4;", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "add_continuous_aggregate_policy", Type: "function", Documentation: "TimescaleDB function that adds a refresh policy to a continuous aggregate.", InsertText: "add_continuous_aggregate_policy('$1', start_offset => INTERVAL '$2', end_offset => INTERVAL '$3', schedule_interval => INTERVAL '$4')", Parameters: []string{"continuous_aggregate", "start_offset", "end_offset", "schedule_interval"}, ReturnType: "integer", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "refresh_continuous_aggregate", Type: "function", Documentation: "TimescaleDB function that manually refreshes a continuous aggregate for a specific time range.", InsertText: "refresh_continuous_aggregate('$1', '$2', '$3')", Parameters: []string{"continuous_aggregate", "start_time", "end_time"}, ReturnType: "void", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, } // Add version information to documentation for i := range completions { completions[i].Documentation = fmt.Sprintf("TimescaleDB v%s: %s", tsdbContext.Version, completions[i].Documentation) } return completions, nil } // GetAnalyticsFunctionCompletions returns completions for TimescaleDB's analytics functions func (p *TimescaleDBCompletionProvider) GetAnalyticsFunctionCompletions(ctx context.Context, dbID string, useCase UseCaseProvider) ([]CompletionItem, error) { // First check if TimescaleDB is available tsdbContext, err := p.contextProvider.DetectTimescaleDB(ctx, dbID, useCase) if err != nil { return nil, fmt.Errorf("failed to detect TimescaleDB: %w", err) } if !tsdbContext.IsTimescaleDB { return nil, fmt.Errorf("TimescaleDB is not available in the database %s", dbID) } // Define analytics function completions completions := []CompletionItem{ { Name: "first", Type: "function", Documentation: "TimescaleDB function that returns the value of the specified column at the first time ordered by time within each group.", InsertText: "first($1, $2)", Parameters: []string{"value", "time"}, ReturnType: "same as value", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "last", Type: "function", Documentation: "TimescaleDB function that returns the value of the specified column at the last time ordered by time within each group.", InsertText: "last($1, $2)", Parameters: []string{"value", "time"}, ReturnType: "same as value", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "time_weight", Type: "function", Documentation: "TimescaleDB function that returns the time-weighted average of a value over time.", InsertText: "time_weight($1, $2)", Parameters: []string{"value", "time"}, ReturnType: "double precision", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "histogram", Type: "function", Documentation: "TimescaleDB function that buckets values and returns a histogram showing the distribution.", InsertText: "histogram($1, $2, $3, $4)", Parameters: []string{"value", "min", "max", "num_buckets"}, ReturnType: "histogram", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "approx_percentile", Type: "function", Documentation: "TimescaleDB function that calculates approximate percentiles using the t-digest method.", InsertText: "approx_percentile($1, $2)", Parameters: []string{"value", "percentile"}, ReturnType: "double precision", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, } // Add version information to documentation for i := range completions { completions[i].Documentation = fmt.Sprintf("TimescaleDB v%s: %s", tsdbContext.Version, completions[i].Documentation) } return completions, nil } // GetAllFunctionCompletions returns completions for all TimescaleDB functions func (p *TimescaleDBCompletionProvider) GetAllFunctionCompletions(ctx context.Context, dbID string, useCase UseCaseProvider) ([]CompletionItem, error) { // Check if TimescaleDB is available by using the DetectTimescaleDB method // which already checks the database type tsdbContext, err := p.contextProvider.DetectTimescaleDB(ctx, dbID, useCase) if err != nil { return nil, fmt.Errorf("failed to detect TimescaleDB: %w", err) } if !tsdbContext.IsTimescaleDB { return nil, fmt.Errorf("TimescaleDB is not available in the database %s", dbID) } // Define predefined completions for all categories var allCompletions []CompletionItem // Time bucket functions allCompletions = append(allCompletions, []CompletionItem{ { Name: "time_bucket", Type: "function", Documentation: "TimescaleDB function that groups time into buckets. Useful for downsampling time-series data.", InsertText: "time_bucket($1, $2)", Parameters: []string{"interval", "timestamp"}, ReturnType: "timestamp", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "time_bucket_gapfill", Type: "function", Documentation: "TimescaleDB function similar to time_bucket but fills in missing values (gaps) in the result.", InsertText: "time_bucket_gapfill($1, $2)", Parameters: []string{"interval", "timestamp"}, ReturnType: "timestamp", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "time_bucket_ng", Type: "function", Documentation: "TimescaleDB next-generation time bucket function that supports timezone-aware bucketing.", InsertText: "time_bucket_ng('$1', $2)", Parameters: []string{"interval", "timestamp", "timezone"}, ReturnType: "timestamp with time zone", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, }...) // Hypertable functions allCompletions = append(allCompletions, []CompletionItem{ { Name: "create_hypertable", Type: "function", Documentation: "TimescaleDB function that converts a standard PostgreSQL table into a hypertable partitioned by time.", InsertText: "create_hypertable('$1', '$2')", Parameters: []string{"table_name", "time_column_name"}, ReturnType: "void", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "add_dimension", Type: "function", Documentation: "TimescaleDB function that adds another dimension to a hypertable for partitioning.", InsertText: "add_dimension('$1', '$2')", Parameters: []string{"hypertable", "column_name"}, ReturnType: "void", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "add_compression_policy", Type: "function", Documentation: "TimescaleDB function that adds an automatic compression policy to a hypertable.", InsertText: "add_compression_policy('$1', INTERVAL '$2')", Parameters: []string{"hypertable", "older_than"}, ReturnType: "integer", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "add_retention_policy", Type: "function", Documentation: "TimescaleDB function that adds an automatic data retention policy to a hypertable.", InsertText: "add_retention_policy('$1', INTERVAL '$2')", Parameters: []string{"hypertable", "drop_after"}, ReturnType: "integer", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, }...) // Continuous aggregate functions allCompletions = append(allCompletions, []CompletionItem{ { Name: "create_materialized_view", Type: "function", Documentation: "TimescaleDB function that creates a continuous aggregate view.", InsertText: "CREATE MATERIALIZED VIEW $1 WITH (timescaledb.continuous) AS SELECT $2 FROM $3 GROUP BY $4;", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "add_continuous_aggregate_policy", Type: "function", Documentation: "TimescaleDB function that adds a refresh policy to a continuous aggregate.", InsertText: "add_continuous_aggregate_policy('$1', start_offset => INTERVAL '$2', end_offset => INTERVAL '$3', schedule_interval => INTERVAL '$4')", Parameters: []string{"continuous_aggregate", "start_offset", "end_offset", "schedule_interval"}, ReturnType: "integer", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, }...) // Analytics functions allCompletions = append(allCompletions, []CompletionItem{ { Name: "first", Type: "function", Documentation: "TimescaleDB function that returns the value of the specified column at the first time ordered by time within each group.", InsertText: "first($1, $2)", Parameters: []string{"value", "time"}, ReturnType: "same as value", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "last", Type: "function", Documentation: "TimescaleDB function that returns the value of the specified column at the last time ordered by time within each group.", InsertText: "last($1, $2)", Parameters: []string{"value", "time"}, ReturnType: "same as value", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, { Name: "time_weight", Type: "function", Documentation: "TimescaleDB function that returns the time-weighted average of a value over time.", InsertText: "time_weight($1, $2)", Parameters: []string{"value", "time"}, ReturnType: "double precision", Category: "TimescaleDB", CommitCharacters: []string{"("}, }, }...) // Add version information to documentation for i := range allCompletions { allCompletions[i].Documentation = fmt.Sprintf("TimescaleDB v%s: %s", tsdbContext.Version, allCompletions[i].Documentation) } return allCompletions, nil } // GetQuerySuggestions returns TimescaleDB query suggestions based on the database schema func (p *TimescaleDBCompletionProvider) GetQuerySuggestions(ctx context.Context, dbID string, useCase UseCaseProvider) ([]QuerySuggestion, error) { // First check if TimescaleDB is available tsdbContext, err := p.contextProvider.GetTimescaleDBContext(ctx, dbID, useCase) if err != nil { return nil, fmt.Errorf("failed to get TimescaleDB context: %w", err) } if !tsdbContext.IsTimescaleDB { return nil, fmt.Errorf("TimescaleDB is not available in the database %s", dbID) } // Base suggestions that don't depend on schema suggestions := []QuerySuggestion{ { Title: "Basic Time Bucket Aggregation", Description: "Groups time-series data into time buckets and calculates aggregates", Query: "SELECT time_bucket('1 hour', time_column) AS bucket, avg(value_column), min(value_column), max(value_column) FROM table_name WHERE time_column > now() - INTERVAL '1 day' GROUP BY bucket ORDER BY bucket;", Category: "Time Buckets", }, { Title: "Time Bucket with Gap Filling", Description: "Groups time-series data with gap filling for missing values", Query: "SELECT time_bucket_gapfill('1 hour', time_column) AS bucket, avg(value_column), min(value_column), max(value_column) FROM table_name WHERE time_column > now() - INTERVAL '1 day' AND time_column <= now() GROUP BY bucket ORDER BY bucket;", Category: "Time Buckets", }, { Title: "Create Hypertable", Description: "Converts a standard PostgreSQL table into a TimescaleDB hypertable", Query: "SELECT create_hypertable('table_name', 'time_column');", Category: "Hypertable Management", }, { Title: "Add Compression Policy", Description: "Adds an automatic compression policy to a hypertable", Query: "SELECT add_compression_policy('table_name', INTERVAL '7 days');", Category: "Hypertable Management", }, { Title: "Add Retention Policy", Description: "Adds an automatic data retention policy to a hypertable", Query: "SELECT add_retention_policy('table_name', INTERVAL '30 days');", Category: "Hypertable Management", }, { Title: "Create Continuous Aggregate", Description: "Creates a materialized view that automatically maintains aggregated data", Query: "CREATE MATERIALIZED VIEW view_name WITH (timescaledb.continuous) AS SELECT time_bucket('1 hour', time_column) as bucket, avg(value_column) FROM table_name GROUP BY bucket;", Category: "Continuous Aggregates", }, { Title: "Add Continuous Aggregate Policy", Description: "Adds a refresh policy to a continuous aggregate", Query: "SELECT add_continuous_aggregate_policy('view_name', start_offset => INTERVAL '2 days', end_offset => INTERVAL '1 hour', schedule_interval => INTERVAL '1 hour');", Category: "Continuous Aggregates", }, { Title: "Hypertable Size", Description: "Shows the size of a hypertable including all chunks", Query: "SELECT * FROM hypertable_size('table_name');", Category: "Diagnostics", }, { Title: "Hypertable Detailed Size", Description: "Shows detailed size information for a hypertable", Query: "SELECT * FROM hypertable_detailed_size('table_name');", Category: "Diagnostics", }, { Title: "Compression Stats", Description: "Shows compression statistics for a hypertable", Query: "SELECT * FROM hypertable_compression_stats('table_name');", Category: "Diagnostics", }, { Title: "Job Stats", Description: "Shows statistics for background jobs like compression and retention policies", Query: "SELECT * FROM timescaledb_information.jobs;", Category: "Diagnostics", }, } // If we have hypertable information, use it to create tailored suggestions if len(tsdbContext.Hypertables) > 0 { for _, ht := range tsdbContext.Hypertables { tableName := ht.TableName timeColumn := ht.TimeColumn // Skip if we don't have both table name and time column if tableName == "" || timeColumn == "" { continue } // Add schema-specific suggestions suggestions = append(suggestions, []QuerySuggestion{ { Title: fmt.Sprintf("Time Bucket Aggregation for %s", tableName), Description: fmt.Sprintf("Groups data from %s table into time buckets", tableName), Query: fmt.Sprintf("SELECT time_bucket('1 hour', %s) AS bucket, avg(value_column) FROM %s WHERE %s > now() - INTERVAL '1 day' GROUP BY bucket ORDER BY bucket;", timeColumn, tableName, timeColumn), Category: "Time Buckets", }, { Title: fmt.Sprintf("Compression Policy for %s", tableName), Description: fmt.Sprintf("Adds compression policy to %s hypertable", tableName), Query: fmt.Sprintf("SELECT add_compression_policy('%s', INTERVAL '7 days');", tableName), Category: "Hypertable Management", }, { Title: fmt.Sprintf("Retention Policy for %s", tableName), Description: fmt.Sprintf("Adds retention policy to %s hypertable", tableName), Query: fmt.Sprintf("SELECT add_retention_policy('%s', INTERVAL '30 days');", tableName), Category: "Hypertable Management", }, { Title: fmt.Sprintf("Continuous Aggregate for %s", tableName), Description: fmt.Sprintf("Creates a continuous aggregate view for %s", tableName), Query: fmt.Sprintf("CREATE MATERIALIZED VIEW %s_hourly WITH (timescaledb.continuous) AS SELECT time_bucket('1 hour', %s) as bucket, avg(value_column) FROM %s GROUP BY bucket;", tableName, timeColumn, tableName), Category: "Continuous Aggregates", }, { Title: fmt.Sprintf("Recent Data from %s", tableName), Description: fmt.Sprintf("Retrieves recent data from %s with time ordering", tableName), Query: fmt.Sprintf("SELECT * FROM %s WHERE %s > now() - INTERVAL '1 day' ORDER BY %s DESC LIMIT 100;", tableName, timeColumn, timeColumn), Category: "Data Retrieval", }, { Title: fmt.Sprintf("First/Last Analysis for %s", tableName), Description: fmt.Sprintf("Uses first/last functions to analyze %s by segments", tableName), Query: fmt.Sprintf("SELECT segment_column, first(value_column, %s), last(value_column, %s) FROM %s GROUP BY segment_column;", timeColumn, timeColumn, tableName), Category: "Analytics", }, }...) } } return suggestions, nil }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/FreePeak/db-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server