@wentorai/research-plugins 1.4.3 → 1.4.4
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.
- package/package.json +1 -1
- package/skills/literature/search/arxiv-api/SKILL.md +11 -2
- package/src/tools/arxiv.ts +12 -6
- package/src/tools/biorxiv.ts +16 -14
- package/src/tools/crossref.ts +12 -3
- package/src/tools/datacite.ts +14 -6
- package/src/tools/dblp.ts +19 -5
- package/src/tools/doaj.ts +12 -4
- package/src/tools/europe-pmc.ts +22 -8
- package/src/tools/hal.ts +10 -2
- package/src/tools/inspire-hep.ts +14 -4
- package/src/tools/openaire.ts +10 -2
- package/src/tools/openalex.ts +6 -5
- package/src/tools/opencitations.ts +3 -3
- package/src/tools/orcid.ts +11 -4
- package/src/tools/osf-preprints.ts +5 -5
- package/src/tools/pubmed.ts +12 -3
- package/src/tools/ror.ts +10 -3
- package/src/tools/unpaywall.ts +1 -1
- package/src/tools/zenodo.ts +11 -3
package/package.json
CHANGED
|
@@ -24,12 +24,21 @@ The API is free to use with no authentication required. It supports complex bool
|
|
|
24
24
|
|
|
25
25
|
No authentication required. The arXiv API is fully open. However, users must respect the rate limit of 3 requests per second. Excessive usage may result in temporary IP-based blocking. Including a descriptive User-Agent header is considered good practice.
|
|
26
26
|
|
|
27
|
-
##
|
|
27
|
+
## Using the `search_arxiv` Tool
|
|
28
|
+
|
|
29
|
+
**IMPORTANT:** When calling the `search_arxiv` tool, use parameter name `query` (NOT
|
|
30
|
+
`search_query`). The raw API uses `search_query`, but the tool wrapper accepts `query`.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
search_arxiv({ query: "ti:transformer AND cat:cs.CL", sort_by: "submittedDate" })
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Core Endpoints (Raw API Reference)
|
|
28
37
|
|
|
29
38
|
### Query: Search for Articles
|
|
30
39
|
|
|
31
40
|
- **URL**: `GET http://export.arxiv.org/api/query`
|
|
32
|
-
- **Parameters
|
|
41
|
+
- **Parameters** (raw API — the `search_arxiv` tool wraps these automatically):
|
|
33
42
|
| Param | Type | Required | Description |
|
|
34
43
|
|-------|------|----------|-------------|
|
|
35
44
|
| search_query | string | Yes | Query string using arXiv field prefixes (ti, au, abs, cat, id) |
|
package/src/tools/arxiv.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
|
|
3
|
-
import { toolResult, trackedFetch, isTrackedError, validEnum } from "./util.js";
|
|
3
|
+
import { toolResult, trackedFetch, isTrackedError, validEnum, validParam } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const BASE = "https://export.arxiv.org/api/query";
|
|
6
6
|
|
|
@@ -103,21 +103,27 @@ export function createArxivTools(
|
|
|
103
103
|
Type.String({ description: "Sort order: 'ascending' or 'descending'" }),
|
|
104
104
|
),
|
|
105
105
|
}),
|
|
106
|
-
execute: async (input: {
|
|
106
|
+
execute: async (_toolCallId: string, input: {
|
|
107
107
|
query: string;
|
|
108
108
|
max_results?: number;
|
|
109
109
|
sort_by?: string;
|
|
110
110
|
sort_order?: string;
|
|
111
111
|
}) => {
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
const query = validParam(input?.query);
|
|
113
|
+
if (!query) {
|
|
114
|
+
return toolResult({
|
|
115
|
+
error:
|
|
116
|
+
"query parameter is required and must not be empty. " +
|
|
117
|
+
"Use the tool parameter 'query' (not 'search_query'). " +
|
|
118
|
+
"Example: search_arxiv({ query: \"ti:transformer AND cat:cs.CL\" })",
|
|
119
|
+
});
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
const SORT_BY = ["relevance", "lastUpdatedDate", "submittedDate"] as const;
|
|
117
123
|
const SORT_ORDER = ["ascending", "descending"] as const;
|
|
118
124
|
|
|
119
125
|
const params = new URLSearchParams({
|
|
120
|
-
search_query:
|
|
126
|
+
search_query: query,
|
|
121
127
|
max_results: String(Math.min(input.max_results ?? 10, 50)),
|
|
122
128
|
sortBy: validEnum(input.sort_by, SORT_BY, "relevance"),
|
|
123
129
|
sortOrder: validEnum(input.sort_order, SORT_ORDER, "descending"),
|
|
@@ -157,7 +163,7 @@ export function createArxivTools(
|
|
|
157
163
|
description: "arXiv paper ID, e.g. '2301.00001' or '2301.00001v2'",
|
|
158
164
|
}),
|
|
159
165
|
}),
|
|
160
|
-
execute: async (input: { arxiv_id: string }) => {
|
|
166
|
+
execute: async (_toolCallId: string, input: { arxiv_id: string }) => {
|
|
161
167
|
if (!input?.arxiv_id) {
|
|
162
168
|
return toolResult({ error: 'arxiv_id parameter is required (e.g., "2301.00001" or "2301.00001v2")' });
|
|
163
169
|
}
|
package/src/tools/biorxiv.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
|
|
3
|
-
import { toolResult, trackedFetch, isTrackedError, validEnum } from "./util.js";
|
|
3
|
+
import { toolResult, trackedFetch, isTrackedError, validEnum, validParam } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const BASE = "https://api.biorxiv.org";
|
|
6
6
|
|
|
@@ -23,16 +23,17 @@ export function createBiorxivTools(
|
|
|
23
23
|
Type.Number({ description: "Pagination offset (default 0, each page returns up to 100)" }),
|
|
24
24
|
),
|
|
25
25
|
}),
|
|
26
|
-
execute: async (input: { interval: string; cursor?: number }) => {
|
|
27
|
-
|
|
26
|
+
execute: async (_toolCallId: string, input: { interval: string; cursor?: number }) => {
|
|
27
|
+
const interval = validParam(input?.interval);
|
|
28
|
+
if (!interval) {
|
|
28
29
|
return toolResult({ error: 'interval parameter is required (date range in YYYY-MM-DD/YYYY-MM-DD format, e.g. "2026-03-01/2026-03-18")' });
|
|
29
30
|
}
|
|
30
31
|
// Validate date range format — bioRxiv API only accepts YYYY-MM-DD/YYYY-MM-DD
|
|
31
|
-
if (!/^\d{4}-\d{2}-\d{2}\/\d{4}-\d{2}-\d{2}$/.test(
|
|
32
|
-
return toolResult({ error: `Invalid interval format "${
|
|
32
|
+
if (!/^\d{4}-\d{2}-\d{2}\/\d{4}-\d{2}-\d{2}$/.test(interval)) {
|
|
33
|
+
return toolResult({ error: `Invalid interval format "${interval}". Must be YYYY-MM-DD/YYYY-MM-DD (e.g. "2026-03-01/2026-03-18")` });
|
|
33
34
|
}
|
|
34
|
-
const cursor = input
|
|
35
|
-
const tracked = await trackedFetch("biorxiv", `${BASE}/details/biorxiv/${
|
|
35
|
+
const cursor = input?.cursor ?? 0;
|
|
36
|
+
const tracked = await trackedFetch("biorxiv", `${BASE}/details/biorxiv/${interval}/${cursor}/json`, undefined, 30_000);
|
|
36
37
|
if (isTrackedError(tracked)) return tracked;
|
|
37
38
|
const data = await tracked.res.json();
|
|
38
39
|
|
|
@@ -76,15 +77,16 @@ export function createBiorxivTools(
|
|
|
76
77
|
Type.Number({ description: "Pagination offset (default 0)" }),
|
|
77
78
|
),
|
|
78
79
|
}),
|
|
79
|
-
execute: async (input: { interval: string; cursor?: number }) => {
|
|
80
|
-
|
|
80
|
+
execute: async (_toolCallId: string, input: { interval: string; cursor?: number }) => {
|
|
81
|
+
const interval = validParam(input?.interval);
|
|
82
|
+
if (!interval) {
|
|
81
83
|
return toolResult({ error: 'interval parameter is required (date range in YYYY-MM-DD/YYYY-MM-DD format, e.g. "2026-03-01/2026-03-18")' });
|
|
82
84
|
}
|
|
83
|
-
if (!/^\d{4}-\d{2}-\d{2}\/\d{4}-\d{2}-\d{2}$/.test(
|
|
84
|
-
return toolResult({ error: `Invalid interval format "${
|
|
85
|
+
if (!/^\d{4}-\d{2}-\d{2}\/\d{4}-\d{2}-\d{2}$/.test(interval)) {
|
|
86
|
+
return toolResult({ error: `Invalid interval format "${interval}". Must be YYYY-MM-DD/YYYY-MM-DD (e.g. "2026-03-01/2026-03-18")` });
|
|
85
87
|
}
|
|
86
|
-
const cursor = input
|
|
87
|
-
const tracked = await trackedFetch("medrxiv", `${BASE}/details/medrxiv/${
|
|
88
|
+
const cursor = input?.cursor ?? 0;
|
|
89
|
+
const tracked = await trackedFetch("medrxiv", `${BASE}/details/medrxiv/${interval}/${cursor}/json`, undefined, 30_000);
|
|
88
90
|
if (isTrackedError(tracked)) return tracked;
|
|
89
91
|
const data = await tracked.res.json();
|
|
90
92
|
|
|
@@ -124,7 +126,7 @@ export function createBiorxivTools(
|
|
|
124
126
|
Type.String({ description: "Server: 'biorxiv' or 'medrxiv' (default: biorxiv)" }),
|
|
125
127
|
),
|
|
126
128
|
}),
|
|
127
|
-
execute: async (input: { doi: string; server?: string }) => {
|
|
129
|
+
execute: async (_toolCallId: string, input: { doi: string; server?: string }) => {
|
|
128
130
|
if (!input?.doi) {
|
|
129
131
|
return toolResult({ error: 'doi parameter is required (e.g., "10.1101/2024.01.15.575123")' });
|
|
130
132
|
}
|
package/src/tools/crossref.ts
CHANGED
|
@@ -23,7 +23,7 @@ export function createCrossRefTools(
|
|
|
23
23
|
description: "DOI to resolve, e.g. '10.1038/nature12373'",
|
|
24
24
|
}),
|
|
25
25
|
}),
|
|
26
|
-
execute: async (input: { doi: string }) => {
|
|
26
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
27
27
|
if (!input?.doi) {
|
|
28
28
|
return toolResult({ error: 'doi parameter is required (e.g., "10.1038/nature12373")' });
|
|
29
29
|
}
|
|
@@ -94,7 +94,7 @@ export function createCrossRefTools(
|
|
|
94
94
|
Type.Number({ description: "Max results (default 10, max 100)" }),
|
|
95
95
|
),
|
|
96
96
|
}),
|
|
97
|
-
execute: async (input: {
|
|
97
|
+
execute: async (_toolCallId: string, input: {
|
|
98
98
|
query: string;
|
|
99
99
|
journal?: string;
|
|
100
100
|
issn?: string;
|
|
@@ -105,8 +105,17 @@ export function createCrossRefTools(
|
|
|
105
105
|
sort?: string;
|
|
106
106
|
limit?: number;
|
|
107
107
|
}) => {
|
|
108
|
+
const query = validParam(input?.query);
|
|
109
|
+
if (!query) {
|
|
110
|
+
return toolResult({
|
|
111
|
+
error:
|
|
112
|
+
"query parameter is required and must not be empty. " +
|
|
113
|
+
"Example: search_crossref({ query: \"PFAS machine learning\", from_year: 2023 })",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
108
117
|
const params = new URLSearchParams({
|
|
109
|
-
query
|
|
118
|
+
query,
|
|
110
119
|
rows: String(Math.min(input.limit ?? 10, 100)),
|
|
111
120
|
});
|
|
112
121
|
|
package/src/tools/datacite.ts
CHANGED
|
@@ -31,23 +31,31 @@ export function createDataCiteTools(
|
|
|
31
31
|
Type.Number({ description: "Published from this year onward" }),
|
|
32
32
|
),
|
|
33
33
|
}),
|
|
34
|
-
execute: async (input: {
|
|
34
|
+
execute: async (_toolCallId: string, input: {
|
|
35
35
|
query: string;
|
|
36
36
|
max_results?: number;
|
|
37
37
|
resource_type?: string;
|
|
38
38
|
from_year?: number;
|
|
39
39
|
}) => {
|
|
40
|
-
const
|
|
40
|
+
const query = validParam(input?.query);
|
|
41
|
+
if (!query) {
|
|
42
|
+
return toolResult({
|
|
43
|
+
error:
|
|
44
|
+
"query parameter is required and must not be empty. " +
|
|
45
|
+
"Example: search_datacite({ query: \"climate dataset\" })",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const pageSize = Math.min(input?.max_results ?? 10, 100);
|
|
41
49
|
const params = new URLSearchParams({
|
|
42
|
-
query
|
|
50
|
+
query,
|
|
43
51
|
"page[size]": String(pageSize),
|
|
44
52
|
});
|
|
45
53
|
const resourceType = validParam(input.resource_type);
|
|
46
54
|
if (resourceType) {
|
|
47
55
|
params.set("resource-type-id", resourceType.toLowerCase());
|
|
48
56
|
}
|
|
49
|
-
if (input
|
|
50
|
-
params.set("query", `${
|
|
57
|
+
if (input?.from_year) {
|
|
58
|
+
params.set("query", `${query} AND publicationYear:[${input.from_year} TO *]`);
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
const tracked = await trackedFetch("datacite", `${BASE}/dois?${params}`, undefined, 15_000);
|
|
@@ -118,7 +126,7 @@ export function createDataCiteTools(
|
|
|
118
126
|
description: "DOI to resolve, e.g. '10.5281/zenodo.1234567'",
|
|
119
127
|
}),
|
|
120
128
|
}),
|
|
121
|
-
execute: async (input: { doi: string }) => {
|
|
129
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
122
130
|
if (!input?.doi) {
|
|
123
131
|
return toolResult({ error: 'doi parameter is required (e.g., "10.5281/zenodo.1234567")' });
|
|
124
132
|
}
|
package/src/tools/dblp.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
|
|
3
|
-
import { toolResult, trackedFetch, isTrackedError } from "./util.js";
|
|
3
|
+
import { toolResult, trackedFetch, isTrackedError, validParam } from "./util.js";
|
|
4
4
|
|
|
5
5
|
export function createDblpTools(
|
|
6
6
|
_ctx: OpenClawPluginToolContext,
|
|
@@ -23,13 +23,20 @@ export function createDblpTools(
|
|
|
23
23
|
Type.Number({ description: "Result offset for pagination" }),
|
|
24
24
|
),
|
|
25
25
|
}),
|
|
26
|
-
execute: async (input: {
|
|
26
|
+
execute: async (_toolCallId: string, input: {
|
|
27
27
|
query: string;
|
|
28
28
|
max_results?: number;
|
|
29
29
|
offset?: number;
|
|
30
30
|
}) => {
|
|
31
|
+
const query = validParam(input?.query);
|
|
32
|
+
if (!query) {
|
|
33
|
+
return toolResult({
|
|
34
|
+
error: "query parameter is required and must not be empty.",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
const params = new URLSearchParams({
|
|
32
|
-
q:
|
|
39
|
+
q: query,
|
|
33
40
|
format: "json",
|
|
34
41
|
h: String(Math.min(input.max_results ?? 10, 1000)),
|
|
35
42
|
});
|
|
@@ -90,9 +97,16 @@ export function createDblpTools(
|
|
|
90
97
|
Type.Number({ description: "Max results (default 10)" }),
|
|
91
98
|
),
|
|
92
99
|
}),
|
|
93
|
-
execute: async (input: { query: string; max_results?: number }) => {
|
|
100
|
+
execute: async (_toolCallId: string, input: { query: string; max_results?: number }) => {
|
|
101
|
+
const query = validParam(input?.query);
|
|
102
|
+
if (!query) {
|
|
103
|
+
return toolResult({
|
|
104
|
+
error: "query parameter is required and must not be empty.",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
94
108
|
const params = new URLSearchParams({
|
|
95
|
-
q:
|
|
109
|
+
q: query,
|
|
96
110
|
format: "json",
|
|
97
111
|
h: String(Math.min(input.max_results ?? 10, 100)),
|
|
98
112
|
});
|
package/src/tools/doaj.ts
CHANGED
|
@@ -29,16 +29,24 @@ export function createDoajTools(
|
|
|
29
29
|
Type.String({ description: "Sort field, e.g. 'created_date:desc'" }),
|
|
30
30
|
),
|
|
31
31
|
}),
|
|
32
|
-
execute: async (input: {
|
|
32
|
+
execute: async (_toolCallId: string, input: {
|
|
33
33
|
query: string;
|
|
34
34
|
max_results?: number;
|
|
35
35
|
page?: number;
|
|
36
36
|
sort?: string;
|
|
37
37
|
}) => {
|
|
38
|
-
const
|
|
39
|
-
|
|
38
|
+
const query = validParam(input?.query);
|
|
39
|
+
if (!query) {
|
|
40
|
+
return toolResult({
|
|
41
|
+
error:
|
|
42
|
+
"query parameter is required and must not be empty. " +
|
|
43
|
+
"Example: search_doaj({ query: \"open access genomics\" })",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const pageSize = Math.min(input?.max_results ?? 10, 100);
|
|
47
|
+
const page = input?.page ?? 1;
|
|
40
48
|
|
|
41
|
-
let url = `${BASE}/search/articles/${encodeURIComponent(
|
|
49
|
+
let url = `${BASE}/search/articles/${encodeURIComponent(query)}?page=${page}&pageSize=${pageSize}`;
|
|
42
50
|
const sort = validParam(input.sort);
|
|
43
51
|
if (sort) url += `&sort=${encodeURIComponent(sort)}`;
|
|
44
52
|
|
package/src/tools/europe-pmc.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
|
|
3
|
-
import { toolResult, trackedFetch, isTrackedError, validParam
|
|
3
|
+
import { toolResult, trackedFetch, isTrackedError, validParam } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const BASE = "https://www.ebi.ac.uk/europepmc/webservices/rest";
|
|
6
6
|
|
|
@@ -31,14 +31,22 @@ export function createEuropePmcTools(
|
|
|
31
31
|
Type.String({ description: "Pagination cursor (use value from previous response)" }),
|
|
32
32
|
),
|
|
33
33
|
}),
|
|
34
|
-
execute: async (input: {
|
|
34
|
+
execute: async (_toolCallId: string, input: {
|
|
35
35
|
query: string;
|
|
36
36
|
max_results?: number;
|
|
37
37
|
sort?: string;
|
|
38
38
|
cursor?: string;
|
|
39
39
|
}) => {
|
|
40
|
+
const query = validParam(input?.query);
|
|
41
|
+
if (!query) {
|
|
42
|
+
return toolResult({
|
|
43
|
+
error:
|
|
44
|
+
"query parameter is required and must not be empty. " +
|
|
45
|
+
"Example: search_europe_pmc({ query: \"CRISPR gene editing\" })",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
40
48
|
const params = new URLSearchParams({
|
|
41
|
-
query
|
|
49
|
+
query,
|
|
42
50
|
format: "json",
|
|
43
51
|
pageSize: String(Math.min(input.max_results ?? 10, 1000)),
|
|
44
52
|
resultType: "core",
|
|
@@ -92,11 +100,14 @@ export function createEuropePmcTools(
|
|
|
92
100
|
),
|
|
93
101
|
page: Type.Optional(Type.Number({ description: "Page number (default 1)" })),
|
|
94
102
|
}),
|
|
95
|
-
execute: async (input: { pmid: string; max_results?: number; page?: number }) => {
|
|
103
|
+
execute: async (_toolCallId: string, input: { pmid: string; max_results?: number; page?: number }) => {
|
|
104
|
+
if (!input?.pmid) {
|
|
105
|
+
return toolResult({ error: 'pmid parameter is required (PubMed ID, e.g., "33116299")' });
|
|
106
|
+
}
|
|
96
107
|
const params = new URLSearchParams({
|
|
97
108
|
format: "json",
|
|
98
|
-
pageSize: String(input
|
|
99
|
-
page: String(input
|
|
109
|
+
pageSize: String(input?.max_results ?? 25),
|
|
110
|
+
page: String(input?.page ?? 1),
|
|
100
111
|
});
|
|
101
112
|
const tracked = await trackedFetch("europe_pmc", `${BASE}/MED/${input.pmid}/citations?${params}`);
|
|
102
113
|
if (isTrackedError(tracked)) return tracked;
|
|
@@ -130,10 +141,13 @@ export function createEuropePmcTools(
|
|
|
130
141
|
Type.Number({ description: "Max references to return (default 25)" }),
|
|
131
142
|
),
|
|
132
143
|
}),
|
|
133
|
-
execute: async (input: { pmid: string; max_results?: number }) => {
|
|
144
|
+
execute: async (_toolCallId: string, input: { pmid: string; max_results?: number }) => {
|
|
145
|
+
if (!input?.pmid) {
|
|
146
|
+
return toolResult({ error: 'pmid parameter is required (PubMed ID, e.g., "33116299")' });
|
|
147
|
+
}
|
|
134
148
|
const params = new URLSearchParams({
|
|
135
149
|
format: "json",
|
|
136
|
-
pageSize: String(input
|
|
150
|
+
pageSize: String(input?.max_results ?? 25),
|
|
137
151
|
});
|
|
138
152
|
const tracked = await trackedFetch("europe_pmc", `${BASE}/MED/${input.pmid}/references?${params}`);
|
|
139
153
|
if (isTrackedError(tracked)) return tracked;
|
package/src/tools/hal.ts
CHANGED
|
@@ -49,13 +49,21 @@ export function createHalTools(
|
|
|
49
49
|
}),
|
|
50
50
|
),
|
|
51
51
|
}),
|
|
52
|
-
execute: async (input: {
|
|
52
|
+
execute: async (_toolCallId: string, input: {
|
|
53
53
|
query: string;
|
|
54
54
|
rows?: number;
|
|
55
55
|
sort?: string;
|
|
56
56
|
doc_type?: string;
|
|
57
57
|
}) => {
|
|
58
|
-
|
|
58
|
+
const query = validParam(input?.query);
|
|
59
|
+
if (!query) {
|
|
60
|
+
return toolResult({
|
|
61
|
+
error:
|
|
62
|
+
"query parameter is required and must not be empty. " +
|
|
63
|
+
"Example: search_hal({ query: \"machine learning\" })",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
let q = query;
|
|
59
67
|
const docType = validParam(input.doc_type);
|
|
60
68
|
if (docType) {
|
|
61
69
|
q = `(${q}) AND docType_s:${docType}`;
|
package/src/tools/inspire-hep.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
|
|
3
|
-
import { toolResult, trackedFetch, isTrackedError, validEnum } from "./util.js";
|
|
3
|
+
import { toolResult, trackedFetch, isTrackedError, validEnum, validParam } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const BASE = "https://inspirehep.net/api";
|
|
6
6
|
|
|
@@ -98,10 +98,17 @@ export function createInspireHepTools(
|
|
|
98
98
|
}),
|
|
99
99
|
),
|
|
100
100
|
}),
|
|
101
|
-
execute: async (input: { query: string; size?: number; sort?: string }) => {
|
|
101
|
+
execute: async (_toolCallId: string, input: { query: string; size?: number; sort?: string }) => {
|
|
102
|
+
const query = validParam(input?.query);
|
|
103
|
+
if (!query) {
|
|
104
|
+
return toolResult({
|
|
105
|
+
error: "query parameter is required and must not be empty.",
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
102
109
|
const sort = validEnum(input.sort, ["mostrecent", "mostcited", "bestmatch"] as const, "bestmatch");
|
|
103
110
|
const params = new URLSearchParams({
|
|
104
|
-
q:
|
|
111
|
+
q: query,
|
|
105
112
|
size: String(Math.min(input.size ?? 10, 100)),
|
|
106
113
|
sort,
|
|
107
114
|
});
|
|
@@ -137,7 +144,10 @@ export function createInspireHepTools(
|
|
|
137
144
|
"Paper identifier: arXiv ID (e.g. '1207.7214') or DOI (e.g. '10.1016/j.physletb.2012.08.020')",
|
|
138
145
|
}),
|
|
139
146
|
}),
|
|
140
|
-
execute: async (input: { identifier: string }) => {
|
|
147
|
+
execute: async (_toolCallId: string, input: { identifier: string }) => {
|
|
148
|
+
if (!input?.identifier) {
|
|
149
|
+
return toolResult({ error: 'identifier parameter is required (arXiv ID e.g. "1207.7214" or DOI e.g. "10.1016/j.physletb.2012.08.020")' });
|
|
150
|
+
}
|
|
141
151
|
// Determine if it's a DOI or arXiv ID
|
|
142
152
|
const id = input.identifier.trim();
|
|
143
153
|
let url: string;
|
package/src/tools/openaire.ts
CHANGED
|
@@ -112,7 +112,7 @@ export function createOpenAireTools(
|
|
|
112
112
|
Type.Number({ description: "Max results (default 10, max 50)" }),
|
|
113
113
|
),
|
|
114
114
|
}),
|
|
115
|
-
execute: async (input: {
|
|
115
|
+
execute: async (_toolCallId: string, input: {
|
|
116
116
|
keywords: string;
|
|
117
117
|
author?: string;
|
|
118
118
|
doi?: string;
|
|
@@ -122,8 +122,16 @@ export function createOpenAireTools(
|
|
|
122
122
|
funder?: string;
|
|
123
123
|
max_results?: number;
|
|
124
124
|
}) => {
|
|
125
|
+
const keywords = validParam(input?.keywords);
|
|
126
|
+
if (!keywords) {
|
|
127
|
+
return toolResult({
|
|
128
|
+
error:
|
|
129
|
+
"keywords parameter is required and must not be empty. " +
|
|
130
|
+
"Example: search_openaire({ keywords: \"machine learning\" })",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
125
133
|
const params = new URLSearchParams({
|
|
126
|
-
keywords
|
|
134
|
+
keywords,
|
|
127
135
|
format: "json",
|
|
128
136
|
size: String(Math.min(input.max_results ?? 10, 50)),
|
|
129
137
|
});
|
package/src/tools/openalex.ts
CHANGED
|
@@ -38,7 +38,7 @@ export function createOpenAlexTools(
|
|
|
38
38
|
}),
|
|
39
39
|
),
|
|
40
40
|
}),
|
|
41
|
-
execute: async (input: {
|
|
41
|
+
execute: async (_toolCallId: string, input: {
|
|
42
42
|
query: string;
|
|
43
43
|
limit?: number;
|
|
44
44
|
from_year?: number;
|
|
@@ -46,7 +46,8 @@ export function createOpenAlexTools(
|
|
|
46
46
|
open_access?: boolean;
|
|
47
47
|
sort_by?: string;
|
|
48
48
|
}) => {
|
|
49
|
-
|
|
49
|
+
const query = validParam(input?.query);
|
|
50
|
+
if (!query) {
|
|
50
51
|
return toolResult({ error: "query parameter is required and must not be empty" });
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -58,7 +59,7 @@ export function createOpenAlexTools(
|
|
|
58
59
|
if (input.open_access) filters.push("is_oa:true");
|
|
59
60
|
|
|
60
61
|
const params = new URLSearchParams({
|
|
61
|
-
search:
|
|
62
|
+
search: query,
|
|
62
63
|
per_page: String(Math.min(input.limit ?? 10, 200)),
|
|
63
64
|
});
|
|
64
65
|
if (filters.length > 0) params.set("filter", filters.join(","));
|
|
@@ -102,7 +103,7 @@ export function createOpenAlexTools(
|
|
|
102
103
|
"Work identifier: OpenAlex ID (e.g. 'W2741809807'), DOI URL, or PMID",
|
|
103
104
|
}),
|
|
104
105
|
}),
|
|
105
|
-
execute: async (input: { work_id: string }) => {
|
|
106
|
+
execute: async (_toolCallId: string, input: { work_id: string }) => {
|
|
106
107
|
if (!input?.work_id) {
|
|
107
108
|
return toolResult({ error: 'work_id parameter is required (e.g., "W2741809807" or a DOI like "10.1234/example")' });
|
|
108
109
|
}
|
|
@@ -146,7 +147,7 @@ export function createOpenAlexTools(
|
|
|
146
147
|
"Author identifier: OpenAlex ID (e.g. 'A5023888391'), ORCID, or name search",
|
|
147
148
|
}),
|
|
148
149
|
}),
|
|
149
|
-
execute: async (input: { author_id: string }) => {
|
|
150
|
+
execute: async (_toolCallId: string, input: { author_id: string }) => {
|
|
150
151
|
if (!input?.author_id) {
|
|
151
152
|
return toolResult({ error: 'author_id parameter is required (OpenAlex ID e.g. "A5023888391", ORCID, or author name)' });
|
|
152
153
|
}
|
|
@@ -28,7 +28,7 @@ export function createOpenCitationsTools(
|
|
|
28
28
|
description: "DOI of the paper, e.g. '10.1038/nature12373'",
|
|
29
29
|
}),
|
|
30
30
|
}),
|
|
31
|
-
execute: async (input: { doi: string }) => {
|
|
31
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
32
32
|
if (!input?.doi) {
|
|
33
33
|
return toolResult({ error: 'doi parameter is required (e.g., "10.1038/nature12373")' });
|
|
34
34
|
}
|
|
@@ -60,7 +60,7 @@ export function createOpenCitationsTools(
|
|
|
60
60
|
description: "DOI of the paper, e.g. '10.1038/nature12373'",
|
|
61
61
|
}),
|
|
62
62
|
}),
|
|
63
|
-
execute: async (input: { doi: string }) => {
|
|
63
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
64
64
|
if (!input?.doi) {
|
|
65
65
|
return toolResult({ error: 'doi parameter is required (e.g., "10.1038/nature12373")' });
|
|
66
66
|
}
|
|
@@ -92,7 +92,7 @@ export function createOpenCitationsTools(
|
|
|
92
92
|
description: "DOI of the paper, e.g. '10.1038/nature12373'",
|
|
93
93
|
}),
|
|
94
94
|
}),
|
|
95
|
-
execute: async (input: { doi: string }) => {
|
|
95
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
96
96
|
if (!input?.doi) {
|
|
97
97
|
return toolResult({ error: 'doi parameter is required (e.g., "10.1038/nature12373")' });
|
|
98
98
|
}
|
package/src/tools/orcid.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
|
|
3
|
-
import { toolResult, trackedFetch, isTrackedError } from "./util.js";
|
|
3
|
+
import { toolResult, trackedFetch, isTrackedError, validParam } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const BASE = "https://pub.orcid.org/v3.0";
|
|
6
6
|
|
|
@@ -27,9 +27,16 @@ export function createOrcidTools(
|
|
|
27
27
|
Type.Number({ description: "Max results (default 10, max 100)" }),
|
|
28
28
|
),
|
|
29
29
|
}),
|
|
30
|
-
execute: async (input: { query: string; limit?: number }) => {
|
|
30
|
+
execute: async (_toolCallId: string, input: { query: string; limit?: number }) => {
|
|
31
|
+
const query = validParam(input?.query);
|
|
32
|
+
if (!query) {
|
|
33
|
+
return toolResult({
|
|
34
|
+
error: "query parameter is required and must not be empty.",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
const params = new URLSearchParams({
|
|
32
|
-
q:
|
|
39
|
+
q: query,
|
|
33
40
|
rows: String(Math.min(input.limit ?? 10, 100)),
|
|
34
41
|
});
|
|
35
42
|
|
|
@@ -71,7 +78,7 @@ export function createOrcidTools(
|
|
|
71
78
|
description: "ORCID iD, e.g. '0000-0002-1825-0097'",
|
|
72
79
|
}),
|
|
73
80
|
}),
|
|
74
|
-
execute: async (input: { orcid: string }) => {
|
|
81
|
+
execute: async (_toolCallId: string, input: { orcid: string }) => {
|
|
75
82
|
if (!input?.orcid) {
|
|
76
83
|
return toolResult({ error: 'orcid parameter is required (e.g., "0000-0002-1825-0097")' });
|
|
77
84
|
}
|
|
@@ -28,17 +28,17 @@ export function createOsfPreprintsTools(
|
|
|
28
28
|
Type.Number({ description: "Page number (default 1)" }),
|
|
29
29
|
),
|
|
30
30
|
}),
|
|
31
|
-
execute: async (input: {
|
|
31
|
+
execute: async (_toolCallId: string, input: {
|
|
32
32
|
provider?: string;
|
|
33
33
|
size?: number;
|
|
34
34
|
page?: number;
|
|
35
35
|
}) => {
|
|
36
36
|
const params = new URLSearchParams({
|
|
37
|
-
"page[size]": String(Math.min(input
|
|
37
|
+
"page[size]": String(Math.min(input?.size ?? 10, 100)),
|
|
38
38
|
});
|
|
39
|
-
const provider = validParam(input
|
|
39
|
+
const provider = validParam(input?.provider);
|
|
40
40
|
if (provider) params.set("filter[provider]", provider);
|
|
41
|
-
if (input
|
|
41
|
+
if (input?.page) params.set("page", String(input.page));
|
|
42
42
|
|
|
43
43
|
const result = await trackedFetch(
|
|
44
44
|
"osf_preprints",
|
|
@@ -58,7 +58,7 @@ export function createOsfPreprintsTools(
|
|
|
58
58
|
|
|
59
59
|
return toolResult({
|
|
60
60
|
total_results: linksMeta?.total,
|
|
61
|
-
page: input
|
|
61
|
+
page: input?.page ?? 1,
|
|
62
62
|
source: "osf_preprints",
|
|
63
63
|
preprints: (items ?? []).map((item) => {
|
|
64
64
|
const attrs = item.attributes as Record<string, unknown> | undefined;
|
package/src/tools/pubmed.ts
CHANGED
|
@@ -34,20 +34,29 @@ export function createPubMedTools(
|
|
|
34
34
|
Type.String({ description: "Max publication date (YYYY/MM/DD)" }),
|
|
35
35
|
),
|
|
36
36
|
}),
|
|
37
|
-
execute: async (input: {
|
|
37
|
+
execute: async (_toolCallId: string, input: {
|
|
38
38
|
query: string;
|
|
39
39
|
max_results?: number;
|
|
40
40
|
sort?: string;
|
|
41
41
|
min_date?: string;
|
|
42
42
|
max_date?: string;
|
|
43
43
|
}) => {
|
|
44
|
+
const query = validParam(input?.query);
|
|
45
|
+
if (!query) {
|
|
46
|
+
return toolResult({
|
|
47
|
+
error:
|
|
48
|
+
"query parameter is required and must not be empty. " +
|
|
49
|
+
"Example: search_pubmed({ query: \"CRISPR[Title] AND 2024[PDAT]\" })",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
const sort = validEnum(input.sort, ["relevance", "pub_date"] as const, "relevance");
|
|
45
54
|
const minDate = validParam(input.min_date);
|
|
46
55
|
const maxDate = validParam(input.max_date);
|
|
47
56
|
|
|
48
57
|
const searchParams = new URLSearchParams({
|
|
49
58
|
db: "pubmed",
|
|
50
|
-
term:
|
|
59
|
+
term: query,
|
|
51
60
|
retmax: String(Math.min(input.max_results ?? 10, 100)),
|
|
52
61
|
retmode: "json",
|
|
53
62
|
sort,
|
|
@@ -112,7 +121,7 @@ export function createPubMedTools(
|
|
|
112
121
|
parameters: Type.Object({
|
|
113
122
|
pmid: Type.String({ description: "PubMed ID (numeric string)" }),
|
|
114
123
|
}),
|
|
115
|
-
execute: async (input: { pmid: string }) => {
|
|
124
|
+
execute: async (_toolCallId: string, input: { pmid: string }) => {
|
|
116
125
|
if (!input?.pmid) {
|
|
117
126
|
return toolResult({ error: 'pmid parameter is required (numeric PubMed ID, e.g., "33116299")' });
|
|
118
127
|
}
|
package/src/tools/ror.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
|
|
3
|
-
import { toolResult, trackedFetch, isTrackedError } from "./util.js";
|
|
3
|
+
import { toolResult, trackedFetch, isTrackedError, validParam } from "./util.js";
|
|
4
4
|
|
|
5
5
|
const BASE = "https://api.ror.org/v2";
|
|
6
6
|
|
|
@@ -22,9 +22,16 @@ export function createRorTools(
|
|
|
22
22
|
Type.Number({ description: "Max results (default 10, max 50)" }),
|
|
23
23
|
),
|
|
24
24
|
}),
|
|
25
|
-
execute: async (input: { query: string; max_results?: number }) => {
|
|
25
|
+
execute: async (_toolCallId: string, input: { query: string; max_results?: number }) => {
|
|
26
|
+
const query = validParam(input?.query);
|
|
27
|
+
if (!query) {
|
|
28
|
+
return toolResult({
|
|
29
|
+
error: "query parameter is required and must not be empty.",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
26
33
|
const params = new URLSearchParams({
|
|
27
|
-
query
|
|
34
|
+
query,
|
|
28
35
|
});
|
|
29
36
|
|
|
30
37
|
const tracked = await trackedFetch("ror", `${BASE}/organizations?${params}`, undefined, 10_000);
|
package/src/tools/unpaywall.ts
CHANGED
|
@@ -21,7 +21,7 @@ export function createUnpaywallTools(
|
|
|
21
21
|
description: "DOI of the paper, e.g. '10.1038/nature12373'",
|
|
22
22
|
}),
|
|
23
23
|
}),
|
|
24
|
-
execute: async (input: { doi: string }) => {
|
|
24
|
+
execute: async (_toolCallId: string, input: { doi: string }) => {
|
|
25
25
|
if (!input?.doi) {
|
|
26
26
|
return toolResult({ error: 'doi parameter is required (e.g., "10.1038/nature12373")' });
|
|
27
27
|
}
|
package/src/tools/zenodo.ts
CHANGED
|
@@ -37,15 +37,23 @@ export function createZenodoTools(
|
|
|
37
37
|
}),
|
|
38
38
|
),
|
|
39
39
|
}),
|
|
40
|
-
execute: async (input: {
|
|
40
|
+
execute: async (_toolCallId: string, input: {
|
|
41
41
|
query: string;
|
|
42
42
|
type?: string;
|
|
43
43
|
size?: number;
|
|
44
44
|
sort?: string;
|
|
45
45
|
access_right?: string;
|
|
46
46
|
}) => {
|
|
47
|
+
const query = validParam(input?.query);
|
|
48
|
+
if (!query) {
|
|
49
|
+
return toolResult({
|
|
50
|
+
error:
|
|
51
|
+
"query parameter is required and must not be empty. " +
|
|
52
|
+
"Example: search_zenodo({ query: \"climate dataset\" })",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
47
55
|
const params = new URLSearchParams({
|
|
48
|
-
q:
|
|
56
|
+
q: query,
|
|
49
57
|
size: String(Math.min(input.size ?? 10, 100)),
|
|
50
58
|
});
|
|
51
59
|
const type = validParam(input.type);
|
|
@@ -105,7 +113,7 @@ export function createZenodoTools(
|
|
|
105
113
|
description: "Zenodo record ID (numeric), e.g. '7042164'",
|
|
106
114
|
}),
|
|
107
115
|
}),
|
|
108
|
-
execute: async (input: { record_id: string }) => {
|
|
116
|
+
execute: async (_toolCallId: string, input: { record_id: string }) => {
|
|
109
117
|
if (!input?.record_id) {
|
|
110
118
|
return toolResult({ error: 'record_id parameter is required (e.g., "1234567")' });
|
|
111
119
|
}
|