scitex 2.11.0__py3-none-any.whl → 2.13.0__py3-none-any.whl

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 (148) hide show
  1. scitex/__main__.py +24 -5
  2. scitex/__version__.py +1 -1
  3. scitex/_optional_deps.py +33 -0
  4. scitex/ai/classification/reporters/_ClassificationReporter.py +1 -1
  5. scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +2 -2
  6. scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +2 -2
  7. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +2 -2
  8. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +2 -2
  9. scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +2 -2
  10. scitex/ai/classification/timeseries/_normalize_timestamp.py +1 -1
  11. scitex/ai/metrics/_calc_seizure_prediction_metrics.py +1 -1
  12. scitex/ai/plt/_plot_feature_importance.py +1 -1
  13. scitex/ai/plt/_plot_learning_curve.py +1 -1
  14. scitex/ai/plt/_plot_optuna_study.py +1 -1
  15. scitex/ai/plt/_plot_pre_rec_curve.py +1 -1
  16. scitex/ai/plt/_plot_roc_curve.py +1 -1
  17. scitex/ai/plt/_stx_conf_mat.py +1 -1
  18. scitex/ai/training/_LearningCurveLogger.py +1 -1
  19. scitex/audio/mcp_server.py +38 -8
  20. scitex/browser/automation/CookieHandler.py +1 -1
  21. scitex/browser/core/BrowserMixin.py +1 -1
  22. scitex/browser/core/ChromeProfileManager.py +1 -1
  23. scitex/browser/debugging/_browser_logger.py +1 -1
  24. scitex/browser/debugging/_highlight_element.py +1 -1
  25. scitex/browser/debugging/_show_grid.py +1 -1
  26. scitex/browser/interaction/click_center.py +1 -1
  27. scitex/browser/interaction/click_with_fallbacks.py +1 -1
  28. scitex/browser/interaction/close_popups.py +1 -1
  29. scitex/browser/interaction/fill_with_fallbacks.py +1 -1
  30. scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +1 -1
  31. scitex/browser/pdf/detect_chrome_pdf_viewer.py +1 -1
  32. scitex/browser/stealth/HumanBehavior.py +1 -1
  33. scitex/browser/stealth/StealthManager.py +1 -1
  34. scitex/canvas/_mcp_handlers.py +372 -0
  35. scitex/canvas/_mcp_tool_schemas.py +219 -0
  36. scitex/canvas/mcp_server.py +151 -0
  37. scitex/capture/mcp_server.py +41 -12
  38. scitex/cli/audio.py +233 -0
  39. scitex/cli/capture.py +307 -0
  40. scitex/cli/main.py +27 -4
  41. scitex/cli/repro.py +233 -0
  42. scitex/cli/resource.py +240 -0
  43. scitex/cli/stats.py +325 -0
  44. scitex/cli/template.py +236 -0
  45. scitex/cli/tex.py +286 -0
  46. scitex/cli/web.py +11 -12
  47. scitex/dev/__init__.py +3 -0
  48. scitex/dev/_pyproject.py +405 -0
  49. scitex/dev/plt/__init__.py +2 -2
  50. scitex/dev/plt/mpl/get_dir_ax.py +1 -1
  51. scitex/dev/plt/mpl/get_signatures.py +1 -1
  52. scitex/dev/plt/mpl/get_signatures_details.py +1 -1
  53. scitex/diagram/_mcp_handlers.py +400 -0
  54. scitex/diagram/_mcp_tool_schemas.py +157 -0
  55. scitex/diagram/mcp_server.py +151 -0
  56. scitex/dsp/_demo_sig.py +51 -5
  57. scitex/dsp/_mne.py +13 -2
  58. scitex/dsp/_modulation_index.py +15 -3
  59. scitex/dsp/_pac.py +23 -5
  60. scitex/dsp/_psd.py +16 -4
  61. scitex/dsp/_resample.py +24 -4
  62. scitex/dsp/_transform.py +16 -3
  63. scitex/dsp/add_noise.py +15 -1
  64. scitex/dsp/norm.py +17 -2
  65. scitex/dsp/reference.py +17 -1
  66. scitex/dsp/utils/_differential_bandpass_filters.py +20 -2
  67. scitex/dsp/utils/_zero_pad.py +18 -4
  68. scitex/dt/_normalize_timestamp.py +1 -1
  69. scitex/git/_session.py +1 -1
  70. scitex/io/_load_modules/_con.py +12 -1
  71. scitex/io/_load_modules/_eeg.py +12 -1
  72. scitex/io/_load_modules/_optuna.py +21 -63
  73. scitex/io/_load_modules/_torch.py +11 -3
  74. scitex/io/_save_modules/_optuna_study_as_csv_and_pngs.py +13 -2
  75. scitex/io/_save_modules/_torch.py +11 -3
  76. scitex/mcp_server.py +159 -0
  77. scitex/plt/_mcp_handlers.py +361 -0
  78. scitex/plt/_mcp_tool_schemas.py +169 -0
  79. scitex/plt/mcp_server.py +205 -0
  80. scitex/repro/README_RandomStateManager.md +3 -3
  81. scitex/repro/_RandomStateManager.py +14 -14
  82. scitex/repro/_gen_ID.py +1 -1
  83. scitex/repro/_gen_timestamp.py +1 -1
  84. scitex/repro/_hash_array.py +4 -4
  85. scitex/scholar/__main__.py +24 -2
  86. scitex/scholar/_mcp_handlers.py +685 -0
  87. scitex/scholar/_mcp_tool_schemas.py +339 -0
  88. scitex/scholar/docs/template.py +1 -1
  89. scitex/scholar/examples/07_storage_integration.py +1 -1
  90. scitex/scholar/impact_factor/jcr/ImpactFactorJCREngine.py +1 -1
  91. scitex/scholar/impact_factor/jcr/build_database.py +1 -1
  92. scitex/scholar/mcp_server.py +315 -0
  93. scitex/scholar/pdf_download/ScholarPDFDownloader.py +1 -1
  94. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +1 -1
  95. scitex/scholar/pipelines/ScholarPipelineParallel.py +1 -1
  96. scitex/scholar/pipelines/ScholarPipelineSingle.py +1 -1
  97. scitex/scholar/storage/PaperIO.py +1 -1
  98. scitex/session/README.md +4 -4
  99. scitex/session/__init__.py +1 -1
  100. scitex/session/_decorator.py +9 -9
  101. scitex/session/_lifecycle.py +5 -5
  102. scitex/session/template.py +1 -1
  103. scitex/stats/__main__.py +281 -0
  104. scitex/stats/_mcp_handlers.py +1191 -0
  105. scitex/stats/_mcp_tool_schemas.py +384 -0
  106. scitex/stats/correct/_correct_bonferroni.py +1 -1
  107. scitex/stats/correct/_correct_fdr.py +1 -1
  108. scitex/stats/correct/_correct_fdr_.py +1 -1
  109. scitex/stats/correct/_correct_holm.py +1 -1
  110. scitex/stats/correct/_correct_sidak.py +1 -1
  111. scitex/stats/effect_sizes/_cliffs_delta.py +1 -1
  112. scitex/stats/effect_sizes/_cohens_d.py +1 -1
  113. scitex/stats/effect_sizes/_epsilon_squared.py +1 -1
  114. scitex/stats/effect_sizes/_eta_squared.py +1 -1
  115. scitex/stats/effect_sizes/_prob_superiority.py +1 -1
  116. scitex/stats/mcp_server.py +405 -0
  117. scitex/stats/posthoc/_dunnett.py +1 -1
  118. scitex/stats/posthoc/_games_howell.py +1 -1
  119. scitex/stats/posthoc/_tukey_hsd.py +1 -1
  120. scitex/stats/power/_power.py +1 -1
  121. scitex/stats/utils/_effect_size.py +1 -1
  122. scitex/stats/utils/_formatters.py +1 -1
  123. scitex/stats/utils/_power.py +1 -1
  124. scitex/template/_mcp_handlers.py +259 -0
  125. scitex/template/_mcp_tool_schemas.py +112 -0
  126. scitex/template/mcp_server.py +186 -0
  127. scitex/utils/_verify_scitex_format.py +2 -2
  128. scitex/utils/template.py +1 -1
  129. scitex/web/__init__.py +12 -11
  130. scitex/web/_scraping.py +26 -265
  131. scitex/web/download_images.py +316 -0
  132. scitex/writer/Writer.py +1 -1
  133. scitex/writer/_clone_writer_project.py +1 -1
  134. scitex/writer/_validate_tree_structures.py +1 -1
  135. scitex/writer/dataclasses/config/_WriterConfig.py +1 -1
  136. scitex/writer/dataclasses/contents/_ManuscriptContents.py +1 -1
  137. scitex/writer/dataclasses/core/_Document.py +1 -1
  138. scitex/writer/dataclasses/core/_DocumentSection.py +1 -1
  139. scitex/writer/dataclasses/results/_CompilationResult.py +1 -1
  140. scitex/writer/dataclasses/results/_LaTeXIssue.py +1 -1
  141. scitex/writer/utils/.legacy_git_retry.py +7 -5
  142. scitex/writer/utils/_parse_latex_logs.py +1 -1
  143. {scitex-2.11.0.dist-info → scitex-2.13.0.dist-info}/METADATA +431 -269
  144. {scitex-2.11.0.dist-info → scitex-2.13.0.dist-info}/RECORD +147 -118
  145. scitex-2.13.0.dist-info/entry_points.txt +11 -0
  146. scitex-2.11.0.dist-info/entry_points.txt +0 -2
  147. {scitex-2.11.0.dist-info → scitex-2.13.0.dist-info}/WHEEL +0 -0
  148. {scitex-2.11.0.dist-info → scitex-2.13.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,400 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-01-08
3
+ # File: src/scitex/diagram/_mcp_handlers.py
4
+ # ----------------------------------------
5
+
6
+ """
7
+ MCP Handler implementations for SciTeX diagram module.
8
+
9
+ Provides async handlers for paper-optimized diagram generation.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import asyncio
15
+ from typing import Optional
16
+
17
+
18
+ async def create_diagram_handler(
19
+ spec_path: Optional[str] = None,
20
+ spec_dict: Optional[dict] = None,
21
+ ) -> dict:
22
+ """
23
+ Create a diagram from specification.
24
+
25
+ Parameters
26
+ ----------
27
+ spec_path : str, optional
28
+ Path to YAML spec file
29
+ spec_dict : dict, optional
30
+ Spec as dictionary
31
+
32
+ Returns
33
+ -------
34
+ dict
35
+ Success status and diagram info
36
+ """
37
+ try:
38
+ from scitex.diagram import Diagram
39
+
40
+ if spec_path:
41
+ loop = asyncio.get_event_loop()
42
+ diagram = await loop.run_in_executor(
43
+ None,
44
+ lambda: Diagram.from_yaml(spec_path),
45
+ )
46
+ elif spec_dict:
47
+ diagram = Diagram(spec_dict)
48
+ else:
49
+ return {
50
+ "success": False,
51
+ "error": "Either spec_path or spec_dict must be provided",
52
+ }
53
+
54
+ # Get diagram info
55
+ spec = diagram.spec if hasattr(diagram, "spec") else {}
56
+
57
+ return {
58
+ "success": True,
59
+ "diagram_type": spec.get("type", "unknown"),
60
+ "node_count": len(spec.get("nodes", [])),
61
+ "edge_count": len(spec.get("edges", [])),
62
+ "message": "Diagram created successfully",
63
+ }
64
+ except Exception as e:
65
+ return {
66
+ "success": False,
67
+ "error": str(e),
68
+ }
69
+
70
+
71
+ async def compile_mermaid_handler(
72
+ spec_path: Optional[str] = None,
73
+ output_path: Optional[str] = None,
74
+ spec_dict: Optional[dict] = None,
75
+ ) -> dict:
76
+ """
77
+ Compile diagram to Mermaid format.
78
+
79
+ Parameters
80
+ ----------
81
+ spec_path : str, optional
82
+ Path to YAML spec file
83
+ output_path : str, optional
84
+ Output .mmd file path
85
+ spec_dict : dict, optional
86
+ Spec as dictionary
87
+
88
+ Returns
89
+ -------
90
+ dict
91
+ Success status and Mermaid output
92
+ """
93
+ try:
94
+ from scitex.diagram import Diagram, compile_to_mermaid
95
+
96
+ if spec_path:
97
+ loop = asyncio.get_event_loop()
98
+ diagram = await loop.run_in_executor(
99
+ None,
100
+ lambda: Diagram.from_yaml(spec_path),
101
+ )
102
+ elif spec_dict:
103
+ diagram = Diagram(spec_dict)
104
+ else:
105
+ return {
106
+ "success": False,
107
+ "error": "Either spec_path or spec_dict must be provided",
108
+ }
109
+
110
+ # Compile to Mermaid
111
+ loop = asyncio.get_event_loop()
112
+ mermaid_code = await loop.run_in_executor(
113
+ None,
114
+ lambda: compile_to_mermaid(diagram.spec),
115
+ )
116
+
117
+ # Save if output path provided
118
+ if output_path:
119
+ from pathlib import Path
120
+
121
+ Path(output_path).write_text(mermaid_code)
122
+
123
+ return {
124
+ "success": True,
125
+ "mermaid_code": mermaid_code,
126
+ "output_path": output_path,
127
+ "message": f"Compiled to Mermaid{f' and saved to {output_path}' if output_path else ''}",
128
+ }
129
+ except Exception as e:
130
+ return {
131
+ "success": False,
132
+ "error": str(e),
133
+ }
134
+
135
+
136
+ async def compile_graphviz_handler(
137
+ spec_path: Optional[str] = None,
138
+ output_path: Optional[str] = None,
139
+ spec_dict: Optional[dict] = None,
140
+ ) -> dict:
141
+ """
142
+ Compile diagram to Graphviz DOT format.
143
+
144
+ Parameters
145
+ ----------
146
+ spec_path : str, optional
147
+ Path to YAML spec file
148
+ output_path : str, optional
149
+ Output .dot file path
150
+ spec_dict : dict, optional
151
+ Spec as dictionary
152
+
153
+ Returns
154
+ -------
155
+ dict
156
+ Success status and DOT output
157
+ """
158
+ try:
159
+ from scitex.diagram import Diagram, compile_to_graphviz
160
+
161
+ if spec_path:
162
+ loop = asyncio.get_event_loop()
163
+ diagram = await loop.run_in_executor(
164
+ None,
165
+ lambda: Diagram.from_yaml(spec_path),
166
+ )
167
+ elif spec_dict:
168
+ diagram = Diagram(spec_dict)
169
+ else:
170
+ return {
171
+ "success": False,
172
+ "error": "Either spec_path or spec_dict must be provided",
173
+ }
174
+
175
+ # Compile to Graphviz
176
+ loop = asyncio.get_event_loop()
177
+ dot_code = await loop.run_in_executor(
178
+ None,
179
+ lambda: compile_to_graphviz(diagram.spec),
180
+ )
181
+
182
+ # Save if output path provided
183
+ if output_path:
184
+ from pathlib import Path
185
+
186
+ Path(output_path).write_text(dot_code)
187
+
188
+ return {
189
+ "success": True,
190
+ "dot_code": dot_code,
191
+ "output_path": output_path,
192
+ "message": f"Compiled to Graphviz{f' and saved to {output_path}' if output_path else ''}",
193
+ }
194
+ except Exception as e:
195
+ return {
196
+ "success": False,
197
+ "error": str(e),
198
+ }
199
+
200
+
201
+ async def list_presets_handler() -> dict:
202
+ """
203
+ List available diagram presets.
204
+
205
+ Returns
206
+ -------
207
+ dict
208
+ Success status and preset list
209
+ """
210
+ try:
211
+ presets = [
212
+ {
213
+ "name": "workflow",
214
+ "description": "Linear workflow diagrams (step1 → step2 → step3)",
215
+ "use_case": "Methods section, data processing pipelines",
216
+ "direction": "left-to-right",
217
+ },
218
+ {
219
+ "name": "decision",
220
+ "description": "Decision tree/flowchart diagrams",
221
+ "use_case": "Algorithm flowcharts, decision processes",
222
+ "direction": "top-to-bottom",
223
+ },
224
+ {
225
+ "name": "pipeline",
226
+ "description": "Data pipeline with parallel branches",
227
+ "use_case": "Complex data flows, parallel processing",
228
+ "direction": "left-to-right",
229
+ },
230
+ ]
231
+
232
+ return {
233
+ "success": True,
234
+ "count": len(presets),
235
+ "presets": presets,
236
+ }
237
+ except Exception as e:
238
+ return {
239
+ "success": False,
240
+ "error": str(e),
241
+ }
242
+
243
+
244
+ async def get_preset_handler(preset_name: str) -> dict:
245
+ """
246
+ Get a specific preset configuration.
247
+
248
+ Parameters
249
+ ----------
250
+ preset_name : str
251
+ Preset name (workflow, decision, pipeline)
252
+
253
+ Returns
254
+ -------
255
+ dict
256
+ Success status and preset config
257
+ """
258
+ try:
259
+ from scitex.diagram import DECISION_PRESET, PIPELINE_PRESET, WORKFLOW_PRESET
260
+
261
+ presets = {
262
+ "workflow": WORKFLOW_PRESET,
263
+ "decision": DECISION_PRESET,
264
+ "pipeline": PIPELINE_PRESET,
265
+ }
266
+
267
+ if preset_name not in presets:
268
+ return {
269
+ "success": False,
270
+ "error": f"Unknown preset: {preset_name}",
271
+ "available": list(presets.keys()),
272
+ }
273
+
274
+ preset = presets[preset_name]
275
+
276
+ return {
277
+ "success": True,
278
+ "preset_name": preset_name,
279
+ "config": preset,
280
+ }
281
+ except Exception as e:
282
+ return {
283
+ "success": False,
284
+ "error": str(e),
285
+ }
286
+
287
+
288
+ async def split_diagram_handler(
289
+ spec_path: str,
290
+ strategy: str = "horizontal",
291
+ max_nodes_per_part: int = 10,
292
+ ) -> dict:
293
+ """
294
+ Split a large diagram into smaller parts.
295
+
296
+ Parameters
297
+ ----------
298
+ spec_path : str
299
+ Path to YAML spec file
300
+ strategy : str
301
+ Split strategy (horizontal, vertical, semantic)
302
+ max_nodes_per_part : int
303
+ Max nodes per part
304
+
305
+ Returns
306
+ -------
307
+ dict
308
+ Success status and split results
309
+ """
310
+ try:
311
+ from scitex.diagram import Diagram, SplitConfig, SplitStrategy, split_diagram
312
+
313
+ # Map strategy string to enum
314
+ strategy_map = {
315
+ "horizontal": SplitStrategy.HORIZONTAL,
316
+ "vertical": SplitStrategy.VERTICAL,
317
+ "semantic": SplitStrategy.SEMANTIC,
318
+ }
319
+
320
+ if strategy not in strategy_map:
321
+ return {
322
+ "success": False,
323
+ "error": f"Unknown strategy: {strategy}",
324
+ "available": list(strategy_map.keys()),
325
+ }
326
+
327
+ loop = asyncio.get_event_loop()
328
+ diagram = await loop.run_in_executor(
329
+ None,
330
+ lambda: Diagram.from_yaml(spec_path),
331
+ )
332
+
333
+ config = SplitConfig(
334
+ strategy=strategy_map[strategy],
335
+ max_nodes_per_part=max_nodes_per_part,
336
+ )
337
+
338
+ result = await loop.run_in_executor(
339
+ None,
340
+ lambda: split_diagram(diagram.spec, config),
341
+ )
342
+
343
+ return {
344
+ "success": True,
345
+ "strategy": strategy,
346
+ "parts_count": len(result.parts) if hasattr(result, "parts") else 0,
347
+ "result": str(result),
348
+ }
349
+ except Exception as e:
350
+ return {
351
+ "success": False,
352
+ "error": str(e),
353
+ }
354
+
355
+
356
+ async def get_paper_modes_handler() -> dict:
357
+ """
358
+ Get available paper layout modes.
359
+
360
+ Returns
361
+ -------
362
+ dict
363
+ Success status and paper modes
364
+ """
365
+ try:
366
+ from scitex.diagram import PaperMode
367
+
368
+ modes = []
369
+ for mode in PaperMode:
370
+ modes.append(
371
+ {
372
+ "name": mode.name,
373
+ "value": mode.value,
374
+ }
375
+ )
376
+
377
+ return {
378
+ "success": True,
379
+ "count": len(modes),
380
+ "modes": modes,
381
+ "description": "Paper modes control diagram sizing for publication layouts",
382
+ }
383
+ except Exception as e:
384
+ return {
385
+ "success": False,
386
+ "error": str(e),
387
+ }
388
+
389
+
390
+ __all__ = [
391
+ "create_diagram_handler",
392
+ "compile_mermaid_handler",
393
+ "compile_graphviz_handler",
394
+ "list_presets_handler",
395
+ "get_preset_handler",
396
+ "split_diagram_handler",
397
+ "get_paper_modes_handler",
398
+ ]
399
+
400
+ # EOF
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-01-08
3
+ # File: src/scitex/diagram/_mcp_tool_schemas.py
4
+ # ----------------------------------------
5
+
6
+ """
7
+ MCP Tool schemas for SciTeX diagram module.
8
+
9
+ Defines tools for paper-optimized diagram generation:
10
+ - create_diagram: Create diagram from YAML spec
11
+ - compile_mermaid: Export to Mermaid format
12
+ - compile_graphviz: Export to Graphviz DOT format
13
+ - list_presets: List available diagram presets
14
+ - apply_preset: Apply workflow/decision/pipeline preset
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import mcp.types as types
20
+
21
+
22
+ def get_tool_schemas() -> list[types.Tool]:
23
+ """Return list of available MCP tools for diagram operations."""
24
+ return [
25
+ # Create diagram from YAML
26
+ types.Tool(
27
+ name="create_diagram",
28
+ description="Create a diagram from a YAML specification file or dictionary",
29
+ inputSchema={
30
+ "type": "object",
31
+ "properties": {
32
+ "spec_path": {
33
+ "type": "string",
34
+ "description": "Path to YAML specification file",
35
+ },
36
+ "spec_dict": {
37
+ "type": "object",
38
+ "description": "Diagram specification as dictionary (alternative to spec_path)",
39
+ },
40
+ },
41
+ "required": [],
42
+ },
43
+ ),
44
+ # Compile to Mermaid
45
+ types.Tool(
46
+ name="compile_mermaid",
47
+ description="Compile diagram specification to Mermaid format",
48
+ inputSchema={
49
+ "type": "object",
50
+ "properties": {
51
+ "spec_path": {
52
+ "type": "string",
53
+ "description": "Path to YAML specification file",
54
+ },
55
+ "output_path": {
56
+ "type": "string",
57
+ "description": "Output file path for .mmd file",
58
+ },
59
+ "spec_dict": {
60
+ "type": "object",
61
+ "description": "Diagram specification as dictionary (alternative to spec_path)",
62
+ },
63
+ },
64
+ "required": [],
65
+ },
66
+ ),
67
+ # Compile to Graphviz
68
+ types.Tool(
69
+ name="compile_graphviz",
70
+ description="Compile diagram specification to Graphviz DOT format",
71
+ inputSchema={
72
+ "type": "object",
73
+ "properties": {
74
+ "spec_path": {
75
+ "type": "string",
76
+ "description": "Path to YAML specification file",
77
+ },
78
+ "output_path": {
79
+ "type": "string",
80
+ "description": "Output file path for .dot file",
81
+ },
82
+ "spec_dict": {
83
+ "type": "object",
84
+ "description": "Diagram specification as dictionary (alternative to spec_path)",
85
+ },
86
+ },
87
+ "required": [],
88
+ },
89
+ ),
90
+ # List presets
91
+ types.Tool(
92
+ name="list_presets",
93
+ description="List available diagram presets (workflow, decision, pipeline)",
94
+ inputSchema={
95
+ "type": "object",
96
+ "properties": {},
97
+ "required": [],
98
+ },
99
+ ),
100
+ # Get preset
101
+ types.Tool(
102
+ name="get_preset",
103
+ description="Get a diagram preset configuration by name",
104
+ inputSchema={
105
+ "type": "object",
106
+ "properties": {
107
+ "preset_name": {
108
+ "type": "string",
109
+ "description": "Preset name",
110
+ "enum": ["workflow", "decision", "pipeline"],
111
+ },
112
+ },
113
+ "required": ["preset_name"],
114
+ },
115
+ ),
116
+ # Split diagram
117
+ types.Tool(
118
+ name="split_diagram",
119
+ description="Split a large diagram into smaller parts for multi-column layouts",
120
+ inputSchema={
121
+ "type": "object",
122
+ "properties": {
123
+ "spec_path": {
124
+ "type": "string",
125
+ "description": "Path to YAML specification file",
126
+ },
127
+ "strategy": {
128
+ "type": "string",
129
+ "description": "Split strategy",
130
+ "enum": ["horizontal", "vertical", "semantic"],
131
+ "default": "horizontal",
132
+ },
133
+ "max_nodes_per_part": {
134
+ "type": "integer",
135
+ "description": "Maximum nodes per split part",
136
+ "default": 10,
137
+ },
138
+ },
139
+ "required": ["spec_path"],
140
+ },
141
+ ),
142
+ # Get paper modes
143
+ types.Tool(
144
+ name="get_paper_modes",
145
+ description="Get available paper layout modes and their constraints",
146
+ inputSchema={
147
+ "type": "object",
148
+ "properties": {},
149
+ "required": [],
150
+ },
151
+ ),
152
+ ]
153
+
154
+
155
+ __all__ = ["get_tool_schemas"]
156
+
157
+ # EOF
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-01-08
3
+ # File: src/scitex/diagram/mcp_server.py
4
+ # ----------------------------------------
5
+
6
+ """
7
+ MCP Server for SciTeX diagram - Paper-optimized diagram generation.
8
+
9
+ Provides tools for:
10
+ - Creating diagrams from YAML specs
11
+ - Compiling to Mermaid/Graphviz formats
12
+ - Using workflow/decision/pipeline presets
13
+ - Splitting large diagrams for publication layouts
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import asyncio
19
+
20
+ # Graceful MCP dependency handling
21
+ try:
22
+ import mcp.types as types
23
+ from mcp.server import NotificationOptions, Server
24
+ from mcp.server.models import InitializationOptions
25
+ from mcp.server.stdio import stdio_server
26
+
27
+ MCP_AVAILABLE = True
28
+ except ImportError:
29
+ MCP_AVAILABLE = False
30
+ types = None # type: ignore
31
+ Server = None # type: ignore
32
+ NotificationOptions = None # type: ignore
33
+ InitializationOptions = None # type: ignore
34
+ stdio_server = None # type: ignore
35
+
36
+ __all__ = ["DiagramServer", "main", "MCP_AVAILABLE"]
37
+
38
+
39
+ class DiagramServer:
40
+ """MCP Server for Paper-Optimized Diagram Generation."""
41
+
42
+ def __init__(self):
43
+ self.server = Server("scitex-diagram")
44
+ self.setup_handlers()
45
+
46
+ def setup_handlers(self):
47
+ """Set up MCP server handlers."""
48
+ from ._mcp_handlers import (
49
+ compile_graphviz_handler,
50
+ compile_mermaid_handler,
51
+ create_diagram_handler,
52
+ get_paper_modes_handler,
53
+ get_preset_handler,
54
+ list_presets_handler,
55
+ split_diagram_handler,
56
+ )
57
+ from ._mcp_tool_schemas import get_tool_schemas
58
+
59
+ @self.server.list_tools()
60
+ async def handle_list_tools():
61
+ return get_tool_schemas()
62
+
63
+ @self.server.call_tool()
64
+ async def handle_call_tool(name: str, arguments: dict):
65
+ if name == "create_diagram":
66
+ return await self._wrap_result(create_diagram_handler(**arguments))
67
+
68
+ elif name == "compile_mermaid":
69
+ return await self._wrap_result(compile_mermaid_handler(**arguments))
70
+
71
+ elif name == "compile_graphviz":
72
+ return await self._wrap_result(compile_graphviz_handler(**arguments))
73
+
74
+ elif name == "list_presets":
75
+ return await self._wrap_result(list_presets_handler())
76
+
77
+ elif name == "get_preset":
78
+ return await self._wrap_result(get_preset_handler(**arguments))
79
+
80
+ elif name == "split_diagram":
81
+ return await self._wrap_result(split_diagram_handler(**arguments))
82
+
83
+ elif name == "get_paper_modes":
84
+ return await self._wrap_result(get_paper_modes_handler())
85
+
86
+ else:
87
+ raise ValueError(f"Unknown tool: {name}")
88
+
89
+ async def _wrap_result(self, coro):
90
+ """Wrap handler result as MCP TextContent."""
91
+ import json
92
+
93
+ try:
94
+ result = await coro
95
+ return [
96
+ types.TextContent(
97
+ type="text",
98
+ text=json.dumps(result, indent=2, default=str),
99
+ )
100
+ ]
101
+ except Exception as e:
102
+ return [
103
+ types.TextContent(
104
+ type="text",
105
+ text=json.dumps({"success": False, "error": str(e)}, indent=2),
106
+ )
107
+ ]
108
+
109
+
110
+ async def _run_server():
111
+ """Run the MCP server (internal)."""
112
+ server = DiagramServer()
113
+ async with stdio_server() as (read_stream, write_stream):
114
+ await server.server.run(
115
+ read_stream,
116
+ write_stream,
117
+ InitializationOptions(
118
+ server_name="scitex-diagram",
119
+ server_version="0.1.0",
120
+ capabilities=server.server.get_capabilities(
121
+ notification_options=NotificationOptions(),
122
+ experimental_capabilities={},
123
+ ),
124
+ ),
125
+ )
126
+
127
+
128
+ def main():
129
+ """Main entry point for the MCP server."""
130
+ if not MCP_AVAILABLE:
131
+ import sys
132
+
133
+ print("=" * 60)
134
+ print("MCP Server 'scitex-diagram' requires the 'mcp' package.")
135
+ print()
136
+ print("Install with:")
137
+ print(" pip install mcp")
138
+ print()
139
+ print("Or install scitex with MCP support:")
140
+ print(" pip install scitex[mcp]")
141
+ print("=" * 60)
142
+ sys.exit(1)
143
+
144
+ asyncio.run(_run_server())
145
+
146
+
147
+ if __name__ == "__main__":
148
+ main()
149
+
150
+
151
+ # EOF