@perplexity-ai/mcp-server 0.7.0 → 0.8.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.
- package/.claude-plugin/marketplace.json +2 -2
- package/dist/server.js +78 -4
- package/package.json +2 -2
|
@@ -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.
|
|
9
|
+
"version": "0.8.1"
|
|
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.
|
|
16
|
+
"version": "0.8.1",
|
|
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
|
-
|
|
100
|
-
|
|
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) {
|
|
@@ -203,8 +277,8 @@ export async function performSearch(query, maxResults = 10, maxTokensPerPage = 1
|
|
|
203
277
|
}
|
|
204
278
|
export function createPerplexityServer(serviceOrigin) {
|
|
205
279
|
const server = new McpServer({
|
|
206
|
-
name: "
|
|
207
|
-
version: "0.
|
|
280
|
+
name: "ai.perplexity/mcp-server",
|
|
281
|
+
version: "0.8.1",
|
|
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.
|
|
4
|
-
"mcpName": "
|
|
3
|
+
"version": "0.8.1",
|
|
4
|
+
"mcpName": "ai.perplexity/mcp-server",
|
|
5
5
|
"description": "Real-time web search, reasoning, and research through Perplexity's API",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"ai",
|