academia-mcp 1.11.4__py3-none-any.whl → 1.11.5__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.
- academia_mcp/server.py +2 -0
- academia_mcp/tools/__init__.py +8 -1
- academia_mcp/tools/s2.py +68 -19
- academia_mcp/utils.py +1 -1
- {academia_mcp-1.11.4.dist-info → academia_mcp-1.11.5.dist-info}/METADATA +1 -1
- {academia_mcp-1.11.4.dist-info → academia_mcp-1.11.5.dist-info}/RECORD +10 -10
- {academia_mcp-1.11.4.dist-info → academia_mcp-1.11.5.dist-info}/WHEEL +0 -0
- {academia_mcp-1.11.4.dist-info → academia_mcp-1.11.5.dist-info}/entry_points.txt +0 -0
- {academia_mcp-1.11.4.dist-info → academia_mcp-1.11.5.dist-info}/licenses/LICENSE +0 -0
- {academia_mcp-1.11.4.dist-info → academia_mcp-1.11.5.dist-info}/top_level.txt +0 -0
academia_mcp/server.py
CHANGED
@@ -17,6 +17,7 @@ from academia_mcp.tools.s2 import (
|
|
17
17
|
s2_get_references,
|
18
18
|
s2_corpus_id_from_arxiv_id,
|
19
19
|
s2_get_info,
|
20
|
+
s2_search,
|
20
21
|
)
|
21
22
|
from academia_mcp.tools.hf_datasets_search import hf_datasets_search
|
22
23
|
from academia_mcp.tools.anthology_search import anthology_search
|
@@ -86,6 +87,7 @@ def create_server(
|
|
86
87
|
server.add_tool(s2_get_citations, structured_output=True)
|
87
88
|
server.add_tool(s2_get_references, structured_output=True)
|
88
89
|
server.add_tool(s2_get_info, structured_output=True)
|
90
|
+
server.add_tool(s2_search, structured_output=True)
|
89
91
|
server.add_tool(s2_corpus_id_from_arxiv_id)
|
90
92
|
server.add_tool(hf_datasets_search)
|
91
93
|
server.add_tool(anthology_search)
|
academia_mcp/tools/__init__.py
CHANGED
@@ -2,7 +2,13 @@ from .arxiv_search import arxiv_search
|
|
2
2
|
from .anthology_search import anthology_search
|
3
3
|
from .arxiv_download import arxiv_download
|
4
4
|
from .hf_datasets_search import hf_datasets_search
|
5
|
-
from .s2 import
|
5
|
+
from .s2 import (
|
6
|
+
s2_get_references,
|
7
|
+
s2_get_citations,
|
8
|
+
s2_corpus_id_from_arxiv_id,
|
9
|
+
s2_get_info,
|
10
|
+
s2_search,
|
11
|
+
)
|
6
12
|
from .document_qa import document_qa
|
7
13
|
from .latex import (
|
8
14
|
compile_latex,
|
@@ -26,6 +32,7 @@ __all__ = [
|
|
26
32
|
"s2_get_citations",
|
27
33
|
"s2_corpus_id_from_arxiv_id",
|
28
34
|
"s2_get_info",
|
35
|
+
"s2_search",
|
29
36
|
"hf_datasets_search",
|
30
37
|
"document_qa",
|
31
38
|
"compile_latex",
|
academia_mcp/tools/s2.py
CHANGED
@@ -8,10 +8,12 @@ from pydantic import BaseModel, Field
|
|
8
8
|
from academia_mcp.utils import get_with_retries
|
9
9
|
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
BASE_URL = "https://api.semanticscholar.org/graph/v1"
|
12
|
+
PAPER_URL_TEMPLATE = "{base_url}/paper/{paper_id}"
|
13
|
+
CITATIONS_URL_TEMPLATE = "{base_url}/paper/{paper_id}/citations"
|
14
|
+
REFERENCES_URL_TEMPLATE = "{base_url}/paper/{paper_id}/references"
|
15
|
+
SEARCH_URL_TEMPLATE = "{base_url}/paper/search"
|
16
|
+
FIELDS = "paperId,title,authors,externalIds,venue,citationCount,publicationDate"
|
15
17
|
|
16
18
|
|
17
19
|
class S2PaperInfo(BaseModel): # type: ignore
|
@@ -38,7 +40,10 @@ def _format_authors(authors: List[Dict[str, Any]]) -> List[str]:
|
|
38
40
|
|
39
41
|
|
40
42
|
def _clean_entry(entry: Dict[str, Any]) -> S2PaperInfo:
|
41
|
-
|
43
|
+
if "citingPaper" in entry:
|
44
|
+
entry = entry["citingPaper"]
|
45
|
+
elif "citedPaper" in entry:
|
46
|
+
entry = entry["citedPaper"]
|
42
47
|
external_ids = entry.get("externalIds")
|
43
48
|
if not external_ids:
|
44
49
|
external_ids = dict()
|
@@ -88,17 +93,17 @@ def s2_get_citations(
|
|
88
93
|
arxiv_id = arxiv_id.split("v")[0]
|
89
94
|
paper_id = f"arxiv:{arxiv_id}"
|
90
95
|
|
91
|
-
url = CITATIONS_URL_TEMPLATE.format(
|
92
|
-
|
93
|
-
)
|
94
|
-
response = get_with_retries(url)
|
96
|
+
url = CITATIONS_URL_TEMPLATE.format(base_url=BASE_URL, paper_id=paper_id)
|
97
|
+
payload = {"fields": FIELDS, "offset": offset, "limit": limit}
|
98
|
+
response = get_with_retries(url, params=payload)
|
95
99
|
result = response.json()
|
96
100
|
entries = result["data"]
|
97
101
|
total_count = len(result["data"]) + result["offset"]
|
98
102
|
|
99
103
|
if "next" in result:
|
100
|
-
paper_url = PAPER_URL_TEMPLATE.format(
|
101
|
-
|
104
|
+
paper_url = PAPER_URL_TEMPLATE.format(base_url=BASE_URL, paper_id=paper_id)
|
105
|
+
payload = {"fields": FIELDS}
|
106
|
+
paper_response = get_with_retries(paper_url, params=payload)
|
102
107
|
paper_result = paper_response.json()
|
103
108
|
total_count = paper_result["citationCount"]
|
104
109
|
|
@@ -123,10 +128,9 @@ def s2_get_references(
|
|
123
128
|
arxiv_id = arxiv_id.split("v")[0]
|
124
129
|
paper_id = f"arxiv:{arxiv_id}"
|
125
130
|
|
126
|
-
url = REFERENCES_URL_TEMPLATE.format(
|
127
|
-
|
128
|
-
)
|
129
|
-
response = get_with_retries(url)
|
131
|
+
url = REFERENCES_URL_TEMPLATE.format(base_url=BASE_URL, paper_id=paper_id)
|
132
|
+
payload = {"fields": FIELDS, "offset": offset, "limit": limit}
|
133
|
+
response = get_with_retries(url, params=payload)
|
130
134
|
result = response.json()
|
131
135
|
entries = result["data"]
|
132
136
|
total_count = len(result["data"]) + result["offset"]
|
@@ -143,8 +147,9 @@ def s2_corpus_id_from_arxiv_id(arxiv_id: str) -> int:
|
|
143
147
|
assert isinstance(arxiv_id, str), "Error: Your arxiv_id must be a string"
|
144
148
|
if "v" in arxiv_id:
|
145
149
|
arxiv_id = arxiv_id.split("v")[0]
|
146
|
-
paper_url = PAPER_URL_TEMPLATE.format(paper_id=f"arxiv:{arxiv_id}"
|
147
|
-
|
150
|
+
paper_url = PAPER_URL_TEMPLATE.format(base_url=BASE_URL, paper_id=f"arxiv:{arxiv_id}")
|
151
|
+
payload = {"fields": "externalIds"}
|
152
|
+
response = get_with_retries(paper_url, params=payload)
|
148
153
|
result = response.json()
|
149
154
|
return int(result["externalIds"]["CorpusId"])
|
150
155
|
|
@@ -159,8 +164,10 @@ def s2_get_info(arxiv_id: str) -> S2PaperInfo:
|
|
159
164
|
assert isinstance(arxiv_id, str), "Error: Your arxiv_id must be a string"
|
160
165
|
if "v" in arxiv_id:
|
161
166
|
arxiv_id = arxiv_id.split("v")[0]
|
162
|
-
|
163
|
-
|
167
|
+
paper_id = f"arxiv:{arxiv_id}"
|
168
|
+
payload = {"fields": FIELDS}
|
169
|
+
paper_url = PAPER_URL_TEMPLATE.format(base_url=BASE_URL, paper_id=paper_id)
|
170
|
+
response = get_with_retries(paper_url, params=payload)
|
164
171
|
json_data = response.json()
|
165
172
|
return S2PaperInfo(
|
166
173
|
arxiv_id=json_data.get("externalIds", {}).get("ArXiv"),
|
@@ -171,3 +178,45 @@ def s2_get_info(arxiv_id: str) -> S2PaperInfo:
|
|
171
178
|
citation_count=int(json_data.get("citationCount", 0)),
|
172
179
|
publication_date=str(json_data.get("publicationDate", "")),
|
173
180
|
)
|
181
|
+
|
182
|
+
|
183
|
+
def s2_search(
|
184
|
+
query: str,
|
185
|
+
offset: int = 0,
|
186
|
+
limit: int = 5,
|
187
|
+
min_citation_count: int = 0,
|
188
|
+
publication_date: Optional[str] = None,
|
189
|
+
) -> S2SearchResponse:
|
190
|
+
"""
|
191
|
+
Search the S2 corpus for a given query.
|
192
|
+
|
193
|
+
Args:
|
194
|
+
query: The query to search for.
|
195
|
+
offset: The offset to scroll through results. 10 items will be skipped if offset=10. 0 by default.
|
196
|
+
limit: The maximum number of items to return. limit=50 by default.
|
197
|
+
min_citation_count: The minimum citation count to return. 0 by default.
|
198
|
+
publication_date: Restricts results to the given range of publication dates or years (inclusive).
|
199
|
+
Accepts the format <startDate>:<endDate> with each date in YYYY-MM-DD format. None by default.
|
200
|
+
"""
|
201
|
+
url = SEARCH_URL_TEMPLATE.format(base_url=BASE_URL)
|
202
|
+
payload = {
|
203
|
+
"query": query,
|
204
|
+
"offset": offset,
|
205
|
+
"limit": limit,
|
206
|
+
"minCitationCount": min_citation_count,
|
207
|
+
"fields": FIELDS,
|
208
|
+
}
|
209
|
+
if publication_date:
|
210
|
+
payload["publicationDateOrYear"] = publication_date
|
211
|
+
response = get_with_retries(url, params=payload, backoff_factor=10.0, num_retries=5)
|
212
|
+
result = response.json()
|
213
|
+
if "data" not in result:
|
214
|
+
return S2SearchResponse(
|
215
|
+
total_count=0,
|
216
|
+
returned_count=0,
|
217
|
+
offset=offset if offset else 0,
|
218
|
+
results=[],
|
219
|
+
)
|
220
|
+
entries = result["data"]
|
221
|
+
total_count = result["total"]
|
222
|
+
return _format_entries(entries, offset if offset else 0, total_count)
|
academia_mcp/utils.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: academia-mcp
|
3
|
-
Version: 1.11.
|
3
|
+
Version: 1.11.5
|
4
4
|
Summary: MCP server that provides different tools to search for scientific publications
|
5
5
|
Author-email: Ilya Gusev <phoenixilya@gmail.com>
|
6
6
|
Project-URL: Homepage, https://github.com/IlyaGusev/academia_mcp
|
@@ -4,12 +4,12 @@ academia_mcp/files.py,sha256=J_81COWWryBp2bHVe_9dg9c0rbVIzv7-zWkJ7CS9lr4,485
|
|
4
4
|
academia_mcp/llm.py,sha256=zpGkuJFf58Ofgys_fi28-47_wJ1a7sIs_yZvI1Si6z0,993
|
5
5
|
academia_mcp/pdf.py,sha256=9PlXzHGhb6ay3ldbTdxCcTWvH4TkET3bnb64mgoh9i0,1273
|
6
6
|
academia_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
academia_mcp/server.py,sha256=
|
7
|
+
academia_mcp/server.py,sha256=oKlEjIhqzd2xmK8gtCDMKzqiXMd9K50K-3fyHhcke5Y,6466
|
8
8
|
academia_mcp/settings.py,sha256=c5s4dI8V_cWmMED-jKDmHjfdIaBcxwEK4HdHNQ3WUIg,1096
|
9
|
-
academia_mcp/utils.py,sha256=
|
9
|
+
academia_mcp/utils.py,sha256=BVoEkGlW0HPH4bbWIFLzVwjA95ocT6OnoZBUhs87DK0,4904
|
10
10
|
academia_mcp/latex_templates/agents4science_2025/agents4science_2025.sty,sha256=hGcEPCYBJS4vdhWvN_yEaJC4GvT_yDroI94CfY2Oguk,12268
|
11
11
|
academia_mcp/latex_templates/agents4science_2025/agents4science_2025.tex,sha256=Tl1QkHXHRopw9VEfWrD3Layr5JP_0gIzVQjL4KXIWqc,15814
|
12
|
-
academia_mcp/tools/__init__.py,sha256=
|
12
|
+
academia_mcp/tools/__init__.py,sha256=RpOCL2t-_fJev55rXL1za1MdrY6sESaTCszUCsEW474,1614
|
13
13
|
academia_mcp/tools/anthology_search.py,sha256=rhFpJZqGLABgr0raDuH0CARBiAJNJtEI4dlMrKNHfDQ,7669
|
14
14
|
academia_mcp/tools/arxiv_download.py,sha256=4zl9QkWF7uU2BYz4XH7Fu_51htolSYtO7a2v4_ikhxg,10633
|
15
15
|
academia_mcp/tools/arxiv_search.py,sha256=p4DeCvV3TUpj58etx0QOxm-GYKAWkP9AGjLv4HUUqUc,8857
|
@@ -20,14 +20,14 @@ academia_mcp/tools/image_processing.py,sha256=BFj5D0lYbe6OCAL9LSHjS1adr8C0BrtWBX
|
|
20
20
|
academia_mcp/tools/latex.py,sha256=B1Leqt1FHY6H3DlUgeYse4LMFpf4-K1FQViXl5MKk8A,6144
|
21
21
|
academia_mcp/tools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
22
|
academia_mcp/tools/review.py,sha256=Va0lFJJKuk-NvWhKS3UZ-Dnuk7CyuDQ4S1nd70D-ffE,11117
|
23
|
-
academia_mcp/tools/s2.py,sha256=
|
23
|
+
academia_mcp/tools/s2.py,sha256=_Ea7IyF5wH7ZWT59mI2b8wsxQp33W3URQV_EUiZuL2s,8221
|
24
24
|
academia_mcp/tools/speech_to_text.py,sha256=YZzMqdvunzXkpcadP_mYhm6cs4qH1Y_42SfY-7eX4O4,1601
|
25
25
|
academia_mcp/tools/visit_webpage.py,sha256=uJZx9vBGS8q-J-VH4Pr7T9lNtDsWU83gJhlotcd1ajg,3788
|
26
26
|
academia_mcp/tools/web_search.py,sha256=CHgco8DufTFwtVecgDOOMylIY99iUmCdb0oZtpGntx0,8646
|
27
27
|
academia_mcp/tools/yt_transcript.py,sha256=ilfOpX14moC1bKHbFmOVvZ8-_NxuQQUoQbV28e9FBaE,1217
|
28
|
-
academia_mcp-1.11.
|
29
|
-
academia_mcp-1.11.
|
30
|
-
academia_mcp-1.11.
|
31
|
-
academia_mcp-1.11.
|
32
|
-
academia_mcp-1.11.
|
33
|
-
academia_mcp-1.11.
|
28
|
+
academia_mcp-1.11.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
29
|
+
academia_mcp-1.11.5.dist-info/METADATA,sha256=ExVjjzRzBc5eF9kVNZW_kAQdVCY3QZwE-jpZlcZSO6Q,6517
|
30
|
+
academia_mcp-1.11.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
31
|
+
academia_mcp-1.11.5.dist-info/entry_points.txt,sha256=gxkiKJ74w2FwJpSECpjA3XtCfI5ZfrM6N8cqnwsq4yY,51
|
32
|
+
academia_mcp-1.11.5.dist-info/top_level.txt,sha256=CzGpRFsRRJRqWEb1e3SUlcfGqRzOxevZGaJWrtGF8W0,13
|
33
|
+
academia_mcp-1.11.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|