Skip to main content
Glama
index.ts9.97 kB
#!/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"; import axios, { AxiosInstance } from "axios"; const BASE_URL = "https://operator.opus.com"; interface OpusConfig { serviceKey: string; } class OpusMCPServer { private server: Server; private axiosInstance: AxiosInstance; private config: OpusConfig; constructor() { this.config = { serviceKey: process.env.OPUS_SERVICE_KEY || "", }; if (!this.config.serviceKey) { console.error( "ERROR: OPUS_SERVICE_KEY environment variable is required" ); process.exit(1); } this.axiosInstance = axios.create({ baseURL: BASE_URL, headers: { "x-service-key": this.config.serviceKey, "Content-Type": "application/json", }, }); this.server = new Server( { name: "opus-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); this.setupToolHandlers(); this.setupErrorHandling(); } private setupErrorHandling(): void { this.server.onerror = (error) => { console.error("[MCP Error]", error); }; process.on("SIGINT", async () => { await this.server.close(); process.exit(0); }); } private setupToolHandlers(): void { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: this.getTools(), })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "get_workflow_details": return await this.getWorkflowDetails(args); case "initiate_job": return await this.initiateJob(args); case "generate_file_upload_url": return await this.generateFileUploadUrl(args); case "execute_job": return await this.executeJob(args); case "get_job_status": return await this.getJobStatus(args); case "get_job_results": return await this.getJobResults(args); case "get_job_audit_log": return await this.getJobAuditLog(args); default: throw new Error(`Unknown tool: ${name}`); } } catch (error: any) { const errorMessage = error.response?.data?.message || error.message || "Unknown error"; return { content: [ { type: "text", text: `Error: ${errorMessage}`, }, ], isError: true, }; } }); } private getTools(): Tool[] { return [ { name: "get_workflow_details", description: "Get workflow details including jobPayloadSchema that defines required inputs for a workflow", inputSchema: { type: "object", properties: { workflowId: { type: "string", description: "The ID of the workflow to retrieve details for", }, }, required: ["workflowId"], }, }, { name: "initiate_job", description: "Initiate a new job for a workflow. Returns jobExecutionId required for subsequent operations", inputSchema: { type: "object", properties: { workflowId: { type: "string", description: "The ID of the workflow to execute", }, title: { type: "string", description: "Title for the job", }, description: { type: "string", description: "Description for the job", }, }, required: ["workflowId", "title", "description"], }, }, { name: "generate_file_upload_url", description: "Generate presigned URL for file upload. Returns presignedUrl (for uploading) and fileUrl (to reference in job execution)", inputSchema: { type: "object", properties: { fileExtension: { type: "string", description: "File extension with dot (e.g., .pdf, .jpeg, .png, .docx, .csv, .xlsx, .txt, .json, .html, .xml)", }, accessScope: { type: "string", enum: ["all", "user", "workspace", "organization"], description: "Access scope for the file", default: "organization", }, }, required: ["fileExtension"], }, }, { name: "execute_job", description: "Execute a job with populated input values. Use jobPayloadSchema from get_workflow_details to structure inputs correctly", inputSchema: { type: "object", properties: { jobExecutionId: { type: "string", description: "The job execution ID from initiate_job response", }, jobPayloadSchemaInstance: { type: "object", description: "Job payload with all inputs populated according to workflow schema", }, }, required: ["jobExecutionId", "jobPayloadSchemaInstance"], }, }, { name: "get_job_status", description: "Get the current status of a job execution (e.g., IN PROGRESS, COMPLETED, FAILED)", inputSchema: { type: "object", properties: { jobExecutionId: { type: "string", description: "The job execution ID to check status for", }, }, required: ["jobExecutionId"], }, }, { name: "get_job_results", description: "Get the results of a completed job execution. Only works when job status is COMPLETED", inputSchema: { type: "object", properties: { jobExecutionId: { type: "string", description: "The job execution ID to retrieve results for", }, }, required: ["jobExecutionId"], }, }, { name: "get_job_audit_log", description: "Get detailed audit log of all system actions during job execution", inputSchema: { type: "object", properties: { jobExecutionId: { type: "string", description: "The job execution ID to retrieve audit log for", }, }, required: ["jobExecutionId"], }, }, ]; } private async getWorkflowDetails(args: any) { const { workflowId } = args; const response = await this.axiosInstance.get( `/workflow/${workflowId}` ); return { content: [ { type: "text", text: JSON.stringify(response.data, null, 2), }, ], }; } private async initiateJob(args: any) { const { workflowId, title, description } = args; const response = await this.axiosInstance.post("/job/initiate", { workflowId, title, description, }); return { content: [ { type: "text", text: JSON.stringify(response.data, null, 2), }, ], }; } private async generateFileUploadUrl(args: any) { const { fileExtension, accessScope = "organization" } = args; const response = await this.axiosInstance.post("/job/file/upload", { fileExtension, accessScope, }); return { content: [ { type: "text", text: JSON.stringify( { presignedUrl: response.data.presignedUrl, fileUrl: response.data.fileUrl, instructions: "Use presignedUrl to upload file via PUT request (no auth headers). Use fileUrl in job execution payload.", }, null, 2 ), }, ], }; } private async executeJob(args: any) { const { jobExecutionId, jobPayloadSchemaInstance } = args; const response = await this.axiosInstance.post("/job/execute", { jobExecutionId, jobPayloadSchemaInstance, }); return { content: [ { type: "text", text: JSON.stringify(response.data, null, 2), }, ], }; } private async getJobStatus(args: any) { const { jobExecutionId } = args; const response = await this.axiosInstance.get( `/job/${jobExecutionId}/status` ); return { content: [ { type: "text", text: JSON.stringify(response.data, null, 2), }, ], }; } private async getJobResults(args: any) { const { jobExecutionId } = args; const response = await this.axiosInstance.get( `/job/${jobExecutionId}/results` ); return { content: [ { type: "text", text: JSON.stringify(response.data, null, 2), }, ], }; } private async getJobAuditLog(args: any) { const { jobExecutionId } = args; const response = await this.axiosInstance.get( `/job/${jobExecutionId}/audit` ); return { content: [ { type: "text", text: JSON.stringify(response.data, null, 2), }, ], }; } async run(): Promise<void> { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("Opus MCP Server running on stdio"); } } const server = new OpusMCPServer(); server.run().catch(console.error);

Implementation Reference

Latest Blog Posts

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/Moenamatics/Opus-MCP'

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