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
scitex/cli/introspect.py CHANGED
@@ -11,6 +11,13 @@ import sys
11
11
  import click
12
12
 
13
13
 
14
+ def _normalize_path(ctx, param, value):
15
+ """Normalize dotted path: convert hyphens to underscores for Python module names."""
16
+ if value:
17
+ return value.replace("-", "_")
18
+ return value
19
+
20
+
14
21
  @click.group(
15
22
  context_settings={"help_option_names": ["-h", "--help"]},
16
23
  invoke_without_command=True,
@@ -48,7 +55,7 @@ def introspect(ctx, help_recursive):
48
55
 
49
56
 
50
57
  @introspect.command()
51
- @click.argument("dotted_path")
58
+ @click.argument("dotted_path", callback=_normalize_path)
52
59
  @click.option("--no-defaults", is_flag=True, help="Exclude default values")
53
60
  @click.option("--no-annotations", is_flag=True, help="Exclude type annotations")
54
61
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
@@ -90,7 +97,7 @@ def q(dotted_path, no_defaults, no_annotations, as_json):
90
97
 
91
98
 
92
99
  @introspect.command()
93
- @click.argument("dotted_path")
100
+ @click.argument("dotted_path", callback=_normalize_path)
94
101
  @click.option("--max-lines", "-n", type=int, help="Limit output to N lines")
95
102
  @click.option("--no-decorators", is_flag=True, help="Exclude decorator lines")
96
103
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
@@ -125,7 +132,7 @@ def qq(dotted_path, max_lines, no_decorators, as_json):
125
132
 
126
133
 
127
134
  @introspect.command("dir")
128
- @click.argument("dotted_path")
135
+ @click.argument("dotted_path", callback=_normalize_path)
129
136
  @click.option(
130
137
  "--filter",
131
138
  "-f",
@@ -176,7 +183,7 @@ def dir_cmd(dotted_path, filter, kind, inherited, as_json):
176
183
 
177
184
 
178
185
  @introspect.command()
179
- @click.argument("dotted_path")
186
+ @click.argument("dotted_path", callback=_normalize_path)
180
187
  @click.option("--max-depth", "-d", type=int, default=5, help="Max recursion depth")
181
188
  @click.option("--docstring", is_flag=True, help="Include docstrings")
182
189
  @click.option("--root-only", is_flag=True, help="Show only root-level items")
@@ -214,7 +221,7 @@ def api(dotted_path, max_depth, docstring, root_only, as_json):
214
221
 
215
222
 
216
223
  @introspect.command()
217
- @click.argument("dotted_path")
224
+ @click.argument("dotted_path", callback=_normalize_path)
218
225
  @click.option(
219
226
  "--format",
220
227
  "-f",
@@ -259,7 +266,7 @@ def docstring(dotted_path, format, as_json):
259
266
 
260
267
 
261
268
  @introspect.command()
262
- @click.argument("dotted_path")
269
+ @click.argument("dotted_path", callback=_normalize_path)
263
270
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
264
271
  def exports(dotted_path, as_json):
265
272
  """
@@ -289,7 +296,7 @@ def exports(dotted_path, as_json):
289
296
 
290
297
 
291
298
  @introspect.command()
292
- @click.argument("dotted_path")
299
+ @click.argument("dotted_path", callback=_normalize_path)
293
300
  @click.option("--search-paths", "-p", help="Comma-separated search paths")
294
301
  @click.option("--max-results", "-n", type=int, default=10, help="Max examples")
295
302
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
@@ -332,7 +339,7 @@ def examples(dotted_path, search_paths, max_results, as_json):
332
339
 
333
340
 
334
341
  @introspect.command("hierarchy")
335
- @click.argument("dotted_path")
342
+ @click.argument("dotted_path", callback=_normalize_path)
336
343
  @click.option("--builtins", is_flag=True, help="Include builtin classes")
337
344
  @click.option("--max-depth", "-d", type=int, default=10, help="Max subclass depth")
338
345
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
@@ -368,7 +375,7 @@ def _print_subclasses(subclasses, indent=0):
368
375
 
369
376
 
370
377
  @introspect.command("hints")
371
- @click.argument("dotted_path")
378
+ @click.argument("dotted_path", callback=_normalize_path)
372
379
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
373
380
  def type_hints(dotted_path, as_json):
374
381
  """Get detailed type hint analysis"""
@@ -392,7 +399,7 @@ def type_hints(dotted_path, as_json):
392
399
 
393
400
 
394
401
  @introspect.command("imports")
395
- @click.argument("dotted_path")
402
+ @click.argument("dotted_path", callback=_normalize_path)
396
403
  @click.option("--no-categorize", is_flag=True, help="Don't group by category")
397
404
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
398
405
  def imports(dotted_path, no_categorize, as_json):
@@ -421,7 +428,7 @@ def imports(dotted_path, no_categorize, as_json):
421
428
 
422
429
 
423
430
  @introspect.command("deps")
424
- @click.argument("dotted_path")
431
+ @click.argument("dotted_path", callback=_normalize_path)
425
432
  @click.option("--recursive", "-r", is_flag=True, help="Recursive analysis")
426
433
  @click.option("--max-depth", "-d", type=int, default=3, help="Max recursion depth")
427
434
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
@@ -444,7 +451,7 @@ def dependencies(dotted_path, recursive, max_depth, as_json):
444
451
 
445
452
 
446
453
  @introspect.command("calls")
447
- @click.argument("dotted_path")
454
+ @click.argument("dotted_path", callback=_normalize_path)
448
455
  @click.option("--timeout", "-t", type=int, default=10, help="Timeout in seconds")
449
456
  @click.option("--all", "all_calls", is_flag=True, help="Include external calls")
450
457
  @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
scitex/cli/plt.py CHANGED
@@ -322,33 +322,90 @@ def convert(input_file, output, fmt):
322
322
  sys.exit(_run_figrecipe(*args))
323
323
 
324
324
 
325
- @plt.command()
326
- @click.option(
327
- "-t",
328
- "--transport",
329
- type=click.Choice(["stdio", "sse", "http"]),
330
- default="stdio",
331
- help="Transport protocol",
332
- )
333
- @click.option("--host", default="0.0.0.0", help="Host for HTTP/SSE transport")
334
- @click.option("--port", default=8087, type=int, help="Port for HTTP/SSE transport")
335
- def serve(transport, host, port):
325
+ @plt.group(invoke_without_command=True)
326
+ @click.pass_context
327
+ def mcp(ctx):
336
328
  """
337
- Run figrecipe MCP server
329
+ MCP (Model Context Protocol) server operations
330
+
331
+ \b
332
+ Commands:
333
+ start - Start the MCP server
334
+ doctor - Check MCP server health
335
+ list-tools - List available MCP tools
336
+ info - Show MCP server information
337
+ install - Show installation instructions
338
338
 
339
339
  \b
340
340
  Examples:
341
- scitex plt serve # stdio for Claude Desktop
342
- scitex plt serve -t http --port 8087
341
+ scitex plt mcp start
342
+ scitex plt mcp doctor
343
343
  """
344
- args = ["mcp", "run", "--transport", transport]
345
- if transport != "stdio":
346
- args.extend(["--host", host, "--port", str(port)])
347
- click.secho(f"Starting figrecipe MCP server ({transport})", fg="cyan")
348
- click.echo(f" Host: {host}")
349
- click.echo(f" Port: {port}")
344
+ if ctx.invoked_subcommand is None:
345
+ click.echo(ctx.get_help())
350
346
 
351
- sys.exit(_run_figrecipe(*args))
347
+
348
+ @mcp.command()
349
+ @click.option("--host", default="0.0.0.0", help="Host for HTTP transport")
350
+ @click.option("--port", default=8087, type=int, help="Port for HTTP transport")
351
+ def start(host, port):
352
+ """
353
+ Start the MCP server
354
+
355
+ \b
356
+ Example:
357
+ scitex plt mcp start
358
+ scitex plt mcp start --port 8087
359
+ """
360
+ sys.exit(_run_figrecipe("mcp", "run", "--host", host, "--port", str(port)))
361
+
362
+
363
+ @mcp.command()
364
+ def doctor():
365
+ """
366
+ Check MCP server health
367
+
368
+ \b
369
+ Example:
370
+ scitex plt mcp doctor
371
+ """
372
+ sys.exit(_run_figrecipe("mcp", "doctor"))
373
+
374
+
375
+ @mcp.command("list-tools")
376
+ def list_tools_mcp():
377
+ """
378
+ List available MCP tools
379
+
380
+ \b
381
+ Example:
382
+ scitex plt mcp list-tools
383
+ """
384
+ sys.exit(_run_figrecipe("mcp", "list-tools"))
385
+
386
+
387
+ @mcp.command()
388
+ def info():
389
+ """
390
+ Show MCP server information
391
+
392
+ \b
393
+ Example:
394
+ scitex plt mcp info
395
+ """
396
+ sys.exit(_run_figrecipe("mcp", "info"))
397
+
398
+
399
+ @mcp.command()
400
+ def install():
401
+ """
402
+ Show installation instructions
403
+
404
+ \b
405
+ Example:
406
+ scitex plt mcp install
407
+ """
408
+ sys.exit(_run_figrecipe("mcp", "install"))
352
409
 
353
410
 
354
411
  if __name__ == "__main__":
@@ -21,6 +21,11 @@ CrossRef database (167M+ papers via crossref-local):
21
21
  scitex scholar crossref-scitex get 10.1038/nature12373
22
22
  scitex scholar crossref-scitex count "epilepsy seizure"
23
23
  scitex scholar crossref-scitex info
24
+
25
+ OpenAlex database (284M+ works via openalex-local):
26
+ scitex scholar openalex-scitex search "neural networks"
27
+ scitex scholar openalex-scitex search-by-doi 10.1038/nature12373
28
+ scitex scholar openalex-scitex status
24
29
  """
25
30
 
26
31
  from __future__ import annotations
@@ -31,6 +36,7 @@ from ._crossref_scitex import crossref_scitex
31
36
  from ._fetch import fetch
32
37
  from ._jobs import jobs
33
38
  from ._library import config, library
39
+ from ._openalex_scitex import openalex_scitex
34
40
 
35
41
 
36
42
  @click.group(
@@ -40,8 +46,7 @@ from ._library import config, library
40
46
  @click.option("--help-recursive", is_flag=True, help="Show help for all subcommands")
41
47
  @click.pass_context
42
48
  def scholar(ctx, help_recursive):
43
- """
44
- Scientific paper management
49
+ r"""Scientific paper management.
45
50
 
46
51
  \b
47
52
  Fetch papers, manage your library, and track background jobs.
@@ -93,7 +98,160 @@ def _print_help_recursive(ctx):
93
98
  click.echo(sub_cmd.get_help(sub_sub_ctx))
94
99
 
95
100
 
101
+ @scholar.group(invoke_without_command=True)
102
+ @click.pass_context
103
+ def mcp(ctx):
104
+ r"""MCP (Model Context Protocol) server operations.
105
+
106
+ \b
107
+ Commands:
108
+ start - Start the MCP server
109
+ doctor - Check MCP server health
110
+ list-tools - List available MCP tools
111
+
112
+ \b
113
+ Examples:
114
+ scitex scholar mcp start
115
+ scitex scholar mcp list-tools
116
+ """
117
+ if ctx.invoked_subcommand is None:
118
+ click.echo(ctx.get_help())
119
+
120
+
121
+ @mcp.command()
122
+ @click.option(
123
+ "-t",
124
+ "--transport",
125
+ type=click.Choice(["stdio", "sse", "http"]),
126
+ default="stdio",
127
+ help="Transport protocol (default: stdio)",
128
+ )
129
+ @click.option("--host", default="0.0.0.0", help="Host for HTTP/SSE (default: 0.0.0.0)")
130
+ @click.option(
131
+ "--port", default=8085, type=int, help="Port for HTTP/SSE (default: 8085)"
132
+ )
133
+ def start(transport, host, port):
134
+ r"""Start the MCP server with scholar tools.
135
+
136
+ \b
137
+ NOTE: This now uses the unified scitex MCP server which includes
138
+ all scholar tools plus other scitex tools (plt, stats, etc.)
139
+
140
+ \b
141
+ Examples:
142
+ scitex scholar mcp start
143
+ scitex scholar mcp start -t http --port 8085
144
+
145
+ \b
146
+ Equivalent to: scitex serve -t <transport>
147
+ """
148
+ import sys
149
+
150
+ try:
151
+ from scitex.mcp_server import run_server
152
+
153
+ if transport != "stdio":
154
+ click.secho(f"Starting unified scitex MCP server ({transport})", fg="cyan")
155
+ click.echo(f" Host: {host}")
156
+ click.echo(f" Port: {port}")
157
+ click.echo(" Includes: scholar, plt, stats, audio, and more")
158
+
159
+ run_server(transport=transport, host=host, port=port)
160
+
161
+ except ImportError as e:
162
+ click.secho(f"Error: {e}", fg="red", err=True)
163
+ click.echo("\nInstall dependencies: pip install fastmcp")
164
+ sys.exit(1)
165
+ except Exception as e:
166
+ click.secho(f"Error: {e}", fg="red", err=True)
167
+ sys.exit(1)
168
+
169
+
170
+ @mcp.command()
171
+ def doctor():
172
+ r"""Check MCP server health and dependencies.
173
+
174
+ \b
175
+ Example:
176
+ scitex scholar mcp doctor
177
+ """
178
+ click.secho("Scholar MCP Server Health Check", fg="cyan", bold=True)
179
+ click.echo()
180
+
181
+ click.echo("Checking FastMCP... ", nl=False)
182
+ try:
183
+ import fastmcp # noqa: F401
184
+
185
+ click.secho("OK", fg="green")
186
+ except ImportError:
187
+ click.secho("NOT INSTALLED", fg="red")
188
+ click.echo(" Install with: pip install fastmcp")
189
+
190
+ click.echo("Checking scholar module... ", nl=False)
191
+ try:
192
+ from scitex import scholar as _ # noqa: F401
193
+
194
+ click.secho("OK", fg="green")
195
+ except ImportError as e:
196
+ click.secho(f"FAIL ({e})", fg="red")
197
+
198
+ click.echo("Checking crossref-local... ", nl=False)
199
+ try:
200
+ import crossref_local # noqa: F401
201
+
202
+ click.secho("OK", fg="green")
203
+ except ImportError:
204
+ click.secho("NOT INSTALLED (optional)", fg="yellow")
205
+
206
+ click.echo("Checking openalex-local... ", nl=False)
207
+ try:
208
+ import openalex_local # noqa: F401
209
+
210
+ click.secho("OK", fg="green")
211
+ except ImportError:
212
+ click.secho("NOT INSTALLED (optional)", fg="yellow")
213
+
214
+
215
+ @mcp.command("list-tools")
216
+ def list_tools():
217
+ r"""List available MCP tools.
218
+
219
+ \b
220
+ Example:
221
+ scitex scholar mcp list-tools
222
+ """
223
+ click.secho("Scholar MCP Tools", fg="cyan", bold=True)
224
+ click.echo()
225
+ tools = [
226
+ ("search_papers", "Search for papers by query"),
227
+ ("resolve_dois", "Resolve DOIs to metadata"),
228
+ ("enrich_bibtex", "Enrich BibTeX with abstracts/DOIs"),
229
+ ("download_pdf", "Download PDF for a paper"),
230
+ ("download_pdfs_batch", "Batch download PDFs"),
231
+ ("get_library_status", "Get library status"),
232
+ ("parse_bibtex", "Parse BibTeX file"),
233
+ ("validate_pdfs", "Validate downloaded PDFs"),
234
+ ("authenticate", "Authenticate with institution"),
235
+ ("check_auth_status", "Check authentication status"),
236
+ ("fetch_papers", "Fetch papers by DOIs (async)"),
237
+ # CrossRef-Local (167M+ papers)
238
+ ("crossref_search", "Search CrossRef database (167M+ papers)"),
239
+ ("crossref_get", "Get paper by DOI from CrossRef"),
240
+ ("crossref_count", "Count papers matching query"),
241
+ ("crossref_citations", "Get citation relationships"),
242
+ ("crossref_info", "Get CrossRef database status"),
243
+ # OpenAlex-Local (284M+ works)
244
+ ("openalex_search", "Search OpenAlex database (284M+ works)"),
245
+ ("openalex_get", "Get paper by DOI/ID from OpenAlex"),
246
+ ("openalex_count", "Count papers matching query"),
247
+ ("openalex_info", "Get OpenAlex database status"),
248
+ ]
249
+ for name, desc in tools:
250
+ click.echo(f" {name}: {desc}")
251
+
252
+
96
253
  scholar.add_command(crossref_scitex)
254
+ scholar.add_command(openalex_scitex)
97
255
  scholar.add_command(fetch)
98
256
  scholar.add_command(library)
99
257
  scholar.add_command(config)