@digital-science-dsl/dimensions-analytics-mcp 1.4.2 → 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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Allow `search_*` tools to omit `query` for filters-only searches. The shared input schema previously required a free-text `query` string even though the description already said it could be omitted and the DSL query builder already supported filter-only searches, causing tool calls with only `filters` to fail validation. Also clarified the `filters` field description with the required `field`/`operator`/`value` shape and supported operators.
8
+
9
+ ## 1.4.3
10
+
11
+ ### Patch Changes
12
+
13
+ - Package the existing local server as a Claude Desktop extension and publish it with each release.
14
+
3
15
  ## 1.4.2
4
16
 
5
17
  ### Patch Changes
@@ -36,7 +36,7 @@ export declare const SHARED_SEARCH_INPUT: {
36
36
  readonly confirmLargeFetch: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
37
37
  readonly skip: z.ZodOptional<z.ZodNumber>;
38
38
  readonly page: z.ZodOptional<z.ZodNumber>;
39
- readonly query: z.ZodString;
39
+ readonly query: z.ZodOptional<z.ZodString>;
40
40
  readonly limit: z.ZodDefault<z.ZodNumber>;
41
41
  };
42
42
  /** Output schema fields for paginated search responses. */
@@ -1 +1 @@
1
- {"version":3,"file":"search-input.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/search-input.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,uEAAuE;AACvE,eAAO,MAAM,uBAAuB;;;CAmB1B,CAAC;AAEX,uDAAuD;AACvD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BtB,CAAC;AAEX,2DAA2D;AAC3D,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;CAuB3B,CAAC"}
1
+ {"version":3,"file":"search-input.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/search-input.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,uEAAuE;AACvE,eAAO,MAAM,uBAAuB;;;CAmB1B,CAAC;AAEX,uDAAuD;AACvD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCtB,CAAC;AAEX,2DAA2D;AAC3D,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;CAuB3B,CAAC"}
@@ -25,7 +25,8 @@ export const PAGINATION_INPUT_SCHEMA = {
25
25
  export const SHARED_SEARCH_INPUT = {
26
26
  query: z
27
27
  .string()
28
- .describe("Search query terms (omit with filters-only search if the API allows)"),
28
+ .optional()
29
+ .describe("Free-text search terms. Optional — omit for a filters-only search that relies solely on `filters` and/or the entity-specific convenience filters (e.g. yearFrom/yearTo)."),
29
30
  limit: z
30
31
  .number()
31
32
  .int()
@@ -41,7 +42,9 @@ export const SHARED_SEARCH_INPUT = {
41
42
  filters: z
42
43
  .array(ExtendedWhereFilterSchema)
43
44
  .optional()
44
- .describe("Additional where-clause filters"),
45
+ .describe("Additional where-clause filters. Each item needs field, operator, and (except for is_empty/is_not_empty) value, " +
46
+ 'e.g. {"field": "researchers.id", "operator": "=", "value": "ur.01343614074.49"}. ' +
47
+ "Supported operators: =, !=, >, <, >=, <=, in, is_empty, is_not_empty."),
45
48
  confirmLargeFetch: z
46
49
  .boolean()
47
50
  .optional()
@@ -1 +1 @@
1
- {"version":3,"file":"search-input.js","sourceRoot":"","sources":["../../../src/mcp/tools/search-input.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE9E,uEAAuE;AACvE,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,aAAa,CAAC,SAAS,GAAG,CAAC,CAAC;SAChC,QAAQ,EAAE;SACV,QAAQ,CACP,wEAAwE,aAAa,CAAC,SAAS,iBAAiB,CACjH;IACH,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,aAAa,CAAC,QAAQ,GAAG,CAAC,CAAC;SAC/B,QAAQ,EAAE;SACV,QAAQ,CACP,qEAAqE,aAAa,CAAC,QAAQ,GAAG,CAAC,GAAG,CACnG;CACK,CAAC;AAEX,uDAAuD;AACvD,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,QAAQ,CAAC,sEAAsE,CAAC;IACnF,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC;SAC3B,OAAO,CAAC,GAAG,CAAC;SACZ,QAAQ,CAAC,kCAAkC,aAAa,CAAC,QAAQ,gBAAgB,CAAC;IACrF,GAAG,uBAAuB;IAC1B,MAAM,EAAE,CAAC;SACN,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,EAAE;SACV,QAAQ,CACP,kKAAkK,CACnK;IACH,OAAO,EAAE,CAAC;SACP,KAAK,CAAC,yBAAyB,CAAC;SAChC,QAAQ,EAAE;SACV,QAAQ,CAAC,iCAAiC,CAAC;IAC9C,iBAAiB,EAAE,CAAC;SACjB,OAAO,EAAE;SACT,QAAQ,EAAE;SACV,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CACP,yIAAyI,CAC1I;CACK,CAAC;AAEX,2DAA2D;AAC3D,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,UAAU,EAAE,CAAC;SACV,MAAM,CAAC;QACN,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;QACpB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC/B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;QAC3B,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE;QAC7B,2BAA2B,EAAE,CAAC,CAAC,MAAM,EAAE;QACvC,0BAA0B,EAAE,CAAC,CAAC,MAAM,EAAE;KACvC,CAAC;SACD,QAAQ,EAAE;SACV,QAAQ,CAAC,sDAAsD,CAAC;IACnE,kBAAkB,EAAE,CAAC;SAClB,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,oEAAoE,CAAC;IACjF,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,kEAAkE,CAAC;CACvE,CAAC"}
1
+ {"version":3,"file":"search-input.js","sourceRoot":"","sources":["../../../src/mcp/tools/search-input.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE9E,uEAAuE;AACvE,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,aAAa,CAAC,SAAS,GAAG,CAAC,CAAC;SAChC,QAAQ,EAAE;SACV,QAAQ,CACP,wEAAwE,aAAa,CAAC,SAAS,iBAAiB,CACjH;IACH,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,aAAa,CAAC,QAAQ,GAAG,CAAC,CAAC;SAC/B,QAAQ,EAAE;SACV,QAAQ,CACP,qEAAqE,aAAa,CAAC,QAAQ,GAAG,CAAC,GAAG,CACnG;CACK,CAAC;AAEX,uDAAuD;AACvD,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,0KAA0K,CAC3K;IACH,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC;SAC3B,OAAO,CAAC,GAAG,CAAC;SACZ,QAAQ,CAAC,kCAAkC,aAAa,CAAC,QAAQ,gBAAgB,CAAC;IACrF,GAAG,uBAAuB;IAC1B,MAAM,EAAE,CAAC;SACN,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,EAAE;SACV,QAAQ,CACP,kKAAkK,CACnK;IACH,OAAO,EAAE,CAAC;SACP,KAAK,CAAC,yBAAyB,CAAC;SAChC,QAAQ,EAAE;SACV,QAAQ,CACP,kHAAkH;QAChH,mFAAmF;QACnF,uEAAuE,CAC1E;IACH,iBAAiB,EAAE,CAAC;SACjB,OAAO,EAAE;SACT,QAAQ,EAAE;SACV,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CACP,yIAAyI,CAC1I;CACK,CAAC;AAEX,2DAA2D;AAC3D,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,UAAU,EAAE,CAAC;SACV,MAAM,CAAC;QACN,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;QACpB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC/B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE;QAC3B,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE;QAC7B,2BAA2B,EAAE,CAAC,CAAC,MAAM,EAAE;QACvC,0BAA0B,EAAE,CAAC,CAAC,MAAM,EAAE;KACvC,CAAC;SACD,QAAQ,EAAE;SACV,QAAQ,CAAC,sDAAsD,CAAC;IACnE,kBAAkB,EAAE,CAAC;SAClB,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,oEAAoE,CAAC;IACjF,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,kEAAkE,CAAC;CACvE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digital-science-dsl/dimensions-analytics-mcp",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "mcpName": "ai.dimensions/analytics-mcp",
5
5
  "description": "Dimensions Analytics MCP server for Dimensions DSL querying",
6
6
  "keywords": [
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { unpackExtension } from "@anthropic-ai/mcpb";
7
+ import { Client } from "@modelcontextprotocol/client";
8
+ import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
9
+
10
+ const root = resolve(fileURLToPath(new URL("../../..", import.meta.url)));
11
+ const output = join(root, "dist/dimensions-analytics-mcp.mcpb");
12
+ const temp = mkdtempSync(join(tmpdir(), "dimensions-mcpb-smoke-"));
13
+ const client = new Client({ name: "mcpb-smoke", version: "1.0.0" });
14
+
15
+ try {
16
+ if (!(await unpackExtension({ mcpbPath: output, outputDir: temp, silent: true }))) {
17
+ throw new Error("Could not unpack the MCPB");
18
+ }
19
+ const manifest = JSON.parse(readFileSync(join(temp, "manifest.json"), "utf8"));
20
+ const pkg = JSON.parse(readFileSync(join(root, "apps/mcp/package.json"), "utf8"));
21
+ if (manifest.version !== pkg.version) throw new Error("Bundle version differs from package");
22
+ const fixture = JSON.parse(
23
+ readFileSync(join(root, "apps/mcp/test/fixtures/describe-schema.json"), "utf8"),
24
+ );
25
+ const cache = join(temp, "schema.json");
26
+ writeFileSync(cache, JSON.stringify({ cachedAt: new Date().toISOString(), schema: fixture }));
27
+
28
+ const transport = new StdioClientTransport({
29
+ command: process.execPath,
30
+ args: [join(temp, manifest.server.entry_point)],
31
+ env: {
32
+ ...process.env,
33
+ DIMENSIONS_API_KEY: "smoke-test-key",
34
+ DIMENSIONS_MCP_UPDATE_CHECK: "0",
35
+ DIMENSIONS_MCP_AUTO_UPDATE: "0",
36
+ SCHEMA_CACHE_PATH: cache,
37
+ },
38
+ });
39
+ const timeout = setTimeout(() => {
40
+ console.error("MCPB smoke test timed out");
41
+ process.exitCode = 1;
42
+ void transport.close();
43
+ }, 20_000);
44
+ try {
45
+ await client.connect(transport);
46
+ const tools = await client.listTools();
47
+ if (!tools.tools.some((tool) => tool.name === "search_publications")) {
48
+ throw new Error("Bundled server did not expose search_publications");
49
+ }
50
+ console.log(`MCPB smoke test passed (${tools.tools.length} tools)`);
51
+ } finally {
52
+ clearTimeout(timeout);
53
+ await client.close();
54
+ }
55
+ } finally {
56
+ rmSync(temp, { recursive: true, force: true });
57
+ }
@@ -32,7 +32,10 @@ export const PAGINATION_INPUT_SCHEMA = {
32
32
  export const SHARED_SEARCH_INPUT = {
33
33
  query: z
34
34
  .string()
35
- .describe("Search query terms (omit with filters-only search if the API allows)"),
35
+ .optional()
36
+ .describe(
37
+ "Free-text search terms. Optional — omit for a filters-only search that relies solely on `filters` and/or the entity-specific convenience filters (e.g. yearFrom/yearTo).",
38
+ ),
36
39
  limit: z
37
40
  .number()
38
41
  .int()
@@ -50,7 +53,11 @@ export const SHARED_SEARCH_INPUT = {
50
53
  filters: z
51
54
  .array(ExtendedWhereFilterSchema)
52
55
  .optional()
53
- .describe("Additional where-clause filters"),
56
+ .describe(
57
+ "Additional where-clause filters. Each item needs field, operator, and (except for is_empty/is_not_empty) value, " +
58
+ 'e.g. {"field": "researchers.id", "operator": "=", "value": "ur.01343614074.49"}. ' +
59
+ "Supported operators: =, !=, >, <, >=, <=, in, is_empty, is_not_empty.",
60
+ ),
54
61
  confirmLargeFetch: z
55
62
  .boolean()
56
63
  .optional()
@@ -407,6 +407,25 @@ describe("search tools", () => {
407
407
  const dsl = client.rawQuery.mock.calls[0][0] as string;
408
408
  expect(dsl).toContain("is empty");
409
409
  });
410
+
411
+ it("supports a filters-only search with no query", async () => {
412
+ client.rawQuery.mockResolvedValue(
413
+ apiRows("publications", [{ id: "pub1", title: "Filters-only Paper" }], 1),
414
+ );
415
+
416
+ const result = await callTool(handlers, "search_publications", {
417
+ filters: [{ field: "researchers.id", operator: "=", value: "ur.014403456555.45" }],
418
+ sortBy: "year",
419
+ limit: 100,
420
+ });
421
+
422
+ expect(result.isError).toBeFalsy();
423
+ const dsl = client.rawQuery.mock.calls[0][0] as string;
424
+ expect(dsl).toContain("search publications");
425
+ expect(dsl).not.toContain(" for ");
426
+ expect(dsl).toContain("researchers.id");
427
+ expect(parseToolResult(result).publications).toHaveLength(1);
428
+ });
410
429
  });
411
430
 
412
431
  describe("pagination", () => {