@perplexity-ai/mcp-server 0.7.0 → 0.8.0

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.
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Official Perplexity AI plugin providing real-time web search, reasoning, and research capabilities",
9
- "version": "0.7.0"
9
+ "version": "0.8.0"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "perplexity",
14
14
  "source": "./",
15
15
  "description": "Real-time web search, reasoning, and research through Perplexity's API",
16
- "version": "0.7.0",
16
+ "version": "0.8.0",
17
17
  "author": {
18
18
  "name": "Perplexity AI",
19
19
  "email": "api@perplexity.ai"
package/dist/server.js CHANGED
@@ -43,16 +43,85 @@ export function validateMessages(messages, toolName) {
43
43
  export function stripThinkingTokens(content) {
44
44
  return content.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
45
45
  }
46
+ export async function consumeSSEStream(response) {
47
+ const body = response.body;
48
+ if (!body) {
49
+ throw new Error("Response body is null");
50
+ }
51
+ const reader = body.getReader();
52
+ const decoder = new TextDecoder();
53
+ let contentParts = [];
54
+ let citations;
55
+ let usage;
56
+ let id;
57
+ let model;
58
+ let created;
59
+ let buffer = "";
60
+ while (true) {
61
+ const { done, value } = await reader.read();
62
+ if (done)
63
+ break;
64
+ buffer += decoder.decode(value, { stream: true });
65
+ const lines = buffer.split("\n");
66
+ // Keep the last potentially incomplete line in the buffer
67
+ buffer = lines.pop() || "";
68
+ for (const line of lines) {
69
+ const trimmed = line.trim();
70
+ if (!trimmed || !trimmed.startsWith("data:"))
71
+ continue;
72
+ const data = trimmed.slice("data:".length).trim();
73
+ if (data === "[DONE]")
74
+ continue;
75
+ try {
76
+ const parsed = JSON.parse(data);
77
+ if (parsed.id)
78
+ id = parsed.id;
79
+ if (parsed.model)
80
+ model = parsed.model;
81
+ if (parsed.created)
82
+ created = parsed.created;
83
+ if (parsed.citations)
84
+ citations = parsed.citations;
85
+ if (parsed.usage)
86
+ usage = parsed.usage;
87
+ const delta = parsed.choices?.[0]?.delta;
88
+ if (delta?.content) {
89
+ contentParts.push(delta.content);
90
+ }
91
+ }
92
+ catch {
93
+ // Skip malformed JSON chunks (e.g. keep-alive pings)
94
+ }
95
+ }
96
+ }
97
+ const assembled = {
98
+ choices: [
99
+ {
100
+ message: { content: contentParts.join("") },
101
+ finish_reason: "stop",
102
+ index: 0,
103
+ },
104
+ ],
105
+ ...(citations && { citations }),
106
+ ...(usage && { usage }),
107
+ ...(id && { id }),
108
+ ...(model && { model }),
109
+ ...(created && { created }),
110
+ };
111
+ return ChatCompletionResponseSchema.parse(assembled);
112
+ }
46
113
  export async function performChatCompletion(messages, model = "sonar-pro", stripThinking = false, serviceOrigin, options) {
47
114
  if (!PERPLEXITY_API_KEY) {
48
115
  throw new Error("PERPLEXITY_API_KEY environment variable is required");
49
116
  }
50
117
  // Read timeout fresh each time to respect env var changes
51
118
  const TIMEOUT_MS = parseInt(process.env.PERPLEXITY_TIMEOUT_MS || "300000", 10);
119
+ const useStreaming = model === "sonar-deep-research";
52
120
  const url = new URL(`${PERPLEXITY_BASE_URL}/chat/completions`);
53
121
  const body = {
54
122
  model: model,
55
123
  messages: messages,
124
+ ...(useStreaming && { stream: true }),
56
125
  ...(options?.search_recency_filter && { search_recency_filter: options.search_recency_filter }),
57
126
  ...(options?.search_domain_filter && { search_domain_filter: options.search_domain_filter }),
58
127
  ...(options?.search_context_size && { web_search_options: { search_context_size: options.search_context_size } }),
@@ -96,8 +165,13 @@ export async function performChatCompletion(messages, model = "sonar-pro", strip
96
165
  }
97
166
  let data;
98
167
  try {
99
- const json = await response.json();
100
- data = ChatCompletionResponseSchema.parse(json);
168
+ if (useStreaming) {
169
+ data = await consumeSSEStream(response);
170
+ }
171
+ else {
172
+ const json = await response.json();
173
+ data = ChatCompletionResponseSchema.parse(json);
174
+ }
101
175
  }
102
176
  catch (error) {
103
177
  if (error instanceof z.ZodError) {
@@ -204,7 +278,7 @@ export async function performSearch(query, maxResults = 10, maxTokensPerPage = 1
204
278
  export function createPerplexityServer(serviceOrigin) {
205
279
  const server = new McpServer({
206
280
  name: "io.github.perplexityai/mcp-server",
207
- version: "0.7.0",
281
+ version: "0.8.0",
208
282
  }, {
209
283
  instructions: "Perplexity AI server for web-grounded search, research, and reasoning. " +
210
284
  "Use perplexity_search for finding URLs, facts, and recent news. " +
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@perplexity-ai/mcp-server",
3
- "version": "0.7.0",
4
- "mcpName": "io.github.perplexityai/mcp-server",
3
+ "version": "0.8.0",
4
+ "mcpName": "ai.perplexityai/mcp-server",
5
5
  "description": "Real-time web search, reasoning, and research through Perplexity's API",
6
6
  "keywords": [
7
7
  "ai",