@cyanheads/pubmed-mcp-server
Version:
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.
91 lines (90 loc) • 4.81 kB
JavaScript
/**
* @fileoverview Main logic handler for the 'get_pubmed_article_connections' MCP tool.
* Orchestrates calls to ELink or citation formatting handlers.
* @module src/mcp-server/tools/getPubMedArticleConnections/logic/index
*/
import { z } from "zod";
import { BaseErrorCode, McpError } from "../../../../types-global/errors.js";
import { logger, requestContextService, sanitizeInputForLogging, } from "../../../../utils/index.js";
import { handleCitationFormats } from "./citationFormatter.js";
import { handleELinkRelationships } from "./elinkHandler.js";
/**
* Zod schema for the input parameters of the 'get_pubmed_article_connections' tool.
*/
export const GetPubMedArticleConnectionsInputSchema = z.object({
sourcePmid: z
.string()
.regex(/^\d+$/)
.describe("The PubMed Unique Identifier (PMID) of the source article for which to find connections or format citations. This PMID must be a valid number string."),
relationshipType: z
.enum([
"pubmed_similar_articles",
"pubmed_citedin",
"pubmed_references",
"citation_formats",
])
.default("pubmed_similar_articles")
.describe("Specifies the type of connection or action: \n- 'pubmed_similar_articles': Finds articles similar to the source PMID (uses ELink `cmd=neighbor`). \n- 'pubmed_citedin': Finds articles in PubMed that cite the source PMID (uses ELink `linkname=pubmed_pubmed_citedin`). \n- 'pubmed_references': Finds articles in PubMed referenced by the source PMID (uses ELink `linkname=pubmed_pubmed_refs`). \n- 'citation_formats': Retrieves data for the source PMID and formats it into specified citation styles (RIS, BibTeX, APA, MLA via NCBI EFetch and server-side formatting)."),
maxRelatedResults: z
.number()
.int()
.positive()
.max(50, "Maximum 50 related results can be requested.")
.optional()
.default(5)
.describe("Maximum number of related articles to retrieve when 'relationshipType' is 'pubmed_similar_articles', 'pubmed_citedin', or 'pubmed_references'. ELink results from NCBI will be truncated by the server if they exceed this number. Default is 5, maximum is 50."),
citationStyles: z
.array(z.enum(["ris", "bibtex", "apa_string", "mla_string"]))
.optional()
.default(["ris"])
.describe("An array of citation styles to format the source article into when 'relationshipType' is 'citation_formats'. Supported styles: 'ris', 'bibtex', 'apa_string', 'mla_string'. Default is ['ris']. Formatting is performed server-side based on data fetched via EFetch."),
});
/**
* Main handler for the 'get_pubmed_article_connections' tool.
* @param {GetPubMedArticleConnectionsInput} input - Validated input parameters.
* @param {RequestContext} context - The request context for this tool invocation.
* @returns {Promise<ToolOutputData>} The result of the tool call.
*/
export async function handleGetPubMedArticleConnections(input, context) {
const toolLogicContext = requestContextService.createRequestContext({
parentRequestId: context.requestId,
operation: "handleGetPubMedArticleConnections",
toolName: "get_pubmed_article_connections",
input: sanitizeInputForLogging(input),
});
logger.info("Executing get_pubmed_article_connections tool", toolLogicContext);
const outputData = {
sourcePmid: input.sourcePmid,
relationshipType: input.relationshipType,
relatedArticles: [],
citations: {},
retrievedCount: 0,
eUtilityUrl: undefined,
message: undefined,
};
switch (input.relationshipType) {
case "pubmed_similar_articles":
case "pubmed_citedin":
case "pubmed_references":
await handleELinkRelationships(input, outputData, toolLogicContext);
break;
case "citation_formats":
await handleCitationFormats(input, outputData, toolLogicContext);
break;
default:
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Unsupported relationshipType: ${input.relationshipType}`, { ...toolLogicContext, receivedType: input.relationshipType });
}
if (outputData.retrievedCount === 0 &&
!outputData.message &&
(input.relationshipType !== "citation_formats" ||
Object.keys(outputData.citations).length === 0)) {
outputData.message = "No results found for the given parameters.";
}
logger.notice("Successfully executed get_pubmed_article_connections tool.", {
...toolLogicContext,
relationshipType: input.relationshipType,
retrievedCount: outputData.retrievedCount,
citationsGenerated: Object.keys(outputData.citations).length,
});
return outputData;
}