import pixeltable as pxt from mcp.server.fastmcp import FastMCP mcp = FastMCP("Pixeltable") @mcp.tool() def create_table(table_name: str, columns: dict[str, str]) -> str: """Create a table in Pixeltable. Args: table_name: The name of the table to create. columns: A dictionary of column names and types. Eligible column types: from .type_system import ( Array, Audio, Bool, Document, Float, Image, Int, Json, Required, String, Timestamp, Video, ) Example: columns = { "name": String, "age": Int, "is_active": Bool, } """ # Map string type names to actual Pixeltable types type_mapping = { "array": pxt.Array, "audio": pxt.Audio, "bool": pxt.Bool, "document": pxt.Document, "float": pxt.Float, "image": pxt.Image, "int": pxt.Int, "json": pxt.Json, "required": pxt.Required, "string": pxt.String, "timestamp": pxt.Timestamp, "video": pxt.Video, } # Convert string type names to actual type objects converted_columns = {} for col_name, col_type in columns.items(): col_type_lower = col_type.lower() if col_type_lower in type_mapping: converted_columns[col_name] = type_mapping[col_type_lower] else: return f"Invalid column type: {col_type}. Valid types are: {', '.join(type_mapping.keys())}" pxt.create_table(table_name, schema_or_df=converted_columns, if_exists="replace") if table_name in pxt.list_tables(): return f"Table {table_name} created successfully." else: return f"Table {table_name} creation failed." @mcp.tool() def insert_data(table_name: str, data: list[dict]) -> str: """Insert data into a table in Pixeltable. Args: table_name: The name of the table to insert data into. data: A list of dictionaries, each representing a row of data. The keys of the dictionary should match the column names and types of the table. """ try: table = pxt.get_table(table_name) if table is None: return f"Error: Table {table_name} not found." table.insert(data) return f"Data inserted successfully." except Exception as e: return f"Error inserting data: {str(e)}" @mcp.tool() def add_computed_column(table_name: str, column_name: str, expression: str) -> str: """Add a computed column to a table in Pixeltable. Args: table_name: The name of the table to add the computed column to. column_name: The name of the computed column to add. expression: A string representation of the Python expression to compute the column. The expression should refer to other columns in the table using the notation 'table.column_name'. Example: add_computed_column("my_table", "full_name", "table.first_name + ' ' + table.last_name") add_computed_column("my_table", "yoy_change", "table.pop_2023 - table.pop_2022") """ try: table = pxt.get_table(table_name) if table is None: return f"Error: Table {table_name} not found." # Replace 'table.' with the actual table reference modified_expr = expression.replace("table.", f"table.") # Construct the expression column_expr = eval(modified_expr, {"table": table}) # Add the computed column with kwargs format kwargs = {column_name: column_expr} table.add_computed_column(**kwargs) return f"Computed column '{column_name}' added successfully to table '{table_name}'." except Exception as e: return f"Error adding computed column: {str(e)}" @mcp.tool() def create_view(view_name: str, table_name: str, filter_expr: str = None) -> str: """Create a view based on a table in Pixeltable. Args: view_name: The name of the view to create. table_name: The name of the base table for the view. filter_expr: Optional filter expression as a string. The expression should refer to columns using 'table.column_name'. Example: create_view("active_users", "users", "table.is_active == True") create_view("adult_users", "users", "table.age >= 18") """ try: table = pxt.get_table(table_name) if table is None: return f"Error: Table {table_name} not found." if filter_expr: # Replace 'table.' with the actual table reference modified_expr = filter_expr.replace("table.", f"{table.name}.") # Use eval to convert the string expression to a Python expression filter_condition = eval(modified_expr) # Create the view with the filter view = pxt.create_view(view_name, table.where(filter_condition)) else: # Create a view without a filter view = pxt.create_view(view_name, table) return f"View '{view_name}' created successfully." except Exception as e: return f"Error creating view: {str(e)}" @mcp.tool() def execute_query( table_or_view_name: str, select_columns: list[str] = None, where_expr: str = None, order_by_column: str = None, order_asc: bool = True, limit: int = None, ) -> str: """Execute a query on a table or view in Pixeltable. Args: table_or_view_name: The name of the table or view to query. select_columns: List of column names to select. If None, selects all columns. where_expr: Optional filter expression as a string. The expression should refer to columns using 'table.column_name'. order_by_column: Optional column name to order the results by. order_asc: Whether to order ascending (True) or descending (False). limit: Maximum number of rows to return. Example: execute_query("users", ["name", "email"], "table.age > 25", "name", True, 10) """ try: # Get the table or view data_source = pxt.get_table(table_or_view_name) if data_source is None: data_source = pxt.get_view(table_or_view_name) if data_source is None: return f"Error: Table or view '{table_or_view_name}' not found." # Start building the query query = data_source # Apply where clause if provided if where_expr: modified_expr = where_expr.replace("table.", f"{data_source.name}.") where_condition = eval(modified_expr) query = query.where(where_condition) # Apply order by if provided if order_by_column: # Handle ordering on a specific column if hasattr(data_source, order_by_column): order_col = getattr(data_source, order_by_column) query = query.order_by(order_col, asc=order_asc) else: return f"Error: Column '{order_by_column}' not found in '{table_or_view_name}'." # Apply limit if provided if limit is not None: query = query.limit(limit) # Apply select if provided if select_columns: select_args = [] for col_name in select_columns: if hasattr(data_source, col_name): select_args.append(getattr(data_source, col_name)) else: return f"Error: Column '{col_name}' not found in '{table_or_view_name}'." # Execute the query with select result = query.select(*select_args).collect() else: # Execute the query for all columns result = query.collect() # Convert result to string representation result_str = result.to_pandas().to_string() return f"Query executed successfully:\n\n{result_str}" except Exception as e: return f"Error executing query: {str(e)}" @mcp.tool() def create_query(query_name: str, table_name: str, query_function: str) -> str: """Create a named query in Pixeltable. Args: query_name: The name of the query to create. table_name: The name of the table the query will operate on. query_function: A string representation of the query function body. Should be a function that returns a query result. Example: create_query( "get_active_users", "users", "return users.where(users.is_active == True).select(users.name, users.email)" ) """ try: table = pxt.get_table(table_name) if table is None: return f"Error: Table {table_name} not found." # Create a function definition func_def = f""" @{table.name}.query def {query_name}(): {query_function} """ # Execute the function definition exec(func_def) return f"Query '{query_name}' created successfully for table '{table_name}'." except Exception as e: return f"Error creating query: {str(e)}"