#
tokens: 6643/50000 8/8 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .github
│   ├── mcp.json
│   └── workflows
│       └── release.yml
├── .gitignore
├── .goreleaser.yaml
├── .vscode
│   └── settings.json
├── go.mod
├── go.sum
├── LICENSE
├── main_test.go
├── main.go
└── README.md
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | .env.test
2 | perplexity-mcp-server
3 | dist/
4 | 
```

--------------------------------------------------------------------------------
/.goreleaser.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | # This is an example .goreleaser.yml file with some sensible defaults.
 2 | # Make sure to check the documentation at https://goreleaser.com
 3 | 
 4 | # The lines below are called `modelines`. See `:help modeline`
 5 | # Feel free to remove those if you don't want/need to use them.
 6 | # yaml-language-server: $schema=https://goreleaser.com/static/schema.json
 7 | # vim: set ts=2 sw=2 tw=0 fo=cnqoj
 8 | 
 9 | version: 2
10 | 
11 | before:
12 |   hooks:
13 |     - go mod tidy
14 | 
15 | builds:
16 |   - env:
17 |       - CGO_ENABLED=0
18 |     goos:
19 |       - linux
20 |       - windows
21 |       - darwin
22 | 
23 | archives:
24 |   - formats: [tar.gz]
25 |     # this name template makes the OS and Arch compatible with the results of `uname`.
26 |     name_template: >-
27 |       {{ .ProjectName }}_
28 |       {{- title .Os }}_
29 |       {{- if eq .Arch "amd64" }}x86_64
30 |       {{- else if eq .Arch "386" }}i386
31 |       {{- else }}{{ .Arch }}{{ end }}
32 |       {{- if .Arm }}v{{ .Arm }}{{ end }}
33 |     # use zip for windows archives
34 |     format_overrides:
35 |       - goos: windows
36 |         formats: [zip]
37 | 
38 | 
39 | release:
40 |   footer: >-
41 | 
42 |     ---
43 |     Copyright 2025 Alcova AI Pty Ltd. All rights reserved.
44 |     Released under the [MIT License](https://opensource.org/licenses/MIT).
45 | 
46 | brews:
47 |   - name: perplexity-mcp
48 |     repository:
49 |       owner: Alcova-AI
50 |       name: homebrew-tap
51 |       token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
52 |     download_strategy: CurlDownloadStrategy
53 |     homepage: "https://github.com/Alcova-AI/perplexity-mcp"
54 |     description: "A Model Context Protocol (MCP) server for the Perplexity API"
55 |     license: "MIT"
56 |     test: |
57 |       system "#{bin}/perplexity-mcp --help"
58 |     install: |
59 |       bin.install "perplexity-mcp"
60 |     commit_author:
61 |       name: "Alcova AI"
62 |     directory: "Formula"
63 | 
64 | checksum:
65 |   name_template: 'checksums.txt'
66 | 
67 | snapshot:
68 |   name_template: "{{ incpatch .Version }}-next"
69 | 
70 | changelog:
71 |   sort: asc
72 |   use: github
73 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Perplexity MCP Server
  2 | 
  3 | A Model Context Protocol (MCP) server for the Perplexity API written in Go. This server enables AI assistants like Claude (Code and Desktop) and Cursor to seamlessly access Perplexity's powerful search and reasoning capabilities directly from their interfaces.
  4 | 
  5 | ## Description
  6 | 
  7 | The Perplexity MCP Server acts as a bridge between AI assistants and the Perplexity API, allowing them to:
  8 | 
  9 | 1. **Search the web and retrieve up-to-date information** using Perplexity's Sonar Pro model via the `perplexity_ask` tool
 10 | 2. **Perform complex reasoning tasks** using Perplexity's Sonar Reasoning Pro model via the `perplexity_reason` tool
 11 | 
 12 | This integration lets AI assistants like Claude access real-time information and specialized reasoning capabilities without leaving their interface, creating a seamless experience for users.
 13 | 
 14 | ### Key Benefits
 15 | 
 16 | - **Access to real-time information**: Get current data, news, and information from the web
 17 | - **Enhanced reasoning capabilities**: Leverage specialized models for complex problem-solving tasks
 18 | - **Seamless integration**: Works natively with Claude Code, Claude Desktop, and Cursor
 19 | - **Simple installation**: Quick setup with Homebrew, Go, or pre-built binaries
 20 | - **Customizable**: Configure which Perplexity models to use for different tasks
 21 | 
 22 | ## Installation
 23 | 
 24 | ### Using Homebrew (macOS and Linux)
 25 | 
 26 | ```sh
 27 | brew tap alcova-ai/tap
 28 | brew install perplexity-mcp
 29 | ```
 30 | 
 31 | ### From Source
 32 | 
 33 | Clone the repository and build manually:
 34 | 
 35 | ```sh
 36 | git clone https://github.com/Alcova-AI/perplexity-mcp.git
 37 | cd perplexity-mcp
 38 | go build -o perplexity-mcp-server .
 39 | ```
 40 | 
 41 | ### From Binary Releases (Other platforms)
 42 | 
 43 | Download pre-built binaries from the [releases page](https://github.com/Alcova-AI/perplexity-mcp/releases).
 44 | 
 45 | ## Usage
 46 | 
 47 | This server supports only the `stdio` protocol for MCP communication.
 48 | 
 49 | ### Setup with Claude Code
 50 | 
 51 | Adding to Claude Code:
 52 | 
 53 | ```sh
 54 | claude mcp add-json --scope user perplexity-mcp '{"type":"stdio","command":"perplexity-mcp","env":{"PERPLEXITY_API_KEY":"pplx-YOUR-API-KEY-HERE"}}'
 55 | ```
 56 | 
 57 | That's it! You can now use Perplexity in Claude Code.
 58 | 
 59 | ### Setup with Claude Desktop
 60 | 
 61 | Adding to Claude Desktop:
 62 | 
 63 | 1. Exit the Claude Desktop MCP config:
 64 | 
 65 | ```sh
 66 | code ~/Library/Application\ Support/Claude/claude_desktop_config.json
 67 | ```
 68 | 
 69 | 2. Add the Perplexity MCP server:
 70 | 
 71 | ```diff
 72 |   {
 73 |     "mcpServers": {
 74 | +        "perplexity-mcp": {
 75 | +            "command": "perplexity-mcp",
 76 | +            "args": [
 77 | +                "--model",
 78 | +                "sonar-pro",
 79 | +                "--reasoning-model",
 80 | +                "sonar-reasoning-pro"
 81 | +            ],
 82 | +            "env": {
 83 | +                "PERPLEXITY_API_KEY": "pplx-YOUR-API-KEY-HERE"
 84 | +            }
 85 | +        }
 86 |     }
 87 |   }
 88 | ```
 89 | 
 90 | ### Command Line Options
 91 | 
 92 | - `--model, -m`: Specify the Perplexity model to use for search (default: "sonar-pro")
 93 |   - Can also be set with the `PERPLEXITY_MODEL` environment variable
 94 | - `--reasoning-model, -r`: Specify the Perplexity model to use for reasoning (default: "sonar-reasoning-pro")
 95 |   - Can also be set with the `PERPLEXITY_REASONING_MODEL` environment variable
 96 | 
 97 | Example:
 98 | 
 99 | ```sh
100 | perplexity-mcp --model sonar-pro --reasoning-model sonar-reasoning-pro
101 | ```
102 | 
103 | ### Direct Execution
104 | 
105 | If you want to run the server directly (not recommended for most users):
106 | 
107 | 1. Set your Perplexity API key as an environment variable:
108 | 
109 |    ```sh
110 |    export PERPLEXITY_API_KEY=your-api-key-here
111 |    ```
112 | 
113 | 2. Run the server:
114 | 
115 |    ```sh
116 |    perplexity-mcp
117 |    ```
118 | 
119 | 
120 | 
121 | ## License
122 | 
123 | MIT
124 | 
```

--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "go.testEnvFile": ".env.test"
3 | }
4 | 
```

--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Release
 2 | 
 3 | on:
 4 |   push:
 5 |     tags:
 6 |       - 'v*'
 7 | 
 8 | permissions:
 9 |   contents: write
10 |   packages: write
11 |   id-token: write
12 |   attestations: write
13 | 
14 | jobs:
15 |   goreleaser:
16 |     runs-on: ubuntu-latest
17 |     steps:
18 |       - name: Checkout
19 |         uses: actions/checkout@v4
20 |         with:
21 |           fetch-depth: 0
22 | 
23 |       - name: Set up Go
24 |         uses: actions/setup-go@v4
25 |         with:
26 |           go-version-file: go.mod
27 | 
28 |       - name: Run GoReleaser
29 |         uses: goreleaser/goreleaser-action@v6
30 |         with:
31 |           distribution: goreleaser
32 |           version: latest
33 |           args: release --clean
34 |         env:
35 |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36 |           HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
37 | 
38 |       - name: Attest build provenance
39 |         uses: actions/attest-build-provenance@v2
40 |         with:
41 |           subject-checksums: ./dist/checksums.txt
42 | 
```

--------------------------------------------------------------------------------
/.github/mcp.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "perplexity-mcp",
 3 |   "version": "0.2.0",
 4 |   "description": "A Model Context Protocol server for the Perplexity API, allowing access to Perplexity's Sonar models for search and reasoning through MCP.",
 5 |   "author": "Ivan Vanderbyl",
 6 |   "license": "MIT",
 7 |   "categories": ["ai", "llm", "api"],
 8 |   "keywords": ["perplexity", "sonar", "chat", "completion", "reasoning", "mcp"],
 9 |   "executable": {
10 |     "path": "perplexity-mcp"
11 |   },
12 |   "tools": [
13 |     {
14 |       "name": "perplexity_ask",
15 |       "description": "Engages in a conversation using the Sonar API for search. Accepts an array of messages (each with a role and content) and returns a chat completion response from the Perplexity model."
16 |     },
17 |     {
18 |       "name": "perplexity_reason",
19 |       "description": "Uses the Perplexity reasoning model to perform complex reasoning tasks. Accepts a query string and returns a comprehensive reasoned response."
20 |     }
21 |   ],
22 |   "env": [
23 |     {
24 |       "name": "PERPLEXITY_API_KEY",
25 |       "description": "API key for the Perplexity API",
26 |       "required": true
27 |     },
28 |     {
29 |       "name": "PERPLEXITY_MODEL",
30 |       "description": "Model identifier for search queries (default: sonar-pro)",
31 |       "required": false
32 |     },
33 |     {
34 |       "name": "PERPLEXITY_REASONING_MODEL",
35 |       "description": "Model identifier for reasoning tasks (default: sonar-reasoning-pro)",
36 |       "required": false
37 |     }
38 |   ]
39 | }
```

--------------------------------------------------------------------------------
/main_test.go:
--------------------------------------------------------------------------------

```go
 1 | package main
 2 | 
 3 | import (
 4 | 	"os"
 5 | 	"testing"
 6 | )
 7 | 
 8 | func TestPerformChatCompletion(t *testing.T) {
 9 | 	// Skip this test if no API key is provided
10 | 	apiKey := os.Getenv("PERPLEXITY_API_KEY")
11 | 	if apiKey == "" {
12 | 		t.Skip("Skipping test: PERPLEXITY_API_KEY environment variable not set")
13 | 	}
14 | 
15 | 	// Test message
16 | 	messages := []Message{
17 | 		{Role: "system", Content: "You are a helpful assistant."},
18 | 		{Role: "user", Content: "What is the capital of France?"},
19 | 	}
20 | 
21 | 	// Test with default model
22 | 	result, err := performChatCompletion(apiKey, "sonar-pro", messages)
23 | 	if err != nil {
24 | 		t.Fatalf("Expected no error, got %v", err)
25 | 	}
26 | 
27 | 	if result == "" {
28 | 		t.Fatalf("Expected non-empty result, got empty string")
29 | 	}
30 | 
31 | 	// Additional checks can be added here based on expected response format
32 | 	t.Logf("API Response: %s", result)
33 | }
34 | 
35 | func TestPerformReasoning(t *testing.T) {
36 | 	// Skip this test if no API key is provided
37 | 	apiKey := os.Getenv("PERPLEXITY_API_KEY")
38 | 	if apiKey == "" {
39 | 		t.Skip("Skipping test: PERPLEXITY_API_KEY environment variable not set")
40 | 	}
41 | 
42 | 	// Test message for reasoning task
43 | 	messages := []Message{
44 | 		{Role: "system", Content: "You are a reasoning assistant focused on solving complex problems through step-by-step reasoning."},
45 | 		{Role: "user", Content: "If a train travels at 120 km/h and another train travels at 80 km/h in the opposite direction, how long will it take for them to be 500 km apart if they start at the same location?"},
46 | 	}
47 | 
48 | 	// Test with reasoning model
49 | 	result, err := performChatCompletion(apiKey, "sonar-reasoning-pro", messages)
50 | 	if err != nil {
51 | 		t.Fatalf("Expected no error, got %v", err)
52 | 	}
53 | 
54 | 	if result == "" {
55 | 		t.Fatalf("Expected non-empty result, got empty string")
56 | 	}
57 | 
58 | 	// Additional checks can be added here based on expected response format
59 | 	t.Logf("Reasoning API Response: %s", result)
60 | }
61 | 
```

--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------

```go
  1 | package main
  2 | 
  3 | import (
  4 | 	"bytes"
  5 | 	"context"
  6 | 	"encoding/json"
  7 | 	"fmt"
  8 | 	"io"
  9 | 	"log/slog"
 10 | 	"net/http"
 11 | 	"os"
 12 | 	"runtime/debug"
 13 | 
 14 | 	"github.com/mark3labs/mcp-go/mcp"
 15 | 	"github.com/mark3labs/mcp-go/server"
 16 | 	"github.com/urfave/cli/v2"
 17 | )
 18 | 
 19 | const (
 20 | 	apiURL = "https://api.perplexity.ai/chat/completions"
 21 | )
 22 | 
 23 | // PerplexityConfig holds configuration for the Perplexity API
 24 | type PerplexityConfig struct {
 25 | 	APIKey         string
 26 | 	Model          string
 27 | 	ReasoningModel string
 28 | }
 29 | 
 30 | // Message represents a message in the chat completion request
 31 | type Message struct {
 32 | 	Role    string `json:"role"`
 33 | 	Content string `json:"content"`
 34 | }
 35 | 
 36 | // ChatCompletionRequest represents the request to the Perplexity API
 37 | type ChatCompletionRequest struct {
 38 | 	Model    string    `json:"model"`
 39 | 	Messages []Message `json:"messages"`
 40 | }
 41 | 
 42 | // ChatCompletionResponse represents the response from the Perplexity API
 43 | type ChatCompletionResponse struct {
 44 | 	ID      string `json:"id"`
 45 | 	Object  string `json:"object"`
 46 | 	Created int    `json:"created"`
 47 | 	Model   string `json:"model"`
 48 | 	Choices []struct {
 49 | 		Message struct {
 50 | 			Role    string `json:"role"`
 51 | 			Content string `json:"content"`
 52 | 		} `json:"message"`
 53 | 	} `json:"choices"`
 54 | 	Citations []string `json:"citations,omitempty"`
 55 | }
 56 | 
 57 | // performChatCompletion sends a request to the Perplexity API and returns the response
 58 | func performChatCompletion(apiKey string, model string, messages []Message) (string, error) {
 59 | 	request := ChatCompletionRequest{
 60 | 		Model:    model,
 61 | 		Messages: messages,
 62 | 	}
 63 | 
 64 | 	requestBody, err := json.Marshal(request)
 65 | 	if err != nil {
 66 | 		return "", fmt.Errorf("error marshaling request: %v", err)
 67 | 	}
 68 | 
 69 | 	req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(requestBody))
 70 | 	if err != nil {
 71 | 		return "", fmt.Errorf("error creating request: %v", err)
 72 | 	}
 73 | 
 74 | 	req.Header.Set("Content-Type", "application/json")
 75 | 	req.Header.Set("Authorization", "Bearer "+apiKey)
 76 | 
 77 | 	client := http.DefaultClient
 78 | 	resp, err := client.Do(req)
 79 | 	if err != nil {
 80 | 		return "", fmt.Errorf("error sending request: %v", err)
 81 | 	}
 82 | 	defer resp.Body.Close()
 83 | 
 84 | 	body, err := io.ReadAll(resp.Body)
 85 | 	if err != nil {
 86 | 		return "", fmt.Errorf("error reading response body: %v", err)
 87 | 	}
 88 | 
 89 | 	if resp.StatusCode != http.StatusOK {
 90 | 		return "", fmt.Errorf("API request failed with status code %d: %s", resp.StatusCode, string(body))
 91 | 	}
 92 | 
 93 | 	var response ChatCompletionResponse
 94 | 	err = json.Unmarshal(body, &response)
 95 | 	if err != nil {
 96 | 		return "", fmt.Errorf("error unmarshaling response: %v", err)
 97 | 	}
 98 | 
 99 | 	if len(response.Choices) == 0 {
100 | 		return "", fmt.Errorf("no choices returned in response")
101 | 	}
102 | 
103 | 	// Get the message content from the response
104 | 	messageContent := response.Choices[0].Message.Content
105 | 
106 | 	// Append citations to the message content if they exist
107 | 	if len(response.Citations) > 0 {
108 | 		messageContent += "\n\nCitations:\n"
109 | 		for i, citation := range response.Citations {
110 | 			messageContent += fmt.Sprintf("[%d] %s\n", i+1, citation)
111 | 		}
112 | 	}
113 | 
114 | 	return messageContent, nil
115 | }
116 | 
117 | // parseMessagesFromRequest extracts and validates messages from an MCP tool request
118 | func parseMessagesFromRequest(request mcp.CallToolRequest) ([]Message, error) {
119 | 	messagesRaw, ok := request.Params.Arguments["messages"].([]any)
120 | 	if !ok {
121 | 		return nil, fmt.Errorf("'messages' must be an array")
122 | 	}
123 | 
124 | 	var messages []Message
125 | 	for _, msgRaw := range messagesRaw {
126 | 		msgMap, ok := msgRaw.(map[string]any)
127 | 		if !ok {
128 | 			return nil, fmt.Errorf("invalid message format")
129 | 		}
130 | 
131 | 		role, ok := msgMap["role"].(string)
132 | 		if !ok {
133 | 			return nil, fmt.Errorf("message must have a 'role' field of type string")
134 | 		}
135 | 
136 | 		content, ok := msgMap["content"].(string)
137 | 		if !ok {
138 | 			return nil, fmt.Errorf("message must have a 'content' field of type string")
139 | 		}
140 | 
141 | 		messages = append(messages, Message{Role: role, Content: content})
142 | 	}
143 | 
144 | 	return messages, nil
145 | }
146 | 
147 | // handlePerplexityAsk handles the perplexity_ask tool request
148 | func handlePerplexityAsk(config PerplexityConfig) server.ToolHandlerFunc {
149 | 	return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
150 | 		messages, err := parseMessagesFromRequest(request)
151 | 		if err != nil {
152 | 			return mcp.NewToolResultError(err.Error()), nil
153 | 		}
154 | 
155 | 		result, err := performChatCompletion(config.APIKey, config.Model, messages)
156 | 		if err != nil {
157 | 			return mcp.NewToolResultError(fmt.Sprintf("Error calling Perplexity API: %v", err)), nil
158 | 		}
159 | 
160 | 		return mcp.NewToolResultText(result), nil
161 | 	}
162 | }
163 | 
164 | // handlePerplexityReason handles the perplexity_reason tool request
165 | func handlePerplexityReason(config PerplexityConfig) server.ToolHandlerFunc {
166 | 	return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
167 | 		query, ok := request.Params.Arguments["query"].(string)
168 | 		if !ok {
169 | 			return mcp.NewToolResultError("'query' must be a string"), nil
170 | 		}
171 | 
172 | 		messages := []Message{
173 | 			{Role: "system", Content: "You are a reasoning assistant focused on solving complex problems through step-by-step reasoning."},
174 | 			{Role: "user", Content: query},
175 | 		}
176 | 
177 | 		result, err := performChatCompletion(config.APIKey, config.ReasoningModel, messages)
178 | 		if err != nil {
179 | 			return mcp.NewToolResultError(fmt.Sprintf("Error calling Perplexity API: %v", err)), nil
180 | 		}
181 | 
182 | 		return mcp.NewToolResultText(result), nil
183 | 	}
184 | }
185 | 
186 | // registerPerplexityAskTool creates and registers the perplexity_ask tool
187 | func registerPerplexityAskTool(s *server.MCPServer, config PerplexityConfig) {
188 | 	perplexityTool := mcp.NewTool("perplexity_ask",
189 | 		mcp.WithDescription("Engages in a conversation using the Perplexity to search the internet and answer questions. Accepts an array of messages (each with a role and content) and returns a chat completion response from the Perplexity model."),
190 | 		mcp.WithArray("messages",
191 | 			mcp.Required(),
192 | 			mcp.Description("Array of conversation messages"),
193 | 			mcp.Items(map[string]any{
194 | 				"type": "object",
195 | 				"properties": map[string]any{
196 | 					"role": map[string]any{
197 | 						"type":        "string",
198 | 						"description": "Role of the message (e.g., system, user, assistant)",
199 | 					},
200 | 					"content": map[string]any{
201 | 						"type":        "string",
202 | 						"description": "The content of the message",
203 | 					},
204 | 				},
205 | 				"required": []string{"role", "content"},
206 | 			}),
207 | 		),
208 | 	)
209 | 
210 | 	s.AddTool(perplexityTool, handlePerplexityAsk(config))
211 | }
212 | 
213 | // registerPerplexityReasonTool creates and registers the perplexity_reason tool
214 | func registerPerplexityReasonTool(s *server.MCPServer, config PerplexityConfig) {
215 | 	reasoningTool := mcp.NewTool("perplexity_reason",
216 | 		mcp.WithDescription("Uses the Perplexity reasoning model to perform complex reasoning tasks. Accepts a query string and returns a comprehensive reasoned response."),
217 | 		mcp.WithString("query",
218 | 			mcp.Required(),
219 | 			mcp.Description("The query or problem to reason about"),
220 | 		),
221 | 	)
222 | 
223 | 	s.AddTool(reasoningTool, handlePerplexityReason(config))
224 | }
225 | 
226 | func main() {
227 | 	app := &cli.App{
228 | 		Name:  "perplexity-mcp",
229 | 		Usage: "A Model Context Protocol server for Perplexity API",
230 | 		Flags: []cli.Flag{
231 | 			&cli.StringFlag{
232 | 				Name:    "model",
233 | 				Aliases: []string{"m"},
234 | 				Value:   "sonar-pro",
235 | 				Usage:   "The model to use for chat completions",
236 | 				EnvVars: []string{"PERPLEXITY_MODEL"},
237 | 			},
238 | 			&cli.StringFlag{
239 | 				Name:    "reasoning-model",
240 | 				Aliases: []string{"r"},
241 | 				Value:   "sonar-reasoning-pro",
242 | 				Usage:   "The model to use for reasoning tasks",
243 | 				EnvVars: []string{"PERPLEXITY_REASONING_MODEL"},
244 | 			},
245 | 			&cli.StringFlag{
246 | 				Name:     "api-key",
247 | 				Aliases:  []string{"k"},
248 | 				Usage:    "The API key to use for Perplexity API requests",
249 | 				EnvVars:  []string{"PERPLEXITY_API_KEY"},
250 | 				Required: true,
251 | 			},
252 | 		},
253 | 		Action: func(c *cli.Context) error {
254 | 			// Create configuration from CLI arguments
255 | 			config := PerplexityConfig{
256 | 				APIKey:         c.String("api-key"),
257 | 				Model:          c.String("model"),
258 | 				ReasoningModel: c.String("reasoning-model"),
259 | 			}
260 | 
261 | 			buildInfo, ok := debug.ReadBuildInfo()
262 | 			version := "v0.0.1"
263 | 			if ok {
264 | 				version = buildInfo.Main.Version
265 | 			}
266 | 
267 | 			// Create a new MCP server
268 | 			s := server.NewMCPServer(
269 | 				"perplexity-mcp",
270 | 				version,
271 | 			)
272 | 
273 | 			// Register tools
274 | 			registerPerplexityAskTool(s, config)
275 | 			registerPerplexityReasonTool(s, config)
276 | 
277 | 			// Start the server
278 | 			if err := server.ServeStdio(s); err != nil {
279 | 				return cli.Exit(fmt.Sprintf("Server error: %v", err), 1)
280 | 			}
281 | 
282 | 			return nil
283 | 		},
284 | 	}
285 | 
286 | 	err := app.Run(os.Args)
287 | 	if err != nil {
288 | 		slog.Error("Server error", "error", err)
289 | 		os.Exit(1)
290 | 	}
291 | }
292 | 
```