Reorganize community environments - Move lean_proof_env, router_env, and philosophical_rlaif_env.py to environments/community/ - Add comprehensive README for community environments - This organizes community-contributed environments into a dedicated community folder for better maintainability and discoverability

This commit is contained in:
Shannon Sands 2025-05-23 13:31:13 +10:00
parent 945ea30c3a
commit e85a170c34
53 changed files with 85 additions and 0 deletions

View file

@ -0,0 +1,21 @@
FROM node:22.12-alpine AS builder
COPY . /app
WORKDIR /app
RUN --mount=type=cache,target=/root/.npm npm install
FROM node:22-alpine AS release
WORKDIR /app
COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/package.json /app/package.json
COPY --from=builder /app/package-lock.json /app/package-lock.json
ENV NODE_ENV=production
RUN npm ci --ignore-scripts --omit-dev
ENTRYPOINT ["node", "dist/index.js"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

View file

@ -0,0 +1,310 @@
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
/**
* Definition of the Perplexity Ask Tool.
* This tool accepts an array of messages and returns a chat completion response
* from the Perplexity API, with citations appended to the message if provided.
*/
const PERPLEXITY_ASK_TOOL: Tool = {
name: "perplexity_ask",
description:
"Engages in a conversation using the Sonar API. " +
"Accepts an array of messages (each with a role and content) " +
"and returns a ask completion response from the Perplexity model.",
inputSchema: {
type: "object",
properties: {
messages: {
type: "array",
items: {
type: "object",
properties: {
role: {
type: "string",
description: "Role of the message (e.g., system, user, assistant)",
},
content: {
type: "string",
description: "The content of the message",
},
},
required: ["role", "content"],
},
description: "Array of conversation messages",
},
},
required: ["messages"],
},
};
/**
* Definition of the Perplexity Research Tool.
* This tool performs deep research queries using the Perplexity API.
*/
const PERPLEXITY_RESEARCH_TOOL: Tool = {
name: "perplexity_research",
description:
"Performs deep research using the Perplexity API. " +
"Accepts an array of messages (each with a role and content) " +
"and returns a comprehensive research response with citations.",
inputSchema: {
type: "object",
properties: {
messages: {
type: "array",
items: {
type: "object",
properties: {
role: {
type: "string",
description: "Role of the message (e.g., system, user, assistant)",
},
content: {
type: "string",
description: "The content of the message",
},
},
required: ["role", "content"],
},
description: "Array of conversation messages",
},
},
required: ["messages"],
},
};
/**
* Definition of the Perplexity Reason Tool.
* This tool performs reasoning queries using the Perplexity API.
*/
const PERPLEXITY_REASON_TOOL: Tool = {
name: "perplexity_reason",
description:
"Performs reasoning tasks using the Perplexity API. " +
"Accepts an array of messages (each with a role and content) " +
"and returns a well-reasoned response using the sonar-reasoning-pro model.",
inputSchema: {
type: "object",
properties: {
messages: {
type: "array",
items: {
type: "object",
properties: {
role: {
type: "string",
description: "Role of the message (e.g., system, user, assistant)",
},
content: {
type: "string",
description: "The content of the message",
},
},
required: ["role", "content"],
},
description: "Array of conversation messages",
},
},
required: ["messages"],
},
};
// Retrieve the Perplexity API key from environment variables
const PERPLEXITY_API_KEY = process.env.PERPLEXITY_API_KEY;
if (!PERPLEXITY_API_KEY) {
console.error("Error: PERPLEXITY_API_KEY environment variable is required");
process.exit(1);
}
/**
* Performs a chat completion by sending a request to the Perplexity API.
* Appends citations to the returned message content if they exist.
*
* @param {Array<{ role: string; content: string }>} messages - An array of message objects.
* @param {string} model - The model to use for the completion.
* @returns {Promise<string>} The chat completion result with appended citations.
* @throws Will throw an error if the API request fails.
*/
async function performChatCompletion(
messages: Array<{ role: string; content: string }>,
model: string = "sonar-pro"
): Promise<string> {
// Construct the API endpoint URL and request body
const url = new URL("https://api.perplexity.ai/chat/completions");
const body = {
model: model, // Model identifier passed as parameter
messages: messages,
// Additional parameters can be added here if required (e.g., max_tokens, temperature, etc.)
// See the Sonar API documentation for more details:
// https://docs.perplexity.ai/api-reference/chat-completions
};
let response;
try {
response = await fetch(url.toString(), {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${PERPLEXITY_API_KEY}`,
},
body: JSON.stringify(body),
});
} catch (error) {
throw new Error(`Network error while calling Perplexity API: ${error}`);
}
// Check for non-successful HTTP status
if (!response.ok) {
let errorText;
try {
errorText = await response.text();
} catch (parseError) {
errorText = "Unable to parse error response";
}
throw new Error(
`Perplexity API error: ${response.status} ${response.statusText}\n${errorText}`
);
}
// Attempt to parse the JSON response from the API
let data;
try {
data = await response.json();
} catch (jsonError) {
throw new Error(`Failed to parse JSON response from Perplexity API: ${jsonError}`);
}
// Directly retrieve the main message content from the response
let messageContent = data.choices[0].message.content;
// If citations are provided, append them to the message content
if (data.citations && Array.isArray(data.citations) && data.citations.length > 0) {
messageContent += "\n\nCitations:\n";
data.citations.forEach((citation: string, index: number) => {
messageContent += `[${index + 1}] ${citation}\n`;
});
}
return messageContent;
}
// Initialize the server with tool metadata and capabilities
const server = new Server(
{
name: "example-servers/perplexity-ask",
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
}
);
/**
* Registers a handler for listing available tools.
* When the client requests a list of tools, this handler returns all available Perplexity tools.
*/
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [PERPLEXITY_ASK_TOOL, PERPLEXITY_RESEARCH_TOOL, PERPLEXITY_REASON_TOOL],
}));
/**
* Registers a handler for calling a specific tool.
* Processes requests by validating input and invoking the appropriate tool.
*
* @param {object} request - The incoming tool call request.
* @returns {Promise<object>} The response containing the tool's result or an error.
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
const { name, arguments: args } = request.params;
if (!args) {
throw new Error("No arguments provided");
}
switch (name) {
case "perplexity_ask": {
if (!Array.isArray(args.messages)) {
throw new Error("Invalid arguments for perplexity_ask: 'messages' must be an array");
}
// Invoke the chat completion function with the provided messages
const messages = args.messages;
const result = await performChatCompletion(messages, "sonar-pro");
return {
content: [{ type: "text", text: result }],
isError: false,
};
}
case "perplexity_research": {
if (!Array.isArray(args.messages)) {
throw new Error("Invalid arguments for perplexity_research: 'messages' must be an array");
}
// Invoke the chat completion function with the provided messages using the deep research model
const messages = args.messages;
const result = await performChatCompletion(messages, "sonar-deep-research");
return {
content: [{ type: "text", text: result }],
isError: false,
};
}
case "perplexity_reason": {
if (!Array.isArray(args.messages)) {
throw new Error("Invalid arguments for perplexity_reason: 'messages' must be an array");
}
// Invoke the chat completion function with the provided messages using the reasoning model
const messages = args.messages;
const result = await performChatCompletion(messages, "sonar-reasoning-pro");
return {
content: [{ type: "text", text: result }],
isError: false,
};
}
default:
// Respond with an error if an unknown tool is requested
return {
content: [{ type: "text", text: `Unknown tool: ${name}` }],
isError: true,
};
}
} catch (error) {
// Return error details in the response
return {
content: [
{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
}
});
/**
* Initializes and runs the server using standard I/O for communication.
* Logs an error and exits if the server fails to start.
*/
async function runServer() {
try {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Perplexity MCP Server running on stdio with Ask, Research, and Reason tools");
} catch (error) {
console.error("Fatal error running server:", error);
process.exit(1);
}
}
// Start the server and catch any startup errors
runServer().catch((error) => {
console.error("Fatal error running server:", error);
process.exit(1);
});