@digital-science-dsl/dimensions-analytics-mcp 1.1.0 → 1.3.0
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/CHANGELOG.md +14 -0
- package/dist/http-main.js +2 -0
- package/dist/http-main.js.map +1 -1
- package/dist/main.d.ts +0 -5
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +3 -1
- package/dist/main.js.map +1 -1
- package/dist/mcp/auto-update.d.ts +63 -0
- package/dist/mcp/auto-update.d.ts.map +1 -0
- package/dist/mcp/auto-update.js +295 -0
- package/dist/mcp/auto-update.js.map +1 -0
- package/dist/mcp/examples/dsl-examples.d.ts +2 -2
- package/dist/mcp/examples/dsl-examples.d.ts.map +1 -1
- package/dist/mcp/examples/dsl-examples.js +5 -1
- package/dist/mcp/examples/dsl-examples.js.map +1 -1
- package/dist/mcp/examples/usage-scenarios.d.ts.map +1 -1
- package/dist/mcp/examples/usage-scenarios.js +38 -0
- package/dist/mcp/examples/usage-scenarios.js.map +1 -1
- package/dist/mcp/http-server.js +3 -3
- package/dist/mcp/http-server.js.map +1 -1
- package/dist/mcp/middleware/field-aliases.d.ts +1 -1
- package/dist/mcp/middleware/field-aliases.d.ts.map +1 -1
- package/dist/mcp/middleware/field-aliases.js +0 -6
- package/dist/mcp/middleware/field-aliases.js.map +1 -1
- package/dist/mcp/resources/schema.d.ts +1 -1
- package/dist/mcp/resources/schema.d.ts.map +1 -1
- package/dist/mcp/resources/schema.js +11 -11
- package/dist/mcp/resources/schema.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -2
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +8 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/analytics.d.ts +1 -1
- package/dist/mcp/tools/analytics.d.ts.map +1 -1
- package/dist/mcp/tools/analytics.js +21 -21
- package/dist/mcp/tools/analytics.js.map +1 -1
- package/dist/mcp/tools/fetch-search-pages.d.ts +1 -1
- package/dist/mcp/tools/fetch-search-pages.d.ts.map +1 -1
- package/dist/mcp/tools/fetch-search-pages.js +4 -9
- package/dist/mcp/tools/fetch-search-pages.js.map +1 -1
- package/dist/mcp/tools/functions.d.ts +1 -1
- package/dist/mcp/tools/functions.d.ts.map +1 -1
- package/dist/mcp/tools/functions.js +8 -13
- package/dist/mcp/tools/functions.js.map +1 -1
- package/dist/mcp/tools/lookup.d.ts +1 -1
- package/dist/mcp/tools/lookup.d.ts.map +1 -1
- package/dist/mcp/tools/lookup.js +12 -17
- package/dist/mcp/tools/lookup.js.map +1 -1
- package/dist/mcp/tools/query.d.ts +1 -1
- package/dist/mcp/tools/query.d.ts.map +1 -1
- package/dist/mcp/tools/query.js +4 -9
- package/dist/mcp/tools/query.js.map +1 -1
- package/dist/mcp/tools/schema.d.ts +1 -1
- package/dist/mcp/tools/schema.d.ts.map +1 -1
- package/dist/mcp/tools/schema.js +7 -11
- package/dist/mcp/tools/schema.js.map +1 -1
- package/dist/mcp/tools/search.d.ts +1 -1
- package/dist/mcp/tools/search.d.ts.map +1 -1
- package/dist/mcp/tools/search.js +4 -9
- package/dist/mcp/tools/search.js.map +1 -1
- package/dist/mcp/tools/similar-documents.d.ts +26 -0
- package/dist/mcp/tools/similar-documents.d.ts.map +1 -0
- package/dist/mcp/tools/similar-documents.js +166 -0
- package/dist/mcp/tools/similar-documents.js.map +1 -0
- package/dist/mcp/usage-tracking.d.ts +1 -1
- package/dist/mcp/usage-tracking.d.ts.map +1 -1
- package/dist/mcp/utils.d.ts +1 -2
- package/dist/mcp/utils.d.ts.map +1 -1
- package/dist/mcp/utils.js +0 -4
- package/dist/mcp/utils.js.map +1 -1
- package/package.json +6 -2
- package/src/http-main.ts +3 -0
- package/src/main.ts +4 -2
- package/src/mcp/auto-update.ts +379 -0
- package/src/mcp/examples/dsl-examples.ts +5 -1
- package/src/mcp/examples/usage-scenarios.ts +38 -0
- package/src/mcp/http-server.ts +3 -3
- package/src/mcp/middleware/field-aliases.ts +1 -2
- package/src/mcp/resources/schema.ts +12 -12
- package/src/mcp/server.ts +9 -5
- package/src/mcp/tools/analytics.ts +26 -26
- package/src/mcp/tools/fetch-search-pages.ts +5 -6
- package/src/mcp/tools/functions.ts +9 -10
- package/src/mcp/tools/lookup.ts +13 -14
- package/src/mcp/tools/query.ts +5 -6
- package/src/mcp/tools/schema.ts +8 -9
- package/src/mcp/tools/search.ts +5 -6
- package/src/mcp/tools/similar-documents.ts +231 -0
- package/src/mcp/usage-tracking.ts +1 -1
- package/src/mcp/utils.ts +1 -3
- package/test/examples/dsl-examples.test.ts +8 -0
- package/test/examples/usage-scenarios.test.ts +5 -3
- package/test/integration/harness.ts +1 -3
- package/test/integration/hosted-client.ts +1 -3
- package/test/integration/hosted.e2e.test.ts +1 -1
- package/test/integration/suites/index.ts +2 -0
- package/test/integration/suites/similar-documents.integration.ts +51 -0
- package/test/integration/verify-usage-scenarios.ts +1 -3
- package/test/mcp/auto-update.test.ts +347 -0
- package/test/middleware/field-aliases.test.ts +1 -1
- package/test/resources/schema.test.ts +5 -1
- package/test/server.test.ts +17 -3
- package/test/tools/similar-documents.test.ts +173 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -12,11 +12,13 @@ export const DSL_EXAMPLES_BY_ENTITY: Record<string, readonly string[]> = {
|
|
|
12
12
|
'search publications for "machine learning" return publications[id+title+doi] limit 10',
|
|
13
13
|
'search publications for "CRISPR" where year >= 2020 return publications[id+title+times_cited+year] sort by times_cited desc limit 25',
|
|
14
14
|
'search publications in title_only for "gene therapy" return publications[basics] limit 10',
|
|
15
|
+
'search publications for similar_documents("After spinal cord injury, macrophages infiltrate the lesion site") where year > 2015 return publications[id+title+year] sort by score desc limit 5',
|
|
15
16
|
"search publications return publications facet journal limit 20",
|
|
16
17
|
],
|
|
17
18
|
grants: [
|
|
18
19
|
'search grants for "cancer immunotherapy" return grants limit 10',
|
|
19
20
|
'search grants where funder_org_name = "National Institutes of Health" return grants[basics] limit 10',
|
|
21
|
+
'search grants for similar_documents("Cancer immunotherapy with checkpoint inhibitors and CAR-T") where start_year >= 2020 return grants[id+title] sort by score desc limit 5',
|
|
20
22
|
"search grants return grants aggregate funding_usd",
|
|
21
23
|
],
|
|
22
24
|
patents: [
|
|
@@ -53,13 +55,15 @@ export const DSL_EXAMPLES_BY_ENTITY: Record<string, readonly string[]> = {
|
|
|
53
55
|
],
|
|
54
56
|
};
|
|
55
57
|
|
|
56
|
-
/** General DSL patterns (describe, functions, pagination). */
|
|
58
|
+
/** General DSL patterns (describe, functions, pagination, similarity). */
|
|
57
59
|
export const DSL_GENERAL_EXAMPLES = [
|
|
58
60
|
"describe version",
|
|
59
61
|
"describe schema",
|
|
60
62
|
"describe source publications",
|
|
61
63
|
'extract_concepts(text: "CRISPR gene editing enables precise DNA modification")',
|
|
62
64
|
'classify(text: "Stem cell therapy for diabetes", system: "for")',
|
|
65
|
+
'search publications for similar_documents("After spinal cord injury, macrophages infiltrate the lesion site") where year > 2015 return publications[id+title+year] sort by score desc limit 5',
|
|
66
|
+
'search grants for similar_documents("Cancer immunotherapy with checkpoint inhibitors and CAR-T") where start_year >= 2020 return grants[id+title] sort by score desc limit 5',
|
|
63
67
|
] as const;
|
|
64
68
|
|
|
65
69
|
/** Link to official DSL documentation. */
|
|
@@ -164,6 +164,44 @@ export const USAGE_SCENARIOS: readonly UsageScenario[] = [
|
|
|
164
164
|
},
|
|
165
165
|
dslContains: ["large language model", "citations_per_year(2018, 2024)"],
|
|
166
166
|
},
|
|
167
|
+
{
|
|
168
|
+
id: "similar-documents",
|
|
169
|
+
goal: "Similar papers from an abstract",
|
|
170
|
+
prompt: "Find publications similar to this abstract about spinal cord injury macrophages.",
|
|
171
|
+
tool: "similar_documents",
|
|
172
|
+
args: {
|
|
173
|
+
entityType: "publications",
|
|
174
|
+
text: "After spinal cord injury, macrophages infiltrate the lesion site and contribute to both tissue damage and repair processes.",
|
|
175
|
+
yearFrom: 2016,
|
|
176
|
+
limit: 10,
|
|
177
|
+
fields: ["id", "title", "year"],
|
|
178
|
+
},
|
|
179
|
+
dslContains: [
|
|
180
|
+
"search publications for similar_documents(",
|
|
181
|
+
"year >= 2016",
|
|
182
|
+
"sort by score desc",
|
|
183
|
+
"limit 10",
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: "similar-grants",
|
|
188
|
+
goal: "Similar grants from a description",
|
|
189
|
+
prompt: "Find grants similar to this description of CAR-T cancer immunotherapy.",
|
|
190
|
+
tool: "similar_documents",
|
|
191
|
+
args: {
|
|
192
|
+
entityType: "grants",
|
|
193
|
+
text: "Development of novel cancer immunotherapy approaches using checkpoint inhibitors and CAR-T cell engineering for solid tumors.",
|
|
194
|
+
yearFrom: 2020,
|
|
195
|
+
limit: 10,
|
|
196
|
+
fields: ["id", "title", "funding_usd", "start_year"],
|
|
197
|
+
},
|
|
198
|
+
dslContains: [
|
|
199
|
+
"search grants for similar_documents(",
|
|
200
|
+
"start_year >= 2020",
|
|
201
|
+
"sort by score desc",
|
|
202
|
+
"limit 10",
|
|
203
|
+
],
|
|
204
|
+
},
|
|
167
205
|
{
|
|
168
206
|
id: "build-complex-query",
|
|
169
207
|
goal: "Build a complex query",
|
package/src/mcp/http-server.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import type { IncomingMessage, Server } from "node:http";
|
|
8
|
-
import { createMcpExpressApp } from "@modelcontextprotocol/
|
|
9
|
-
import {
|
|
8
|
+
import { createMcpExpressApp } from "@modelcontextprotocol/express";
|
|
9
|
+
import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
|
|
10
10
|
import type { Request, Response } from "express";
|
|
11
11
|
import type { HostedEnvConfig } from "../client/deployment-config.js";
|
|
12
12
|
import { AuthenticationError } from "../client/errors.js";
|
|
@@ -73,7 +73,7 @@ export function startHostedHttpServer(options: HostedHttpServerOptions): HostedH
|
|
|
73
73
|
mcpClient,
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
const transport = new
|
|
76
|
+
const transport = new NodeStreamableHTTPServerTransport({
|
|
77
77
|
sessionIdGenerator: undefined,
|
|
78
78
|
});
|
|
79
79
|
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* via a higher-order function (decorator) pattern.
|
|
5
5
|
* @module mcp/middleware/field-aliases
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import type { CallToolResult } from "@modelcontextprotocol/server";
|
|
9
8
|
import type { EntityType } from "../../dsl/index.js";
|
|
10
9
|
|
|
11
10
|
// ---------------------------------------------------------------------------
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* @module mcp/resources/schema
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { McpServer } from "@modelcontextprotocol/
|
|
7
|
-
import { ResourceTemplate } from "@modelcontextprotocol/
|
|
6
|
+
import type { McpServer } from "@modelcontextprotocol/server";
|
|
7
|
+
import { ResourceTemplate } from "@modelcontextprotocol/server";
|
|
8
8
|
import {
|
|
9
9
|
buildUsagePolicy,
|
|
10
10
|
type DescribeField,
|
|
@@ -32,7 +32,7 @@ import type { SchemaContext } from "../schema/context.js";
|
|
|
32
32
|
*/
|
|
33
33
|
export function registerSchemaResources(server: McpServer, context: SchemaContext): void {
|
|
34
34
|
const schemaStore = () => context.store;
|
|
35
|
-
server.
|
|
35
|
+
server.registerResource(
|
|
36
36
|
"schema-summary",
|
|
37
37
|
"dimensions://schema/summary",
|
|
38
38
|
{
|
|
@@ -52,7 +52,7 @@ export function registerSchemaResources(server: McpServer, context: SchemaContex
|
|
|
52
52
|
);
|
|
53
53
|
|
|
54
54
|
// Full schema
|
|
55
|
-
server.
|
|
55
|
+
server.registerResource(
|
|
56
56
|
"schema-full",
|
|
57
57
|
"dimensions://schema",
|
|
58
58
|
{
|
|
@@ -72,7 +72,7 @@ export function registerSchemaResources(server: McpServer, context: SchemaContex
|
|
|
72
72
|
);
|
|
73
73
|
|
|
74
74
|
// Version
|
|
75
|
-
server.
|
|
75
|
+
server.registerResource(
|
|
76
76
|
"schema-version",
|
|
77
77
|
"dimensions://schema/version",
|
|
78
78
|
{
|
|
@@ -95,7 +95,7 @@ export function registerSchemaResources(server: McpServer, context: SchemaContex
|
|
|
95
95
|
);
|
|
96
96
|
|
|
97
97
|
// Limits (static until upstream `describe limits` exists)
|
|
98
|
-
server.
|
|
98
|
+
server.registerResource(
|
|
99
99
|
"schema-limits",
|
|
100
100
|
"dimensions://schema/limits",
|
|
101
101
|
{
|
|
@@ -113,7 +113,7 @@ export function registerSchemaResources(server: McpServer, context: SchemaContex
|
|
|
113
113
|
}),
|
|
114
114
|
);
|
|
115
115
|
|
|
116
|
-
server.
|
|
116
|
+
server.registerResource(
|
|
117
117
|
"schema-policy",
|
|
118
118
|
"dimensions://schema/policy",
|
|
119
119
|
{
|
|
@@ -132,7 +132,7 @@ export function registerSchemaResources(server: McpServer, context: SchemaContex
|
|
|
132
132
|
}),
|
|
133
133
|
);
|
|
134
134
|
|
|
135
|
-
server.
|
|
135
|
+
server.registerResource(
|
|
136
136
|
"dsl-examples",
|
|
137
137
|
"dimensions://examples",
|
|
138
138
|
{
|
|
@@ -160,7 +160,7 @@ export function registerSchemaResources(server: McpServer, context: SchemaContex
|
|
|
160
160
|
}),
|
|
161
161
|
);
|
|
162
162
|
|
|
163
|
-
server.
|
|
163
|
+
server.registerResource(
|
|
164
164
|
"dsl-examples-by-source",
|
|
165
165
|
new ResourceTemplate("dimensions://examples/{source}", {
|
|
166
166
|
list: async () => ({
|
|
@@ -219,7 +219,7 @@ export function registerSchemaResources(server: McpServer, context: SchemaContex
|
|
|
219
219
|
);
|
|
220
220
|
|
|
221
221
|
const sourceNames = () => schemaStore().sourceNames();
|
|
222
|
-
server.
|
|
222
|
+
server.registerResource(
|
|
223
223
|
"schema-sources",
|
|
224
224
|
new ResourceTemplate("dimensions://schema/sources/{sourceName}", {
|
|
225
225
|
list: async () => ({
|
|
@@ -256,7 +256,7 @@ export function registerSchemaResources(server: McpServer, context: SchemaContex
|
|
|
256
256
|
);
|
|
257
257
|
|
|
258
258
|
const entityNames = () => schemaStore().entityNames();
|
|
259
|
-
server.
|
|
259
|
+
server.registerResource(
|
|
260
260
|
"schema-entities",
|
|
261
261
|
new ResourceTemplate("dimensions://schema/entities/{entityName}", {
|
|
262
262
|
list: async () => ({
|
|
@@ -294,7 +294,7 @@ export function registerSchemaResources(server: McpServer, context: SchemaContex
|
|
|
294
294
|
|
|
295
295
|
// Compatibility view for structured search tools
|
|
296
296
|
const entityTypes = () => schemaStore().structuredEntityTypes();
|
|
297
|
-
server.
|
|
297
|
+
server.registerResource(
|
|
298
298
|
"fields",
|
|
299
299
|
new ResourceTemplate("dimensions://fields/{entityType}", {
|
|
300
300
|
list: async () => ({
|
package/src/mcp/server.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* @module mcp/server
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
7
|
+
import type { Transport } from "@modelcontextprotocol/server";
|
|
8
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
9
|
+
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
|
|
10
10
|
import pkg from "../../package.json" with { type: "json" };
|
|
11
11
|
import { type HostedEnvConfig, loadDeploymentConfig } from "../client/index.js";
|
|
12
12
|
import { createBootstrapDimensionsClient, createDimensionsClient } from "../dsl/create-client.js";
|
|
@@ -27,6 +27,7 @@ import { registerLookupTools } from "./tools/lookup.js";
|
|
|
27
27
|
import { registerQueryTools } from "./tools/query.js";
|
|
28
28
|
import { registerSchemaTools, validateFieldAliases } from "./tools/schema.js";
|
|
29
29
|
import { registerSearchTools } from "./tools/search.js";
|
|
30
|
+
import { registerSimilarDocumentsTool } from "./tools/similar-documents.js";
|
|
30
31
|
import { initMcpUsageTracking } from "./usage-tracking.js";
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -94,7 +95,7 @@ export function buildServerInstructions(schemaStore: SchemaStore): string {
|
|
|
94
95
|
[
|
|
95
96
|
"Workflow:",
|
|
96
97
|
"(1) Discover schema — read dimensions://schema/summary, dimensions://fields/{entity}, dimensions://examples/{source}, or describe_schema;",
|
|
97
|
-
"(2) Search — search_* for keyword discovery with filters; get_by_doi, get_by_pmid, get_by_id for known identifiers;",
|
|
98
|
+
"(2) Search — search_* for keyword discovery with filters; similar_documents for topic-similar pubs/grants from abstract text; get_by_doi, get_by_pmid, get_by_id for known identifiers;",
|
|
98
99
|
"(3) Analyze — facet_query, aggregate_query, citation_trend, funding_trend;",
|
|
99
100
|
"(4) Drill down — get_by_id, search_* with filters (e.g. researchers.id, research_orgs.id), facet_query for top researchers at an org.",
|
|
100
101
|
].join(" "),
|
|
@@ -105,12 +106,14 @@ export function buildServerInstructions(schemaStore: SchemaStore): string {
|
|
|
105
106
|
"search_organizations for institution lookup — prefer GRID id in filters when the name is ambiguous;",
|
|
106
107
|
"search_source_titles for journals / ISSN lookup (not articles); search_reports for technical reports;",
|
|
107
108
|
"search_funder_groups / search_research_org_groups for curated group name → member GRID ids;",
|
|
109
|
+
"similar_documents finds concept-similar publications/grants from prose (not embeddings); for a known ID, get_by_id then pass abstract/description as text;",
|
|
108
110
|
"facet_query supports yearFrom/yearTo for year-scoped facets.",
|
|
109
111
|
].join(" "),
|
|
110
112
|
[
|
|
111
113
|
"Query construction:",
|
|
112
114
|
"for ranked publication search (e.g. most-cited since 2020), use search_publications with query, yearFrom, sortBy (times_cited or total_citations), limit — do not hand-write execute_dsl;",
|
|
113
|
-
"use
|
|
115
|
+
"for similar-document lookup, use similar_documents — do not hand-write similar_documents() in execute_dsl;",
|
|
116
|
+
"use execute_dsl for boolean concept groups and remaining DSL special functions (classify, extract_concepts);",
|
|
114
117
|
"use execute_dsl only when structured tools are insufficient; DSL order is return ... sort by FIELD order limit N (never limit before sort).",
|
|
115
118
|
].join(" "),
|
|
116
119
|
[
|
|
@@ -138,6 +141,7 @@ function registerAllTools(
|
|
|
138
141
|
registerLookupTools(server, client);
|
|
139
142
|
registerQueryTools(server, client, schemaStore);
|
|
140
143
|
registerFunctionTools(server, client);
|
|
144
|
+
registerSimilarDocumentsTool(server, client);
|
|
141
145
|
registerAnalyticsTools(server, client, schemaStore);
|
|
142
146
|
registerSchemaTools(server, client, schemaContext);
|
|
143
147
|
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* @module mcp/tools/analytics
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { McpServer } from "@modelcontextprotocol/
|
|
8
|
-
import {
|
|
7
|
+
import type { McpServer } from "@modelcontextprotocol/server";
|
|
8
|
+
import { ProtocolError, ProtocolErrorCode } from "@modelcontextprotocol/server";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import type { Currency, DimensionsClient, EntityType } from "../../dsl/index.js";
|
|
11
11
|
import {
|
|
@@ -60,8 +60,8 @@ function entityTypeEnum(schemaStore: SchemaStore) {
|
|
|
60
60
|
function assertFacetField(schemaStore: SchemaStore, entity: string, facetField: string): void {
|
|
61
61
|
const valid = schemaStore.facetFields(entity);
|
|
62
62
|
if (!valid.includes(facetField)) {
|
|
63
|
-
throw new
|
|
64
|
-
|
|
63
|
+
throw new ProtocolError(
|
|
64
|
+
ProtocolErrorCode.InvalidParams,
|
|
65
65
|
`Invalid facet field "${facetField}" for entity "${entity}". Valid fields: ${valid.join(", ") || "(none)"}`,
|
|
66
66
|
);
|
|
67
67
|
}
|
|
@@ -77,8 +77,8 @@ function assertIndicators(schemaStore: SchemaStore, entity: string, indicators:
|
|
|
77
77
|
const valid = new Set(schemaStore.metrics(entity));
|
|
78
78
|
const invalid = indicators.filter((name) => !valid.has(name));
|
|
79
79
|
if (invalid.length > 0) {
|
|
80
|
-
throw new
|
|
81
|
-
|
|
80
|
+
throw new ProtocolError(
|
|
81
|
+
ProtocolErrorCode.InvalidParams,
|
|
82
82
|
`Invalid indicator(s) for entity "${entity}": ${invalid.join(", ")}. Valid metrics: ${[...valid].join(", ") || "(none)"}`,
|
|
83
83
|
);
|
|
84
84
|
}
|
|
@@ -131,7 +131,7 @@ export function registerAnalyticsTools(
|
|
|
131
131
|
description:
|
|
132
132
|
"Distribution analysis across entity dimensions. Returns facet buckets showing how records " +
|
|
133
133
|
"are distributed across a categorical field. Example: 'What journals publish the most CRISPR research?'",
|
|
134
|
-
inputSchema: {
|
|
134
|
+
inputSchema: z.object({
|
|
135
135
|
entityType: entityTypeSchema,
|
|
136
136
|
facetField: z
|
|
137
137
|
.string()
|
|
@@ -165,13 +165,13 @@ export function registerAnalyticsTools(
|
|
|
165
165
|
.array(ExtendedWhereFilterSchema)
|
|
166
166
|
.optional()
|
|
167
167
|
.describe("Additional where-clause filters before faceting"),
|
|
168
|
-
},
|
|
169
|
-
outputSchema: {
|
|
168
|
+
}),
|
|
169
|
+
outputSchema: z.object({
|
|
170
170
|
entityType: z.string().describe("Entity type analyzed"),
|
|
171
171
|
facetField: z.string().describe("Field faceted on"),
|
|
172
172
|
totalBuckets: z.number().describe("Number of facet buckets returned"),
|
|
173
173
|
buckets: z.array(z.record(z.string(), z.unknown())).describe("Facet bucket distribution"),
|
|
174
|
-
},
|
|
174
|
+
}),
|
|
175
175
|
annotations: READ_ONLY_API_ANNOTATIONS,
|
|
176
176
|
},
|
|
177
177
|
withFieldAliases(
|
|
@@ -221,7 +221,7 @@ export function registerAnalyticsTools(
|
|
|
221
221
|
description:
|
|
222
222
|
"Metric aggregation on facet buckets. Returns facet buckets enriched with aggregated indicator " +
|
|
223
223
|
"values. Example: 'Which funders have the highest average citation ratio?'",
|
|
224
|
-
inputSchema: {
|
|
224
|
+
inputSchema: z.object({
|
|
225
225
|
entityType: entityTypeSchema,
|
|
226
226
|
facetField: z
|
|
227
227
|
.string()
|
|
@@ -263,8 +263,8 @@ export function registerAnalyticsTools(
|
|
|
263
263
|
.array(ExtendedWhereFilterSchema)
|
|
264
264
|
.optional()
|
|
265
265
|
.describe("Additional where-clause filters before aggregating"),
|
|
266
|
-
},
|
|
267
|
-
outputSchema: {
|
|
266
|
+
}),
|
|
267
|
+
outputSchema: z.object({
|
|
268
268
|
entityType: z.string().describe("Entity type analyzed"),
|
|
269
269
|
facetField: z.string().describe("Field faceted on"),
|
|
270
270
|
indicators: z.array(z.string()).describe("Aggregation indicators computed"),
|
|
@@ -272,7 +272,7 @@ export function registerAnalyticsTools(
|
|
|
272
272
|
buckets: z
|
|
273
273
|
.array(z.record(z.string(), z.unknown()))
|
|
274
274
|
.describe("Facet buckets with aggregated values"),
|
|
275
|
-
},
|
|
275
|
+
}),
|
|
276
276
|
annotations: READ_ONLY_API_ANNOTATIONS,
|
|
277
277
|
},
|
|
278
278
|
withFieldAliases(
|
|
@@ -330,7 +330,7 @@ export function registerAnalyticsTools(
|
|
|
330
330
|
description:
|
|
331
331
|
"Year-by-year citation counts for a topic. Returns annual citation totals for publications " +
|
|
332
332
|
"matching the query over the specified year range.",
|
|
333
|
-
inputSchema: {
|
|
333
|
+
inputSchema: z.object({
|
|
334
334
|
query: z
|
|
335
335
|
.string()
|
|
336
336
|
.min(1)
|
|
@@ -347,22 +347,22 @@ export function registerAnalyticsTools(
|
|
|
347
347
|
.min(1900)
|
|
348
348
|
.max(2100)
|
|
349
349
|
.describe("Last year of the time range (inclusive)"),
|
|
350
|
-
},
|
|
351
|
-
outputSchema: {
|
|
350
|
+
}),
|
|
351
|
+
outputSchema: z.object({
|
|
352
352
|
query: z.string().describe("Search query used"),
|
|
353
353
|
startYear: z.number().describe("First year of range"),
|
|
354
354
|
endYear: z.number().describe("Last year of range"),
|
|
355
355
|
citationsPerYear: z
|
|
356
356
|
.array(z.record(z.string(), z.unknown()))
|
|
357
357
|
.describe("Annual citation totals"),
|
|
358
|
-
},
|
|
358
|
+
}),
|
|
359
359
|
annotations: READ_ONLY_API_ANNOTATIONS,
|
|
360
360
|
},
|
|
361
361
|
async (args) => {
|
|
362
362
|
try {
|
|
363
363
|
if (args.startYear > args.endYear) {
|
|
364
|
-
throw new
|
|
365
|
-
|
|
364
|
+
throw new ProtocolError(
|
|
365
|
+
ProtocolErrorCode.InvalidParams,
|
|
366
366
|
`startYear (${args.startYear}) must be ≤ endYear (${args.endYear})`,
|
|
367
367
|
);
|
|
368
368
|
}
|
|
@@ -393,7 +393,7 @@ export function registerAnalyticsTools(
|
|
|
393
393
|
description:
|
|
394
394
|
"Year-by-year funding totals for a topic. Returns annual grant funding amounts for grants " +
|
|
395
395
|
"matching the query over the specified year range.",
|
|
396
|
-
inputSchema: {
|
|
396
|
+
inputSchema: z.object({
|
|
397
397
|
query: z
|
|
398
398
|
.string()
|
|
399
399
|
.min(1)
|
|
@@ -416,8 +416,8 @@ export function registerAnalyticsTools(
|
|
|
416
416
|
.describe(
|
|
417
417
|
`Currency for funding amounts (default USD). Supported: ${SUPPORTED_CURRENCIES.join(", ")}.`,
|
|
418
418
|
),
|
|
419
|
-
},
|
|
420
|
-
outputSchema: {
|
|
419
|
+
}),
|
|
420
|
+
outputSchema: z.object({
|
|
421
421
|
query: z.string().describe("Search query used"),
|
|
422
422
|
startYear: z.number().describe("First year of range"),
|
|
423
423
|
endYear: z.number().describe("Last year of range"),
|
|
@@ -425,14 +425,14 @@ export function registerAnalyticsTools(
|
|
|
425
425
|
fundingPerYear: z
|
|
426
426
|
.array(z.record(z.string(), z.unknown()))
|
|
427
427
|
.describe("Annual funding totals"),
|
|
428
|
-
},
|
|
428
|
+
}),
|
|
429
429
|
annotations: READ_ONLY_API_ANNOTATIONS,
|
|
430
430
|
},
|
|
431
431
|
async (args) => {
|
|
432
432
|
try {
|
|
433
433
|
if (args.startYear > args.endYear) {
|
|
434
|
-
throw new
|
|
435
|
-
|
|
434
|
+
throw new ProtocolError(
|
|
435
|
+
ProtocolErrorCode.InvalidParams,
|
|
436
436
|
`startYear (${args.startYear}) must be ≤ endYear (${args.endYear})`,
|
|
437
437
|
);
|
|
438
438
|
}
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Supports single-page, aggregate, and JSONL file export modes.
|
|
4
4
|
* @module mcp/tools/fetch-search-pages
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import type { McpServer } from "@modelcontextprotocol/server";
|
|
8
7
|
import { z } from "zod";
|
|
9
8
|
import { ValidationError } from "../../client/index.js";
|
|
10
9
|
import {
|
|
@@ -95,7 +94,7 @@ export function registerFetchSearchPagesTools(
|
|
|
95
94
|
"Default mode returns one page. Use aggregate for ID counts/sums without full rows. " +
|
|
96
95
|
"Use file mode to stream JSONL or CSV locally — never for bulk mirroring of Dimensions data. " +
|
|
97
96
|
`Entities: ${entityList}.`,
|
|
98
|
-
inputSchema: {
|
|
97
|
+
inputSchema: z.object({
|
|
99
98
|
entityType: z
|
|
100
99
|
.enum(STRUCTURED_ENTITY_TYPES)
|
|
101
100
|
.describe("Dimensions source to search (same as search_* tools)"),
|
|
@@ -156,8 +155,8 @@ export function registerFetchSearchPagesTools(
|
|
|
156
155
|
.describe(
|
|
157
156
|
"Required when maxPages > 5 or planned records > 5000. Acknowledges reasonable-use policy.",
|
|
158
157
|
),
|
|
159
|
-
},
|
|
160
|
-
outputSchema: {
|
|
158
|
+
}),
|
|
159
|
+
outputSchema: z.object({
|
|
161
160
|
mode: z.string(),
|
|
162
161
|
entityType: z.string(),
|
|
163
162
|
totalCount: z.number().optional(),
|
|
@@ -176,7 +175,7 @@ export function registerFetchSearchPagesTools(
|
|
|
176
175
|
totalFundingUsd: z.number().optional(),
|
|
177
176
|
})
|
|
178
177
|
.optional(),
|
|
179
|
-
},
|
|
178
|
+
}),
|
|
180
179
|
annotations: READ_ONLY_API_ANNOTATIONS,
|
|
181
180
|
},
|
|
182
181
|
withFieldAliases(
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Entity resolution helpers (affiliations, grants).
|
|
4
4
|
* @module mcp/tools/functions
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import type { McpServer } from "@modelcontextprotocol/server";
|
|
8
7
|
import { z } from "zod";
|
|
9
8
|
import type { DimensionsClient } from "../../dsl/index.js";
|
|
10
9
|
import { registerTrackedTool } from "../usage-tracking.js";
|
|
@@ -24,7 +23,7 @@ export function registerFunctionTools(server: McpServer, client: DimensionsClien
|
|
|
24
23
|
"Resolve and disambiguate organization affiliations using Dimensions entity resolution. " +
|
|
25
24
|
"Takes freetext affiliation strings or structured organization data and returns matched " +
|
|
26
25
|
"organizations with GRID/ROR identifiers and confidence scores.",
|
|
27
|
-
inputSchema: {
|
|
26
|
+
inputSchema: z.object({
|
|
28
27
|
affiliations: z
|
|
29
28
|
.array(
|
|
30
29
|
z.object({
|
|
@@ -42,11 +41,11 @@ export function registerFunctionTools(server: McpServer, client: DimensionsClien
|
|
|
42
41
|
.describe(
|
|
43
42
|
"Array of affiliations to resolve. Each must have either 'affiliation' or 'name'",
|
|
44
43
|
),
|
|
45
|
-
},
|
|
46
|
-
outputSchema: {
|
|
44
|
+
}),
|
|
45
|
+
outputSchema: z.object({
|
|
47
46
|
matchCount: z.number().describe("Number of resolved affiliations"),
|
|
48
47
|
affiliations: z.array(z.record(z.string(), z.unknown())).describe("Resolved affiliations"),
|
|
49
|
-
},
|
|
48
|
+
}),
|
|
50
49
|
annotations: READ_ONLY_API_ANNOTATIONS,
|
|
51
50
|
},
|
|
52
51
|
async (args) => {
|
|
@@ -71,7 +70,7 @@ export function registerFunctionTools(server: McpServer, client: DimensionsClien
|
|
|
71
70
|
"Resolve grant numbers to Dimensions grant records. " +
|
|
72
71
|
"Takes a grant number and optional funder information to find matching grants " +
|
|
73
72
|
"with full metadata including funding amounts, dates, and funder details.",
|
|
74
|
-
inputSchema: {
|
|
73
|
+
inputSchema: z.object({
|
|
75
74
|
grant_number: z.string().min(1).describe("Grant number to look up (e.g., 'R01HL117329')"),
|
|
76
75
|
fundref: z
|
|
77
76
|
.string()
|
|
@@ -81,11 +80,11 @@ export function registerFunctionTools(server: McpServer, client: DimensionsClien
|
|
|
81
80
|
.string()
|
|
82
81
|
.optional()
|
|
83
82
|
.describe("Funder name as alternative to FundRef ID (e.g., 'NIH')"),
|
|
84
|
-
},
|
|
85
|
-
outputSchema: {
|
|
83
|
+
}),
|
|
84
|
+
outputSchema: z.object({
|
|
86
85
|
matchCount: z.number().describe("Number of matched grants"),
|
|
87
86
|
grants: z.array(z.record(z.string(), z.unknown())).describe("Matched grant records"),
|
|
88
|
-
},
|
|
87
|
+
}),
|
|
89
88
|
annotations: READ_ONLY_API_ANNOTATIONS,
|
|
90
89
|
},
|
|
91
90
|
async (args) => {
|
package/src/mcp/tools/lookup.ts
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Provides direct lookup by identifiers (DOI, PMID, Dimensions ID).
|
|
4
4
|
* @module mcp/tools/lookup
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import type { McpServer } from "@modelcontextprotocol/server";
|
|
8
7
|
import { z } from "zod";
|
|
9
8
|
import { type DimensionsClient, EntitySchema, type EntityType } from "../../dsl/index.js";
|
|
10
9
|
import { withFieldAliases } from "../middleware/field-aliases.js";
|
|
@@ -29,18 +28,18 @@ export function registerLookupTools(server: McpServer, client: DimensionsClient)
|
|
|
29
28
|
{
|
|
30
29
|
description:
|
|
31
30
|
"Retrieve a publication by its Digital Object Identifier (DOI). Returns full publication details including abstract, citations, and authors.",
|
|
32
|
-
inputSchema: {
|
|
31
|
+
inputSchema: z.object({
|
|
33
32
|
doi: z.string().describe("The DOI to look up (e.g., '10.1038/nature12373')"),
|
|
34
33
|
fields: z
|
|
35
34
|
.array(z.string())
|
|
36
35
|
.optional()
|
|
37
36
|
.describe("Fields to return (e.g., ['id', 'title', 'abstract', 'times_cited'])"),
|
|
38
|
-
},
|
|
39
|
-
outputSchema: {
|
|
37
|
+
}),
|
|
38
|
+
outputSchema: z.object({
|
|
40
39
|
found: z.boolean().describe("Whether the entity was found"),
|
|
41
40
|
publication: z.record(z.string(), z.unknown()).optional().describe("The found entity"),
|
|
42
41
|
message: z.string().optional().describe("Message when not found"),
|
|
43
|
-
},
|
|
42
|
+
}),
|
|
44
43
|
annotations: READ_ONLY_API_ANNOTATIONS,
|
|
45
44
|
},
|
|
46
45
|
withFieldAliases(
|
|
@@ -83,18 +82,18 @@ export function registerLookupTools(server: McpServer, client: DimensionsClient)
|
|
|
83
82
|
{
|
|
84
83
|
description:
|
|
85
84
|
"Retrieve a publication by its PubMed ID (PMID). Returns full publication details from Dimensions.",
|
|
86
|
-
inputSchema: {
|
|
85
|
+
inputSchema: z.object({
|
|
87
86
|
pmid: z.string().describe("The PubMed ID to look up (e.g., '23846567')"),
|
|
88
87
|
fields: z
|
|
89
88
|
.array(z.string())
|
|
90
89
|
.optional()
|
|
91
90
|
.describe("Fields to return (e.g., ['id', 'title', 'abstract', 'times_cited'])"),
|
|
92
|
-
},
|
|
93
|
-
outputSchema: {
|
|
91
|
+
}),
|
|
92
|
+
outputSchema: z.object({
|
|
94
93
|
found: z.boolean().describe("Whether the entity was found"),
|
|
95
94
|
publication: z.record(z.string(), z.unknown()).optional().describe("The found entity"),
|
|
96
95
|
message: z.string().optional().describe("Message when not found"),
|
|
97
|
-
},
|
|
96
|
+
}),
|
|
98
97
|
annotations: READ_ONLY_API_ANNOTATIONS,
|
|
99
98
|
},
|
|
100
99
|
withFieldAliases(
|
|
@@ -137,17 +136,17 @@ export function registerLookupTools(server: McpServer, client: DimensionsClient)
|
|
|
137
136
|
{
|
|
138
137
|
description:
|
|
139
138
|
"Retrieve any entity by its Dimensions ID. Supports publications, grants, patents, clinical trials, datasets, policy documents, researchers, organizations, reports, source titles, funder groups, and research org groups.",
|
|
140
|
-
inputSchema: {
|
|
139
|
+
inputSchema: z.object({
|
|
141
140
|
entityType: EntitySchema.describe("The type of entity to look up"),
|
|
142
141
|
id: z.string().describe("The Dimensions ID to look up"),
|
|
143
142
|
fields: z.array(z.string()).optional().describe("Fields to return"),
|
|
144
|
-
},
|
|
145
|
-
outputSchema: {
|
|
143
|
+
}),
|
|
144
|
+
outputSchema: z.object({
|
|
146
145
|
found: z.boolean().describe("Whether the entity was found"),
|
|
147
146
|
entity: z.record(z.string(), z.unknown()).optional().describe("The found entity"),
|
|
148
147
|
entityType: z.string().optional().describe("The type of the found entity"),
|
|
149
148
|
message: z.string().optional().describe("Message when not found"),
|
|
150
|
-
},
|
|
149
|
+
}),
|
|
151
150
|
annotations: READ_ONLY_API_ANNOTATIONS,
|
|
152
151
|
},
|
|
153
152
|
withFieldAliases(
|
package/src/mcp/tools/query.ts
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* Provides raw DSL query execution for power users.
|
|
4
4
|
* @module mcp/tools/query
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import type { McpServer } from "@modelcontextprotocol/server";
|
|
8
7
|
import { z } from "zod";
|
|
9
8
|
import {
|
|
10
9
|
buildExecuteDslPolicyHints,
|
|
@@ -47,7 +46,7 @@ Examples:
|
|
|
47
46
|
- return funders aggregate funding_usd
|
|
48
47
|
|
|
49
48
|
Structured entity types: ${sources}`,
|
|
50
|
-
inputSchema: {
|
|
49
|
+
inputSchema: z.object({
|
|
51
50
|
dsl: z.string().describe("Complete DSL query string to execute"),
|
|
52
51
|
confirmLargeFetch: z
|
|
53
52
|
.boolean()
|
|
@@ -56,8 +55,8 @@ Structured entity types: ${sources}`,
|
|
|
56
55
|
.describe(
|
|
57
56
|
"Required for deep pagination in DSL (skip≥5000). See dimensions://schema/policy.",
|
|
58
57
|
),
|
|
59
|
-
},
|
|
60
|
-
outputSchema: {
|
|
58
|
+
}),
|
|
59
|
+
outputSchema: z.object({
|
|
61
60
|
result: z.record(z.string(), z.unknown()).describe("Raw DSL query response"),
|
|
62
61
|
policyNotice: z.string().optional().describe("Reasonable-use reminder"),
|
|
63
62
|
largeResultWarning: z
|
|
@@ -68,7 +67,7 @@ Structured entity types: ${sources}`,
|
|
|
68
67
|
.string()
|
|
69
68
|
.optional()
|
|
70
69
|
.describe("Pagination guidance when results are truncated"),
|
|
71
|
-
},
|
|
70
|
+
}),
|
|
72
71
|
annotations: {
|
|
73
72
|
readOnlyHint: true,
|
|
74
73
|
destructiveHint: false,
|