octagon-13f-holdings-mcp
Version:
MCP server for 13F Holdings Analysis. Provides specialized AI-powered institutional ownership and holdings data analysis with comprehensive Form 13F filings, fund performance analytics, and industry-level holdings insights.
115 lines (114 loc) • 5.75 kB
JavaScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import dotenv from "dotenv";
import { readFile } from "fs/promises";
import OpenAI from "openai";
import path from "path";
import { fileURLToPath } from "url";
import { z } from "zod";
// Get package.json info
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const packageJsonPath = path.join(__dirname, "..", "package.json");
const packageJsonContent = await readFile(packageJsonPath, "utf8");
const packageInfo = JSON.parse(packageJsonContent);
// Load environment variables
dotenv.config();
// Check for required environment variables
const OCTAGON_API_KEY = process.env.OCTAGON_API_KEY;
const OCTAGON_API_BASE_URL = process.env.OCTAGON_API_BASE_URL || "https://api.octagonagents.com/v1";
if (!OCTAGON_API_KEY) {
console.error("Error: OCTAGON_API_KEY is not set in the environment variables");
console.error("Please set the OCTAGON_API_KEY environment variable or use 'env OCTAGON_API_KEY=your_key npx -y octagon-13f-holdings-mcp'");
process.exit(1);
}
// Initialize OpenAI client with Octagon API
const octagonClient = new OpenAI({
apiKey: OCTAGON_API_KEY,
baseURL: OCTAGON_API_BASE_URL,
defaultHeaders: {
"User-Agent": `${packageInfo.name}/${packageInfo.version} (Node.js/${process.versions.node})`
},
});
// Create MCP server
const server = new McpServer({
name: packageInfo.name,
version: packageInfo.version,
});
// Helper function to process streaming responses
async function processStreamingResponse(stream) {
let fullResponse = "";
let citations = [];
try {
// Process the streaming response
for await (const chunk of stream) {
// For Chat Completions API
if (chunk.choices && chunk.choices[0]?.delta?.content) {
fullResponse += chunk.choices[0].delta.content;
// Check for citations in the final chunk
if (chunk.choices[0]?.finish_reason === "stop" && chunk.choices[0]?.citations) {
citations = chunk.choices[0].citations;
}
}
// For Responses API
if (chunk.type === "response.output_text.delta") {
fullResponse += chunk.text?.delta || "";
}
}
return fullResponse;
}
catch (error) {
console.error("Error processing streaming response:", error);
throw error;
}
}
// Define a schema for the 'prompt' parameter that all tools will use
const promptSchema = z.object({
prompt: z.string().describe("Your natural language query or request for the agent"),
});
// Holdings Agent
server.tool("octagon-holdings-agent", "[PUBLIC MARKET INTELLIGENCE] Specialized agent for institutional ownership and holdings data, providing comprehensive insights into Form 13F filings, institutional investor activity, fund performance, and industry-level holdings analytics. Capabilities: Retrieve the latest Form 13F and related institutional ownership filings; Analyze institutional holder filings for specific securities and periods; Summarize performance of institutional holders (funds, asset managers); Break down institutional portfolios by industry/sector; Summarize institutional positions for a given security; Benchmark industry performance based on institutional holdings. Use Cases: Tracking institutional buying and selling activity for specific stocks; Analyzing fund manager performance and portfolio allocation; Benchmarking industry and sector performance based on institutional holdings; Identifying new or exited positions by major funds; Understanding industry exposure and concentration in institutional portfolios. Example queries: '@octagon-holdings-agent Retrieve the most recent Form 13F and related filings submitted by institutional investors, limited to 50 records on page 0.'; '@octagon-holdings-agent Retrieve analytics for institutional holder filings for AAPL in Q2 of 2023, limited to 20 records on page 0.'; '@octagon-holdings-agent Get a summary of the performance of the institutional holder with CIK 0001166559.'; '@octagon-holdings-agent Retrieve the industry breakdown for the holder with CIK 0001067983 for Q4 of 2024.'; '@octagon-holdings-agent Get a summary of institutional positions for AAPL for Q4 of 2024.'; '@octagon-holdings-agent Get a financial performance summary for all industries for Q4 of 2024.'", {
prompt: z.string().describe("Your natural language query or request for the agent"),
}, async ({ prompt }) => {
try {
const response = await octagonClient.chat.completions.create({
model: "octagon-holdings-agent",
messages: [{ role: "user", content: prompt }],
stream: true,
metadata: { tool: "mcp" }
});
const result = await processStreamingResponse(response);
return {
content: [
{
type: "text",
text: result,
},
],
};
}
catch (error) {
console.error("Error calling Holdings agent:", error);
return {
isError: true,
content: [
{
type: "text",
text: `Error: Failed to process holdings query. ${error}`,
},
],
};
}
});
// Start the server with stdio transport
async function main() {
try {
const transport = new StdioServerTransport();
await server.connect(transport);
}
catch (error) {
process.exit(1);
}
}
main();