@@ -7,14 +7,18 @@ import type {
77 AgentSearchResult ,
88 AgentToolOptions ,
99 AgentCallHooks ,
10+ ApiKeyProvider ,
11+ PerplexityServerOptions ,
1012 SearchResponse ,
1113 UndiciRequestOptions
1214} from "./types.js" ;
1315import { AgentResponseSchema , SearchResponseSchema } from "./validation.js" ;
1416
17+ export type { ApiKeyProvider , PerplexityServerOptions } from "./types.js" ;
18+
1519const PERPLEXITY_API_KEY = process . env . PERPLEXITY_API_KEY ;
1620const PERPLEXITY_BASE_URL = process . env . PERPLEXITY_BASE_URL || "https://api.perplexity.ai" ;
17- const VERSION = "1.1 .0" ;
21+ const VERSION = "1.2 .0" ;
1822
1923// Agent API presets backing each tool: https://docs.perplexity.ai/docs/agent-api/presets
2024export const ASK_PRESET = "fast" ;
@@ -68,9 +72,21 @@ async function makeApiRequest(
6872 body : Record < string , unknown > ,
6973 serviceOrigin : string | undefined ,
7074 signal ?: AbortSignal ,
75+ apiKey ?: ApiKeyProvider ,
7176) : Promise < Response > {
72- if ( ! PERPLEXITY_API_KEY ) {
73- throw new Error ( "PERPLEXITY_API_KEY environment variable is required" ) ;
77+ // A configured provider fully replaces the env var: falling back would let
78+ // a multi-tenant misconfiguration silently bill the process-wide key.
79+ let resolvedApiKey : string | undefined ;
80+ if ( apiKey ) {
81+ resolvedApiKey = apiKey ( ) ;
82+ if ( ! resolvedApiKey ) {
83+ throw new Error ( "API key provider returned no key" ) ;
84+ }
85+ } else {
86+ resolvedApiKey = PERPLEXITY_API_KEY ;
87+ if ( ! resolvedApiKey ) {
88+ throw new Error ( "PERPLEXITY_API_KEY environment variable is required" ) ;
89+ }
7490 }
7591
7692 // Read timeout fresh each time to respect env var changes
@@ -92,7 +108,7 @@ async function makeApiRequest(
92108 try {
93109 const headers : Record < string , string > = {
94110 "Content-Type" : "application/json" ,
95- "Authorization" : `Bearer ${ PERPLEXITY_API_KEY } ` ,
111+ "Authorization" : `Bearer ${ resolvedApiKey } ` ,
96112 "User-Agent" : `perplexity-mcp/${ VERSION } ` ,
97113 "X-Source" : "pplx-mcp-server" ,
98114 } ;
@@ -134,9 +150,9 @@ async function makeApiRequest(
134150}
135151
136152/** Best-effort cancellation of an agent run so an abandoned request stops billing. */
137- export async function cancelAgentResponse ( responseId : string , serviceOrigin ?: string ) : Promise < void > {
153+ export async function cancelAgentResponse ( responseId : string , serviceOrigin ?: string , apiKey ?: ApiKeyProvider ) : Promise < void > {
138154 try {
139- await makeApiRequest ( `v1/agent/${ encodeURIComponent ( responseId ) } /cancel` , { } , serviceOrigin ) ;
155+ await makeApiRequest ( `v1/agent/${ encodeURIComponent ( responseId ) } /cancel` , { } , serviceOrigin , undefined , apiKey ) ;
140156 } catch {
141157 // The run may already be terminal; nothing actionable either way.
142158 }
@@ -151,6 +167,7 @@ export async function consumeAgentStream(
151167 hooks ?: AgentCallHooks ,
152168 serviceOrigin ?: string ,
153169 deadlineSignal ?: AbortSignal ,
170+ apiKey ?: ApiKeyProvider ,
154171) : Promise < AgentResponse > {
155172 const body = response . body ;
156173 if ( ! body ) {
@@ -265,7 +282,7 @@ export async function consumeAgentStream(
265282 if ( hooks ?. signal ?. aborted || deadlineSignal ?. aborted ) {
266283 if ( responseId ) {
267284 // Stop the server-side run so an abandoned request stops billing.
268- void cancelAgentResponse ( responseId , serviceOrigin ) ;
285+ void cancelAgentResponse ( responseId , serviceOrigin , apiKey ) ;
269286 }
270287 if ( hooks ?. signal ?. aborted ) {
271288 throw new Error ( "Request cancelled" ) ;
@@ -277,7 +294,7 @@ export async function consumeAgentStream(
277294
278295 if ( hooks ?. signal ?. aborted ) {
279296 if ( responseId ) {
280- void cancelAgentResponse ( responseId , serviceOrigin ) ;
297+ void cancelAgentResponse ( responseId , serviceOrigin , apiKey ) ;
281298 }
282299 throw new Error ( "Request cancelled" ) ;
283300 }
@@ -385,7 +402,8 @@ export async function performAgentResponse(
385402 preset : string ,
386403 serviceOrigin ?: string ,
387404 options ?: AgentToolOptions ,
388- hooks ?: AgentCallHooks
405+ hooks ?: AgentCallHooks ,
406+ apiKey ?: ApiKeyProvider ,
389407) : Promise < string > {
390408 const webSearchTool = buildWebSearchTool ( options ) ;
391409
@@ -418,8 +436,8 @@ export async function performAgentResponse(
418436 }
419437
420438 try {
421- const response = await makeApiRequest ( "v1/agent" , body , serviceOrigin , deadline . signal ) ;
422- const agentResponse = await consumeAgentStream ( response , hooks , serviceOrigin , deadline . signal ) ;
439+ const response = await makeApiRequest ( "v1/agent" , body , serviceOrigin , deadline . signal , apiKey ) ;
440+ const agentResponse = await consumeAgentStream ( response , hooks , serviceOrigin , deadline . signal , apiKey ) ;
423441 return formatAgentResponseText ( agentResponse ) ;
424442 } catch ( error ) {
425443 if ( hooks ?. signal ?. aborted ) {
@@ -463,7 +481,8 @@ export async function performSearch(
463481 maxTokensPerPage : number = 1024 ,
464482 country ?: string ,
465483 filters ?: Pick < AgentToolOptions , "search_recency_filter" | "search_domain_filter" > ,
466- serviceOrigin ?: string
484+ serviceOrigin ?: string ,
485+ apiKey ?: ApiKeyProvider ,
467486) : Promise < string > {
468487 const body : Record < string , unknown > = {
469488 query : query ,
@@ -474,7 +493,7 @@ export async function performSearch(
474493 ...( filters ?. search_domain_filter && { search_domain_filter : filters . search_domain_filter } ) ,
475494 } ;
476495
477- const response = await makeApiRequest ( "search" , body , serviceOrigin ) ;
496+ const response = await makeApiRequest ( "search" , body , serviceOrigin , undefined , apiKey ) ;
478497
479498 let data : SearchResponse ;
480499 try {
@@ -518,7 +537,7 @@ function buildHooks(extra: ToolExtra | undefined): AgentCallHooks {
518537 } ;
519538}
520539
521- export function createPerplexityServer ( serviceOrigin ?: string ) {
540+ export function createPerplexityServer ( serviceOrigin ?: string , serverOptions ?: PerplexityServerOptions ) {
522541 const server = new McpServer (
523542 {
524543 name : "ai.perplexity/mcp-server" ,
@@ -604,6 +623,7 @@ export function createPerplexityServer(serviceOrigin?: string) {
604623 serviceOrigin ,
605624 Object . keys ( options ) . length > 0 ? options : undefined ,
606625 buildHooks ( extra ) ,
626+ serverOptions ?. apiKey ,
607627 ) ;
608628 return {
609629 content : [ { type : "text" as const , text : result } ] ,
@@ -640,6 +660,7 @@ export function createPerplexityServer(serviceOrigin?: string) {
640660 serviceOrigin ,
641661 undefined ,
642662 buildHooks ( extra ) ,
663+ serverOptions ?. apiKey ,
643664 ) ;
644665 return {
645666 content : [ { type : "text" as const , text : result } ] ,
@@ -686,6 +707,7 @@ export function createPerplexityServer(serviceOrigin?: string) {
686707 serviceOrigin ,
687708 Object . keys ( options ) . length > 0 ? options : undefined ,
688709 buildHooks ( extra ) ,
710+ serverOptions ?. apiKey ,
689711 ) ;
690712 return {
691713 content : [ { type : "text" as const , text : result } ] ,
@@ -745,7 +767,7 @@ export function createPerplexityServer(serviceOrigin?: string) {
745767 ...( search_domain_filter && { search_domain_filter } ) ,
746768 } ;
747769
748- const result = await performSearch ( query , maxResults , maxTokensPerPage , countryCode , filters , serviceOrigin ) ;
770+ const result = await performSearch ( query , maxResults , maxTokensPerPage , countryCode , filters , serviceOrigin , serverOptions ?. apiKey ) ;
749771 return {
750772 content : [ { type : "text" as const , text : result } ] ,
751773 structuredContent : { results : result } ,
0 commit comments