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.
- scitex/_mcp_resources/__init__.py +2 -0
- scitex/_mcp_resources/_scholar.py +148 -0
- scitex/_mcp_tools/scholar.py +50 -99
- scitex/_mcp_tools/social.py +15 -232
- scitex/_mcp_tools/writer.py +7 -17
- scitex/canvas/mcp_server.py +16 -3
- scitex/capture/mcp_server.py +16 -2
- scitex/cli/audio.py +90 -20
- scitex/cli/capture.py +120 -0
- scitex/cli/introspect.py +19 -12
- scitex/cli/plt.py +78 -21
- scitex/cli/scholar/__init__.py +160 -2
- scitex/cli/scholar/_crossref_scitex.py +25 -266
- scitex/cli/scholar/_openalex_scitex.py +55 -0
- scitex/cli/social.py +63 -22
- scitex/cli/stats.py +121 -3
- scitex/cli/writer.py +49 -423
- scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +90 -0
- scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +1571 -0
- scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +6262 -0
- scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +1274 -0
- scitex/dev/plt/data/mpl/dir_ax.txt +459 -0
- scitex/introspect/_list_api.py +5 -2
- scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
- scitex/scholar/__init__.py +14 -9
- scitex/scholar/_mcp/crossref_tool_schemas.py +133 -0
- scitex/scholar/_mcp/openalex_handlers.py +212 -0
- scitex/scholar/_mcp/openalex_tool_schemas.py +96 -0
- scitex/scholar/_mcp/tool_schemas.py +16 -1
- scitex/scholar/data/.gitkeep +0 -0
- scitex/scholar/data/README.md +44 -0
- scitex/scholar/data/bib_files/bibliography.bib +1952 -0
- scitex/scholar/data/bib_files/neurovista.bib +277 -0
- scitex/scholar/data/bib_files/neurovista_enriched.bib +441 -0
- scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +441 -0
- scitex/scholar/data/bib_files/neurovista_processed.bib +338 -0
- scitex/scholar/data/bib_files/openaccess.bib +89 -0
- scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +2178 -0
- scitex/scholar/data/bib_files/pac.bib +698 -0
- scitex/scholar/data/bib_files/pac_enriched.bib +1061 -0
- scitex/scholar/data/bib_files/pac_processed.bib +0 -0
- scitex/scholar/data/bib_files/pac_titles.txt +75 -0
- scitex/scholar/data/bib_files/paywalled.bib +98 -0
- scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +58 -0
- scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +87 -0
- scitex/scholar/data/bib_files/seizure_prediction.bib +694 -0
- scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
- scitex/scholar/data/bib_files/test_complete_enriched.bib +437 -0
- scitex/scholar/data/bib_files/test_final_enriched.bib +437 -0
- scitex/scholar/data/bib_files/test_seizure.bib +46 -0
- scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
- scitex/scholar/data/impact_factor.db +0 -0
- scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
- scitex/scholar/local_dbs/__init__.py +31 -0
- scitex/scholar/local_dbs/crossref_scitex.py +30 -0
- scitex/scholar/local_dbs/openalex_scitex.py +30 -0
- scitex/scholar/mcp_server.py +59 -4
- scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +2 -2
- scitex/stats/mcp_server.py +16 -3
- scitex/template/mcp_server.py +16 -3
- scitex/ui/mcp_server.py +16 -3
- scitex/writer/__init__.py +43 -34
- {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/METADATA +22 -3
- {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/RECORD +70 -38
- scitex/scholar/crossref_scitex.py +0 -367
- scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +0 -462
- scitex/scholar/url_finder/.tmp/open_url/README.md +0 -223
- scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +0 -694
- scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +0 -1160
- scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +0 -344
- scitex/scholar/url_finder/.tmp/open_url/__init__.py +0 -24
- {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/WHEEL +0 -0
- {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/entry_points.txt +0 -0
- {scitex-2.15.2.dist-info → scitex-2.15.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -20,6 +20,7 @@ from ._cheatsheet import register_cheatsheet_resources
|
|
|
20
20
|
from ._figrecipe import register_figrecipe_resources
|
|
21
21
|
from ._formats import register_format_resources
|
|
22
22
|
from ._modules import register_module_resources
|
|
23
|
+
from ._scholar import register_scholar_resources
|
|
23
24
|
from ._session import register_session_resources
|
|
24
25
|
|
|
25
26
|
__all__ = ["register_resources"]
|
|
@@ -32,6 +33,7 @@ def register_resources(mcp) -> None:
|
|
|
32
33
|
register_module_resources(mcp)
|
|
33
34
|
register_format_resources(mcp)
|
|
34
35
|
register_figrecipe_resources(mcp)
|
|
36
|
+
register_scholar_resources(mcp)
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
# EOF
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2026-01-29
|
|
3
|
+
# File: src/scitex/_mcp_resources/_scholar.py
|
|
4
|
+
"""Scholar library resources for FastMCP unified server.
|
|
5
|
+
|
|
6
|
+
Provides dynamic resources for:
|
|
7
|
+
- scholar://library/{project} - Project paper listings
|
|
8
|
+
- scholar://bibtex/{filename} - BibTeX file contents
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
__all__ = ["register_scholar_resources"]
|
|
19
|
+
|
|
20
|
+
# Directory configuration
|
|
21
|
+
SCITEX_BASE_DIR = Path(os.getenv("SCITEX_DIR", Path.home() / ".scitex"))
|
|
22
|
+
SCITEX_SCHOLAR_DIR = SCITEX_BASE_DIR / "scholar"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_scholar_dir() -> Path:
|
|
26
|
+
"""Get the scholar data directory."""
|
|
27
|
+
SCITEX_SCHOLAR_DIR.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
return SCITEX_SCHOLAR_DIR
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def register_scholar_resources(mcp) -> None:
|
|
32
|
+
"""Register scholar library resources with FastMCP server."""
|
|
33
|
+
|
|
34
|
+
@mcp.resource("scholar://library")
|
|
35
|
+
def list_library_projects() -> str:
|
|
36
|
+
"""List all scholar library projects with paper counts."""
|
|
37
|
+
scholar_dir = _get_scholar_dir()
|
|
38
|
+
library_dir = scholar_dir / "library"
|
|
39
|
+
|
|
40
|
+
if not library_dir.exists():
|
|
41
|
+
return json.dumps({"projects": [], "total": 0}, indent=2)
|
|
42
|
+
|
|
43
|
+
projects = []
|
|
44
|
+
for project_dir in library_dir.iterdir():
|
|
45
|
+
if project_dir.is_dir() and not project_dir.name.startswith("."):
|
|
46
|
+
pdf_count = len(list(project_dir.rglob("*.pdf")))
|
|
47
|
+
metadata_count = len(list(project_dir.rglob("metadata.json")))
|
|
48
|
+
projects.append(
|
|
49
|
+
{
|
|
50
|
+
"name": project_dir.name,
|
|
51
|
+
"pdf_count": pdf_count,
|
|
52
|
+
"paper_count": metadata_count,
|
|
53
|
+
"uri": f"scholar://library/{project_dir.name}",
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return json.dumps(
|
|
58
|
+
{
|
|
59
|
+
"projects": projects,
|
|
60
|
+
"total": len(projects),
|
|
61
|
+
"library_path": str(library_dir),
|
|
62
|
+
},
|
|
63
|
+
indent=2,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
@mcp.resource("scholar://library/{project}")
|
|
67
|
+
def get_library_project(project: str) -> str:
|
|
68
|
+
"""Get papers in a specific library project."""
|
|
69
|
+
library_dir = _get_scholar_dir() / "library" / project
|
|
70
|
+
|
|
71
|
+
if not library_dir.exists():
|
|
72
|
+
return json.dumps({"error": f"Project not found: {project}"}, indent=2)
|
|
73
|
+
|
|
74
|
+
metadata_files = list(library_dir.rglob("metadata.json"))
|
|
75
|
+
papers = []
|
|
76
|
+
|
|
77
|
+
for meta_file in metadata_files[:100]:
|
|
78
|
+
try:
|
|
79
|
+
with open(meta_file) as f:
|
|
80
|
+
meta = json.load(f)
|
|
81
|
+
pdf_exists = any(meta_file.parent.glob("*.pdf"))
|
|
82
|
+
papers.append(
|
|
83
|
+
{
|
|
84
|
+
"id": meta_file.parent.name,
|
|
85
|
+
"title": meta.get("title"),
|
|
86
|
+
"doi": meta.get("doi"),
|
|
87
|
+
"authors": meta.get("authors", [])[:3],
|
|
88
|
+
"year": meta.get("year"),
|
|
89
|
+
"has_pdf": pdf_exists,
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
except Exception:
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
return json.dumps(
|
|
96
|
+
{
|
|
97
|
+
"project": project,
|
|
98
|
+
"paper_count": len(papers),
|
|
99
|
+
"papers": papers,
|
|
100
|
+
},
|
|
101
|
+
indent=2,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
@mcp.resource("scholar://bibtex")
|
|
105
|
+
def list_bibtex_files() -> str:
|
|
106
|
+
"""List recent BibTeX files in scholar directory."""
|
|
107
|
+
scholar_dir = _get_scholar_dir()
|
|
108
|
+
bib_files = []
|
|
109
|
+
|
|
110
|
+
for bib_file in sorted(
|
|
111
|
+
scholar_dir.rglob("*.bib"),
|
|
112
|
+
key=lambda p: p.stat().st_mtime,
|
|
113
|
+
reverse=True,
|
|
114
|
+
)[:20]:
|
|
115
|
+
mtime = datetime.fromtimestamp(bib_file.stat().st_mtime)
|
|
116
|
+
bib_files.append(
|
|
117
|
+
{
|
|
118
|
+
"name": bib_file.name,
|
|
119
|
+
"path": str(bib_file),
|
|
120
|
+
"modified": mtime.isoformat(),
|
|
121
|
+
"uri": f"scholar://bibtex/{bib_file.name}",
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return json.dumps(
|
|
126
|
+
{
|
|
127
|
+
"bibtex_files": bib_files,
|
|
128
|
+
"total": len(bib_files),
|
|
129
|
+
},
|
|
130
|
+
indent=2,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
@mcp.resource("scholar://bibtex/{filename}")
|
|
134
|
+
def get_bibtex_file(filename: str) -> str:
|
|
135
|
+
"""Read a BibTeX file by name."""
|
|
136
|
+
scholar_dir = _get_scholar_dir()
|
|
137
|
+
bib_files = list(scholar_dir.rglob(filename))
|
|
138
|
+
|
|
139
|
+
if not bib_files:
|
|
140
|
+
return json.dumps({"error": f"BibTeX file not found: {filename}"}, indent=2)
|
|
141
|
+
|
|
142
|
+
with open(bib_files[0]) as f:
|
|
143
|
+
content = f.read()
|
|
144
|
+
|
|
145
|
+
return content
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# EOF
|
scitex/_mcp_tools/scholar.py
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
|
-
from typing import List, Optional
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
def _json(data: dict) -> str:
|
|
@@ -20,10 +19,10 @@ def register_scholar_tools(mcp) -> None:
|
|
|
20
19
|
async def scholar_search_papers(
|
|
21
20
|
query: str,
|
|
22
21
|
limit: int = 20,
|
|
23
|
-
year_min:
|
|
24
|
-
year_max:
|
|
22
|
+
year_min: int | None = None,
|
|
23
|
+
year_max: int | None = None,
|
|
25
24
|
search_mode: str = "local",
|
|
26
|
-
sources:
|
|
25
|
+
sources: list[str] | None = None,
|
|
27
26
|
) -> str:
|
|
28
27
|
"""[scholar] Search for scientific papers. Supports local library search and external databases (CrossRef, Semantic Scholar, PubMed, arXiv, OpenAlex)."""
|
|
29
28
|
from scitex.scholar._mcp.handlers import search_papers_handler
|
|
@@ -40,9 +39,9 @@ def register_scholar_tools(mcp) -> None:
|
|
|
40
39
|
|
|
41
40
|
@mcp.tool()
|
|
42
41
|
async def scholar_resolve_dois(
|
|
43
|
-
titles:
|
|
44
|
-
bibtex_path:
|
|
45
|
-
project:
|
|
42
|
+
titles: list[str] | None = None,
|
|
43
|
+
bibtex_path: str | None = None,
|
|
44
|
+
project: str | None = None,
|
|
46
45
|
resume: bool = True,
|
|
47
46
|
) -> str:
|
|
48
47
|
"""[scholar] Resolve DOIs from paper titles using Crossref API. Supports resumable operation for large batches."""
|
|
@@ -59,7 +58,7 @@ def register_scholar_tools(mcp) -> None:
|
|
|
59
58
|
@mcp.tool()
|
|
60
59
|
async def scholar_enrich_bibtex(
|
|
61
60
|
bibtex_path: str,
|
|
62
|
-
output_path:
|
|
61
|
+
output_path: str | None = None,
|
|
63
62
|
add_abstracts: bool = True,
|
|
64
63
|
add_citations: bool = True,
|
|
65
64
|
add_impact_factors: bool = True,
|
|
@@ -94,10 +93,10 @@ def register_scholar_tools(mcp) -> None:
|
|
|
94
93
|
|
|
95
94
|
@mcp.tool()
|
|
96
95
|
async def scholar_download_pdfs_batch(
|
|
97
|
-
dois:
|
|
98
|
-
bibtex_path:
|
|
99
|
-
project:
|
|
100
|
-
output_dir:
|
|
96
|
+
dois: list[str] | None = None,
|
|
97
|
+
bibtex_path: str | None = None,
|
|
98
|
+
project: str | None = None,
|
|
99
|
+
output_dir: str | None = None,
|
|
101
100
|
max_concurrent: int = 3,
|
|
102
101
|
resume: bool = True,
|
|
103
102
|
) -> str:
|
|
@@ -116,7 +115,7 @@ def register_scholar_tools(mcp) -> None:
|
|
|
116
115
|
|
|
117
116
|
@mcp.tool()
|
|
118
117
|
async def scholar_get_library_status(
|
|
119
|
-
project:
|
|
118
|
+
project: str | None = None,
|
|
120
119
|
include_details: bool = False,
|
|
121
120
|
) -> str:
|
|
122
121
|
"""[scholar] Get status of the paper library: download progress, missing PDFs, validation status."""
|
|
@@ -138,8 +137,8 @@ def register_scholar_tools(mcp) -> None:
|
|
|
138
137
|
|
|
139
138
|
@mcp.tool()
|
|
140
139
|
async def scholar_validate_pdfs(
|
|
141
|
-
project:
|
|
142
|
-
pdf_paths:
|
|
140
|
+
project: str | None = None,
|
|
141
|
+
pdf_paths: list[str] | None = None,
|
|
143
142
|
) -> str:
|
|
144
143
|
"""[scholar] Validate PDF files in library for completeness and readability."""
|
|
145
144
|
from scitex.scholar._mcp.handlers import validate_pdfs_handler
|
|
@@ -152,8 +151,8 @@ def register_scholar_tools(mcp) -> None:
|
|
|
152
151
|
|
|
153
152
|
@mcp.tool()
|
|
154
153
|
async def scholar_resolve_openurls(
|
|
155
|
-
dois:
|
|
156
|
-
resolver_url:
|
|
154
|
+
dois: list[str],
|
|
155
|
+
resolver_url: str | None = None,
|
|
157
156
|
resume: bool = True,
|
|
158
157
|
) -> str:
|
|
159
158
|
"""[scholar] Resolve publisher URLs via OpenURL resolver for institutional access."""
|
|
@@ -169,7 +168,7 @@ def register_scholar_tools(mcp) -> None:
|
|
|
169
168
|
@mcp.tool()
|
|
170
169
|
async def scholar_authenticate(
|
|
171
170
|
method: str,
|
|
172
|
-
institution:
|
|
171
|
+
institution: str | None = None,
|
|
173
172
|
force: bool = False,
|
|
174
173
|
confirm: bool = False,
|
|
175
174
|
) -> str:
|
|
@@ -215,7 +214,7 @@ def register_scholar_tools(mcp) -> None:
|
|
|
215
214
|
@mcp.tool()
|
|
216
215
|
async def scholar_export_papers(
|
|
217
216
|
output_path: str,
|
|
218
|
-
project:
|
|
217
|
+
project: str | None = None,
|
|
219
218
|
format: str = "bibtex",
|
|
220
219
|
filter_has_pdf: bool = False,
|
|
221
220
|
) -> str:
|
|
@@ -233,7 +232,7 @@ def register_scholar_tools(mcp) -> None:
|
|
|
233
232
|
@mcp.tool()
|
|
234
233
|
async def scholar_create_project(
|
|
235
234
|
project_name: str,
|
|
236
|
-
description:
|
|
235
|
+
description: str | None = None,
|
|
237
236
|
) -> str:
|
|
238
237
|
"""[scholar] Create a new scholar project for organizing papers."""
|
|
239
238
|
from scitex.scholar._mcp.handlers import create_project_handler
|
|
@@ -255,8 +254,8 @@ def register_scholar_tools(mcp) -> None:
|
|
|
255
254
|
@mcp.tool()
|
|
256
255
|
async def scholar_add_papers_to_project(
|
|
257
256
|
project: str,
|
|
258
|
-
dois:
|
|
259
|
-
bibtex_path:
|
|
257
|
+
dois: list[str] | None = None,
|
|
258
|
+
bibtex_path: str | None = None,
|
|
260
259
|
) -> str:
|
|
261
260
|
"""[scholar] Add papers to a project by DOI or from BibTeX file."""
|
|
262
261
|
from scitex.scholar._mcp.handlers import add_papers_to_project_handler
|
|
@@ -270,14 +269,14 @@ def register_scholar_tools(mcp) -> None:
|
|
|
270
269
|
|
|
271
270
|
@mcp.tool()
|
|
272
271
|
async def scholar_parse_pdf_content(
|
|
273
|
-
pdf_path:
|
|
274
|
-
doi:
|
|
275
|
-
project:
|
|
272
|
+
pdf_path: str | None = None,
|
|
273
|
+
doi: str | None = None,
|
|
274
|
+
project: str | None = None,
|
|
276
275
|
mode: str = "scientific",
|
|
277
276
|
extract_sections: bool = True,
|
|
278
277
|
extract_tables: bool = False,
|
|
279
278
|
extract_images: bool = False,
|
|
280
|
-
max_pages:
|
|
279
|
+
max_pages: int | None = None,
|
|
281
280
|
) -> str:
|
|
282
281
|
"""[scholar] Parse PDF content to extract text, sections (IMRaD), tables, images, and metadata."""
|
|
283
282
|
from scitex.scholar._mcp.handlers import parse_pdf_content_handler
|
|
@@ -297,14 +296,14 @@ def register_scholar_tools(mcp) -> None:
|
|
|
297
296
|
# Job management tools (from job_handlers.py)
|
|
298
297
|
@mcp.tool()
|
|
299
298
|
async def scholar_fetch_papers(
|
|
300
|
-
papers:
|
|
301
|
-
bibtex_path:
|
|
302
|
-
project:
|
|
303
|
-
workers:
|
|
299
|
+
papers: list[str] | None = None,
|
|
300
|
+
bibtex_path: str | None = None,
|
|
301
|
+
project: str | None = None,
|
|
302
|
+
workers: int | None = None,
|
|
304
303
|
browser_mode: str = "stealth",
|
|
305
304
|
chrome_profile: str = "system",
|
|
306
305
|
force: bool = False,
|
|
307
|
-
output:
|
|
306
|
+
output: str | None = None,
|
|
308
307
|
async_mode: bool = True,
|
|
309
308
|
) -> str:
|
|
310
309
|
"""[scholar] Fetch papers to your library. Supports async mode (default) which returns immediately with a job_id for tracking."""
|
|
@@ -325,7 +324,7 @@ def register_scholar_tools(mcp) -> None:
|
|
|
325
324
|
|
|
326
325
|
@mcp.tool()
|
|
327
326
|
async def scholar_list_jobs(
|
|
328
|
-
status:
|
|
327
|
+
status: str | None = None,
|
|
329
328
|
limit: int = 20,
|
|
330
329
|
) -> str:
|
|
331
330
|
"""[scholar] List all background jobs with their status."""
|
|
@@ -369,79 +368,31 @@ def register_scholar_tools(mcp) -> None:
|
|
|
369
368
|
result = await get_job_result_handler(job_id=job_id)
|
|
370
369
|
return _json(result)
|
|
371
370
|
|
|
372
|
-
#
|
|
373
|
-
|
|
374
|
-
# =========================================================================
|
|
371
|
+
# Import crossref-local and openalex-local MCP servers
|
|
372
|
+
_import_local_db_servers(mcp)
|
|
375
373
|
|
|
376
|
-
@mcp.tool()
|
|
377
|
-
async def scholar_crossref_search(
|
|
378
|
-
query: str,
|
|
379
|
-
limit: int = 20,
|
|
380
|
-
offset: int = 0,
|
|
381
|
-
year_min: Optional[int] = None,
|
|
382
|
-
year_max: Optional[int] = None,
|
|
383
|
-
enrich: bool = False,
|
|
384
|
-
) -> str:
|
|
385
|
-
"""[scholar] Search CrossRef database (167M+ papers) via crossref-local. Fast full-text search with optional citation enrichment."""
|
|
386
|
-
from scitex.scholar._mcp.crossref_handlers import crossref_search_handler
|
|
387
374
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
limit=limit,
|
|
391
|
-
offset=offset,
|
|
392
|
-
year_min=year_min,
|
|
393
|
-
year_max=year_max,
|
|
394
|
-
enrich=enrich,
|
|
395
|
-
)
|
|
396
|
-
return _json(result)
|
|
375
|
+
def _import_local_db_servers(mcp) -> None:
|
|
376
|
+
"""Mount crossref-local and openalex-local MCP servers if available.
|
|
397
377
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
"""[scholar] Get paper metadata by DOI from CrossRef database."""
|
|
405
|
-
from scitex.scholar._mcp.crossref_handlers import crossref_get_handler
|
|
378
|
+
Uses fastmcp's mount() for automatic tool delegation.
|
|
379
|
+
Tools are prefixed: crossref_search, openalex_search, etc.
|
|
380
|
+
"""
|
|
381
|
+
# Mount crossref-local MCP server (167M+ papers)
|
|
382
|
+
try:
|
|
383
|
+
from crossref_local.mcp_server import mcp as crossref_mcp
|
|
406
384
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
include_references=include_references,
|
|
411
|
-
)
|
|
412
|
-
return _json(result)
|
|
385
|
+
mcp.mount(crossref_mcp, prefix="crossref")
|
|
386
|
+
except ImportError:
|
|
387
|
+
pass # crossref-local not installed
|
|
413
388
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
from scitex.scholar._mcp.crossref_handlers import crossref_count_handler
|
|
418
|
-
|
|
419
|
-
result = await crossref_count_handler(query=query)
|
|
420
|
-
return _json(result)
|
|
421
|
-
|
|
422
|
-
@mcp.tool()
|
|
423
|
-
async def scholar_crossref_citations(
|
|
424
|
-
doi: str,
|
|
425
|
-
direction: str = "citing",
|
|
426
|
-
limit: int = 100,
|
|
427
|
-
) -> str:
|
|
428
|
-
"""[scholar] Get citation relationships (citing/cited papers) for a DOI."""
|
|
429
|
-
from scitex.scholar._mcp.crossref_handlers import crossref_citations_handler
|
|
389
|
+
# Mount openalex-local MCP server (250M+ papers)
|
|
390
|
+
try:
|
|
391
|
+
from openalex_local._cli.mcp_server import mcp as openalex_mcp
|
|
430
392
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
limit=limit,
|
|
435
|
-
)
|
|
436
|
-
return _json(result)
|
|
437
|
-
|
|
438
|
-
@mcp.tool()
|
|
439
|
-
async def scholar_crossref_info() -> str:
|
|
440
|
-
"""[scholar] Get CrossRef database configuration and status info."""
|
|
441
|
-
from scitex.scholar._mcp.crossref_handlers import crossref_info_handler
|
|
442
|
-
|
|
443
|
-
result = await crossref_info_handler()
|
|
444
|
-
return _json(result)
|
|
393
|
+
mcp.mount(openalex_mcp, prefix="openalex")
|
|
394
|
+
except ImportError:
|
|
395
|
+
pass # openalex-local not installed
|
|
445
396
|
|
|
446
397
|
|
|
447
398
|
# EOF
|