@cyanheads/openalex-mcp-server 0.2.7
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/CLAUDE.md +282 -0
- package/Dockerfile +99 -0
- package/LICENSE +190 -0
- package/README.md +188 -0
- package/dist/config/server-config.d.ts +13 -0
- package/dist/config/server-config.d.ts.map +1 -0
- package/dist/config/server-config.js +18 -0
- package/dist/config/server-config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server/prompts/definitions/literature-review.prompt.d.ts +13 -0
- package/dist/mcp-server/prompts/definitions/literature-review.prompt.d.ts.map +1 -0
- package/dist/mcp-server/prompts/definitions/literature-review.prompt.js +53 -0
- package/dist/mcp-server/prompts/definitions/literature-review.prompt.js.map +1 -0
- package/dist/mcp-server/prompts/definitions/research-landscape.prompt.d.ts +9 -0
- package/dist/mcp-server/prompts/definitions/research-landscape.prompt.d.ts.map +1 -0
- package/dist/mcp-server/prompts/definitions/research-landscape.prompt.js +42 -0
- package/dist/mcp-server/prompts/definitions/research-landscape.prompt.js.map +1 -0
- package/dist/mcp-server/tools/definitions/analyze-trends.tool.d.ts +33 -0
- package/dist/mcp-server/tools/definitions/analyze-trends.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/analyze-trends.tool.js +82 -0
- package/dist/mcp-server/tools/definitions/analyze-trends.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/resolve-name.tool.d.ts +30 -0
- package/dist/mcp-server/tools/definitions/resolve-name.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/resolve-name.tool.js +73 -0
- package/dist/mcp-server/tools/definitions/resolve-name.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts +40 -0
- package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-entities.tool.js +113 -0
- package/dist/mcp-server/tools/definitions/search-entities.tool.js.map +1 -0
- package/dist/services/openalex/openalex-service.d.ts +23 -0
- package/dist/services/openalex/openalex-service.d.ts.map +1 -0
- package/dist/services/openalex/openalex-service.js +254 -0
- package/dist/services/openalex/openalex-service.js.map +1 -0
- package/dist/services/openalex/types.d.ts +68 -0
- package/dist/services/openalex/types.d.ts.map +1 -0
- package/dist/services/openalex/types.js +15 -0
- package/dist/services/openalex/types.js.map +1 -0
- package/package.json +73 -0
- package/server.json +125 -0
package/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>@cyanheads/openalex-mcp-server</h1>
|
|
3
|
+
<p><b>Access the OpenAlex academic research catalog - 270M+ publications through MCP. STDIO & Streamable HTTP.</b></p>
|
|
4
|
+
<p><b>3 Tools · 2 Prompts</b></p>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<div align="center">
|
|
8
|
+
|
|
9
|
+
[](./CHANGELOG.md) [](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) [](https://modelcontextprotocol.io/) [](./LICENSE) [](https://www.typescriptlang.org/)
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Tools
|
|
16
|
+
|
|
17
|
+
Three tools for querying the [OpenAlex](https://openalex.org) academic research catalog:
|
|
18
|
+
|
|
19
|
+
| Tool Name | Description |
|
|
20
|
+
|:----------|:------------|
|
|
21
|
+
| `openalex_search_entities` | Search, filter, sort, or retrieve by ID across all 8 entity types. |
|
|
22
|
+
| `openalex_analyze_trends` | Group-by aggregation for trend and distribution analysis. |
|
|
23
|
+
| `openalex_resolve_name` | Resolve a name or partial name to an OpenAlex ID via autocomplete. |
|
|
24
|
+
|
|
25
|
+
### `openalex_search_entities`
|
|
26
|
+
|
|
27
|
+
Primary discovery and lookup tool. Covers all OpenAlex entity types (works, authors, sources, institutions, topics, keywords, publishers, funders).
|
|
28
|
+
|
|
29
|
+
- Retrieve a single entity by ID (OpenAlex ID, DOI, ORCID, ROR, PMID, PMCID, ISSN)
|
|
30
|
+
- Keyword search with boolean operators, quoted phrases, wildcards, and fuzzy matching
|
|
31
|
+
- Exact and AI semantic search modes
|
|
32
|
+
- Rich filter syntax: AND across fields, OR within fields (`us|gb`), NOT (`!us`), ranges (`2020-2024`), comparisons (`>100`)
|
|
33
|
+
- Field selection via `select` to control payload size (full work records are ~70KB each)
|
|
34
|
+
- Cursor pagination, sorting, up to 100 results per page
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### `openalex_analyze_trends`
|
|
39
|
+
|
|
40
|
+
Aggregate entities into groups and count them for trend, distribution, and comparative analysis.
|
|
41
|
+
|
|
42
|
+
- Group by any supported field (publication year, OA status, institution, country, topic, etc.)
|
|
43
|
+
- Combine with filters to scope the population before aggregation
|
|
44
|
+
- Up to 200 groups per page with cursor pagination
|
|
45
|
+
- Supports `include_unknown` to show entities with no value for the grouped field
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### `openalex_resolve_name`
|
|
50
|
+
|
|
51
|
+
Name-to-ID resolution via autocomplete. **Always use this before filtering by entity** — names are ambiguous, IDs are not.
|
|
52
|
+
|
|
53
|
+
- Returns up to 10 matches with disambiguation hints
|
|
54
|
+
- Accepts partial names and DOIs for direct lookup
|
|
55
|
+
- Optional entity type filter and field-level filters
|
|
56
|
+
- ~200ms response time
|
|
57
|
+
|
|
58
|
+
## Prompts
|
|
59
|
+
|
|
60
|
+
| Prompt | Description |
|
|
61
|
+
|:-------|:------------|
|
|
62
|
+
| `openalex_literature_review` | Guides a systematic literature search: formulate query, search, filter, analyze citation network, synthesize findings. |
|
|
63
|
+
| `openalex_research_landscape` | Analyzes the research landscape for a topic: volume trends, top authors/institutions, open access rates, funding sources. |
|
|
64
|
+
|
|
65
|
+
## Features
|
|
66
|
+
|
|
67
|
+
Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core):
|
|
68
|
+
|
|
69
|
+
- Declarative tool definitions — single file per tool, framework handles registration and validation
|
|
70
|
+
- Unified error handling across all tools
|
|
71
|
+
- Pluggable auth (`none`, `jwt`, `oauth`)
|
|
72
|
+
- Swappable storage backends via the framework (not currently used by this server)
|
|
73
|
+
- Structured logging with optional OpenTelemetry tracing
|
|
74
|
+
- Runs locally (stdio/HTTP) or in Docker from the same codebase
|
|
75
|
+
|
|
76
|
+
OpenAlex-specific:
|
|
77
|
+
|
|
78
|
+
- Typed API client with automatic ID normalization (DOI, ORCID, ROR, PMID, PMCID, ISSN, OpenAlex URLs)
|
|
79
|
+
- Abstract reconstruction from inverted indices
|
|
80
|
+
- Cancellation support via `AbortSignal`
|
|
81
|
+
|
|
82
|
+
## Getting Started
|
|
83
|
+
|
|
84
|
+
Add to your MCP client config (e.g., `claude_desktop_config.json`):
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"mcpServers": {
|
|
89
|
+
"openalex": {
|
|
90
|
+
"type": "stdio",
|
|
91
|
+
"command": "npx",
|
|
92
|
+
"args": ["-y", "@cyanheads/openalex-mcp-server"],
|
|
93
|
+
"env": {
|
|
94
|
+
"OPENALEX_API_KEY": "your-email@example.com"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
No API key needed — just provide your email to access the [polite pool](https://docs.openalex.org/how-to-use-the-api/rate-limits-and-authentication#the-polite-pool) (10x faster rate limits).
|
|
102
|
+
|
|
103
|
+
### Prerequisites
|
|
104
|
+
|
|
105
|
+
- [Bun v1.3.0](https://bun.sh/) or higher (for development)
|
|
106
|
+
|
|
107
|
+
### Installation
|
|
108
|
+
|
|
109
|
+
1. **Clone the repository:**
|
|
110
|
+
```sh
|
|
111
|
+
git clone https://github.com/cyanheads/openalex-mcp-server.git
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
2. **Navigate into the directory:**
|
|
115
|
+
```sh
|
|
116
|
+
cd openalex-mcp-server
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
3. **Install dependencies:**
|
|
120
|
+
```sh
|
|
121
|
+
bun install
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Configuration
|
|
125
|
+
|
|
126
|
+
| Variable | Description | Default |
|
|
127
|
+
|:---------|:------------|:--------|
|
|
128
|
+
| `OPENALEX_API_KEY` | **Required.** Email address for the OpenAlex [polite pool](https://docs.openalex.org/how-to-use-the-api/rate-limits-and-authentication#the-polite-pool) (faster rate limits). | — |
|
|
129
|
+
| `OPENALEX_BASE_URL` | OpenAlex API base URL. | `https://api.openalex.org` |
|
|
130
|
+
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
131
|
+
| `MCP_HTTP_PORT` | Port for HTTP server. | `3010` |
|
|
132
|
+
| `MCP_AUTH_MODE` | Auth mode: `none`, `jwt`, or `oauth`. | `none` |
|
|
133
|
+
| `MCP_LOG_LEVEL` | Log level (RFC 5424). | `debug` |
|
|
134
|
+
| `OTEL_ENABLED` | Enable OpenTelemetry. | `false` |
|
|
135
|
+
|
|
136
|
+
## Running the Server
|
|
137
|
+
|
|
138
|
+
### Local Development
|
|
139
|
+
|
|
140
|
+
- **Build and run the production version:**
|
|
141
|
+
```sh
|
|
142
|
+
bun run build
|
|
143
|
+
bun run start:http # or start:stdio
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- **Run checks and tests:**
|
|
147
|
+
```sh
|
|
148
|
+
bun run devcheck # Lints, formats, type-checks
|
|
149
|
+
bun run test # Runs test suite
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Docker
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
docker build -t openalex-mcp-server .
|
|
156
|
+
docker run -e OPENALEX_API_KEY=your-key -p 3010:3010 openalex-mcp-server
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Project Structure
|
|
160
|
+
|
|
161
|
+
| Directory | Purpose |
|
|
162
|
+
|:----------|:--------|
|
|
163
|
+
| `src/mcp-server/tools/definitions/` | Tool definitions (`*.tool.ts`). |
|
|
164
|
+
| `src/mcp-server/prompts/definitions/` | Prompt definitions (`*.prompt.ts`). |
|
|
165
|
+
| `src/services/openalex/` | OpenAlex API client service and domain types. |
|
|
166
|
+
| `src/config/` | Environment variable parsing and validation with Zod. |
|
|
167
|
+
| `tests/` | Unit and integration tests, mirroring the `src/` structure. |
|
|
168
|
+
|
|
169
|
+
## Development Guide
|
|
170
|
+
|
|
171
|
+
See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
|
|
172
|
+
|
|
173
|
+
- Handlers throw, framework catches — no `try/catch` in tool logic
|
|
174
|
+
- Use `ctx.log` for logging, `ctx.state` for storage
|
|
175
|
+
- Always resolve names to IDs via `openalex_resolve_name` before using them in filters
|
|
176
|
+
|
|
177
|
+
## Contributing
|
|
178
|
+
|
|
179
|
+
Issues and pull requests are welcome. Run checks before submitting:
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
bun run devcheck
|
|
183
|
+
bun run test
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
Apache-2.0 — see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server-specific configuration for OpenAlex API access.
|
|
3
|
+
* @module config/server-config
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
declare const ServerConfigSchema: z.ZodObject<{
|
|
7
|
+
apiKey: z.ZodString;
|
|
8
|
+
baseUrl: z.ZodDefault<z.ZodString>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export type ServerConfig = z.infer<typeof ServerConfigSchema>;
|
|
11
|
+
export declare function getServerConfig(): ServerConfig;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=server-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAE3C,QAAA,MAAM,kBAAkB;;;iBAGtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAM9C"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server-specific configuration for OpenAlex API access.
|
|
3
|
+
* @module config/server-config
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
const ServerConfigSchema = z.object({
|
|
7
|
+
apiKey: z.string().min(1).describe('OpenAlex API key'),
|
|
8
|
+
baseUrl: z.string().url().default('https://api.openalex.org').describe('OpenAlex API base URL'),
|
|
9
|
+
});
|
|
10
|
+
let _config;
|
|
11
|
+
export function getServerConfig() {
|
|
12
|
+
_config ??= ServerConfigSchema.parse({
|
|
13
|
+
apiKey: process.env.OPENALEX_API_KEY,
|
|
14
|
+
baseUrl: process.env.OPENALEX_BASE_URL,
|
|
15
|
+
});
|
|
16
|
+
return _config;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=server-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAE3C,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IACtD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC,QAAQ,CAAC,uBAAuB,CAAC;CAChG,CAAC,CAAC;AAIH,IAAI,OAAiC,CAAC;AAEtC,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,kBAAkB,CAAC,KAAK,CAAC;QACnC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB;QACpC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;KACvC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview openalex-mcp-server MCP server entry point.
|
|
4
|
+
* @module index
|
|
5
|
+
*/
|
|
6
|
+
import { createApp } from '@cyanheads/mcp-ts-core';
|
|
7
|
+
import { literatureReviewPrompt } from './mcp-server/prompts/definitions/literature-review.prompt.js';
|
|
8
|
+
import { researchLandscapePrompt } from './mcp-server/prompts/definitions/research-landscape.prompt.js';
|
|
9
|
+
import { analyzeTrendsTool } from './mcp-server/tools/definitions/analyze-trends.tool.js';
|
|
10
|
+
import { resolveNameTool } from './mcp-server/tools/definitions/resolve-name.tool.js';
|
|
11
|
+
import { searchEntitiesTool } from './mcp-server/tools/definitions/search-entities.tool.js';
|
|
12
|
+
import { initOpenAlexService } from './services/openalex/openalex-service.js';
|
|
13
|
+
await createApp({
|
|
14
|
+
tools: [resolveNameTool, searchEntitiesTool, analyzeTrendsTool],
|
|
15
|
+
prompts: [literatureReviewPrompt, researchLandscapePrompt],
|
|
16
|
+
setup() {
|
|
17
|
+
initOpenAlexService();
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,8DAA8D,CAAC;AACtG,OAAO,EAAE,uBAAuB,EAAE,MAAM,+DAA+D,CAAC;AACxG,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,qDAAqD,CAAC;AACtF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AAC5F,OAAO,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAE9E,MAAM,SAAS,CAAC;IACd,KAAK,EAAE,CAAC,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,CAAC;IAC/D,OAAO,EAAE,CAAC,sBAAsB,EAAE,uBAAuB,CAAC;IAC1D,KAAK;QACH,mBAAmB,EAAE,CAAC;IACxB,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Prompt template for guided systematic literature review using OpenAlex tools.
|
|
3
|
+
* @module mcp-server/prompts/definitions/literature-review.prompt
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
export declare const literatureReviewPrompt: import("@cyanheads/mcp-ts-core").PromptDefinition<z.ZodObject<{
|
|
7
|
+
topic: z.ZodString;
|
|
8
|
+
scope: z.ZodDefault<z.ZodEnum<{
|
|
9
|
+
narrow: "narrow";
|
|
10
|
+
broad: "broad";
|
|
11
|
+
}>>;
|
|
12
|
+
}, z.core.$strip>>;
|
|
13
|
+
//# sourceMappingURL=literature-review.prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"literature-review.prompt.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/literature-review.prompt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,eAAO,MAAM,sBAAsB;;;;;;kBAiDjC,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Prompt template for guided systematic literature review using OpenAlex tools.
|
|
3
|
+
* @module mcp-server/prompts/definitions/literature-review.prompt
|
|
4
|
+
*/
|
|
5
|
+
import { prompt, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
export const literatureReviewPrompt = prompt('openalex_literature_review', {
|
|
7
|
+
description: 'Guides a systematic literature search: formulate query, search, filter, analyze citation network, synthesize findings.',
|
|
8
|
+
args: z.object({
|
|
9
|
+
topic: z.string().describe('Research topic or question to review.'),
|
|
10
|
+
scope: z
|
|
11
|
+
.enum(['narrow', 'broad'])
|
|
12
|
+
.default('narrow')
|
|
13
|
+
.describe('"narrow": focused on specific question. "broad": survey of the field.'),
|
|
14
|
+
}),
|
|
15
|
+
generate: (args) => [
|
|
16
|
+
{
|
|
17
|
+
role: 'user',
|
|
18
|
+
content: {
|
|
19
|
+
type: 'text',
|
|
20
|
+
text: `Conduct a systematic literature review on: "${args.topic}"
|
|
21
|
+
|
|
22
|
+
Scope: ${args.scope}
|
|
23
|
+
|
|
24
|
+
Follow this workflow using the OpenAlex tools:
|
|
25
|
+
|
|
26
|
+
1. **Resolve entities** — Use openalex_resolve_name to identify key authors, institutions, or topics related to "${args.topic}". Collect their OpenAlex IDs.
|
|
27
|
+
|
|
28
|
+
2. **Search literature** — Use openalex_search_entities to find relevant works.${args.scope === 'narrow'
|
|
29
|
+
? `
|
|
30
|
+
- Use exact search mode with specific phrases
|
|
31
|
+
- Filter tightly by resolved topic IDs
|
|
32
|
+
- Focus on a single topic cluster`
|
|
33
|
+
: `
|
|
34
|
+
- Try keyword search for the topic
|
|
35
|
+
- Use semantic search for conceptually related work across subfields
|
|
36
|
+
- Search with multiple related topic IDs to capture adjacent areas`}
|
|
37
|
+
Use select to keep payloads manageable.
|
|
38
|
+
|
|
39
|
+
3. **Identify key papers** — Sort by cited_by_count to find landmark works. Use openalex_search_entities with id to get full details on the most important ones.
|
|
40
|
+
|
|
41
|
+
4. **Trace citations** — For key papers, search with the cited_by filter to find subsequent work, and the cites filter to find foundational work.
|
|
42
|
+
|
|
43
|
+
5. **Analyze the landscape** — Use openalex_analyze_trends to understand:
|
|
44
|
+
- Publication volume over time (group_by: publication_year)
|
|
45
|
+
- Top contributing institutions (group_by: authorships.institutions.id)
|
|
46
|
+
- Open access availability (group_by: oa_status)
|
|
47
|
+
|
|
48
|
+
6. **Synthesize** — Summarize findings: key themes, seminal papers, active research fronts, gaps in the literature, and methodological trends.`,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=literature-review.prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"literature-review.prompt.js","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/literature-review.prompt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,4BAA4B,EAAE;IACzE,WAAW,EACT,wHAAwH;IAC1H,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;QACnE,KAAK,EAAE,CAAC;aACL,IAAI,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;aACzB,OAAO,CAAC,QAAQ,CAAC;aACjB,QAAQ,CAAC,uEAAuE,CAAC;KACrF,CAAC;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QAClB;YACE,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE;gBACP,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,+CAA+C,IAAI,CAAC,KAAK;;SAE9D,IAAI,CAAC,KAAK;;;;mHAIgG,IAAI,CAAC,KAAK;;iFAGnH,IAAI,CAAC,KAAK,KAAK,QAAQ;oBACrB,CAAC,CAAC;;;qCAGuB;oBACzB,CAAC,CAAC;;;sEAIN;;;;;;;;;;;;+IAYuI;aACxI;SACF;KACF;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Prompt template for quantitative research landscape analysis using OpenAlex tools.
|
|
3
|
+
* @module mcp-server/prompts/definitions/research-landscape.prompt
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
export declare const researchLandscapePrompt: import("@cyanheads/mcp-ts-core").PromptDefinition<z.ZodObject<{
|
|
7
|
+
topic: z.ZodString;
|
|
8
|
+
}, z.core.$strip>>;
|
|
9
|
+
//# sourceMappingURL=research-landscape.prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"research-landscape.prompt.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/research-landscape.prompt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,eAAO,MAAM,uBAAuB;;kBAoClC,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Prompt template for quantitative research landscape analysis using OpenAlex tools.
|
|
3
|
+
* @module mcp-server/prompts/definitions/research-landscape.prompt
|
|
4
|
+
*/
|
|
5
|
+
import { prompt, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
export const researchLandscapePrompt = prompt('openalex_research_landscape', {
|
|
7
|
+
description: 'Analyzes the research landscape for a topic: volume trends, top authors/institutions, open access rates, funding sources.',
|
|
8
|
+
args: z.object({
|
|
9
|
+
topic: z.string().describe('Research area to analyze.'),
|
|
10
|
+
}),
|
|
11
|
+
generate: (args) => [
|
|
12
|
+
{
|
|
13
|
+
role: 'user',
|
|
14
|
+
content: {
|
|
15
|
+
type: 'text',
|
|
16
|
+
text: `Analyze the research landscape for: "${args.topic}"
|
|
17
|
+
|
|
18
|
+
Use the OpenAlex tools to build a quantitative profile:
|
|
19
|
+
|
|
20
|
+
1. **Resolve** — Use openalex_resolve_name to find the OpenAlex topic ID for "${args.topic}".
|
|
21
|
+
|
|
22
|
+
2. **Volume & trends** — Use openalex_analyze_trends to group works by publication_year, filtered to the resolved topic. Is the field growing, stable, or declining?
|
|
23
|
+
|
|
24
|
+
3. **Top contributors** — Analyze by:
|
|
25
|
+
- authorships.institutions.id (which institutions lead?)
|
|
26
|
+
- authorships.institutions.country_code (geographic distribution)
|
|
27
|
+
- primary_location.source.id (which journals publish most?)
|
|
28
|
+
|
|
29
|
+
4. **Open access** — Group by oa_status. What fraction is freely available?
|
|
30
|
+
|
|
31
|
+
5. **Funding** — Group by awards.funder_id to identify major funders. The group keys are OpenAlex IDs — resolve the top funder IDs with openalex_resolve_name to get human-readable names.
|
|
32
|
+
|
|
33
|
+
6. **Impact** — Search for the most-cited works (sort by -cited_by_count). Get details on the top 5.
|
|
34
|
+
|
|
35
|
+
7. **Emerging fronts** — Filter to the last 2 years, sort by -cited_by_count to find rising papers. Compare topics to the broader field.
|
|
36
|
+
|
|
37
|
+
Present findings as a structured report with data tables and key takeaways.`,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=research-landscape.prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"research-landscape.prompt.js","sourceRoot":"","sources":["../../../../src/mcp-server/prompts/definitions/research-landscape.prompt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC,6BAA6B,EAAE;IAC3E,WAAW,EACT,2HAA2H;IAC7H,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;KACxD,CAAC;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QAClB;YACE,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE;gBACP,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,wCAAwC,IAAI,CAAC,KAAK;;;;gFAIgB,IAAI,CAAC,KAAK;;;;;;;;;;;;;;;;;4EAiBd;aACrE;SACF;KACF;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Aggregation tool for trend and distribution analysis via OpenAlex group_by.
|
|
3
|
+
* @module mcp-server/tools/definitions/analyze-trends.tool
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
export declare const analyzeTrendsTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
7
|
+
entity_type: z.ZodEnum<{
|
|
8
|
+
works: "works";
|
|
9
|
+
authors: "authors";
|
|
10
|
+
sources: "sources";
|
|
11
|
+
institutions: "institutions";
|
|
12
|
+
topics: "topics";
|
|
13
|
+
keywords: "keywords";
|
|
14
|
+
publishers: "publishers";
|
|
15
|
+
funders: "funders";
|
|
16
|
+
}>;
|
|
17
|
+
group_by: z.ZodString;
|
|
18
|
+
filters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
19
|
+
include_unknown: z.ZodDefault<z.ZodBoolean>;
|
|
20
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
21
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
22
|
+
meta: z.ZodObject<{
|
|
23
|
+
count: z.ZodNumber;
|
|
24
|
+
groups_count: z.ZodNullable<z.ZodNumber>;
|
|
25
|
+
next_cursor: z.ZodNullable<z.ZodString>;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
groups: z.ZodArray<z.ZodObject<{
|
|
28
|
+
key: z.ZodString;
|
|
29
|
+
key_display_name: z.ZodString;
|
|
30
|
+
count: z.ZodNumber;
|
|
31
|
+
}, z.core.$strip>>;
|
|
32
|
+
}, z.core.$strip>>;
|
|
33
|
+
//# sourceMappingURL=analyze-trends.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-trends.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/analyze-trends.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAIjD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;kBA4F5B,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Aggregation tool for trend and distribution analysis via OpenAlex group_by.
|
|
3
|
+
* @module mcp-server/tools/definitions/analyze-trends.tool
|
|
4
|
+
*/
|
|
5
|
+
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { getOpenAlexService } from '../../../services/openalex/openalex-service.js';
|
|
7
|
+
import { ENTITY_TYPES } from '../../../services/openalex/types.js';
|
|
8
|
+
export const analyzeTrendsTool = tool('openalex_analyze_trends', {
|
|
9
|
+
description: 'Aggregate OpenAlex entities into groups and count them. Use for trend analysis (group works by publication_year), distribution analysis (group by oa_status, type, country), and comparative analysis (group by institution or topic). Combine with filters to scope the analysis. Returns up to 200 groups per page — use cursor pagination for fields with many distinct values.',
|
|
10
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
11
|
+
input: z.object({
|
|
12
|
+
entity_type: z.enum(ENTITY_TYPES).describe('Entity type to aggregate.'),
|
|
13
|
+
group_by: z
|
|
14
|
+
.string()
|
|
15
|
+
.describe('Field to group by. Works examples: "publication_year", "type", "oa_status", "primary_topic.field.id", "authorships.institutions.country_code", "is_retracted". Authors: "last_known_institutions.country_code", "has_orcid". Sources: "type", "is_oa", "country_code". Not all fields support group_by — check entity docs if unsure.'),
|
|
16
|
+
filters: z
|
|
17
|
+
.record(z.string(), z.string())
|
|
18
|
+
.optional()
|
|
19
|
+
.describe('Filter criteria (same syntax as openalex_search_entities filters). Narrows the population before aggregation. Example: group works by year, filtered to a specific topic.'),
|
|
20
|
+
include_unknown: z
|
|
21
|
+
.boolean()
|
|
22
|
+
.default(false)
|
|
23
|
+
.describe('Include a group for entities with no value for the grouped field. Hidden by default.'),
|
|
24
|
+
cursor: z
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('Pagination cursor from a previous response. Group-by returns max 200 groups per page. Pass cursor to get the next page. Note: paginated groups are sorted by key, not by count.'),
|
|
28
|
+
}),
|
|
29
|
+
output: z.object({
|
|
30
|
+
meta: z
|
|
31
|
+
.object({
|
|
32
|
+
count: z.number().describe('Total entities matching the filters (before grouping).'),
|
|
33
|
+
groups_count: z.number().nullable().describe('Number of groups on this page (max 200).'),
|
|
34
|
+
next_cursor: z
|
|
35
|
+
.string()
|
|
36
|
+
.nullable()
|
|
37
|
+
.describe('Cursor for next page of groups. null if no more groups.'),
|
|
38
|
+
})
|
|
39
|
+
.describe('Aggregation metadata.'),
|
|
40
|
+
groups: z
|
|
41
|
+
.array(z.object({
|
|
42
|
+
key: z.string().describe('Group key (OpenAlex ID or raw value).'),
|
|
43
|
+
key_display_name: z.string().describe('Human-readable group label.'),
|
|
44
|
+
count: z.number().describe('Number of entities in this group.'),
|
|
45
|
+
}))
|
|
46
|
+
.describe('Aggregation groups with counts.'),
|
|
47
|
+
}),
|
|
48
|
+
async handler(input, ctx) {
|
|
49
|
+
const service = getOpenAlexService();
|
|
50
|
+
const result = await service.analyze({
|
|
51
|
+
entityType: input.entity_type,
|
|
52
|
+
groupBy: input.group_by,
|
|
53
|
+
filters: input.filters,
|
|
54
|
+
includeUnknown: input.include_unknown,
|
|
55
|
+
cursor: input.cursor,
|
|
56
|
+
}, ctx);
|
|
57
|
+
ctx.log.info('Trend analysis completed', {
|
|
58
|
+
entityType: input.entity_type,
|
|
59
|
+
groupBy: input.group_by,
|
|
60
|
+
totalCount: result.meta.count,
|
|
61
|
+
groupCount: result.groups.length,
|
|
62
|
+
});
|
|
63
|
+
return result;
|
|
64
|
+
},
|
|
65
|
+
format: (result) => {
|
|
66
|
+
if (result.groups.length === 0) {
|
|
67
|
+
return [{ type: 'text', text: 'No groups found.' }];
|
|
68
|
+
}
|
|
69
|
+
const sorted = [...result.groups].sort((a, b) => b.count - a.count);
|
|
70
|
+
const MAX_DISPLAY = 50;
|
|
71
|
+
const display = sorted.slice(0, MAX_DISPLAY);
|
|
72
|
+
const lines = display.map((g) => `${g.key_display_name}: ${g.count.toLocaleString()}`);
|
|
73
|
+
const truncated = sorted.length > MAX_DISPLAY ? `\n\n...and ${sorted.length - MAX_DISPLAY} more groups` : '';
|
|
74
|
+
return [
|
|
75
|
+
{
|
|
76
|
+
type: 'text',
|
|
77
|
+
text: `${result.meta.count.toLocaleString()} total entities across ${result.groups.length} groups:\n\n${lines.join('\n')}${truncated}`,
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=analyze-trends.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-trends.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/analyze-trends.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tool for resolving names to OpenAlex IDs via autocomplete.
|
|
3
|
+
* @module mcp-server/tools/definitions/resolve-name.tool
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
export declare const resolveNameTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
7
|
+
entity_type: z.ZodOptional<z.ZodEnum<{
|
|
8
|
+
works: "works";
|
|
9
|
+
authors: "authors";
|
|
10
|
+
sources: "sources";
|
|
11
|
+
institutions: "institutions";
|
|
12
|
+
topics: "topics";
|
|
13
|
+
keywords: "keywords";
|
|
14
|
+
publishers: "publishers";
|
|
15
|
+
funders: "funders";
|
|
16
|
+
}>>;
|
|
17
|
+
query: z.ZodString;
|
|
18
|
+
filters: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
19
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
20
|
+
results: z.ZodArray<z.ZodObject<{
|
|
21
|
+
id: z.ZodString;
|
|
22
|
+
external_id: z.ZodNullable<z.ZodString>;
|
|
23
|
+
display_name: z.ZodString;
|
|
24
|
+
entity_type: z.ZodString;
|
|
25
|
+
cited_by_count: z.ZodNumber;
|
|
26
|
+
works_count: z.ZodNullable<z.ZodNumber>;
|
|
27
|
+
hint: z.ZodNullable<z.ZodString>;
|
|
28
|
+
}, z.core.$strip>>;
|
|
29
|
+
}, z.core.$strip>>;
|
|
30
|
+
//# sourceMappingURL=resolve-name.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-name.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/resolve-name.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAIjD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;kBAkF1B,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tool for resolving names to OpenAlex IDs via autocomplete.
|
|
3
|
+
* @module mcp-server/tools/definitions/resolve-name.tool
|
|
4
|
+
*/
|
|
5
|
+
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { getOpenAlexService } from '../../../services/openalex/openalex-service.js';
|
|
7
|
+
import { ENTITY_TYPES } from '../../../services/openalex/types.js';
|
|
8
|
+
export const resolveNameTool = tool('openalex_resolve_name', {
|
|
9
|
+
description: 'Resolve a name or partial name to an OpenAlex ID. Returns up to 10 matches with disambiguation hints. ALWAYS use this before filtering by entity — names are ambiguous, IDs are not. Also accepts DOIs directly for quick lookup. Response time ~200ms.',
|
|
10
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
11
|
+
input: z.object({
|
|
12
|
+
entity_type: z
|
|
13
|
+
.enum(ENTITY_TYPES)
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Entity type to search. Omit for cross-entity search (useful when entity type is unknown).'),
|
|
16
|
+
query: z
|
|
17
|
+
.string()
|
|
18
|
+
.min(1)
|
|
19
|
+
.describe('Name or partial name to resolve. Also accepts DOIs for quick lookup.'),
|
|
20
|
+
filters: z
|
|
21
|
+
.record(z.string(), z.string())
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Narrow autocomplete results with filters. Example: restrict to a specific country or publication year range.'),
|
|
24
|
+
}),
|
|
25
|
+
output: z.object({
|
|
26
|
+
results: z
|
|
27
|
+
.array(z.object({
|
|
28
|
+
id: z.string().describe('OpenAlex ID.'),
|
|
29
|
+
external_id: z
|
|
30
|
+
.string()
|
|
31
|
+
.nullable()
|
|
32
|
+
.describe('Canonical external ID (DOI, ORCID, ROR, ISSN).'),
|
|
33
|
+
display_name: z.string().describe('Human-readable name.'),
|
|
34
|
+
entity_type: z
|
|
35
|
+
.string()
|
|
36
|
+
.describe('Entity type (work, author, source, institution, etc.).'),
|
|
37
|
+
cited_by_count: z
|
|
38
|
+
.number()
|
|
39
|
+
.describe('Citation count (direct for works, aggregate for others).'),
|
|
40
|
+
works_count: z
|
|
41
|
+
.number()
|
|
42
|
+
.nullable()
|
|
43
|
+
.describe('Associated works. null for works themselves.'),
|
|
44
|
+
hint: z
|
|
45
|
+
.string()
|
|
46
|
+
.nullable()
|
|
47
|
+
.describe('Disambiguation context: author names (works), last institution (authors), host org (sources), location (institutions).'),
|
|
48
|
+
}))
|
|
49
|
+
.describe('Autocomplete matches, up to 10.'),
|
|
50
|
+
}),
|
|
51
|
+
async handler(input, ctx) {
|
|
52
|
+
const service = getOpenAlexService();
|
|
53
|
+
const result = await service.autocomplete({
|
|
54
|
+
entityType: input.entity_type,
|
|
55
|
+
query: input.query,
|
|
56
|
+
filters: input.filters,
|
|
57
|
+
}, ctx);
|
|
58
|
+
ctx.log.info('Name resolved', {
|
|
59
|
+
query: input.query,
|
|
60
|
+
entityType: input.entity_type ?? 'all',
|
|
61
|
+
matchCount: result.results.length,
|
|
62
|
+
});
|
|
63
|
+
return result;
|
|
64
|
+
},
|
|
65
|
+
format: (result) => {
|
|
66
|
+
if (result.results.length === 0) {
|
|
67
|
+
return [{ type: 'text', text: 'No matches found.' }];
|
|
68
|
+
}
|
|
69
|
+
const lines = result.results.map((r) => `${r.display_name} (${r.entity_type}) — ${r.id}${r.hint ? ` [${r.hint}]` : ''}`);
|
|
70
|
+
return [{ type: 'text', text: lines.join('\n') }];
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=resolve-name.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-name.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/resolve-name.tool.ts"],"names":[],"mappings":"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"}
|