Skip to main content
Glama

AgentOps MCP

Official
by AgentOps-AI
index.ts12 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js"; import axios, { AxiosResponse } from "axios"; const HOST = "https://api.agentops.ai"; interface AuthHeaders { [key: string]: string; Authorization: string; } interface ErrorResponse { error: string; } // Server state to hold JWT token class ServerState { private jwtToken: string | null = null; setJwtToken(token: string): void { this.jwtToken = token; } getAuthHeaders(): AuthHeaders | null { if (!this.jwtToken) { return null; } return { Authorization: `Bearer ${this.jwtToken}`, "User-Agent": "agentops-mcp/0.3.5", }; } isAuthenticated(): boolean { return this.jwtToken !== null; } clearAuth(): void { this.jwtToken = null; } } const serverState = new ServerState(); /** * Initialize authentication on server startup if API key is available */ async function initializeAuth(): Promise<void> { const apiKey = process.env["AGENTOPS_API_KEY"]; if (apiKey) { try { const authResult = await authWithApiKey(apiKey); if (typeof authResult === "string") { serverState.setJwtToken(authResult); console.error( "Auto-authentication successful using environment variable", ); } else { console.error(`Auto-authentication failed: ${authResult.error}`); } } catch (error) { console.error( `Auto-authentication error: ${error instanceof Error ? error.message : String(error)}`, ); } } } /** * Authorize using an AgentOps project API key and return JWT token */ async function authWithApiKey(apiKey: string): Promise<string | ErrorResponse> { const data = { api_key: apiKey }; try { const response: AxiosResponse = await axios.post( `${HOST}/public/v1/auth/access_token`, data, ); const bearer = response.data?.bearer; if (!bearer) { throw new Error("No bearer token received from auth endpoint"); } return bearer; } catch (error) { return { error: error instanceof Error ? error.message : String(error) }; } } /** * Clean response by removing empty values */ function clean(response: any): any { if (typeof response === "object" && response !== null) { if (Array.isArray(response)) { return response .map((item) => clean(item)) .filter( (value) => value !== "" && value !== null && !(Array.isArray(value) && value.length === 0) && !(typeof value === "object" && Object.keys(value).length === 0), ); } else { const cleaned: any = {}; for (const [key, value] of Object.entries(response)) { const cleanedValue = clean(value); if ( cleanedValue !== "" && cleanedValue !== null && !(Array.isArray(cleanedValue) && cleanedValue.length === 0) && !( typeof cleanedValue === "object" && Object.keys(cleanedValue).length === 0 ) ) { cleaned[key] = cleanedValue; } } return cleaned; } } return response; } /** * Make authenticated request to AgentOps API using stored JWT token */ async function makeAuthenticatedRequest(endpoint: string): Promise<any> { const authHeaders = serverState.getAuthHeaders(); if (!authHeaders) { throw new Error( "Not authenticated. Please use the 'auth' tool first with your AgentOps API key.", ); } try { const response = await axios.get(`${HOST}${endpoint}`, { headers: authHeaders, }); return clean(response.data); } catch (error) { throw new Error(error instanceof Error ? error.message : String(error)); } } const server = new Server({ name: "agentops-mcp", version: "0.3.0", }); // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "auth", description: "Authorize using the AGENTOPS_API_KEY. If the API key is not provided and cannot be found in the directory, ask the user for the API key.", inputSchema: { type: "object", properties: { api_key: { type: "string", description: "AgentOps project API key (optional if AGENTOPS_API_KEY environment variable is set)", }, }, required: [], }, }, { name: "get_trace", description: "Get trace information and metrics by trace_id.", inputSchema: { type: "object", properties: { trace_id: { type: "string", description: "Trace ID", }, }, required: ["trace_id"], }, }, { name: "get_span", description: "Get span information and metrics by span_id.", inputSchema: { type: "object", properties: { span_id: { type: "string", description: "Span ID", }, }, required: ["span_id"], }, }, { name: "get_complete_trace", description: "Reserved for explicit requests for COMPLETE or ALL data. Get complete trace information and metrics by trace_id.", inputSchema: { type: "object", properties: { trace_id: { type: "string", description: "Trace ID", }, }, required: ["trace_id"], }, }, ], }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "auth": { const { api_key } = args as { api_key?: string }; // Check if already authenticated if (serverState.isAuthenticated() && !api_key) { return { content: [ { type: "text", text: JSON.stringify( { success: true, message: "Already authenticated", source: "Previously authenticated (likely from environment variable on startup)", }, null, 2, ), }, ], }; } // Try to get API key from environment first, then from parameter const actualApiKey = api_key || process.env["AGENTOPS_API_KEY"]; if (!actualApiKey) { throw new Error( "No project API key available. Please provide a project API key.", ); } const authResult = await authWithApiKey(actualApiKey); if (typeof authResult === "object" && "error" in authResult) { throw new Error(`Authentication failed: ${authResult.error}`); } // Store the JWT token in server state serverState.setJwtToken(authResult); const result = await makeAuthenticatedRequest(`/public/v1/project`); const name = result.name; return { content: [ { type: "text", text: JSON.stringify( { success: true, message: "Authentication successful", project: name, }, null, 2, ), }, ], }; } case "get_trace": { const { trace_id } = args as { trace_id: string }; const [traceInfo, traceMetrics] = await Promise.all([ makeAuthenticatedRequest(`/public/v1/traces/${trace_id}`), makeAuthenticatedRequest(`/public/v1/traces/${trace_id}/metrics`), ]); const result = { ...traceInfo, metrics: traceMetrics }; return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } case "get_span": { const { span_id } = args as { span_id: string }; const [spanInfo, spanMetrics] = await Promise.all([ makeAuthenticatedRequest(`/public/v1/spans/${span_id}`), makeAuthenticatedRequest(`/public/v1/spans/${span_id}/metrics`), ]); const result = { ...spanInfo, metrics: spanMetrics }; return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } case "get_complete_trace": { const { trace_id } = args as { trace_id: string }; const [traceInfo, traceMetrics] = await Promise.all([ makeAuthenticatedRequest(`/public/v1/traces/${trace_id}`), makeAuthenticatedRequest(`/public/v1/traces/${trace_id}/metrics`), ]); const parentTrace = { ...traceInfo, metrics: traceMetrics }; if (parentTrace.spans && Array.isArray(parentTrace.spans)) { for (let i = 0; i < parentTrace.spans.length; i++) { if (parentTrace.spans[i].span_id) { const span_id = parentTrace.spans[i].span_id; const [childSpanInfo, childSpanMetrics] = await Promise.all([ makeAuthenticatedRequest(`/public/v1/spans/${span_id}`), makeAuthenticatedRequest(`/public/v1/spans/${span_id}/metrics`), ]); parentTrace.spans[i] = { ...childSpanInfo, metrics: childSpanMetrics, }; } } } return { content: [ { type: "text", text: JSON.stringify(parentTrace, null, 2), }, ], }; } default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: JSON.stringify({ error: errorMessage }, null, 2), }, ], }; } }); async function main() { const transport = new StdioServerTransport(); // Initialize authentication before connecting await initializeAuth(); await server.connect(transport); // Debug: Log environment variable and authentication status const hasApiKey = !!process.env["AGENTOPS_API_KEY"]; const isAuthenticated = serverState.isAuthenticated(); console.error("AgentOps MCP server running on stdio"); console.error( `AGENTOPS_API_KEY environment variable: ${hasApiKey ? "SET" : "NOT SET"}`, ); console.error( `Authentication status: ${isAuthenticated ? "AUTHENTICATED" : "NOT AUTHENTICATED"}`, ); if (hasApiKey) { const keyPreview = process.env["AGENTOPS_API_KEY"]!.substring(0, 8) + "..."; console.error(`API Key preview: ${keyPreview}`); } } // Handle process signals gracefully process.on("SIGINT", () => { console.error("Received SIGINT, shutting down gracefully"); process.exit(0); }); process.on("SIGTERM", () => { console.error("Received SIGTERM, shutting down gracefully"); process.exit(0); }); // Handle uncaught exceptions process.on("uncaughtException", (error) => { console.error("Uncaught exception:", error); process.exit(1); }); process.on("unhandledRejection", (reason, promise) => { console.error("Unhandled rejection at:", promise, "reason:", reason); process.exit(1); }); if (require.main === module) { main().catch((error) => { console.error("Server error:", error); console.error("Stack trace:", error.stack); process.exit(1); }); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AgentOps-AI/agentops-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server