@digital-science-dsl/dimensions-analytics-mcp 0.5.3 → 1.0.1

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.
Files changed (218) hide show
  1. package/CHANGELOG.md +4 -56
  2. package/README.md +1 -5
  3. package/dist/client/deployment-config.d.ts +20 -2
  4. package/dist/client/deployment-config.d.ts.map +1 -1
  5. package/dist/client/deployment-config.js +26 -2
  6. package/dist/client/deployment-config.js.map +1 -1
  7. package/dist/client/internal-dsl-client.d.ts +1 -1
  8. package/dist/client/internal-dsl-client.d.ts.map +1 -1
  9. package/dist/client/internal-dsl-client.js +2 -5
  10. package/dist/client/internal-dsl-client.js.map +1 -1
  11. package/dist/dsl/create-client.d.ts +1 -1
  12. package/dist/dsl/create-client.js +1 -1
  13. package/dist/dsl/fluent-query-builder.d.ts +1 -1
  14. package/dist/dsl/fluent-query-builder.js +1 -1
  15. package/dist/dsl/schema/cache.d.ts +2 -2
  16. package/dist/dsl/schema/cache.d.ts.map +1 -1
  17. package/dist/dsl/schema/cache.js +8 -14
  18. package/dist/dsl/schema/cache.js.map +1 -1
  19. package/dist/dsl/schema/load.d.ts +0 -1
  20. package/dist/dsl/schema/load.d.ts.map +1 -1
  21. package/dist/dsl/schema/load.js +1 -3
  22. package/dist/dsl/schema/load.js.map +1 -1
  23. package/dist/mcp/tools/schema.d.ts +0 -2
  24. package/dist/mcp/tools/schema.d.ts.map +1 -1
  25. package/dist/mcp/tools/schema.js.map +1 -1
  26. package/dist/mcp/utils.d.ts +0 -4
  27. package/dist/mcp/utils.d.ts.map +1 -1
  28. package/dist/mcp/utils.js +0 -7
  29. package/dist/mcp/utils.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/client/deployment-config.ts +39 -2
  32. package/src/client/internal-dsl-client.ts +2 -6
  33. package/src/dsl/create-client.ts +1 -1
  34. package/src/dsl/fluent-query-builder.ts +1 -1
  35. package/src/dsl/schema/cache.ts +8 -13
  36. package/src/dsl/schema/load.ts +1 -4
  37. package/src/mcp/tools/schema.ts +0 -3
  38. package/src/mcp/utils.ts +0 -12
  39. package/test/client/deployment-config.test.ts +25 -1
  40. package/test/client/http-client.test.ts +2 -2
  41. package/test/client/internal-dsl-client.test.ts +3 -0
  42. package/test/dsl/schema/cache.test.ts +2 -4
  43. package/test/helpers/tool-test-harness.ts +2 -19
  44. package/test/middleware/field-aliases.test.ts +1 -1
  45. package/test/tools/analytics.test.ts +2 -2
  46. package/tsconfig.tsbuildinfo +1 -1
  47. package/dist/examples/dsl-examples.d.ts +0 -11
  48. package/dist/examples/dsl-examples.d.ts.map +0 -1
  49. package/dist/examples/dsl-examples.js +0 -49
  50. package/dist/examples/dsl-examples.js.map +0 -1
  51. package/dist/examples/usage-scenarios.d.ts +0 -22
  52. package/dist/examples/usage-scenarios.d.ts.map +0 -1
  53. package/dist/examples/usage-scenarios.js +0 -173
  54. package/dist/examples/usage-scenarios.js.map +0 -1
  55. package/dist/funder-org-names.d.ts +0 -12
  56. package/dist/funder-org-names.d.ts.map +0 -1
  57. package/dist/funder-org-names.js +0 -30
  58. package/dist/funder-org-names.js.map +0 -1
  59. package/dist/http.d.ts +0 -29
  60. package/dist/http.d.ts.map +0 -1
  61. package/dist/http.js +0 -173
  62. package/dist/http.js.map +0 -1
  63. package/dist/kwq/build-query.d.ts +0 -26
  64. package/dist/kwq/build-query.d.ts.map +0 -1
  65. package/dist/kwq/build-query.js +0 -37
  66. package/dist/kwq/build-query.js.map +0 -1
  67. package/dist/kwq/concept-groups.d.ts +0 -49
  68. package/dist/kwq/concept-groups.d.ts.map +0 -1
  69. package/dist/kwq/concept-groups.js +0 -55
  70. package/dist/kwq/concept-groups.js.map +0 -1
  71. package/dist/kwq/dsl-assembler.d.ts +0 -28
  72. package/dist/kwq/dsl-assembler.d.ts.map +0 -1
  73. package/dist/kwq/dsl-assembler.js +0 -124
  74. package/dist/kwq/dsl-assembler.js.map +0 -1
  75. package/dist/kwq/ensemble.d.ts +0 -38
  76. package/dist/kwq/ensemble.d.ts.map +0 -1
  77. package/dist/kwq/ensemble.js +0 -107
  78. package/dist/kwq/ensemble.js.map +0 -1
  79. package/dist/kwq/index.d.ts +0 -16
  80. package/dist/kwq/index.d.ts.map +0 -1
  81. package/dist/kwq/index.js +0 -14
  82. package/dist/kwq/index.js.map +0 -1
  83. package/dist/kwq/types.d.ts +0 -120
  84. package/dist/kwq/types.d.ts.map +0 -1
  85. package/dist/kwq/types.js +0 -11
  86. package/dist/kwq/types.js.map +0 -1
  87. package/dist/lambda.d.ts +0 -10
  88. package/dist/lambda.d.ts.map +0 -1
  89. package/dist/lambda.js +0 -4
  90. package/dist/lambda.js.map +0 -1
  91. package/dist/mcp/tools/concept-profile.d.ts +0 -14
  92. package/dist/mcp/tools/concept-profile.d.ts.map +0 -1
  93. package/dist/mcp/tools/concept-profile.js +0 -130
  94. package/dist/mcp/tools/concept-profile.js.map +0 -1
  95. package/dist/mcp/tools/dataset-profile.d.ts +0 -15
  96. package/dist/mcp/tools/dataset-profile.d.ts.map +0 -1
  97. package/dist/mcp/tools/dataset-profile.js +0 -194
  98. package/dist/mcp/tools/dataset-profile.js.map +0 -1
  99. package/dist/mcp/tools/impact-chain.d.ts +0 -15
  100. package/dist/mcp/tools/impact-chain.d.ts.map +0 -1
  101. package/dist/mcp/tools/impact-chain.js +0 -96
  102. package/dist/mcp/tools/impact-chain.js.map +0 -1
  103. package/dist/mcp/tools/kwq.d.ts +0 -19
  104. package/dist/mcp/tools/kwq.d.ts.map +0 -1
  105. package/dist/mcp/tools/kwq.js +0 -156
  106. package/dist/mcp/tools/kwq.js.map +0 -1
  107. package/dist/mcp/tools/policy-profile.d.ts +0 -15
  108. package/dist/mcp/tools/policy-profile.d.ts.map +0 -1
  109. package/dist/mcp/tools/policy-profile.js +0 -174
  110. package/dist/mcp/tools/policy-profile.js.map +0 -1
  111. package/dist/mcp/tools/profile.d.ts +0 -14
  112. package/dist/mcp/tools/profile.d.ts.map +0 -1
  113. package/dist/mcp/tools/profile.js +0 -349
  114. package/dist/mcp/tools/profile.js.map +0 -1
  115. package/dist/middleware/field-aliases.d.ts +0 -97
  116. package/dist/middleware/field-aliases.d.ts.map +0 -1
  117. package/dist/middleware/field-aliases.js +0 -208
  118. package/dist/middleware/field-aliases.js.map +0 -1
  119. package/dist/resources/schema.d.ts +0 -13
  120. package/dist/resources/schema.d.ts.map +0 -1
  121. package/dist/resources/schema.js +0 -204
  122. package/dist/resources/schema.js.map +0 -1
  123. package/dist/schema/context.d.ts +0 -10
  124. package/dist/schema/context.d.ts.map +0 -1
  125. package/dist/schema/context.js +0 -6
  126. package/dist/schema/context.js.map +0 -1
  127. package/dist/schema/index.d.ts +0 -9
  128. package/dist/schema/index.d.ts.map +0 -1
  129. package/dist/schema/index.js +0 -6
  130. package/dist/schema/index.js.map +0 -1
  131. package/dist/schema/load.d.ts +0 -43
  132. package/dist/schema/load.d.ts.map +0 -1
  133. package/dist/schema/load.js +0 -139
  134. package/dist/schema/load.js.map +0 -1
  135. package/dist/schema/store.d.ts +0 -112
  136. package/dist/schema/store.d.ts.map +0 -1
  137. package/dist/schema/store.js +0 -173
  138. package/dist/schema/store.js.map +0 -1
  139. package/dist/schema/structured-entities.d.ts +0 -21
  140. package/dist/schema/structured-entities.d.ts.map +0 -1
  141. package/dist/schema/structured-entities.js +0 -39
  142. package/dist/schema/structured-entities.js.map +0 -1
  143. package/dist/schema/types.d.ts +0 -36
  144. package/dist/schema/types.d.ts.map +0 -1
  145. package/dist/schema/types.js +0 -6
  146. package/dist/schema/types.js.map +0 -1
  147. package/dist/server.d.ts +0 -57
  148. package/dist/server.d.ts.map +0 -1
  149. package/dist/server.js +0 -144
  150. package/dist/server.js.map +0 -1
  151. package/dist/telemetry.d.ts +0 -172
  152. package/dist/telemetry.d.ts.map +0 -1
  153. package/dist/telemetry.js +0 -233
  154. package/dist/telemetry.js.map +0 -1
  155. package/dist/tools/analytics-filters.d.ts +0 -18
  156. package/dist/tools/analytics-filters.d.ts.map +0 -1
  157. package/dist/tools/analytics-filters.js +0 -35
  158. package/dist/tools/analytics-filters.js.map +0 -1
  159. package/dist/tools/analytics.d.ts +0 -16
  160. package/dist/tools/analytics.d.ts.map +0 -1
  161. package/dist/tools/analytics.js +0 -381
  162. package/dist/tools/analytics.js.map +0 -1
  163. package/dist/tools/concept-profile.d.ts +0 -14
  164. package/dist/tools/concept-profile.d.ts.map +0 -1
  165. package/dist/tools/concept-profile.js +0 -130
  166. package/dist/tools/concept-profile.js.map +0 -1
  167. package/dist/tools/dataset-profile.d.ts +0 -15
  168. package/dist/tools/dataset-profile.d.ts.map +0 -1
  169. package/dist/tools/dataset-profile.js +0 -194
  170. package/dist/tools/dataset-profile.js.map +0 -1
  171. package/dist/tools/functions.d.ts +0 -14
  172. package/dist/tools/functions.d.ts.map +0 -1
  173. package/dist/tools/functions.js +0 -294
  174. package/dist/tools/functions.js.map +0 -1
  175. package/dist/tools/impact-chain.d.ts +0 -15
  176. package/dist/tools/impact-chain.d.ts.map +0 -1
  177. package/dist/tools/impact-chain.js +0 -96
  178. package/dist/tools/impact-chain.js.map +0 -1
  179. package/dist/tools/kwq.d.ts +0 -19
  180. package/dist/tools/kwq.d.ts.map +0 -1
  181. package/dist/tools/kwq.js +0 -156
  182. package/dist/tools/kwq.js.map +0 -1
  183. package/dist/tools/lookup.d.ts +0 -14
  184. package/dist/tools/lookup.d.ts.map +0 -1
  185. package/dist/tools/lookup.js +0 -213
  186. package/dist/tools/lookup.js.map +0 -1
  187. package/dist/tools/policy-profile.d.ts +0 -15
  188. package/dist/tools/policy-profile.d.ts.map +0 -1
  189. package/dist/tools/policy-profile.js +0 -174
  190. package/dist/tools/policy-profile.js.map +0 -1
  191. package/dist/tools/profile.d.ts +0 -14
  192. package/dist/tools/profile.d.ts.map +0 -1
  193. package/dist/tools/profile.js +0 -349
  194. package/dist/tools/profile.js.map +0 -1
  195. package/dist/tools/query.d.ts +0 -16
  196. package/dist/tools/query.d.ts.map +0 -1
  197. package/dist/tools/query.js +0 -53
  198. package/dist/tools/query.js.map +0 -1
  199. package/dist/tools/schema.d.ts +0 -23
  200. package/dist/tools/schema.d.ts.map +0 -1
  201. package/dist/tools/schema.js +0 -124
  202. package/dist/tools/schema.js.map +0 -1
  203. package/dist/tools/search-entity-metadata.d.ts +0 -18
  204. package/dist/tools/search-entity-metadata.d.ts.map +0 -1
  205. package/dist/tools/search-entity-metadata.js +0 -221
  206. package/dist/tools/search-entity-metadata.js.map +0 -1
  207. package/dist/tools/search.d.ts +0 -23
  208. package/dist/tools/search.d.ts.map +0 -1
  209. package/dist/tools/search.js +0 -110
  210. package/dist/tools/search.js.map +0 -1
  211. package/dist/tools/solr-fields.d.ts +0 -39
  212. package/dist/tools/solr-fields.d.ts.map +0 -1
  213. package/dist/tools/solr-fields.js +0 -40
  214. package/dist/tools/solr-fields.js.map +0 -1
  215. package/dist/utils.d.ts +0 -70
  216. package/dist/utils.d.ts.map +0 -1
  217. package/dist/utils.js +0 -104
  218. package/dist/utils.js.map +0 -1
package/dist/server.js DELETED
@@ -1,144 +0,0 @@
1
- /**
2
- * MCP server for Dimensions Analytics API.
3
- * Provides tools for searching and querying the Dimensions database.
4
- * @module mcp/server
5
- */
6
- import { DimensionsClient } from "@digital-science/dimensions-dsl";
7
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
- import pkg from "../package.json" with { type: "json" };
10
- import { registerSchemaResources } from "./resources/schema.js";
11
- import { clearSchemaCache, getOrLoadSchema, loadSchema } from "./schema/index.js";
12
- import { registerAnalyticsTools } from "./tools/analytics.js";
13
- import { registerConceptProfileTools } from "./tools/concept-profile.js";
14
- import { registerDatasetProfileTools } from "./tools/dataset-profile.js";
15
- import { registerFunctionTools } from "./tools/functions.js";
16
- import { registerImpactChainTools } from "./tools/impact-chain.js";
17
- import { registerKwqTools } from "./tools/kwq.js";
18
- import { registerLookupTools } from "./tools/lookup.js";
19
- import { registerPolicyProfileTools } from "./tools/policy-profile.js";
20
- import { registerProfileTools } from "./tools/profile.js";
21
- import { registerQueryTools } from "./tools/query.js";
22
- import { registerSchemaTools, validateFieldAliases } from "./tools/schema.js";
23
- import { registerSearchTools } from "./tools/search.js";
24
- /**
25
- * Reads a positive integer from an environment variable with a default fallback.
26
- * @param raw - Environment variable value
27
- * @param fallback - Default when unset or invalid
28
- * @returns Parsed integer
29
- */
30
- function readIntEnv(raw, fallback, min) {
31
- if (raw == null || raw.trim() === "")
32
- return fallback;
33
- const value = Number.parseInt(raw, 10);
34
- return Number.isFinite(value) && value >= min ? value : fallback;
35
- }
36
- /**
37
- * Builds server instructions from loaded schema.
38
- * @param schemaStore - Loaded describe schema
39
- * @returns Instruction string for MCP clients
40
- */
41
- export function buildServerInstructions(schemaStore) {
42
- const version = schemaStore.version ? ` DSL ${schemaStore.version}.` : "";
43
- return [
44
- `Dimensions Analytics API — scholarly research data.${version}`,
45
- [
46
- "Workflow:",
47
- "(1) Discover schema — read dimensions://schema/summary, dimensions://fields/{entity}, dimensions://examples, or describe_schema;",
48
- "(2) Search — search_* for keyword discovery with filters; get_by_doi, get_by_pmid, get_by_id for known identifiers;",
49
- "(3) Analyze — facet_query, aggregate_query, citation_trend, funding_trend;",
50
- "(4) Drill down — author_profile, org_profile, concept_profile, research_impact_chain, dataset_profile, policy_document_profile.",
51
- ].join(" "),
52
- [
53
- "Routing:",
54
- "search_researchers matches names only — topic→researcher uses facet_query (entityType publications, facetField researchers);",
55
- "search_grants funderOrgName needs exact Dimensions names (NCI/NSF acronyms resolve; discover funders via facet_query/aggregate_query on facetField funder_orgs);",
56
- "org_profile prefers organizationId (GRID id) when the name is ambiguous;",
57
- "facet_query supports yearFrom/yearTo for year-scoped facets.",
58
- ].join(" "),
59
- [
60
- "Query construction:",
61
- "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;",
62
- "use build_query or ensemble_queries for boolean concept groups;",
63
- "use execute_dsl only when structured tools are insufficient; DSL order is return ... sort by FIELD order limit N (never limit before sort).",
64
- ].join(" "),
65
- "Large result sets: structured search returns at most 1000 rows (default 100); if truncated, narrow filters or paginate with execute_dsl skip/limit.",
66
- ].join("\n\n");
67
- }
68
- /**
69
- * Creates and configures the MCP server.
70
- * Loads describe schema at startup unless `schemaStore` is provided.
71
- * @param config - Optional configuration
72
- * @returns Configured MCP server, client, schema context, and store
73
- */
74
- export async function createMcpServerAsync(config = {}) {
75
- const apiKey = config.apiKey ?? process.env.DIMENSIONS_API_KEY;
76
- if (!apiKey) {
77
- throw new Error("Dimensions API key required. Set DIMENSIONS_API_KEY environment variable or pass apiKey in config.");
78
- }
79
- const client = new DimensionsClient({
80
- apiKey,
81
- baseUrl: config.baseUrl ?? process.env.DIMENSIONS_BASE_URL,
82
- maxRetries: readIntEnv(process.env.DIMENSIONS_MAX_RETRIES, 3, 0),
83
- retryDelay: readIntEnv(process.env.DIMENSIONS_RETRY_DELAY_MS, 1000, 1),
84
- rateLimitPerMinute: readIntEnv(process.env.DIMENSIONS_RATE_LIMIT_PER_MINUTE, 30, 1),
85
- });
86
- const schemaStore = config.schemaStore ?? (await getOrLoadSchema(client, process.env.SCHEMA_CACHE_PATH));
87
- client.attachSchemaStore(schemaStore);
88
- const schemaContext = { store: schemaStore };
89
- validateFieldAliases(schemaStore);
90
- const startedAt = Date.now();
91
- console.error(`Schema loaded: ${schemaStore.stats().sourceCount} sources, ${schemaStore.stats().entityCount} entities` +
92
- (schemaStore.version ? `, DSL ${schemaStore.version}` : "") +
93
- (schemaStore.stale ? " (stale cache)" : "") +
94
- ` [${schemaStore.loadSource}]` +
95
- ` (${Date.now() - startedAt}ms)`);
96
- const server = new McpServer({
97
- name: "dimensions",
98
- version: pkg.version,
99
- }, {
100
- capabilities: {
101
- logging: {},
102
- },
103
- instructions: buildServerInstructions(schemaStore),
104
- });
105
- registerSchemaResources(server, schemaContext);
106
- registerSearchTools(server, client, schemaStore);
107
- registerLookupTools(server, client);
108
- registerProfileTools(server, client);
109
- registerQueryTools(server, client, schemaStore);
110
- registerFunctionTools(server, client);
111
- registerAnalyticsTools(server, client, schemaStore);
112
- registerConceptProfileTools(server, client);
113
- registerImpactChainTools(server, client);
114
- registerDatasetProfileTools(server, client);
115
- registerPolicyProfileTools(server, client);
116
- registerKwqTools(server, client, schemaStore);
117
- registerSchemaTools(server, client, schemaContext);
118
- return { server, client, schemaContext, schemaStore };
119
- }
120
- /**
121
- * Starts the MCP server with the given transport (defaults to stdio).
122
- * @param injectedTransport - Optional transport (useful for testing)
123
- * @param config - Optional server configuration
124
- * @returns Shutdown handle
125
- */
126
- export async function startMcpServer(injectedTransport, config = {}) {
127
- const { server } = await createMcpServerAsync(config);
128
- const transport = injectedTransport ?? new StdioServerTransport();
129
- server.server.onerror = (error) => console.error(`MCP server error: ${error.message}`);
130
- if (!injectedTransport) {
131
- server.server.onclose = () => {
132
- process.exit(0);
133
- };
134
- }
135
- await server.connect(transport);
136
- return {
137
- close: async () => {
138
- await server.close();
139
- },
140
- };
141
- }
142
- /** Force reload schema from API (clears module cache). */
143
- export { clearSchemaCache, loadSchema, McpServer };
144
- //# sourceMappingURL=server.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -1,172 +0,0 @@
1
- /**
2
- * Mixpanel telemetry for the MCP server.
3
- *
4
- * Tracks one event per tool invocation (`mcp_tool_call`) with: tool name,
5
- * outcome (success / tool_error / exception), latency, transport, the
6
- * caller's distinct id, plus a truncated capture of the tool arguments
7
- * and response so analysts can reconstruct user conversations after the
8
- * fact. Captures are clipped to {@link MAX_CAPTURE_CHARS} characters per
9
- * field — long enough to read the prompt, short enough to keep events
10
- * cheap and to avoid mirroring full result payloads into Mixpanel.
11
- *
12
- * Telemetry is opt-in: if no {@link TelemetryConfig.client} is provided,
13
- * {@link createTelemetry} returns a no-op so callers can wire it
14
- * unconditionally without worrying about missing-credential branches.
15
- * Callers typically construct that client with
16
- * {@link initMixpanelClient}, which itself returns `undefined` when no
17
- * token is supplied.
18
- *
19
- * Stdio (local) callers should leave this off unless an operator has
20
- * explicitly enabled it for their fleet — by default `main.ts` does not
21
- * initialize and pass a client, even if `MIXPANEL_TOKEN` is set in the
22
- * user's shell.
23
- *
24
- * @module mcp/telemetry
25
- */
26
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
27
- /** Outcome categories tracked for every tool invocation. */
28
- export type ToolOutcome = "success" | "tool_error" | "exception";
29
- /** Subset of the mixpanel-node client surface we depend on (for testability). */
30
- export interface MixpanelClient {
31
- track(event: string, properties: Record<string, unknown>, callback?: (err: Error | undefined) => void): void;
32
- }
33
- /**
34
- * Mixpanel EU data residency endpoint. Default host because the Lambda is
35
- * deployed to `eu-west-1` and our project lives in the EU region — sending
36
- * to the default US host (`api.mixpanel.com`) would either drop events on
37
- * the floor or leak EU user identifiers across the Atlantic.
38
- *
39
- * Override with `MIXPANEL_HOST` (or the `host` argument to
40
- * {@link initMixpanelClient}) for projects on the US data center or a
41
- * regional residency endpoint we don't yet handle by default.
42
- *
43
- * @see https://docs.mixpanel.com/docs/privacy/eu-residency
44
- */
45
- export declare const MIXPANEL_DEFAULT_HOST = "api-eu.mixpanel.com";
46
- /**
47
- * Source label attached to every event. Lets analysts segment AI Analytics
48
- * traffic from other Mixpanel integrations sharing the same project — and
49
- * keeps the human-readable name stable even if package names or transport
50
- * details change later.
51
- */
52
- export declare const TELEMETRY_SOURCE = "AI Analytics MCP";
53
- /**
54
- * Maximum captured length, in characters, for the per-event `args` and
55
- * `response` fields. Mixpanel's per-event payload limit is 1MB but practical
56
- * UI surfaces (Insights, Funnels, raw event view) truncate long strings, so
57
- * we keep these short enough to read at a glance while still preserving
58
- * the verbatim user query — long enough to recover the prompt, short
59
- * enough that an attacker exfiltrating event logs doesn't get the full
60
- * result body.
61
- */
62
- export declare const MAX_CAPTURE_CHARS = 500;
63
- /**
64
- * Initializes the shared Mixpanel client. Returns `undefined` when no token
65
- * is supplied so the rest of the wiring can stay no-op without branching.
66
- *
67
- * Call once at module load (HTTP transport) — the underlying mixpanel-node
68
- * client maintains a keep-alive HTTPS agent that's wasteful to recreate per
69
- * Lambda invocation.
70
- *
71
- * @param token - Mixpanel project token; falsy values disable telemetry.
72
- * @param host - Mixpanel ingestion host; defaults to {@link MIXPANEL_DEFAULT_HOST}
73
- * (EU residency). Pass an empty string or omit to use the default — empty
74
- * strings are treated the same as `undefined` so SST secrets that resolve
75
- * to `""` don't accidentally point Mixpanel at an empty host.
76
- */
77
- export declare function initMixpanelClient(token: string | undefined, host?: string): MixpanelClient | undefined;
78
- /** Configuration for a per-request telemetry instance. */
79
- export interface TelemetryConfig {
80
- /**
81
- * Shared Mixpanel client (from {@link initMixpanelClient}). Omit/undefined
82
- * to disable telemetry — every method becomes a no-op.
83
- */
84
- readonly client?: MixpanelClient;
85
- /** Stable identifier for the caller (e.g. JWT `sub`). Falls back to `"anonymous"`. */
86
- readonly distinctId?: string;
87
- /** Which transport invoked the server. */
88
- readonly transport: "http" | "stdio";
89
- /** MCP server package version, recorded on each event. */
90
- readonly serverVersion: string;
91
- /** Deployment stage / environment name (e.g. `"production"`, `"dev"`). Optional. */
92
- readonly stage?: string;
93
- /**
94
- * Per-HTTP-request correlation id. Lets analysts cluster co-batched
95
- * tool calls (multiple JSON-RPC calls in one POST) into a single
96
- * conversational turn even though the server is otherwise stateless.
97
- * Pair with `distinct_id` (cross-turn) and Mixpanel's event timestamp
98
- * (chronology) to reconstruct a user's session. Optional — omit on
99
- * stdio where there's no transport-level request boundary.
100
- */
101
- readonly requestId?: string;
102
- }
103
- /** Recorded fields for a single tool call. */
104
- export interface ToolCallEvent {
105
- /** Tool name as registered with the MCP server (e.g. `"search_publications"`). */
106
- readonly tool: string;
107
- /** Whether the call succeeded, returned an error result, or threw. */
108
- readonly outcome: ToolOutcome;
109
- /** Wall-clock duration of the handler in milliseconds. */
110
- readonly durationMs: number;
111
- /** Error class name when {@link outcome} is `"exception"`; omitted for `"success"` and `"tool_error"`. */
112
- readonly errorType?: string;
113
- /**
114
- * Verbatim tool arguments (JSON-stringified, clipped to
115
- * {@link MAX_CAPTURE_CHARS}). Empty string when the handler took no
116
- * arguments or the value couldn't be serialized.
117
- */
118
- readonly args: string;
119
- /**
120
- * First {@link MAX_CAPTURE_CHARS} characters of the tool's response.
121
- * Prefer the first `text` content block; falls back to a JSON-stringified
122
- * snippet of the whole result. Empty string for thrown exceptions
123
- * (there's no response to capture in that case).
124
- */
125
- readonly response: string;
126
- }
127
- /** Public telemetry surface — safe to call when telemetry is disabled. */
128
- export interface Telemetry {
129
- /** Record a single tool invocation. Fire-and-forget. */
130
- trackToolCall(event: ToolCallEvent): void;
131
- /**
132
- * Wait for any in-flight events to be sent. Lambda freezes the event loop
133
- * the moment the response is returned, so HTTP callers must `await` this
134
- * before completing the request — otherwise events are silently dropped on
135
- * cold containers.
136
- */
137
- flush(): Promise<void>;
138
- }
139
- /**
140
- * Creates a per-request telemetry instance. Returns a no-op implementation
141
- * when no Mixpanel client is provided.
142
- * @param config - Telemetry configuration.
143
- * @returns A {@link Telemetry} instance.
144
- */
145
- export declare function createTelemetry(config: TelemetryConfig): Telemetry;
146
- /**
147
- * Truncates a string to {@link MAX_CAPTURE_CHARS} characters. Exposed so
148
- * tests can assert length without re-importing the constant.
149
- *
150
- * Operates on JS code units, not extended grapheme clusters — clipping a
151
- * 4-byte emoji at exactly the limit can leave half a surrogate pair, but
152
- * Mixpanel handles invalid UTF-16 silently and the tradeoff (precise
153
- * length budget) is worth more here than perfect grapheme boundaries.
154
- *
155
- * @param value - Source string.
156
- * @returns Same string when within budget, otherwise the leading prefix.
157
- */
158
- export declare function truncateForCapture(value: string): string;
159
- /**
160
- * Wraps {@link McpServer.registerTool} so every subsequent tool registration
161
- * has its handler instrumented with timing, outcome reporting, and a
162
- * truncated capture of the tool args + response. Must be called *before*
163
- * tools are registered.
164
- *
165
- * The wrapper re-throws thrown exceptions so the SDK still surfaces them to
166
- * the client — telemetry is observational, never an error sink.
167
- *
168
- * @param server - The MCP server whose registrations should be instrumented.
169
- * @param telemetry - Telemetry sink (no-op when telemetry is disabled).
170
- */
171
- export declare function instrumentToolRegistrations(server: McpServer, telemetry: Telemetry): void;
172
- //# sourceMappingURL=telemetry.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,4DAA4D;AAC5D,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,CAAC;AAEjE,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,KAAK,CACH,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,SAAS,KAAK,IAAI,GAC1C,IAAI,CAAC;CACT;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAEnD;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,IAAI,CAAC,EAAE,MAAM,GACZ,cAAc,GAAG,SAAS,CAI5B;AAED,0DAA0D;AAC1D,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;IACjC,sFAAsF;IACtF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IACrC,0DAA0D;IAC1D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC5B,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,0GAA0G;IAC1G,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,0EAA0E;AAC1E,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;IAC1C;;;;;OAKG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAOD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CA4ClE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAgDD;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CAwCzF"}
package/dist/telemetry.js DELETED
@@ -1,233 +0,0 @@
1
- /**
2
- * Mixpanel telemetry for the MCP server.
3
- *
4
- * Tracks one event per tool invocation (`mcp_tool_call`) with: tool name,
5
- * outcome (success / tool_error / exception), latency, transport, the
6
- * caller's distinct id, plus a truncated capture of the tool arguments
7
- * and response so analysts can reconstruct user conversations after the
8
- * fact. Captures are clipped to {@link MAX_CAPTURE_CHARS} characters per
9
- * field — long enough to read the prompt, short enough to keep events
10
- * cheap and to avoid mirroring full result payloads into Mixpanel.
11
- *
12
- * Telemetry is opt-in: if no {@link TelemetryConfig.client} is provided,
13
- * {@link createTelemetry} returns a no-op so callers can wire it
14
- * unconditionally without worrying about missing-credential branches.
15
- * Callers typically construct that client with
16
- * {@link initMixpanelClient}, which itself returns `undefined` when no
17
- * token is supplied.
18
- *
19
- * Stdio (local) callers should leave this off unless an operator has
20
- * explicitly enabled it for their fleet — by default `main.ts` does not
21
- * initialize and pass a client, even if `MIXPANEL_TOKEN` is set in the
22
- * user's shell.
23
- *
24
- * @module mcp/telemetry
25
- */
26
- import Mixpanel from "mixpanel";
27
- /**
28
- * Mixpanel EU data residency endpoint. Default host because the Lambda is
29
- * deployed to `eu-west-1` and our project lives in the EU region — sending
30
- * to the default US host (`api.mixpanel.com`) would either drop events on
31
- * the floor or leak EU user identifiers across the Atlantic.
32
- *
33
- * Override with `MIXPANEL_HOST` (or the `host` argument to
34
- * {@link initMixpanelClient}) for projects on the US data center or a
35
- * regional residency endpoint we don't yet handle by default.
36
- *
37
- * @see https://docs.mixpanel.com/docs/privacy/eu-residency
38
- */
39
- export const MIXPANEL_DEFAULT_HOST = "api-eu.mixpanel.com";
40
- /**
41
- * Source label attached to every event. Lets analysts segment AI Analytics
42
- * traffic from other Mixpanel integrations sharing the same project — and
43
- * keeps the human-readable name stable even if package names or transport
44
- * details change later.
45
- */
46
- export const TELEMETRY_SOURCE = "AI Analytics MCP";
47
- /**
48
- * Maximum captured length, in characters, for the per-event `args` and
49
- * `response` fields. Mixpanel's per-event payload limit is 1MB but practical
50
- * UI surfaces (Insights, Funnels, raw event view) truncate long strings, so
51
- * we keep these short enough to read at a glance while still preserving
52
- * the verbatim user query — long enough to recover the prompt, short
53
- * enough that an attacker exfiltrating event logs doesn't get the full
54
- * result body.
55
- */
56
- export const MAX_CAPTURE_CHARS = 500;
57
- /**
58
- * Initializes the shared Mixpanel client. Returns `undefined` when no token
59
- * is supplied so the rest of the wiring can stay no-op without branching.
60
- *
61
- * Call once at module load (HTTP transport) — the underlying mixpanel-node
62
- * client maintains a keep-alive HTTPS agent that's wasteful to recreate per
63
- * Lambda invocation.
64
- *
65
- * @param token - Mixpanel project token; falsy values disable telemetry.
66
- * @param host - Mixpanel ingestion host; defaults to {@link MIXPANEL_DEFAULT_HOST}
67
- * (EU residency). Pass an empty string or omit to use the default — empty
68
- * strings are treated the same as `undefined` so SST secrets that resolve
69
- * to `""` don't accidentally point Mixpanel at an empty host.
70
- */
71
- export function initMixpanelClient(token, host) {
72
- if (!token)
73
- return undefined;
74
- const effectiveHost = host && host.length > 0 ? host : MIXPANEL_DEFAULT_HOST;
75
- return Mixpanel.init(token, { host: effectiveHost });
76
- }
77
- const NOOP = {
78
- trackToolCall: () => { },
79
- flush: () => Promise.resolve(),
80
- };
81
- /**
82
- * Creates a per-request telemetry instance. Returns a no-op implementation
83
- * when no Mixpanel client is provided.
84
- * @param config - Telemetry configuration.
85
- * @returns A {@link Telemetry} instance.
86
- */
87
- export function createTelemetry(config) {
88
- if (!config.client)
89
- return NOOP;
90
- const client = config.client;
91
- const distinctId = config.distinctId ?? "anonymous";
92
- // Each track() schedules one HTTPS POST. We collect those promises so
93
- // flush() can await them before the Lambda freezes.
94
- const pending = new Set();
95
- const trackToolCall = (event) => {
96
- const promise = new Promise((resolve) => {
97
- client.track("mcp_tool_call", {
98
- distinct_id: distinctId,
99
- source: TELEMETRY_SOURCE,
100
- tool: event.tool,
101
- outcome: event.outcome,
102
- duration_ms: event.durationMs,
103
- transport: config.transport,
104
- server_version: config.serverVersion,
105
- args: event.args,
106
- response: event.response,
107
- ...(event.errorType ? { error_type: event.errorType } : {}),
108
- ...(config.stage ? { stage: config.stage } : {}),
109
- ...(config.requestId ? { request_id: config.requestId } : {}),
110
- },
111
- // Mixpanel's API never rejects on network errors — it logs and
112
- // returns. We swallow either way: telemetry must not affect the
113
- // caller's response.
114
- () => resolve());
115
- });
116
- pending.add(promise);
117
- promise.finally(() => pending.delete(promise));
118
- };
119
- const flush = async () => {
120
- if (pending.size === 0)
121
- return;
122
- await Promise.all(pending);
123
- };
124
- return { trackToolCall, flush };
125
- }
126
- /**
127
- * Truncates a string to {@link MAX_CAPTURE_CHARS} characters. Exposed so
128
- * tests can assert length without re-importing the constant.
129
- *
130
- * Operates on JS code units, not extended grapheme clusters — clipping a
131
- * 4-byte emoji at exactly the limit can leave half a surrogate pair, but
132
- * Mixpanel handles invalid UTF-16 silently and the tradeoff (precise
133
- * length budget) is worth more here than perfect grapheme boundaries.
134
- *
135
- * @param value - Source string.
136
- * @returns Same string when within budget, otherwise the leading prefix.
137
- */
138
- export function truncateForCapture(value) {
139
- return value.length <= MAX_CAPTURE_CHARS ? value : value.slice(0, MAX_CAPTURE_CHARS);
140
- }
141
- /**
142
- * Captures the verbatim tool arguments for telemetry. The MCP SDK passes
143
- * the parsed input object as the first arg to the handler — JSON-stringify
144
- * it and clip. Returns an empty string when the input is missing or
145
- * un-serializable (e.g. circular refs); telemetry must never throw and
146
- * never block the response.
147
- *
148
- * @param handlerArgs - The variadic args the SDK passed into the handler.
149
- * @returns Truncated JSON capture of the input object.
150
- */
151
- function captureArgs(handlerArgs) {
152
- if (handlerArgs.length === 0)
153
- return "";
154
- try {
155
- return truncateForCapture(JSON.stringify(handlerArgs[0] ?? {}));
156
- }
157
- catch {
158
- return "";
159
- }
160
- }
161
- /**
162
- * Captures a synopsis of the tool's response. MCP results have shape
163
- * `{ content: [{type, text, ...}], isError?: boolean }` — prefer the first
164
- * `text` content block (that's what the LLM actually reads), falling back
165
- * to a JSON snippet of the whole result on non-standard shapes.
166
- *
167
- * @param result - Whatever the wrapped handler returned (may be `undefined`).
168
- * @returns First {@link MAX_CAPTURE_CHARS} chars of the response synopsis.
169
- */
170
- function captureResponse(result) {
171
- if (result == null || typeof result !== "object")
172
- return "";
173
- const r = result;
174
- if (Array.isArray(r.content)) {
175
- const firstText = r.content.find((c) => c?.type === "text" && typeof c?.text === "string");
176
- if (firstText && typeof firstText.text === "string") {
177
- return truncateForCapture(firstText.text);
178
- }
179
- }
180
- try {
181
- return truncateForCapture(JSON.stringify(result));
182
- }
183
- catch {
184
- return "";
185
- }
186
- }
187
- /**
188
- * Wraps {@link McpServer.registerTool} so every subsequent tool registration
189
- * has its handler instrumented with timing, outcome reporting, and a
190
- * truncated capture of the tool args + response. Must be called *before*
191
- * tools are registered.
192
- *
193
- * The wrapper re-throws thrown exceptions so the SDK still surfaces them to
194
- * the client — telemetry is observational, never an error sink.
195
- *
196
- * @param server - The MCP server whose registrations should be instrumented.
197
- * @param telemetry - Telemetry sink (no-op when telemetry is disabled).
198
- */
199
- export function instrumentToolRegistrations(server, telemetry) {
200
- const original = server.registerTool.bind(server);
201
- const wrapped = (name, config, handler) => {
202
- const instrumented = async (...handlerArgs) => {
203
- const startedAt = Date.now();
204
- const args = captureArgs(handlerArgs);
205
- try {
206
- const result = (await handler(...handlerArgs));
207
- telemetry.trackToolCall({
208
- tool: name,
209
- outcome: result?.isError ? "tool_error" : "success",
210
- durationMs: Date.now() - startedAt,
211
- args,
212
- response: captureResponse(result),
213
- });
214
- return result;
215
- }
216
- catch (error) {
217
- telemetry.trackToolCall({
218
- tool: name,
219
- outcome: "exception",
220
- durationMs: Date.now() - startedAt,
221
- errorType: error instanceof Error ? error.name : "Error",
222
- args,
223
- // No response to capture — the handler threw before returning.
224
- response: "",
225
- });
226
- throw error;
227
- }
228
- };
229
- return original(name, config, instrumented);
230
- };
231
- server.registerTool = wrapped;
232
- }
233
- //# sourceMappingURL=telemetry.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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"}
@@ -1,18 +0,0 @@
1
- /**
2
- * Shared filter helpers for analytics tools (facet_query, aggregate_query).
3
- * @module mcp/tools/analytics-filters
4
- */
5
- import type { EntityType, QueryBuilder } from "@digital-science/dimensions-dsl";
6
- import { type ExtendedWhereFilterInput } from "@digital-science/dimensions-dsl";
7
- /**
8
- * Applies year range and extended where filters to an analytics query builder.
9
- * @param builder - Query builder for the target entity
10
- * @param entityType - Entity being analyzed
11
- * @param args - Tool arguments with optional yearFrom, yearTo, filters
12
- */
13
- export declare function applyAnalyticsFilters(builder: QueryBuilder, entityType: EntityType, args: {
14
- yearFrom?: number;
15
- yearTo?: number;
16
- filters?: readonly ExtendedWhereFilterInput[];
17
- }): void;
18
- //# sourceMappingURL=analytics-filters.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"analytics-filters.d.ts","sourceRoot":"","sources":["../../src/tools/analytics-filters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,iCAAiC,CAAC;AAYzC;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE;IACJ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,SAAS,wBAAwB,EAAE,CAAC;CAC/C,GACA,IAAI,CAaN"}
@@ -1,35 +0,0 @@
1
- /**
2
- * Shared filter helpers for analytics tools (facet_query, aggregate_query).
3
- * @module mcp/tools/analytics-filters
4
- */
5
- import { applyFilters, } from "@digital-science/dimensions-dsl";
6
- /** Primary year field per entity for convenience yearFrom/yearTo filters. */
7
- const ENTITY_YEAR_FIELD = {
8
- publications: "year",
9
- grants: "start_year",
10
- patents: "year",
11
- clinical_trials: "year",
12
- datasets: "year",
13
- policy_documents: "year",
14
- };
15
- /**
16
- * Applies year range and extended where filters to an analytics query builder.
17
- * @param builder - Query builder for the target entity
18
- * @param entityType - Entity being analyzed
19
- * @param args - Tool arguments with optional yearFrom, yearTo, filters
20
- */
21
- export function applyAnalyticsFilters(builder, entityType, args) {
22
- const yearField = ENTITY_YEAR_FIELD[entityType];
23
- if (yearField) {
24
- if (typeof args.yearFrom === "number") {
25
- builder.where(yearField, ">=", args.yearFrom);
26
- }
27
- if (typeof args.yearTo === "number") {
28
- builder.where(yearField, "<=", args.yearTo);
29
- }
30
- }
31
- if (args.filters?.length) {
32
- applyFilters(builder, args.filters);
33
- }
34
- }
35
- //# sourceMappingURL=analytics-filters.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"analytics-filters.js","sourceRoot":"","sources":["../../src/tools/analytics-filters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,YAAY,GAEb,MAAM,iCAAiC,CAAC;AAEzC,6EAA6E;AAC7E,MAAM,iBAAiB,GAAwC;IAC7D,YAAY,EAAE,MAAM;IACpB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,MAAM;IACf,eAAe,EAAE,MAAM;IACvB,QAAQ,EAAE,MAAM;IAChB,gBAAgB,EAAE,MAAM;CACzB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAqB,EACrB,UAAsB,EACtB,IAIC;IAED,MAAM,SAAS,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAChD,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACtC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChD,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACpC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QACzB,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;AACH,CAAC"}
@@ -1,16 +0,0 @@
1
- /**
2
- * Analytics tools for the MCP server.
3
- * Provides tools for facet distribution analysis, metric aggregation, and time-series trends.
4
- * @module mcp/tools/analytics
5
- */
6
- import type { DimensionsClient } from "@digital-science/dimensions-dsl";
7
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
- import type { SchemaStore } from "../schema/index.js";
9
- /**
10
- * Registers all analytics tools with the MCP server.
11
- * @param server - MCP server instance
12
- * @param client - Dimensions client instance
13
- * @param schemaStore - Loaded describe schema
14
- */
15
- export declare function registerAnalyticsTools(server: McpServer, client: DimensionsClient, schemaStore: SchemaStore): void;
16
- //# sourceMappingURL=analytics.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/tools/analytics.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAY,gBAAgB,EAAc,MAAM,iCAAiC,CAAC;AAO9F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AA4FtD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,WAAW,GACvB,IAAI,CA6UN"}