Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,51 @@ For any MCP-compatible client, use:
npx @perplexity-ai/mcp-server
```

### Proxy Setup (For Corporate Networks)

If you are running this server at work—especially behind a company firewall or proxy—you may need to tell the program how to send its internet traffic through your network's proxy. Follow these steps:

**1. Get your proxy details**

- Ask your IT department for your HTTP(S) proxy address and port.
- You may also need a username and password.

**2. Set the proxy environment variable**

The easiest and most reliable way for Perplexity MCP is to use `PERPLEXITY_PROXY`. For example:

```bash
export PERPLEXITY_PROXY=http://your-proxy-host:8080
```

- If your proxy needs a username and password, use:
```bash
export PERPLEXITY_PROXY=http://username:password@your-proxy-host:8080
```

**3. Alternate: Standard environment variables**

If you'd rather use the standard variables, we support `HTTPS_PROXY` and `HTTP_PROXY`.

> [!NOTE]
>The server checks proxy settings in this order: `PERPLEXITY_PROXY` → `HTTPS_PROXY` → `HTTP_PROXY`. If none are set, it connects directly to the internet.

## Troubleshooting

- **API Key Issues**: Ensure `PERPLEXITY_API_KEY` is set correctly
- **Connection Errors**: Check your internet connection and API key validity
- **Tool Not Found**: Make sure the package is installed and the command path is correct
- **Timeout Errors**: For very long research queries, set `PERPLEXITY_TIMEOUT_MS` to a higher value
- **Proxy Issues**: If you're behind a corporate firewall and experience connection errors, you likely need to set up a proxy:
- Obtain your proxy server address and port from your IT department.
- Set the environment variable before running the server, e.g.:
- `export PERPLEXITY_PROXY=http://proxy-address:port`
- If authentication is needed: `export PERPLEXITY_PROXY=http://username:password@proxy-address:port`
- Typical proxy ports include 8080, 3128, or 80.
- The format for authenticated proxies is:
`http://username:password@proxy-host:port`
- Double-check the address, port, and credentials if connections fail or time out.
- If you continue to have issues, your firewall may be blocking traffic; ask IT if traffic for `api.perplexity.ai` is being restricted.

For support, visit [community.perplexity.ai](https://community.perplexity.ai) or [file an issue](https://github.com/perplexityai/modelcontextprotocol/issues).

Expand Down
61 changes: 61 additions & 0 deletions index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,4 +665,65 @@ describe("Perplexity MCP Server", () => {
expect(resultKept).toContain("The answer is 4.");
});
});

describe("Proxy Support", () => {
const originalEnv = process.env;

beforeEach(() => {
// Reset environment variables
process.env = { ...originalEnv };
delete process.env.PERPLEXITY_PROXY;
delete process.env.HTTPS_PROXY;
delete process.env.HTTP_PROXY;
});

afterEach(() => {
process.env = originalEnv;
});

it("should use native fetch when no proxy is configured", async () => {
const mockResponse = {
choices: [{ message: { content: "Test response" } }],
};

global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => mockResponse,
} as Response);

const messages = [{ role: "user", content: "test" }];
await performChatCompletion(messages);

// Verify native fetch was called (not undici)
expect(global.fetch).toHaveBeenCalled();
});

it("should read PERPLEXITY_PROXY environment variable", () => {
process.env.PERPLEXITY_PROXY = "http://proxy.example.com:8080";
expect(process.env.PERPLEXITY_PROXY).toBe("http://proxy.example.com:8080");
});

it("should prioritize PERPLEXITY_PROXY over HTTPS_PROXY", () => {
process.env.PERPLEXITY_PROXY = "http://perplexity-proxy.example.com:8080";
process.env.HTTPS_PROXY = "http://https-proxy.example.com:8080";

// PERPLEXITY_PROXY should take precedence
expect(process.env.PERPLEXITY_PROXY).toBe("http://perplexity-proxy.example.com:8080");
});

it("should fall back to HTTPS_PROXY when PERPLEXITY_PROXY is not set", () => {
delete process.env.PERPLEXITY_PROXY;
process.env.HTTPS_PROXY = "http://https-proxy.example.com:8080";

expect(process.env.HTTPS_PROXY).toBe("http://https-proxy.example.com:8080");
});

it("should fall back to HTTP_PROXY when others are not set", () => {
delete process.env.PERPLEXITY_PROXY;
delete process.env.HTTPS_PROXY;
process.env.HTTP_PROXY = "http://http-proxy.example.com:8080";

expect(process.env.HTTP_PROXY).toBe("http://http-proxy.example.com:8080");
});
});
});
44 changes: 42 additions & 2 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ListToolsRequestSchema,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
import { fetch as undiciFetch, ProxyAgent } from "undici";

/**
* Definition of the Perplexity Ask Tool.
Expand Down Expand Up @@ -169,6 +170,45 @@ if (!PERPLEXITY_API_KEY) {
process.exit(1);
}

/**
* Gets the proxy URL from environment variables.
* Checks PERPLEXITY_PROXY, HTTPS_PROXY, HTTP_PROXY in order.
*
* @returns {string | undefined} The proxy URL if configured, undefined otherwise
*/
function getProxyUrl(): string | undefined {
return process.env.PERPLEXITY_PROXY ||
process.env.HTTPS_PROXY ||
process.env.HTTP_PROXY ||
undefined;
}

/**
* Creates a proxy-aware fetch function.
* Uses undici with ProxyAgent when a proxy is configured, otherwise uses native fetch.
*
* @param {string} url - The URL to fetch
* @param {RequestInit} options - Fetch options
* @returns {Promise<Response>} The fetch response
*/
async function proxyAwareFetch(url: string, options: RequestInit = {}): Promise<Response> {
const proxyUrl = getProxyUrl();

if (proxyUrl) {
// Use undici with ProxyAgent when proxy is configured
const proxyAgent = new ProxyAgent(proxyUrl);
const response = await undiciFetch(url, {
...options,
dispatcher: proxyAgent,
} as any);
// Cast to native Response type for compatibility
return response as unknown as Response;
} else {
// Use native fetch when no proxy is configured
return fetch(url, options);
}
}

/**
* Validates an array of message objects for chat completion tools.
* Ensures each message has a valid role and content field.
Expand Down Expand Up @@ -240,7 +280,7 @@ export async function performChatCompletion(

let response;
try {
response = await fetch(url.toString(), {
response = await proxyAwareFetch(url.toString(), {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand Down Expand Up @@ -371,7 +411,7 @@ export async function performSearch(

let response;
try {
response = await fetch(url.toString(), {
response = await proxyAwareFetch(url.toString(), {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand Down
12 changes: 11 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.21.1",
"dotenv": "^16.6.1"
"dotenv": "^16.6.1",
"undici": "^6.20.0"
},
"devDependencies": {
"@types/node": "^20",
Expand Down