@cyanheads/pubmed-mcp-server 1.0.12 → 1.0.13

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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![TypeScript](https://img.shields.io/badge/TypeScript-^5.8.3-blue.svg)](https://www.typescriptlang.org/)
4
4
  [![Model Context Protocol SDK](https://img.shields.io/badge/MCP%20SDK-1.12.0-green.svg)](https://github.com/modelcontextprotocol/typescript-sdk)
5
5
  [![MCP Spec Version](https://img.shields.io/badge/MCP%20Spec-2025--03--26-lightgrey.svg)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/changelog.mdx)
6
- [![Version](https://img.shields.io/badge/Version-1.0.12-blue.svg)](./CHANGELOG.md)
6
+ [![Version](https://img.shields.io/badge/Version-1.0.13-blue.svg)](./CHANGELOG.md)
7
7
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
8
8
  [![Status](https://img.shields.io/badge/Status-Beta-orange.svg)](https://github.com/cyanheads/pubmed-mcp-server/issues)
9
9
  [![GitHub](https://img.shields.io/github/stars/cyanheads/pubmed-mcp-server?style=social)](https://github.com/cyanheads/pubmed-mcp-server)
@@ -16,12 +16,13 @@ This server acts as a bridge, connecting your AI to NCBI's PubMed and E-utilitie
16
16
 
17
17
  This server equips your AI with specialized tools to interact with PubMed:
18
18
 
19
- | Tool Name | Description | Key Features | Output Structure (JSON Example) |
20
- | :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
21
- | `search_pubmed_articles` | Searches PubMed for articles based on your query. | - Filter by max results, sort order, date range, publication types.<br/>- Uses NCBI ESearch for PMIDs.<br/>- Optionally fetches brief summaries (title, authors, source, dates) via ESummary. | `{"searchParameters": {"queryTerm": "oncology", "maxResults": 5},"effectiveESearchTerm": "oncology","totalFound": 250,"retrievedPmidCount": 5,"pmids": ["12345", "67890"],"briefSummaries": [{"pmid": "12345", "title": "Example Title"}],"eSearchUrl": "https://ncbi...","eSummaryUrl": "https://ncbi..."}` |
22
- | `fetch_pubmed_content` | Retrieves detailed information for PubMed articles. Can use a list of PMIDs or ESearch history (queryKey/webEnv) with pagination. | - Flexible `detailLevel`: `abstract_plus` (parsed details, optional MeSH/grant), `full_xml` (JSON representation of the PubMedArticle XML structure), `medline_text` (MEDLINE format), `citation_data` (minimal for citations).<br/>- Supports direct PMID list or `queryKey`/`webEnv` from ESearch history.<br/>- Supports `retstart`/`retmax` for pagination with history.<br/>- Uses NCBI EFetch. | `{"requestedPmids": ["12345"],"articles": [{"pmid": "12345", "title": "Example Title", "abstractText": "..."}],"notFoundPmids": [],"eFetchDetails": {"urls": ["https://ncbi..."], "requestMethod": "GET"}}` (Or raw string for`detailLevel: 'full_xml'/'medline_text'`with`outputFormat: 'raw_text'`) |
23
- | `get_pubmed_article_connections` | Finds related articles (cited by, similar, references) or formats citations for a PMID. | - Uses NCBI ELink for relationships.<br/>- Uses NCBI EFetch for citation data (RIS, BibTeX, APA, MLA).<br/>- Filter by max related results. | `{"sourcePmid": "12345","relationshipType": "pubmed_similar_articles","relatedArticles": [{"pmid": "67890", "title": "Related Title"}],"citations": {"ris": "RIS formatted citation..." },"retrievedCount": 1,"eUtilityUrl": "https://ncbi...","message": "Success"}` |
24
- | `pubmed_research_agent` | Generates a standardized JSON research plan outline from component details. | - Accepts granular inputs for all research phases.<br/>- Optionally embeds instructive prompts for agent execution.<br/>- Structures rough ideas into a formal, machine-readable plan for further processing. | `{"plan_title": "Comprehensive Research Plan on Topic X","input_summary": {"keywords_received": ["Topic X", "Gene Y"]},"phase_1_conception_and_planning": {"step_1_1_research_question_and_hypothesis": {"primary_research_question": "What is the role of Gene Y in Topic X?"}},"phase_2_data_collection_and_processing": {}}` |
19
+ | Tool Name | Description | Key Features |
20
+ | :-------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
21
+ | [`search_pubmed_articles`](./src/mcp-server/tools/searchPubMedArticles/) | Searches PubMed for articles based on your query. (See [Example](./examples/search_pubmed_articles_example.md)) | - Filter by max results, sort order, date range, publication types.<br/>- Uses NCBI ESearch for PMIDs.<br/>- Optionally fetches brief summaries (title, authors, source, dates) via ESummary. |
22
+ | [`fetch_pubmed_content`](./src/mcp-server/tools/fetchPubMedContent/) | Retrieves detailed information for PubMed articles. Can use a list of PMIDs or ESearch history (queryKey/webEnv) with pagination. (See [Example](./examples/fetch_pubmed_content_example.md)) | - Flexible `detailLevel`: `abstract_plus` (parsed details, optional MeSH/grant), `full_xml` (JSON representation of the PubMedArticle XML structure), `medline_text` (MEDLINE format), `citation_data` (minimal for citations).<br/>- Supports direct PMID list or `queryKey`/`webEnv` from ESearch history.<br/>- Supports `retstart`/`retmax` for pagination with history.<br/>- Uses NCBI EFetch. |
23
+ | [`get_pubmed_article_connections`](./src/mcp-server/tools/getPubMedArticleConnections/) | Finds related articles (cited by, similar, references) or formats citations for a PMID. (See [Ex.1](./examples/get_pubmed_article_connections_1.md), [Ex.2](./examples/get_pubmed_article_connections_2.md)) | - Uses NCBI ELink for relationships.<br/>- Uses NCBI EFetch for citation data (RIS, BibTeX, APA, MLA).<br/>- Filter by max related results. |
24
+ | [`pubmed_research_agent`](./src/mcp-server/tools/pubmedResearchAgent/) | Generates a standardized JSON research plan outline from component details. (See [Example](./examples/pubmed_research_agent_example.md)) | - Accepts granular inputs for all research phases.<br/>- Optionally embeds instructive prompts for agent execution.<br/>- Structures rough ideas into a formal, machine-readable plan for further processing. |
25
+ | [`generate_pubmed_chart`](./src/mcp-server/tools/generatePubMedChart/) | Generates a chart image (SVG) from given input data. (See [Bar](./examples/generate_pubmed_chart_example_bar.svg), [Line](./examples/generate_pubmed_chart_example_line.svg), [Scatter](./examples/generate_pubmed_chart_example_scatter.svg)) | - Supports 'bar', 'line', and 'scatter' chart types.<br/>- Takes data values and field specifications for axes and encoding.<br/>- Constructs a Vega-Lite specification internally and renders it as an SVG. |
25
26
 
26
27
  ---
27
28
 
@@ -64,11 +65,13 @@ Get the PubMed MCP server running in minutes:
64
65
  # RECOMMENDED FOR NCBI E-UTILITIES (for higher rate limits)
65
66
  # NCBI_API_KEY=your_ncbi_api_key_here
66
67
  # NCBI_ADMIN_EMAIL=your_email@example.com # Recommended if using an API key
67
- # NCBI_TOOL_IDENTIFIER=@cyanheads/pubmed-mcp-server/1.0.11 # Optional: Tool identifier for NCBI (defaults to current version)
68
+ # NCBI_TOOL_IDENTIFIER=@cyanheads/pubmed-mcp-server/1.0.13 # Optional: Tool identifier for NCBI (defaults to current version)
68
69
  ```
69
70
 
70
71
  For all options, see the [Configuration](#⚙️-configuration) section below or the [Developer Cheatsheet (.clinerules)](./.clinerules).
71
72
 
73
+ New example files for each tool are available in the `examples/` directory (e.g., `examples/search_pubmed_articles_example.md`, `examples/generate_pubmed_chart_example_bar.svg`).
74
+
72
75
  4. **Build the project:**
73
76
 
74
77
  ```bash
@@ -18,6 +18,8 @@ import { config } from "../config/index.js";
18
18
  import { ErrorHandler, logger, requestContextService } from "../utils/index.js";
19
19
  // import { registerEchoResource } from "./resources/echoResource/index.js"; // To be removed after resource implementations.
20
20
  import { registerFetchPubMedContentTool } from "./tools/fetchPubMedContent/index.js";
21
+ // Removed: import { registerFetchImageTestTool } from "./tools/imageTest/index.js";
22
+ import { registerGeneratePubMedChartTool } from "./tools/generatePubMedChart/index.js"; // Added import
21
23
  import { registerGetPubMedArticleConnectionsTool } from "./tools/getPubMedArticleConnections/index.js";
22
24
  import { registerPubMedResearchAgentTool } from "./tools/pubmedResearchAgent/index.js"; // Added import
23
25
  import { registerSearchPubMedArticlesTool } from "./tools/searchPubMedArticles/index.js";
@@ -68,6 +70,8 @@ async function createMcpServerInstance() {
68
70
  logger.debug("Registering resources and tools...", context);
69
71
  // IMPORTANT: Keep tool registrations in alphabetical order. Do not remove this comment.
70
72
  await registerFetchPubMedContentTool(server);
73
+ // Removed: await registerFetchImageTestTool(server);
74
+ await registerGeneratePubMedChartTool(server); // Added new tool registration
71
75
  await registerGetPubMedArticleConnectionsTool(server);
72
76
  await registerPubMedResearchAgentTool(server); // Added new tool registration
73
77
  await registerSearchPubMedArticlesTool(server);
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Barrel export for the 'generatePubMedChart' tool.
3
+ * This file re-exports the registration function for the tool,
4
+ * making it easier to import and register with the MCP server.
5
+ * @module src/mcp-server/tools/generatePubMedChart/index
6
+ */
7
+ export * from "./registration.js";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Barrel export for the 'generatePubMedChart' tool.
3
+ * This file re-exports the registration function for the tool,
4
+ * making it easier to import and register with the MCP server.
5
+ * @module src/mcp-server/tools/generatePubMedChart/index
6
+ */
7
+ export * from "./registration.js";
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @fileoverview Core logic for the generate_pubmed_chart tool.
3
+ * Generates charts from parameterized input by creating Vega-Lite specifications.
4
+ * @module src/mcp-server/tools/generatePubMedChart/logic
5
+ */
6
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
7
+ import { z } from "zod";
8
+ import { RequestContext } from "../../../utils/index.js";
9
+ export declare const GeneratePubMedChartInputSchema: z.ZodObject<{
10
+ chartType: z.ZodEnum<["bar", "line", "scatter"]>;
11
+ title: z.ZodOptional<z.ZodString>;
12
+ width: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
13
+ height: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
14
+ dataValues: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodAny>, "many">;
15
+ outputFormat: z.ZodDefault<z.ZodEnum<["svg"]>>;
16
+ xField: z.ZodString;
17
+ yField: z.ZodString;
18
+ xFieldType: z.ZodOptional<z.ZodEnum<["nominal", "ordinal", "quantitative", "temporal"]>>;
19
+ yFieldType: z.ZodOptional<z.ZodEnum<["nominal", "ordinal", "quantitative", "temporal"]>>;
20
+ colorField: z.ZodOptional<z.ZodString>;
21
+ colorFieldType: z.ZodOptional<z.ZodEnum<["nominal", "ordinal", "quantitative", "temporal"]>>;
22
+ seriesField: z.ZodOptional<z.ZodString>;
23
+ seriesFieldType: z.ZodOptional<z.ZodEnum<["nominal", "ordinal", "quantitative", "temporal"]>>;
24
+ sizeField: z.ZodOptional<z.ZodString>;
25
+ sizeFieldType: z.ZodOptional<z.ZodEnum<["quantitative", "ordinal"]>>;
26
+ }, "strip", z.ZodTypeAny, {
27
+ width: number;
28
+ height: number;
29
+ outputFormat: "svg";
30
+ chartType: "bar" | "line" | "scatter";
31
+ dataValues: Record<string, any>[];
32
+ xField: string;
33
+ yField: string;
34
+ title?: string | undefined;
35
+ xFieldType?: "nominal" | "ordinal" | "quantitative" | "temporal" | undefined;
36
+ yFieldType?: "nominal" | "ordinal" | "quantitative" | "temporal" | undefined;
37
+ colorField?: string | undefined;
38
+ colorFieldType?: "nominal" | "ordinal" | "quantitative" | "temporal" | undefined;
39
+ seriesField?: string | undefined;
40
+ seriesFieldType?: "nominal" | "ordinal" | "quantitative" | "temporal" | undefined;
41
+ sizeField?: string | undefined;
42
+ sizeFieldType?: "ordinal" | "quantitative" | undefined;
43
+ }, {
44
+ chartType: "bar" | "line" | "scatter";
45
+ dataValues: Record<string, any>[];
46
+ xField: string;
47
+ yField: string;
48
+ title?: string | undefined;
49
+ width?: number | undefined;
50
+ height?: number | undefined;
51
+ outputFormat?: "svg" | undefined;
52
+ xFieldType?: "nominal" | "ordinal" | "quantitative" | "temporal" | undefined;
53
+ yFieldType?: "nominal" | "ordinal" | "quantitative" | "temporal" | undefined;
54
+ colorField?: string | undefined;
55
+ colorFieldType?: "nominal" | "ordinal" | "quantitative" | "temporal" | undefined;
56
+ seriesField?: string | undefined;
57
+ seriesFieldType?: "nominal" | "ordinal" | "quantitative" | "temporal" | undefined;
58
+ sizeField?: string | undefined;
59
+ sizeFieldType?: "ordinal" | "quantitative" | undefined;
60
+ }>;
61
+ export type GeneratePubMedChartInput = z.infer<typeof GeneratePubMedChartInputSchema>;
62
+ export declare function generatePubMedChartLogic(input: GeneratePubMedChartInput, parentRequestContext: RequestContext): Promise<CallToolResult>;
@@ -0,0 +1,240 @@
1
+ import * as vega from "vega";
2
+ import * as vegaLite from "vega-lite";
3
+ import { z } from "zod";
4
+ import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
5
+ import { logger, requestContextService, sanitizeInputForLogging, } from "../../../utils/index.js";
6
+ export const GeneratePubMedChartInputSchema = z.object({
7
+ chartType: z
8
+ .enum(["bar", "line", "scatter"])
9
+ .describe("Type of chart to generate (e.g., 'bar', 'line', 'scatter')."),
10
+ title: z.string().optional().describe("Title for the chart."),
11
+ width: z
12
+ .number()
13
+ .int()
14
+ .positive()
15
+ .optional()
16
+ .default(400)
17
+ .describe("Width of the chart in pixels."),
18
+ height: z
19
+ .number()
20
+ .int()
21
+ .positive()
22
+ .optional()
23
+ .default(300)
24
+ .describe("Height of the chart in pixels."),
25
+ dataValues: z
26
+ .array(z.record(z.string(), z.any()))
27
+ .min(1)
28
+ .describe("Array of data objects to plot. Each object is a record (e.g., { category: 'A', amount: 28 })."),
29
+ outputFormat: z
30
+ .enum(["svg"])
31
+ .default("svg")
32
+ .describe("Specifies the desired output format for the generated chart. " +
33
+ "Currently, only 'svg' (Scalable Vector Graphics) is supported."),
34
+ xField: z
35
+ .string()
36
+ .describe("Field name for the X-axis (e.g., 'category', 'date', 'metric1')."),
37
+ yField: z
38
+ .string()
39
+ .describe("Field name for the Y-axis (e.g., 'amount', 'value', 'metric2')."),
40
+ xFieldType: z
41
+ .enum(["nominal", "ordinal", "quantitative", "temporal"])
42
+ .optional()
43
+ .describe("Type of the X-axis field. Defaults appropriately for chart type if omitted (e.g., 'nominal' for bar, 'temporal' for line, 'quantitative' for scatter)."),
44
+ yFieldType: z
45
+ .enum(["nominal", "ordinal", "quantitative", "temporal"])
46
+ .optional()
47
+ .describe("Type of the Y-axis field. Defaults to 'quantitative' if omitted."),
48
+ // Optional fields for various chart types
49
+ colorField: z
50
+ .string()
51
+ .optional()
52
+ .describe("Optional field name for color encoding."),
53
+ colorFieldType: z
54
+ .enum(["nominal", "ordinal", "quantitative", "temporal"])
55
+ .optional()
56
+ .describe("Type of the color field if provided. Defaults to 'nominal'."),
57
+ seriesField: z
58
+ .string()
59
+ .optional()
60
+ .describe("Optional field name for creating multiple lines/series (typically for line charts)."),
61
+ seriesFieldType: z
62
+ .enum(["nominal", "ordinal", "quantitative", "temporal"])
63
+ .optional()
64
+ .describe("Type of the series field if provided. Defaults to 'nominal'."),
65
+ // Scatter plot specific optional fields (can be expanded)
66
+ sizeField: z
67
+ .string()
68
+ .optional()
69
+ .describe("Optional field for encoding point size in scatter plots."),
70
+ sizeFieldType: z
71
+ .enum(["quantitative", "ordinal"])
72
+ .optional()
73
+ .describe("Type of the size field. Defaults to 'quantitative'."),
74
+ // shapeField: z.string().optional().describe("Optional field for encoding point shape in scatter plots."), // Future enhancement
75
+ // shapeFieldType: z.enum(["nominal", "ordinal"]).optional().describe("Type of the shape field."), // Future enhancement
76
+ });
77
+ export async function generatePubMedChartLogic(input, parentRequestContext) {
78
+ const operationContext = requestContextService.createRequestContext({
79
+ parentRequestId: parentRequestContext.requestId,
80
+ operation: "generatePubMedChartLogicExecution",
81
+ input: sanitizeInputForLogging(input),
82
+ });
83
+ logger.info(`Executing 'generate_pubmed_chart'. Chart type: ${input.chartType}, Output format: ${input.outputFormat}`, operationContext);
84
+ if (input.outputFormat !== "svg") {
85
+ const unsupportedFormatError = new McpError(BaseErrorCode.VALIDATION_ERROR, `Unsupported output format: ${input.outputFormat}. Currently, only 'svg' is supported.`, { requestedFormat: input.outputFormat });
86
+ logger.warning(unsupportedFormatError.message, operationContext);
87
+ return {
88
+ content: [
89
+ {
90
+ type: "text",
91
+ text: JSON.stringify({
92
+ error: {
93
+ code: unsupportedFormatError.code,
94
+ message: unsupportedFormatError.message,
95
+ details: unsupportedFormatError.details,
96
+ },
97
+ }),
98
+ },
99
+ ],
100
+ isError: true,
101
+ };
102
+ }
103
+ try {
104
+ let vegaLiteSpec = {
105
+ $schema: "https://vega.github.io/schema/vega-lite/v5.json",
106
+ title: input.title,
107
+ width: input.width,
108
+ height: input.height,
109
+ data: { values: input.dataValues },
110
+ encoding: {}, // To be populated by chart type specific logic
111
+ };
112
+ // Default field types if not provided
113
+ const yEncType = input.yFieldType || "quantitative";
114
+ const colorEncType = input.colorFieldType || "nominal";
115
+ const seriesEncType = input.seriesFieldType || "nominal";
116
+ const sizeEncType = input.sizeFieldType || "quantitative";
117
+ let xEncType;
118
+ switch (input.chartType) {
119
+ case "bar":
120
+ xEncType = input.xFieldType || "nominal";
121
+ vegaLiteSpec.mark = "bar";
122
+ vegaLiteSpec.encoding = {
123
+ x: {
124
+ field: input.xField,
125
+ type: xEncType,
126
+ axis: { title: input.xField },
127
+ },
128
+ y: {
129
+ field: input.yField,
130
+ type: yEncType,
131
+ axis: { title: input.yField },
132
+ },
133
+ };
134
+ if (input.colorField) {
135
+ vegaLiteSpec.encoding.color = {
136
+ field: input.colorField,
137
+ type: colorEncType,
138
+ };
139
+ }
140
+ break;
141
+ case "line":
142
+ xEncType = input.xFieldType || "temporal";
143
+ vegaLiteSpec.mark = "line";
144
+ vegaLiteSpec.encoding = {
145
+ x: {
146
+ field: input.xField,
147
+ type: xEncType,
148
+ axis: { title: input.xField },
149
+ },
150
+ y: {
151
+ field: input.yField,
152
+ type: yEncType,
153
+ axis: { title: input.yField },
154
+ },
155
+ };
156
+ if (input.seriesField) {
157
+ // For line charts, seriesField is typically used for color
158
+ vegaLiteSpec.encoding.color = {
159
+ field: input.seriesField,
160
+ type: seriesEncType,
161
+ };
162
+ }
163
+ else if (input.colorField) {
164
+ // Allow direct colorField as well
165
+ vegaLiteSpec.encoding.color = {
166
+ field: input.colorField,
167
+ type: colorEncType,
168
+ };
169
+ }
170
+ break;
171
+ case "scatter":
172
+ xEncType = input.xFieldType || "quantitative";
173
+ vegaLiteSpec.mark = "point"; // "circle" is also an option
174
+ vegaLiteSpec.encoding = {
175
+ x: {
176
+ field: input.xField,
177
+ type: xEncType,
178
+ axis: { title: input.xField },
179
+ },
180
+ y: {
181
+ field: input.yField,
182
+ type: yEncType,
183
+ axis: { title: input.yField },
184
+ },
185
+ };
186
+ if (input.colorField) {
187
+ vegaLiteSpec.encoding.color = {
188
+ field: input.colorField,
189
+ type: colorEncType,
190
+ };
191
+ }
192
+ if (input.sizeField) {
193
+ vegaLiteSpec.encoding.size = {
194
+ field: input.sizeField,
195
+ type: sizeEncType,
196
+ };
197
+ }
198
+ // Add shape encoding here if shapeField is implemented
199
+ break;
200
+ // No default case needed as chartType is an enum and Zod validates it.
201
+ }
202
+ const compiledVegaSpec = vegaLite.compile(vegaLiteSpec).spec;
203
+ const view = new vega.View(vega.parse(compiledVegaSpec), {
204
+ renderer: "none",
205
+ });
206
+ const svgString = await view.toSVG();
207
+ const imageBuffer = Buffer.from(svgString, "utf-8");
208
+ const base64Data = imageBuffer.toString("base64");
209
+ const dataUriSvg = `data:image/svg+xml;base64,${base64Data}`;
210
+ return {
211
+ content: [{ type: "text", text: dataUriSvg }],
212
+ isError: false,
213
+ };
214
+ }
215
+ catch (error) {
216
+ logger.error("Execution failed for 'generate_pubmed_chart'", error, operationContext);
217
+ const mcpError = error instanceof McpError
218
+ ? error
219
+ : new McpError(BaseErrorCode.INTERNAL_ERROR, `'generate_pubmed_chart' failed: ${error.message || "Internal server error during chart generation."}`, {
220
+ originalErrorName: error.name,
221
+ originalErrorMessage: error.message,
222
+ requestId: operationContext.requestId,
223
+ });
224
+ return {
225
+ content: [
226
+ {
227
+ type: "text",
228
+ text: JSON.stringify({
229
+ error: {
230
+ code: mcpError.code,
231
+ message: mcpError.message,
232
+ details: mcpError.details,
233
+ },
234
+ }),
235
+ },
236
+ ],
237
+ isError: true,
238
+ };
239
+ }
240
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Registers the 'generate_pubmed_chart' tool with the MCP server.
3
+ * This tool now accepts parameterized input for generating charts.
4
+ * @module src/mcp-server/tools/generatePubMedChart/registration
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ export declare function registerGeneratePubMedChartTool(server: McpServer): void;
@@ -0,0 +1,35 @@
1
+ import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
2
+ import { ErrorHandler, logger, requestContextService, } from "../../../utils/index.js";
3
+ import { GeneratePubMedChartInputSchema, generatePubMedChartLogic, } from "./logic.js";
4
+ export function registerGeneratePubMedChartTool(server) {
5
+ const operation = "registerGeneratePubMedChartTool";
6
+ const regContext = requestContextService.createRequestContext({ operation });
7
+ try {
8
+ server.tool("generate_pubmed_chart", "Generates a chart image (SVG) from given input. " +
9
+ "Supports different chart types like 'bar', 'line', and 'scatter'. " +
10
+ "Provide data values and specify fields for axes and encoding. " +
11
+ "The tool constructs a Vega-Lite specification internally and renders it as an SVG.", GeneratePubMedChartInputSchema.shape, async (validatedInput, mcpProvidedContext) => {
12
+ const handlerRequestContext = requestContextService.createRequestContext({
13
+ parentRequestId: regContext.requestId,
14
+ operation: "generatePubMedChartToolHandler",
15
+ mcpToolContext: mcpProvidedContext,
16
+ });
17
+ return generatePubMedChartLogic(validatedInput, handlerRequestContext);
18
+ });
19
+ logger.notice(`Tool 'generate_pubmed_chart' registered with updated schema (added scatter).`, regContext);
20
+ }
21
+ catch (error) {
22
+ const mcpError = error instanceof McpError
23
+ ? error
24
+ : new McpError(BaseErrorCode.INITIALIZATION_FAILED, `Failed to register 'generate_pubmed_chart': ${error instanceof Error ? error.message : String(error)}`, {
25
+ originalErrorName: error instanceof Error ? error.name : "UnknownError",
26
+ details: "Error during server.tool() call for generate_pubmed_chart.",
27
+ });
28
+ ErrorHandler.handleError(mcpError, {
29
+ operation,
30
+ context: regContext,
31
+ errorCode: BaseErrorCode.INITIALIZATION_FAILED,
32
+ critical: true,
33
+ });
34
+ }
35
+ }
@@ -4,4 +4,4 @@
4
4
  * for accessing various services within the application.
5
5
  * @module src/services/index
6
6
  */
7
- export * from "./llm-providers";
7
+ export * from "./llm-providers/index.js";
@@ -4,4 +4,4 @@
4
4
  * for accessing various services within the application.
5
5
  * @module src/services/index
6
6
  */
7
- export * from "./llm-providers";
7
+ export * from "./llm-providers/index.js";
@@ -4,4 +4,4 @@
4
4
  * making them easily accessible from a single import path.
5
5
  * @module src/services/llm-providers/index
6
6
  */
7
- export * from "./openRouter";
7
+ export * from "./openRouter/index.js";
@@ -4,4 +4,4 @@
4
4
  * making them easily accessible from a single import path.
5
5
  * @module src/services/llm-providers/index
6
6
  */
7
- export * from "./openRouter"; // Changed to export from the new barrel file
7
+ export * from "./openRouter/index.js"; // Changed to export from the new barrel file
@@ -3,4 +3,4 @@
3
3
  * Exports the OpenRouterProvider class and any related types.
4
4
  * @module services/llm-providers/openRouter/index
5
5
  */
6
- export * from "./openRouterProvider";
6
+ export * from "./openRouterProvider.js";
@@ -3,5 +3,5 @@
3
3
  * Exports the OpenRouterProvider class and any related types.
4
4
  * @module services/llm-providers/openRouter/index
5
5
  */
6
- export * from "./openRouterProvider";
6
+ export * from "./openRouterProvider.js";
7
7
  // Add other exports from this module if any in the future
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/pubmed-mcp-server",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "A Model Context Protocol (MCP) server enabling AI agents to intelligently search, retrieve, and analyze biomedical literature from PubMed via NCBI E-utilities. Built on the mcp-ts-template for robust, production-ready performance.",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -33,19 +33,19 @@
33
33
  "start:client-cli": "node dist/mcp-client/cli/mcp-client-cli.js"
34
34
  },
35
35
  "dependencies": {
36
- "axios": "^1.9.0",
37
- "fast-xml-parser": "^5.2.3",
38
36
  "@google/genai": "^1.0.1",
39
37
  "@modelcontextprotocol/sdk": "^1.12.0",
40
38
  "@types/jsonwebtoken": "^9.0.9",
41
39
  "@types/node": "^22.15.21",
42
40
  "@types/sanitize-html": "^2.16.0",
43
41
  "@types/validator": "13.15.1",
42
+ "axios": "^1.9.0",
44
43
  "chalk": "^5.4.1",
45
44
  "chrono-node": "^2.8.0",
46
45
  "cli-table3": "^0.6.5",
47
46
  "dotenv": "^16.5.0",
48
47
  "express": "^5.1.0",
48
+ "fast-xml-parser": "^5.2.3",
49
49
  "ignore": "^7.0.4",
50
50
  "jsonwebtoken": "^9.0.2",
51
51
  "openai": "^4.103.0",
@@ -55,6 +55,8 @@
55
55
  "ts-node": "^10.9.2",
56
56
  "typescript": "^5.8.3",
57
57
  "validator": "13.15.0",
58
+ "vega": "^6.1.2",
59
+ "vega-lite": "^6.1.0",
58
60
  "winston": "^3.17.0",
59
61
  "winston-daily-rotate-file": "^5.0.0",
60
62
  "yargs": "^17.7.2",