From 8bac0e3903587e54f7098029503bddfd5254a8a1 Mon Sep 17 00:00:00 2001 From: James Liounis Date: Tue, 19 May 2026 13:51:24 +0000 Subject: [PATCH] test: regression guard for #101 (no prompt-template wrapping) Issue #101 reports that perplexity_reason returns irrelevant citations because the tool wraps user queries in a verbose prompt template that poisons Perplexity's search-term extraction. The reporter (SangeethsivanSivakumar) debugged the bug themselves and identified the root cause in the third-party DaInfernalCoder/researcher-mcp fork (published on npm as perplexity-mcp v0.2.3), NOT in this repository. Verified the official server: src/server.ts forwards user messages to api.perplexity.ai verbatim with zero wrapping in all four tool handlers (perplexity_ask, perplexity_research, perplexity_reason, perplexity_search). This commit adds a regression test that: 1. Calls performChatCompletion with a representative user query. 2. Asserts the outgoing request body's `messages` field equals the input verbatim. 3. Asserts none of the known poisoning keywords from the third-party fork's template ("error messages, logs, code snippets", "specific situation", "step-by-step reasoning based on the actual context") appear in the request body. If anyone ever re-introduces a wrapper template in this repo, this test will fail immediately. Closes #101 (root cause is in third-party fork, not this repo). --- src/index.test.ts | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/index.test.ts b/src/index.test.ts index 21897b8..641a669 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -174,6 +174,44 @@ describe("Perplexity MCP Server", () => { "Network error while calling Perplexity API" ); }); + + // Regression test for #101 — verifies that the official server does NOT + // wrap user queries in any prompt template (the bug reported in #101 was + // in the third-party DaInfernalCoder/researcher-mcp fork, not this repo). + // If anyone re-introduces template wrapping in this repo, this test will + // fail loudly. + it("should forward user messages verbatim to the API (regression: #101)", async () => { + const mockResponse = { + choices: [{ message: { content: "ok" } }], + }; + + global.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: async () => mockResponse, + } as Response); + + const userQuery = + "Cleveland Fed inflation nowcast March 2026 CPI estimate"; + const messages = [{ role: "user", content: userQuery }]; + + await performChatCompletion(messages, "sonar-reasoning-pro"); + + const fetchMock = global.fetch as ReturnType; + const [, init] = fetchMock.mock.calls[0]; + const sentBody = JSON.parse((init as RequestInit).body as string); + + // The body must contain exactly the user's message — no prompt template + // around it, no synthetic system message added by the server. + expect(sentBody.messages).toEqual(messages); + expect(sentBody.messages[0].content).toBe(userQuery); + + // Defensive guards against known template keywords that poisoned search + // results in the third-party fork (see issue #101). + const sentJson = (init as RequestInit).body as string; + expect(sentJson).not.toContain("error messages, logs, code snippets"); + expect(sentJson).not.toContain("specific situation"); + expect(sentJson).not.toContain("step-by-step reasoning based on the actual context"); + }); }); describe("performSearch", () => {