scitex 2.15.2__py3-none-any.whl → 2.15.4__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 (77) hide show
  1. scitex/_mcp_resources/__init__.py +2 -0
  2. scitex/_mcp_resources/_scholar.py +148 -0
  3. scitex/_mcp_tools/scholar.py +50 -99
  4. scitex/_mcp_tools/social.py +15 -232
  5. scitex/_mcp_tools/writer.py +7 -17
  6. scitex/canvas/mcp_server.py +16 -3
  7. scitex/capture/mcp_server.py +16 -2
  8. scitex/cli/audio.py +90 -20
  9. scitex/cli/capture.py +120 -0
  10. scitex/cli/introspect.py +19 -12
  11. scitex/cli/plt.py +78 -21
  12. scitex/cli/scholar/__init__.py +160 -2
  13. scitex/cli/scholar/_crossref_scitex.py +25 -266
  14. scitex/cli/scholar/_openalex_scitex.py +55 -0
  15. scitex/cli/social.py +63 -22
  16. scitex/cli/stats.py +121 -3
  17. scitex/cli/writer.py +49 -423
  18. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +90 -0
  19. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +1571 -0
  20. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +6262 -0
  21. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +1274 -0
  22. scitex/dev/plt/data/mpl/dir_ax.txt +459 -0
  23. scitex/introspect/_list_api.py +5 -2
  24. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
  25. scitex/scholar/__init__.py +14 -9
  26. scitex/scholar/_mcp/crossref_tool_schemas.py +133 -0
  27. scitex/scholar/_mcp/openalex_handlers.py +212 -0
  28. scitex/scholar/_mcp/openalex_tool_schemas.py +96 -0
  29. scitex/scholar/_mcp/tool_schemas.py +16 -1
  30. scitex/scholar/data/.gitkeep +0 -0
  31. scitex/scholar/data/README.md +44 -0
  32. scitex/scholar/data/bib_files/bibliography.bib +1952 -0
  33. scitex/scholar/data/bib_files/neurovista.bib +277 -0
  34. scitex/scholar/data/bib_files/neurovista_enriched.bib +441 -0
  35. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +441 -0
  36. scitex/scholar/data/bib_files/neurovista_processed.bib +338 -0
  37. scitex/scholar/data/bib_files/openaccess.bib +89 -0
  38. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +2178 -0
  39. scitex/scholar/data/bib_files/pac.bib +698 -0
  40. scitex/scholar/data/bib_files/pac_enriched.bib +1061 -0
  41. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  42. scitex/scholar/data/bib_files/pac_titles.txt +75 -0
  43. scitex/scholar/data/bib_files/paywalled.bib +98 -0
  44. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +58 -0
  45. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +87 -0
  46. scitex/scholar/data/bib_files/seizure_prediction.bib +694 -0
  47. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  48. scitex/scholar/data/bib_files/test_complete_enriched.bib +437 -0
  49. scitex/scholar/data/bib_files/test_final_enriched.bib +437 -0
  50. scitex/scholar/data/bib_files/test_seizure.bib +46 -0
  51. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  52. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  53. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  54. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  55. scitex/scholar/data/impact_factor.db +0 -0
  56. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
  57. scitex/scholar/local_dbs/__init__.py +31 -0
  58. scitex/scholar/local_dbs/crossref_scitex.py +30 -0
  59. scitex/scholar/local_dbs/openalex_scitex.py +30 -0
  60. scitex/scholar/mcp_server.py +59 -4
  61. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
  62. scitex/stats/mcp_server.py +16 -3
  63. scitex/template/mcp_server.py +16 -3
  64. scitex/ui/mcp_server.py +16 -3
  65. scitex/writer/__init__.py +43 -34
  66. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/METADATA +22 -3
  67. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/RECORD +70 -38
  68. scitex/scholar/crossref_scitex.py +0 -367
  69. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +0 -462
  70. scitex/scholar/url_finder/.tmp/open_url/README.md +0 -223
  71. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +0 -694
  72. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +0 -1160
  73. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +0 -344
  74. scitex/scholar/url_finder/.tmp/open_url/__init__.py +0 -24
  75. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/WHEEL +0 -0
  76. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/entry_points.txt +0 -0
  77. {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,142 +1,41 @@
1
1
  #!/usr/bin/env python3
2
- # Timestamp: 2026-01-24
2
+ # Timestamp: 2026-01-29
3
3
  # File: src/scitex/cli/scholar/_crossref_scitex.py
4
- """CrossRef-SciTeX CLI commands for scitex scholar.
4
+ """CrossRef-SciTeX CLI - Thin wrapper delegating to crossref-local.
5
5
 
6
- Provides access to the local CrossRef database (167M+ papers) via crossref-local.
7
- Branded as crossref-scitex to distinguish from official CrossRef API.
6
+ This module provides access to the local CrossRef database (167M+ papers)
7
+ by delegating directly to crossref-local CLI without any modifications.
8
8
  """
9
9
 
10
10
  from __future__ import annotations
11
11
 
12
- import json
13
12
  import sys
14
13
 
15
14
  import click
16
15
 
17
16
 
18
- @click.group("crossref-scitex")
19
- def crossref_scitex():
20
- """
21
- CrossRef-SciTeX database search (167M+ papers)
22
-
23
- \b
24
- Search and query the local CrossRef database via crossref-local.
25
- Supports both direct DB access and HTTP API mode.
26
-
27
- \b
28
- Examples:
29
- scitex scholar crossref-scitex search "deep learning"
30
- scitex scholar crossref-scitex get 10.1038/nature12373
31
- scitex scholar crossref-scitex count "epilepsy seizure"
32
- scitex scholar crossref-scitex info
33
- """
34
- pass
35
-
36
-
37
- @crossref_scitex.command("search")
38
- @click.argument("query")
39
- @click.option("-n", "--limit", default=20, help="Maximum results (default: 20)")
40
- @click.option("--offset", default=0, help="Skip N results for pagination")
41
- @click.option("--year-min", type=int, help="Minimum publication year")
42
- @click.option("--year-max", type=int, help="Maximum publication year")
43
- @click.option("--enrich", is_flag=True, help="Add citation counts and references")
44
- @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
45
- def search_cmd(query, limit, offset, year_min, year_max, enrich, as_json):
46
- """
47
- Search for papers in CrossRef database.
17
+ @click.command(
18
+ "crossref-scitex",
19
+ context_settings={"ignore_unknown_options": True, "allow_extra_args": True},
20
+ )
21
+ @click.option("--help-recursive", is_flag=True, help="Show help for all commands")
22
+ @click.pass_context
23
+ def crossref_scitex(ctx, help_recursive):
24
+ r"""CrossRef-SciTeX database search (167M+ papers).
48
25
 
49
26
  \b
50
- Examples:
51
- scitex scholar crossref-scitex search "hippocampal sharp wave ripples"
52
- scitex scholar crossref search "deep learning" --limit 50
53
- scitex scholar crossref search "CRISPR" --year-min 2020 --enrich
54
- """
55
- try:
56
- from scitex.scholar import crossref_scitex as crossref
57
- except ImportError:
58
- click.secho(
59
- "crossref-local not installed. Install with: pip install crossref-local",
60
- fg="red",
61
- )
62
- sys.exit(1)
63
-
64
- try:
65
- results = crossref.search(query, limit=limit, offset=offset)
66
-
67
- if enrich:
68
- results = crossref.enrich(results)
69
-
70
- papers = []
71
- for work in results:
72
- if year_min and work.year and work.year < year_min:
73
- continue
74
- if year_max and work.year and work.year > year_max:
75
- continue
76
- papers.append(work)
77
- if len(papers) >= limit:
78
- break
79
-
80
- if as_json:
81
- output = {
82
- "query": query,
83
- "total": results.total,
84
- "count": len(papers),
85
- "papers": [
86
- {
87
- "doi": p.doi,
88
- "title": p.title,
89
- "authors": p.authors,
90
- "year": p.year,
91
- "journal": p.journal,
92
- "citation_count": p.citation_count,
93
- }
94
- for p in papers
95
- ],
96
- }
97
- click.echo(json.dumps(output, indent=2))
98
- else:
99
- click.secho(
100
- f"Found {results.total} papers for: {query}", fg="green", bold=True
101
- )
102
- click.echo()
103
-
104
- for i, paper in enumerate(papers, 1):
105
- authors = ", ".join(paper.authors[:3]) if paper.authors else "Unknown"
106
- if paper.authors and len(paper.authors) > 3:
107
- authors += " et al."
108
-
109
- click.secho(f"{i}. {paper.title}", fg="cyan", bold=True)
110
- click.echo(f" Authors: {authors}")
111
- click.echo(
112
- f" Year: {paper.year or 'N/A'} | Journal: {paper.journal or 'N/A'}"
113
- )
114
- click.echo(f" DOI: {paper.doi}")
115
- if paper.citation_count:
116
- click.echo(f" Citations: {paper.citation_count}")
117
- click.echo()
118
-
119
- except Exception as e:
120
- click.secho(f"Error: {e}", fg="red")
121
- sys.exit(1)
122
-
123
-
124
- @crossref_scitex.command("get")
125
- @click.argument("doi")
126
- @click.option("--citations", is_flag=True, help="Include citing papers")
127
- @click.option("--references", is_flag=True, help="Include referenced papers")
128
- @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
129
- def get_cmd(doi, citations, references, as_json):
130
- """
131
- Get paper details by DOI.
27
+ Thin wrapper for crossref-local. All arguments passed directly.
28
+ Run 'crossref-local --help' for full options.
132
29
 
133
30
  \b
134
31
  Examples:
135
- scitex scholar crossref get 10.1038/nature12373
136
- scitex scholar crossref get 10.1126/science.aax0758 --citations
32
+ scitex scholar crossref-scitex search "deep learning" --abstracts
33
+ scitex scholar crossref-scitex search-by-doi 10.1038/nature12373
34
+ scitex scholar crossref-scitex status
35
+ scitex scholar crossref-scitex cache query "neural networks"
137
36
  """
138
37
  try:
139
- from scitex.scholar import crossref_scitex as crossref
38
+ from crossref_local.cli import cli as crossref_cli
140
39
  except ImportError:
141
40
  click.secho(
142
41
  "crossref-local not installed. Install with: pip install crossref-local",
@@ -144,153 +43,13 @@ def get_cmd(doi, citations, references, as_json):
144
43
  )
145
44
  sys.exit(1)
146
45
 
147
- try:
148
- work = crossref.get(doi)
149
-
150
- if work is None:
151
- click.secho(f"DOI not found: {doi}", fg="red")
152
- sys.exit(1)
153
-
154
- if as_json:
155
- output = {
156
- "doi": work.doi,
157
- "title": work.title,
158
- "authors": work.authors,
159
- "year": work.year,
160
- "journal": work.journal,
161
- "abstract": work.abstract,
162
- "citation_count": work.citation_count,
163
- "reference_count": work.reference_count,
164
- "type": work.type,
165
- "publisher": work.publisher,
166
- }
167
- if citations:
168
- output["citing_dois"] = crossref.get_citing(doi)
169
- if references:
170
- output["referenced_dois"] = crossref.get_cited(doi)
171
- click.echo(json.dumps(output, indent=2))
172
- else:
173
- click.secho(work.title, fg="cyan", bold=True)
174
- click.echo()
175
-
176
- if work.authors:
177
- click.echo(f"Authors: {', '.join(work.authors)}")
178
- click.echo(f"Year: {work.year or 'N/A'}")
179
- click.echo(f"Journal: {work.journal or 'N/A'}")
180
- click.echo(f"DOI: {work.doi}")
181
- click.echo(f"Type: {work.type or 'N/A'}")
182
- click.echo(f"Publisher: {work.publisher or 'N/A'}")
183
- click.echo(f"Citations: {work.citation_count or 0}")
184
- click.echo(f"References: {work.reference_count or 0}")
185
-
186
- if work.abstract:
187
- click.echo()
188
- click.secho("Abstract:", bold=True)
189
- click.echo(
190
- work.abstract[:500] + "..."
191
- if len(work.abstract) > 500
192
- else work.abstract
193
- )
194
-
195
- if citations:
196
- citing = crossref.get_citing(doi)
197
- click.echo()
198
- click.secho(f"Citing papers ({len(citing)}):", bold=True)
199
- for c_doi in citing[:10]:
200
- click.echo(f" - {c_doi}")
201
- if len(citing) > 10:
202
- click.echo(f" ... and {len(citing) - 10} more")
203
-
204
- if references:
205
- cited = crossref.get_cited(doi)
206
- click.echo()
207
- click.secho(f"References ({len(cited)}):", bold=True)
208
- for r_doi in cited[:10]:
209
- click.echo(f" - {r_doi}")
210
- if len(cited) > 10:
211
- click.echo(f" ... and {len(cited) - 10} more")
212
-
213
- except Exception as e:
214
- click.secho(f"Error: {e}", fg="red")
215
- sys.exit(1)
46
+ # Handle --help-recursive by delegating to crossref-local
47
+ args = ctx.args
48
+ if help_recursive:
49
+ args = ["--help-recursive"]
216
50
 
217
-
218
- @crossref_scitex.command("count")
219
- @click.argument("query")
220
- def count_cmd(query):
221
- """
222
- Count papers matching a query.
223
-
224
- \b
225
- Examples:
226
- scitex scholar crossref count "machine learning"
227
- scitex scholar crossref count "CRISPR gene editing"
228
- """
229
- try:
230
- from scitex.scholar import crossref_scitex as crossref
231
- except ImportError:
232
- click.secho(
233
- "crossref-local not installed. Install with: pip install crossref-local",
234
- fg="red",
235
- )
236
- sys.exit(1)
237
-
238
- try:
239
- count = crossref.count(query)
240
- click.echo(f"{count:,} papers match: {query}")
241
- except Exception as e:
242
- click.secho(f"Error: {e}", fg="red")
243
- sys.exit(1)
244
-
245
-
246
- @crossref_scitex.command("info")
247
- @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
248
- def info_cmd(as_json):
249
- """
250
- Show CrossRef database configuration and status.
251
-
252
- \b
253
- Examples:
254
- scitex scholar crossref info
255
- scitex scholar crossref info --json
256
- """
257
- try:
258
- from scitex.scholar import crossref_scitex as crossref
259
- except ImportError:
260
- click.secho(
261
- "crossref-local not installed. Install with: pip install crossref-local",
262
- fg="red",
263
- )
264
- sys.exit(1)
265
-
266
- try:
267
- info = crossref.info()
268
- mode = crossref.get_mode()
269
-
270
- if as_json:
271
- info["mode"] = mode
272
- click.echo(json.dumps(info, indent=2))
273
- else:
274
- click.secho("CrossRef Database Status", fg="cyan", bold=True)
275
- click.echo()
276
- click.echo(f"Mode: {mode}")
277
- click.echo(f"Status: {info.get('status', 'unknown')}")
278
-
279
- if "version" in info:
280
- click.echo(f"Version: {info['version']}")
281
-
282
- if "work_count" in info:
283
- click.echo(f"Papers: {info['work_count']:,}")
284
-
285
- if "db_path" in info:
286
- click.echo(f"Database: {info['db_path']}")
287
-
288
- if "api_url" in info:
289
- click.echo(f"API URL: {info['api_url']}")
290
-
291
- except Exception as e:
292
- click.secho(f"Error: {e}", fg="red")
293
- sys.exit(1)
51
+ # Delegate all arguments to crossref-local CLI
52
+ sys.exit(crossref_cli.main(args, standalone_mode=False))
294
53
 
295
54
 
296
55
  # EOF
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2026-01-29
3
+ # File: src/scitex/cli/scholar/_openalex_scitex.py
4
+ """OpenAlex-SciTeX CLI - Thin wrapper delegating to openalex-local.
5
+
6
+ This module provides access to the local OpenAlex database (284M+ works)
7
+ by delegating directly to openalex-local CLI without any modifications.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import sys
13
+
14
+ import click
15
+
16
+
17
+ @click.command(
18
+ "openalex-scitex",
19
+ context_settings={"ignore_unknown_options": True, "allow_extra_args": True},
20
+ )
21
+ @click.option("--help-recursive", is_flag=True, help="Show help for all commands")
22
+ @click.pass_context
23
+ def openalex_scitex(ctx, help_recursive):
24
+ """
25
+ OpenAlex-SciTeX database search (284M+ works)
26
+
27
+ \b
28
+ Thin wrapper for openalex-local. All arguments passed directly.
29
+ Run 'openalex-local --help' for full options.
30
+
31
+ \b
32
+ Examples:
33
+ scitex scholar openalex-scitex search "machine learning"
34
+ scitex scholar openalex-scitex search-by-doi 10.1038/nature12373
35
+ scitex scholar openalex-scitex status
36
+ """
37
+ try:
38
+ from openalex_local.cli import cli as openalex_cli
39
+ except ImportError:
40
+ click.secho(
41
+ "openalex-local not installed. Install with: pip install openalex-local",
42
+ fg="red",
43
+ )
44
+ sys.exit(1)
45
+
46
+ # Handle --help-recursive by delegating to openalex-local
47
+ args = ctx.args
48
+ if help_recursive:
49
+ args = ["--help-recursive"]
50
+
51
+ # Delegate all arguments to openalex-local CLI
52
+ sys.exit(openalex_cli.main(args, standalone_mode=False))
53
+
54
+
55
+ # EOF
scitex/cli/social.py CHANGED
@@ -279,33 +279,74 @@ def thread(platform, file, delay, dry_run, as_json):
279
279
  sys.exit(_run_socialia(*args, json_output=as_json))
280
280
 
281
281
 
282
- @social.command()
283
- @click.option(
284
- "-t",
285
- "--transport",
286
- type=click.Choice(["stdio", "sse", "http"]),
287
- default="stdio",
288
- help="Transport protocol",
289
- )
290
- @click.option("--host", default="0.0.0.0", help="Host for HTTP/SSE transport")
291
- @click.option("--port", default=8086, type=int, help="Port for HTTP/SSE transport")
292
- def serve(transport, host, port):
282
+ @social.group(invoke_without_command=True)
283
+ @click.pass_context
284
+ def mcp(ctx):
293
285
  """
294
- Run socialia MCP server
286
+ MCP (Model Context Protocol) server operations
287
+
288
+ \b
289
+ Commands:
290
+ start - Start the MCP server
291
+ doctor - Check MCP server health
292
+ list-tools - List available MCP tools
293
+ installation - Show Claude Desktop configuration
295
294
 
296
295
  \b
297
296
  Examples:
298
- scitex social serve # stdio for Claude Desktop
299
- scitex social serve -t http --port 8086
297
+ scitex social mcp start
298
+ scitex social mcp doctor
299
+ """
300
+ if ctx.invoked_subcommand is None:
301
+ click.echo(ctx.get_help())
302
+
303
+
304
+ @mcp.command()
305
+ def start():
306
+ """
307
+ Start the MCP server
308
+
309
+ \b
310
+ Example:
311
+ scitex social mcp start
312
+ """
313
+ sys.exit(_run_socialia("mcp", "start"))
314
+
315
+
316
+ @mcp.command()
317
+ def doctor():
318
+ """
319
+ Check MCP server health
320
+
321
+ \b
322
+ Example:
323
+ scitex social mcp doctor
324
+ """
325
+ sys.exit(_run_socialia("mcp", "doctor"))
326
+
327
+
328
+ @mcp.command("list-tools")
329
+ def list_tools():
330
+ """
331
+ List available MCP tools
332
+
333
+ \b
334
+ Example:
335
+ scitex social mcp list-tools
336
+ """
337
+ sys.exit(_run_socialia("mcp", "list-tools"))
338
+
339
+
340
+ @mcp.command()
341
+ def installation():
342
+ """
343
+ Show Claude Desktop configuration
344
+
345
+ \b
346
+ Example:
347
+ scitex social mcp installation
300
348
  """
301
- args = ["mcp", "run", "--transport", transport]
302
- if transport != "stdio":
303
- args.extend(["--host", host, "--port", str(port)])
304
- click.secho(f"Starting socialia MCP server ({transport})", fg="cyan")
305
- click.echo(f" Host: {host}")
306
- click.echo(f" Port: {port}")
307
-
308
- sys.exit(_run_socialia(*args))
349
+ sys.exit(_run_socialia("mcp", "installation"))
309
350
 
310
351
 
311
352
  if __name__ == "__main__":
scitex/cli/stats.py CHANGED
@@ -139,9 +139,9 @@ def recommend(
139
139
  output = {
140
140
  "context": {
141
141
  "n_groups": n_groups,
142
- "sample_sizes": list(sample_sizes)
143
- if sample_sizes
144
- else [30] * n_groups,
142
+ "sample_sizes": (
143
+ list(sample_sizes) if sample_sizes else [30] * n_groups
144
+ ),
145
145
  "outcome_type": outcome_type,
146
146
  "design": design,
147
147
  "paired": paired,
@@ -346,5 +346,123 @@ def tests():
346
346
  sys.exit(1)
347
347
 
348
348
 
349
+ @stats.group(invoke_without_command=True)
350
+ @click.pass_context
351
+ def mcp(ctx):
352
+ """
353
+ MCP (Model Context Protocol) server operations
354
+
355
+ \b
356
+ Commands:
357
+ start - Start the MCP server
358
+ doctor - Check MCP server health
359
+ list-tools - List available MCP tools
360
+
361
+ \b
362
+ Examples:
363
+ scitex stats mcp start
364
+ scitex stats mcp list-tools
365
+ """
366
+ if ctx.invoked_subcommand is None:
367
+ click.echo(ctx.get_help())
368
+
369
+
370
+ @mcp.command()
371
+ @click.option(
372
+ "-t",
373
+ "--transport",
374
+ type=click.Choice(["stdio", "sse", "http"]),
375
+ default="stdio",
376
+ help="Transport protocol (default: stdio)",
377
+ )
378
+ @click.option("--host", default="0.0.0.0", help="Host for HTTP/SSE (default: 0.0.0.0)")
379
+ @click.option(
380
+ "--port", default=8095, type=int, help="Port for HTTP/SSE (default: 8095)"
381
+ )
382
+ def start(transport, host, port):
383
+ """
384
+ Start the stats MCP server
385
+
386
+ \b
387
+ Examples:
388
+ scitex stats mcp start
389
+ scitex stats mcp start -t http --port 8095
390
+ """
391
+ try:
392
+ from scitex.stats.mcp_server import main as run_server
393
+
394
+ if transport != "stdio":
395
+ click.secho(f"Starting stats MCP server ({transport})", fg="cyan")
396
+ click.echo(f" Host: {host}")
397
+ click.echo(f" Port: {port}")
398
+
399
+ run_server()
400
+
401
+ except ImportError as e:
402
+ click.secho(f"Error: {e}", fg="red", err=True)
403
+ click.echo("\nInstall dependencies: pip install fastmcp")
404
+ sys.exit(1)
405
+ except Exception as e:
406
+ click.secho(f"Error: {e}", fg="red", err=True)
407
+ sys.exit(1)
408
+
409
+
410
+ @mcp.command()
411
+ def doctor():
412
+ """
413
+ Check MCP server health and dependencies
414
+
415
+ \b
416
+ Example:
417
+ scitex stats mcp doctor
418
+ """
419
+ click.secho("Stats MCP Server Health Check", fg="cyan", bold=True)
420
+ click.echo()
421
+
422
+ click.echo("Checking FastMCP... ", nl=False)
423
+ try:
424
+ import fastmcp # noqa: F401
425
+
426
+ click.secho("OK", fg="green")
427
+ except ImportError:
428
+ click.secho("NOT INSTALLED", fg="red")
429
+ click.echo(" Install with: pip install fastmcp")
430
+
431
+ click.echo("Checking stats module... ", nl=False)
432
+ try:
433
+ from scitex import stats as _ # noqa: F401
434
+
435
+ click.secho("OK", fg="green")
436
+ except ImportError as e:
437
+ click.secho(f"FAIL ({e})", fg="red")
438
+
439
+
440
+ @mcp.command("list-tools")
441
+ def list_tools():
442
+ """
443
+ List available MCP tools
444
+
445
+ \b
446
+ Example:
447
+ scitex stats mcp list-tools
448
+ """
449
+ click.secho("Stats MCP Tools", fg="cyan", bold=True)
450
+ click.echo()
451
+ tools = [
452
+ ("stats_recommend_tests", "Recommend appropriate statistical tests"),
453
+ ("stats_run_test", "Execute a statistical test on data"),
454
+ ("stats_format_results", "Format results in journal style"),
455
+ ("stats_power_analysis", "Calculate power or sample size"),
456
+ ("stats_correct_pvalues", "Apply multiple comparison correction"),
457
+ ("stats_describe", "Calculate descriptive statistics"),
458
+ ("stats_effect_size", "Calculate effect size"),
459
+ ("stats_normality_test", "Test for normal distribution"),
460
+ ("stats_posthoc_test", "Run post-hoc pairwise comparisons"),
461
+ ("stats_p_to_stars", "Convert p-value to significance stars"),
462
+ ]
463
+ for name, desc in tools:
464
+ click.echo(f" {name}: {desc}")
465
+
466
+
349
467
  if __name__ == "__main__":
350
468
  stats()