@mseep/csv-editor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +53 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +38 -0
  3. package/.github/workflows/deploy-docs.yml +62 -0
  4. package/.github/workflows/publish-github.yml +52 -0
  5. package/.github/workflows/publish.yml +44 -0
  6. package/.github/workflows/test.yml +32 -0
  7. package/.pre-commit-config.yaml +157 -0
  8. package/ALTERNATIVE_PUBLISHING.md +175 -0
  9. package/ARCHITECTURE.md +1011 -0
  10. package/CHANGELOG.md +99 -0
  11. package/CODE_OF_CONDUCT.md +41 -0
  12. package/CONTRIBUTING.md +427 -0
  13. package/Dockerfile +22 -0
  14. package/LICENSE +21 -0
  15. package/MCP_CONFIG.md +505 -0
  16. package/PUBLISHING.md +210 -0
  17. package/README.md +400 -0
  18. package/SECURITY.md +61 -0
  19. package/docs/README.md +41 -0
  20. package/docs/blog/2019-05-28-first-blog-post.md +12 -0
  21. package/docs/blog/2019-05-29-long-blog-post.md +44 -0
  22. package/docs/blog/2021-08-01-mdx-blog-post.mdx +24 -0
  23. package/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
  24. package/docs/blog/2021-08-26-welcome/index.md +29 -0
  25. package/docs/blog/authors.yml +25 -0
  26. package/docs/blog/tags.yml +19 -0
  27. package/docs/docs/api/overview.md +183 -0
  28. package/docs/docs/installation.md +252 -0
  29. package/docs/docs/intro.md +87 -0
  30. package/docs/docs/tutorial-basics/_category_.json +8 -0
  31. package/docs/docs/tutorial-basics/congratulations.md +23 -0
  32. package/docs/docs/tutorial-basics/create-a-blog-post.md +34 -0
  33. package/docs/docs/tutorial-basics/create-a-document.md +57 -0
  34. package/docs/docs/tutorial-basics/create-a-page.md +43 -0
  35. package/docs/docs/tutorial-basics/deploy-your-site.md +31 -0
  36. package/docs/docs/tutorial-basics/markdown-features.mdx +152 -0
  37. package/docs/docs/tutorial-extras/_category_.json +7 -0
  38. package/docs/docs/tutorial-extras/img/docsVersionDropdown.png +0 -0
  39. package/docs/docs/tutorial-extras/img/localeDropdown.png +0 -0
  40. package/docs/docs/tutorial-extras/manage-docs-versions.md +55 -0
  41. package/docs/docs/tutorial-extras/translate-your-site.md +88 -0
  42. package/docs/docs/tutorials/quickstart.md +365 -0
  43. package/docs/docusaurus.config.ts +163 -0
  44. package/docs/package-lock.json +17493 -0
  45. package/docs/package.json +48 -0
  46. package/docs/sidebars.ts +33 -0
  47. package/docs/src/components/HomepageFeatures/index.tsx +71 -0
  48. package/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  49. package/docs/src/css/custom.css +30 -0
  50. package/docs/src/pages/index.module.css +23 -0
  51. package/docs/src/pages/index.tsx +44 -0
  52. package/docs/src/pages/markdown-page.md +7 -0
  53. package/docs/static/.nojekyll +0 -0
  54. package/docs/static/img/docusaurus-social-card.jpg +0 -0
  55. package/docs/static/img/docusaurus.png +0 -0
  56. package/docs/static/img/favicon.ico +0 -0
  57. package/docs/static/img/logo.svg +1 -0
  58. package/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  59. package/docs/static/img/undraw_docusaurus_react.svg +170 -0
  60. package/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  61. package/docs/tsconfig.json +8 -0
  62. package/examples/README.md +48 -0
  63. package/examples/auto_save_demo.py +206 -0
  64. package/examples/auto_save_overwrite.py +201 -0
  65. package/examples/basic_usage.py +135 -0
  66. package/examples/demo.py +139 -0
  67. package/examples/history_demo.py +317 -0
  68. package/examples/test_default_autosave.py +124 -0
  69. package/examples/update_consignee_example.py +179 -0
  70. package/package.json +51 -0
  71. package/plans/2026-04-19-fastmcp3-migration-plan.md +1045 -0
  72. package/pyproject.toml +331 -0
  73. package/requirements-dev.txt +30 -0
  74. package/requirements.txt +22 -0
  75. package/scripts/publish.py +67 -0
  76. package/smithery.yaml +15 -0
  77. package/specs/2026-04-19-fastmcp3-migration-design.md +243 -0
  78. package/src/csv_editor/__init__.py +8 -0
  79. package/src/csv_editor/models/__init__.py +39 -0
  80. package/src/csv_editor/models/auto_save.py +246 -0
  81. package/src/csv_editor/models/csv_session.py +468 -0
  82. package/src/csv_editor/models/data_models.py +244 -0
  83. package/src/csv_editor/models/history_manager.py +456 -0
  84. package/src/csv_editor/prompts/__init__.py +0 -0
  85. package/src/csv_editor/prompts/data_prompts.py +13 -0
  86. package/src/csv_editor/resources/__init__.py +0 -0
  87. package/src/csv_editor/resources/csv_resources.py +22 -0
  88. package/src/csv_editor/server.py +640 -0
  89. package/src/csv_editor/tools/__init__.py +5 -0
  90. package/src/csv_editor/tools/analytics.py +700 -0
  91. package/src/csv_editor/tools/auto_save_operations.py +235 -0
  92. package/src/csv_editor/tools/data_operations.py +3 -0
  93. package/src/csv_editor/tools/history_operations.py +315 -0
  94. package/src/csv_editor/tools/io_operations.py +431 -0
  95. package/src/csv_editor/tools/transformations.py +663 -0
  96. package/src/csv_editor/tools/validation.py +822 -0
  97. package/src/csv_editor/utils/__init__.py +0 -0
  98. package/src/csv_editor/utils/validators.py +205 -0
  99. package/tests/README.md +65 -0
  100. package/tests/__init__.py +7 -0
  101. package/tests/conftest.py +50 -0
  102. package/tests/test_auto_save.py +378 -0
  103. package/tests/test_basic.py +103 -0
  104. package/tests/test_integration.py +356 -0
  105. package/tests/test_server_boot.py +50 -0
  106. package/tests/test_settings.py +184 -0
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env python3
2
+ """Example showing auto-save that overwrites the original file."""
3
+
4
+ import asyncio
5
+ import tempfile
6
+ import os
7
+ import pandas as pd
8
+ from pathlib import Path
9
+
10
+ # Setup path for imports
11
+ import sys
12
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
13
+
14
+ from src.csv_editor.models.csv_session import CSVSession
15
+ from src.csv_editor.models.auto_save import AutoSaveConfig, AutoSaveMode, AutoSaveStrategy
16
+ from src.csv_editor.models.data_models import OperationType
17
+
18
+
19
+ async def overwrite_same_file_example():
20
+ """Demonstrate auto-save that updates the same file."""
21
+
22
+ print("=" * 60)
23
+ print("Auto-Save: Overwrite Same File Example")
24
+ print("=" * 60)
25
+
26
+ # Create a temporary CSV file
27
+ temp_dir = tempfile.mkdtemp()
28
+ original_file = os.path.join(temp_dir, "data.csv")
29
+
30
+ # Create initial data
31
+ initial_data = pd.DataFrame({
32
+ 'product': ['Laptop', 'Mouse', 'Keyboard'],
33
+ 'price': [999.99, 29.99, 79.99],
34
+ 'stock': [50, 200, 150]
35
+ })
36
+
37
+ # Save initial data to file
38
+ initial_data.to_csv(original_file, index=False)
39
+ print(f"\n✓ Created file: {original_file}")
40
+ print(f"Initial content:\n{initial_data}")
41
+
42
+ # Configure auto-save to OVERWRITE the same file
43
+ config = AutoSaveConfig(
44
+ enabled=True,
45
+ mode=AutoSaveMode.AFTER_OPERATION, # Save after each operation
46
+ strategy=AutoSaveStrategy.OVERWRITE, # Overwrite the original file
47
+ )
48
+
49
+ # Create session with auto-save config
50
+ session = CSVSession(auto_save_config=config)
51
+
52
+ # Load the CSV file
53
+ df = pd.read_csv(original_file)
54
+ session.load_data(df, file_path=original_file) # Pass the original file path
55
+
56
+ print(f"\n✓ Loaded data into session from: {original_file}")
57
+
58
+ # Modify the data
59
+ print("\n--- Making changes to the data ---")
60
+
61
+ # Change 1: Increase all prices by 10%
62
+ session.df['price'] = session.df['price'] * 1.1
63
+ session.record_operation(OperationType.TRANSFORM, {"operation": "price_increase_10%"})
64
+ await session.trigger_auto_save_if_needed()
65
+ print("✓ Increased prices by 10% and auto-saved to same file")
66
+
67
+ # Verify the file was updated
68
+ df_check1 = pd.read_csv(original_file)
69
+ print(f"File content after price increase:\n{df_check1}")
70
+
71
+ # Change 2: Add a new product
72
+ new_row = pd.DataFrame({
73
+ 'product': ['Monitor'],
74
+ 'price': [329.989], # Will be rounded when saved
75
+ 'stock': [75]
76
+ })
77
+ session.df = pd.concat([session.df, new_row], ignore_index=True)
78
+ session.record_operation(OperationType.ADD_COLUMN, {"operation": "add_monitor"})
79
+ await session.trigger_auto_save_if_needed()
80
+ print("\n✓ Added new product and auto-saved to same file")
81
+
82
+ # Verify the file was updated again
83
+ df_check2 = pd.read_csv(original_file)
84
+ print(f"File content after adding product:\n{df_check2}")
85
+
86
+ # Change 3: Filter out low stock items (this removes data)
87
+ session.df = session.df[session.df['stock'] >= 100]
88
+ session.record_operation(OperationType.FILTER, {"condition": "stock >= 100"})
89
+ await session.trigger_auto_save_if_needed()
90
+ print("\n✓ Filtered low stock items and auto-saved to same file")
91
+
92
+ # Final verification
93
+ df_final = pd.read_csv(original_file)
94
+ print(f"Final file content:\n{df_final}")
95
+
96
+ print("\n" + "=" * 60)
97
+ print("Summary:")
98
+ print("=" * 60)
99
+ print(f"✓ Original file: {original_file}")
100
+ print("✓ Same file was overwritten after each operation")
101
+ print("✓ No backup copies were created")
102
+ print(f"✓ Final file has {len(df_final)} rows (started with {len(initial_data)})")
103
+
104
+ # Show that only one file exists (no backups)
105
+ files_in_dir = list(Path(temp_dir).glob("*.csv"))
106
+ print(f"\nFiles in directory: {[f.name for f in files_in_dir]}")
107
+ print(f"Total CSV files: {len(files_in_dir)} (only the original, no backups)")
108
+
109
+ return original_file
110
+
111
+
112
+ async def hybrid_example_with_overwrite():
113
+ """Example showing overwrite with periodic saves."""
114
+
115
+ print("\n" + "=" * 60)
116
+ print("Hybrid Mode with Overwrite Example")
117
+ print("=" * 60)
118
+
119
+ # Create a temporary CSV file
120
+ temp_dir = tempfile.mkdtemp()
121
+ original_file = os.path.join(temp_dir, "periodic_data.csv")
122
+
123
+ # Create initial data
124
+ data = pd.DataFrame({
125
+ 'id': [1, 2, 3],
126
+ 'value': [100, 200, 300]
127
+ })
128
+ data.to_csv(original_file, index=False)
129
+ print(f"\n✓ Created file: {original_file}")
130
+
131
+ # Configure hybrid mode with overwrite
132
+ config = AutoSaveConfig(
133
+ enabled=True,
134
+ mode=AutoSaveMode.HYBRID, # Both periodic and after-operation
135
+ strategy=AutoSaveStrategy.OVERWRITE,
136
+ interval_seconds=2 # Save every 2 seconds
137
+ )
138
+
139
+ session = CSVSession(auto_save_config=config)
140
+ df = pd.read_csv(original_file)
141
+ session.load_data(df, file_path=original_file)
142
+
143
+ # Start periodic saves
144
+ await session.auto_save_manager.start_periodic_save(session._save_callback)
145
+ print("✓ Started periodic auto-save (every 2 seconds)")
146
+
147
+ # Make some changes
148
+ print("\nMaking gradual changes...")
149
+
150
+ # Change 1
151
+ session.df['value'] = session.df['value'] + 10
152
+ print(" Added 10 to all values")
153
+
154
+ # Wait for periodic save
155
+ await asyncio.sleep(2.5)
156
+
157
+ # Change 2
158
+ session.df['value'] = session.df['value'] * 2
159
+ session.record_operation(OperationType.TRANSFORM, {"operation": "double_values"})
160
+ await session.trigger_auto_save_if_needed()
161
+ print(" Doubled all values (triggered immediate save)")
162
+
163
+ # Wait for another periodic save
164
+ await asyncio.sleep(2.5)
165
+
166
+ # Stop periodic saves
167
+ await session.auto_save_manager.stop_periodic_save()
168
+ print("\n✓ Stopped periodic saves")
169
+
170
+ # Check final file
171
+ df_final = pd.read_csv(original_file)
172
+ print(f"\nFinal file content:\n{df_final}")
173
+
174
+ return original_file
175
+
176
+
177
+ async def main():
178
+ """Run all examples."""
179
+
180
+ # Example 1: Simple overwrite
181
+ file1 = await overwrite_same_file_example()
182
+
183
+ # Example 2: Hybrid mode with overwrite
184
+ file2 = await hybrid_example_with_overwrite()
185
+
186
+ print("\n" + "=" * 60)
187
+ print("✅ All examples completed!")
188
+ print("=" * 60)
189
+ print("\nKey Takeaways:")
190
+ print("• Use strategy='overwrite' to update the same file")
191
+ print("• Original file path must be provided when loading data")
192
+ print("• No backup copies are created with overwrite strategy")
193
+ print("• Works with all modes: after_operation, periodic, hybrid")
194
+ print("\nFor production use, consider:")
195
+ print("• Backup strategy for safety (creates copies)")
196
+ print("• Overwrite strategy for direct updates")
197
+ print("• Hybrid mode for maximum protection")
198
+
199
+
200
+ if __name__ == "__main__":
201
+ asyncio.run(main())
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Basic usage example for CSV MCP Server
4
+
5
+ This script demonstrates basic CSV operations using the server's tools.
6
+ """
7
+
8
+ import asyncio
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ # Add parent directory to path
13
+ sys.path.insert(0, str(Path(__file__).parent.parent))
14
+
15
+ from src.csv_editor.tools.io_operations import (
16
+ load_csv_from_content,
17
+ export_csv,
18
+ get_session_info
19
+ )
20
+ from src.csv_editor.tools.transformations import (
21
+ filter_rows,
22
+ sort_data,
23
+ fill_missing_values
24
+ )
25
+ from src.csv_editor.tools.analytics import (
26
+ get_statistics,
27
+ get_column_statistics
28
+ )
29
+
30
+ # Sample CSV data
31
+ SAMPLE_DATA = """product,category,price,quantity,date
32
+ Laptop,Electronics,999.99,10,2024-01-15
33
+ Mouse,Electronics,29.99,50,2024-01-16
34
+ Desk,Furniture,299.99,5,2024-01-17
35
+ Chair,Furniture,149.99,15,2024-01-18
36
+ Keyboard,Electronics,79.99,25,2024-01-19
37
+ Monitor,Electronics,399.99,8,2024-01-20
38
+ Bookshelf,Furniture,199.99,7,2024-01-21
39
+ Headphones,Electronics,89.99,20,2024-01-22
40
+ Table,Furniture,499.99,3,2024-01-23
41
+ Webcam,Electronics,59.99,30,2024-01-24
42
+ """
43
+
44
+ async def main():
45
+ print("CSV MCP Server - Basic Usage Example")
46
+ print("=" * 40)
47
+
48
+ # 1. Load CSV data
49
+ print("\n1. Loading CSV data...")
50
+ result = await load_csv_from_content(
51
+ content=SAMPLE_DATA,
52
+ delimiter=","
53
+ )
54
+
55
+ if not result["success"]:
56
+ print(f"Failed to load data: {result.get('error', 'Unknown error')}")
57
+ return
58
+
59
+ session_id = result["session_id"]
60
+ print(f"✓ Loaded {result['rows_affected']} rows")
61
+ print(f" Session ID: {session_id}")
62
+ print(f" Columns: {', '.join(result['columns_affected'])}")
63
+
64
+ # 2. Get statistics
65
+ print("\n2. Calculating statistics...")
66
+ stats = await get_statistics(
67
+ session_id=session_id,
68
+ columns=["price", "quantity"]
69
+ )
70
+
71
+ if stats["success"]:
72
+ print("✓ Statistics calculated:")
73
+ for col, col_stats in stats["statistics"].items():
74
+ if isinstance(col_stats, dict):
75
+ print(f"\n {col}:")
76
+ print(f" Mean: ${col_stats.get('mean', 0):.2f}" if col == "price" else f" Mean: {col_stats.get('mean', 0):.1f}")
77
+ print(f" Min: ${col_stats.get('min', 0):.2f}" if col == "price" else f" Min: {col_stats.get('min', 0):.0f}")
78
+ print(f" Max: ${col_stats.get('max', 0):.2f}" if col == "price" else f" Max: {col_stats.get('max', 0):.0f}")
79
+
80
+ # 3. Filter data
81
+ print("\n3. Filtering electronics over $50...")
82
+ filtered = await filter_rows(
83
+ session_id=session_id,
84
+ conditions=[
85
+ {"column": "category", "operator": "==", "value": "Electronics"},
86
+ {"column": "price", "operator": ">", "value": 50}
87
+ ],
88
+ mode="and"
89
+ )
90
+
91
+ if filtered["success"]:
92
+ print(f"✓ Filtered: {filtered['rows_before']} → {filtered['rows_after']} rows")
93
+
94
+ # 4. Sort data
95
+ print("\n4. Sorting by price (descending)...")
96
+ sorted_result = await sort_data(
97
+ session_id=session_id,
98
+ columns=[{"column": "price", "ascending": False}]
99
+ )
100
+
101
+ if sorted_result["success"]:
102
+ print("✓ Data sorted by price")
103
+
104
+ # 5. Export results
105
+ print("\n5. Exporting results...")
106
+ output_dir = Path("output")
107
+ output_dir.mkdir(exist_ok=True)
108
+
109
+ # Export as JSON
110
+ json_file = output_dir / "filtered_products.json"
111
+ export_result = await export_csv(
112
+ session_id=session_id,
113
+ file_path=str(json_file),
114
+ format="json"
115
+ )
116
+
117
+ if export_result["success"]:
118
+ print(f"✓ Exported to: {json_file}")
119
+
120
+ # Export as CSV
121
+ csv_file = output_dir / "filtered_products.csv"
122
+ export_result = await export_csv(
123
+ session_id=session_id,
124
+ file_path=str(csv_file),
125
+ format="csv"
126
+ )
127
+
128
+ if export_result["success"]:
129
+ print(f"✓ Exported to: {csv_file}")
130
+
131
+ print("\n" + "=" * 40)
132
+ print("Example completed successfully!")
133
+
134
+ if __name__ == "__main__":
135
+ asyncio.run(main())
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ CSV MCP Server Demo
4
+
5
+ Demonstrates the full capabilities of the CSV MCP Server.
6
+ Run this to see all features in action.
7
+ """
8
+
9
+ import asyncio
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ sys.path.insert(0, str(Path(__file__).parent))
14
+
15
+ from src.csv_editor.tools.io_operations import load_csv_from_content
16
+ from src.csv_editor.tools.transformations import filter_rows, sort_data
17
+ from src.csv_editor.tools.analytics import get_statistics, profile_data
18
+ from src.csv_editor.tools.validation import check_data_quality
19
+
20
+ # Demo data
21
+ DEMO_CSV = """employee_id,name,department,salary,years_experience,performance_rating
22
+ 1001,Alice Johnson,Engineering,95000,5,4.5
23
+ 1002,Bob Smith,Marketing,65000,3,3.8
24
+ 1003,Charlie Davis,Engineering,105000,7,4.7
25
+ 1004,Diana Wilson,Sales,72000,4,4.2
26
+ 1005,Eve Martinez,Engineering,88000,4,4.1
27
+ 1006,Frank Brown,Marketing,58000,2,3.5
28
+ 1007,Grace Lee,Sales,78000,5,4.4
29
+ 1008,Henry Taylor,Engineering,115000,10,4.9
30
+ 1009,Iris Chen,Marketing,70000,4,4.0
31
+ 1010,Jack White,Sales,68000,3,3.9
32
+ """
33
+
34
+ class Demo:
35
+ @staticmethod
36
+ def header(title: str):
37
+ print(f"\n{'='*50}")
38
+ print(f" {title}")
39
+ print(f"{'='*50}")
40
+
41
+ @staticmethod
42
+ def success(msg: str):
43
+ print(f"✅ {msg}")
44
+
45
+ @staticmethod
46
+ def info(msg: str):
47
+ print(f"ℹ️ {msg}")
48
+
49
+ @staticmethod
50
+ def result(label: str, value: any):
51
+ print(f" {label}: {value}")
52
+
53
+ async def run_demo():
54
+ Demo.header("CSV MCP Server - Feature Demo")
55
+
56
+ # Load data
57
+ Demo.info("Loading employee data...")
58
+ result = await load_csv_from_content(content=DEMO_CSV)
59
+
60
+ if not result["success"]:
61
+ print(f"Error loading data: {result.get('error')}")
62
+ return
63
+
64
+ session_id = result["session_id"]
65
+ Demo.success(f"Loaded {result['rows_affected']} employees")
66
+
67
+ # Data Quality Check
68
+ Demo.header("Data Quality Assessment")
69
+ quality = await check_data_quality(session_id=session_id)
70
+
71
+ if quality["success"]:
72
+ metrics = quality["quality_results"]["metrics"]
73
+ Demo.result("Overall Score", f"{quality['quality_results']['overall_score']:.1f}%")
74
+ Demo.result("Completeness", f"{metrics['completeness']:.1f}%")
75
+ Demo.result("Uniqueness", f"{metrics['uniqueness']:.1f}%")
76
+ Demo.result("Consistency", f"{metrics['consistency']:.1f}%")
77
+
78
+ # Statistics
79
+ Demo.header("Salary Statistics by Department")
80
+ stats = await get_statistics(
81
+ session_id=session_id,
82
+ columns=["salary", "years_experience", "performance_rating"]
83
+ )
84
+
85
+ if stats["success"]:
86
+ salary_stats = stats["statistics"].get("salary", {})
87
+ Demo.result("Average Salary", f"${salary_stats.get('mean', 0):,.2f}")
88
+ Demo.result("Salary Range", f"${salary_stats.get('min', 0):,.0f} - ${salary_stats.get('max', 0):,.0f}")
89
+ Demo.result("Median Salary", f"${salary_stats.get('50%', 0):,.2f}")
90
+
91
+ # Filter high performers
92
+ Demo.header("High Performers Analysis")
93
+ filtered = await filter_rows(
94
+ session_id=session_id,
95
+ conditions=[
96
+ {"column": "performance_rating", "operator": ">=", "value": 4.0},
97
+ {"column": "salary", "operator": ">", "value": 70000}
98
+ ],
99
+ mode="and"
100
+ )
101
+
102
+ if filtered["success"]:
103
+ Demo.success(f"Found {filtered['rows_after']} high performers")
104
+ Demo.info(f"({filtered['rows_after']}/{filtered['rows_before']} = {filtered['rows_after']/filtered['rows_before']*100:.1f}% of employees)")
105
+
106
+ # Profile the data
107
+ Demo.header("Data Profile Summary")
108
+ profile = await profile_data(
109
+ session_id=session_id,
110
+ include_correlations=True,
111
+ include_outliers=False
112
+ )
113
+
114
+ if profile["success"]:
115
+ summary = profile["profile"]["summary"]
116
+ Demo.result("Total Rows", summary["total_rows"])
117
+ Demo.result("Total Columns", summary["total_columns"])
118
+ Demo.result("Numeric Columns", summary["numeric_columns"])
119
+ Demo.result("Text Columns", summary["text_columns"])
120
+
121
+ # Show correlations if any
122
+ if "correlations" in profile["profile"]:
123
+ correlations = profile["profile"]["correlations"]
124
+ if "significant_correlations" in correlations:
125
+ Demo.info("\nSignificant Correlations:")
126
+ for corr in correlations["significant_correlations"][:3]:
127
+ Demo.result(
128
+ f"{corr['column1']} ↔ {corr['column2']}",
129
+ f"{corr['correlation']:.3f}"
130
+ )
131
+
132
+ Demo.header("Demo Complete!")
133
+ Demo.success("All features demonstrated successfully")
134
+ Demo.info(f"Session ID: {session_id}")
135
+ print()
136
+
137
+ if __name__ == "__main__":
138
+ print("\n🚀 Starting CSV MCP Server Demo...\n")
139
+ asyncio.run(run_demo())