Skip to main content
Glama
vespo92

OPNSense MCP Server

macro_play

Execute a saved macro on the OPNSense MCP Server to automate network tasks, configure firewall rules, or manage VLANs. Supports parameter substitution and dry-run mode for testing.

Instructions

Play a saved macro

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
dryRunNoExecute in dry-run mode without making actual API calls
idYesMacro ID
parametersNoParameters to substitute in the macro

Implementation Reference

  • Core handler function that loads a macro by ID, substitutes parameters in its API calls, executes them sequentially via the OPNSense API client, supports dry-run mode, error handling options, and pre/post call hooks.
    async playMacro(id: string, options?: MacroPlaybackOptions): Promise<any[]> {
      const macro = await this.loadMacro(id);
      if (!macro) {
        throw new Error(`Macro ${id} not found`);
      }
      
      const results: any[] = [];
      const params = options?.parameters || {};
      
      for (const call of macro.calls) {
        try {
          // Apply parameter substitution
          const processedCall = this.substituteParameters(call, macro.parameters, params);
          
          // Allow pre-processing
          const finalCall = options?.beforeCall 
            ? await options.beforeCall(processedCall) 
            : processedCall;
          
          if (!finalCall) continue; // Skip if beforeCall returns null
          
          // Execute if not dry run
          if (!options?.dryRun) {
            const startTime = Date.now();
            let response;
            
            switch (finalCall.method) {
              case 'GET':
                response = await this.client.get(finalCall.path);
                break;
              case 'POST':
                response = await this.client.post(finalCall.path, finalCall.payload);
                break;
              case 'PUT':
                response = await this.client.put(finalCall.path, finalCall.payload);
                break;
              case 'DELETE':
                response = await this.client.delete(finalCall.path);
                break;
            }
            
            const duration = Date.now() - startTime;
            
            // Record the result
            const result = {
              call: finalCall,
              response,
              duration
            };
            
            results.push(result);
            
            // Allow post-processing
            if (options?.afterCall) {
              await options.afterCall(finalCall, response);
            }
          } else {
            // Dry run - just collect the calls
            results.push({
              call: finalCall,
              response: null,
              duration: 0,
              dryRun: true
            });
          }
        } catch (error) {
          if (options?.stopOnError) {
            throw error;
          }
          results.push({
            call,
            error,
            duration: 0
          });
        }
      }
      
      return results;
    }
  • Type definition for options passed to the macro_play tool, defining parameters for substitution, execution modes, and callback hooks.
    export interface MacroPlaybackOptions {
      parameters?: Record<string, any>;
      dryRun?: boolean;
      stopOnError?: boolean;
      beforeCall?: (call: APICall) => Promise<APICall | null>;
      afterCall?: (call: APICall, response: any) => Promise<void>;
    }
  • Method signature in IMacroRecorder interface declaring the playMacro functionality, serving as the contract for macro playback tool implementation.
    playMacro(id: string, options?: MacroPlaybackOptions): Promise<any[]>;
  • Helper method that substitutes template parameters ({{param}} or JSONPath) in API calls using provided values, enabling dynamic macro execution.
    private substituteParameters(
      call: APICall, 
      parameters: MacroParameter[], 
      values: Record<string, any>
    ): APICall {
      // Deep clone the call object
      const substitutedCall = JSON.parse(JSON.stringify(call));
      
      // Create a context object that includes all values plus any response data
      const context = {
        ...values,
        $: values  // Allow $ as root context for JSONPath expressions
      };
      
      // Convert call to string for pattern matching
      const callStr = JSON.stringify(substitutedCall);
      
      // Find all template expressions in the call
      const templateRegex = /\{\{([^}]+)\}\}/g;
      let modifiedStr = callStr;
      let match;
      
      while ((match = templateRegex.exec(callStr)) !== null) {
        const expression = match[1].trim();
        let value;
        
        if (expression.startsWith('$')) {
          // JSONPath expression
          try {
            const results = jsonpath.query(context, expression);
            value = results.length > 0 ? results[0] : match[0]; // Keep original if no match
          } catch (error) {
            console.warn(`Invalid JSONPath expression: ${expression}`, error);
            value = match[0]; // Keep original on error
          }
        } else {
          // Simple variable substitution
          const param = parameters.find(p => p.name === expression);
          value = values[expression] ?? param?.defaultValue;
        }
        
        if (value !== undefined && value !== match[0]) {
          // Replace the template with the value
          modifiedStr = modifiedStr.replace(match[0], JSON.stringify(value));
        }
      }
      
      return JSON.parse(modifiedStr);
    }

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/vespo92/OPNSenseMCP'

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