Module Service Protocol

 

Version 1.0

This document outlines the Protocol specification for creating service that implements the Module Service Protocol, compatible with SynthGrid.

Your service must implement two main components: a metadata endpoint and one or more action endpoints.

High-level MSP overview

flowchart LR
  subgraph YM["Your Module Service"]
    direction LR
      meta["/meta"]
      action1["/action1"]
      action2["/action2"]
    end
      
  SynthGrid --Reads--> meta
  SynthGrid --Calls--> action1

  action1 --> YI["Your Data/Services"]

Module Service Protocol Sequence

sequenceDiagram
    participant SG as SynthGrid
    participant CM as Custom Module

    Note over SG,CM: Module Registration
    SG->>CM: Request module metadata
    CM-->>SG: Provide metadata (actions, schemas)
    Note over SG: Register and setup module
    Note over SG,CM: Action Execution
    SG->>CM: Call specific action
    activate CM
    Note over CM: Execute action
    CM-->>SG: Return action result
    deactivate CM
    Note over SG: Process result
Protocol Version

The current version of this Protocol specification is 1.0.

Base URL

You may choose any base URL for your service. The endpoints described below should be appended to your chosen base URL. We highly encourage HTTPS, CORS restrictions and strong TLS settings.

Meta Endpoint

Request
  • URL: /meta
  • Method: GET
Response

The response must be a JSON object with the following structure:

{
  "protocolVersion": 1,
  "moduleVersion": "x.y.z",
  "moduleName": "string",
  "description": "string",
  "actions": [
    {
      "name": "string",
      "description": "string",
      "route": "string",
      "riskLevel": "safe" | "machineApprovalRequired" | "humanApprovalRequired" | "forbidden",
      "input": {
        // JSON Schema for input
      },
      "output": {
        // JSON Schema for output
      }
    }
  ]
}
Field Description
protocolVersion The MSP version your module implements.
moduleVersion The version of YOUR module (following semantic versioning: MAJOR.MINOR.PATCH).
moduleName A unique name for your module.
description A brief description of what your module does.
actions An array of action objects, each describing an action your module can perform.

Action Schema

Each action in a SynthGrid third-party module is defined by the following schema:

Field Description
name Unique identifier for the action within the module.
description Brief explanation of the action’s purpose.
route API endpoint for invoking this action.
riskLevel Indicates the action’s risk level and required approval process.
input JSON schema defining the expected input parameters.
output JSON schema defining the structure of the action’s response.

Risk Levels

The riskLevel field can have one of four values:

Number Risk Level Description
1 "safe" No significant risk. Executed immediately without additional checks.
2 "machineApprovalRequired" Potential risk, requires automated verification before execution.
3 "humanApprovalRequired" High-risk, requires human review and approval before execution.
4 "forbidden" Not allowed to be executed under any circumstances. Immediately rejected.

Action Endpoints

For each action defined in your metadata, you must implement a corresponding endpoint.

Request

  • URL: The route specified in the action’s metadata (e.g., /action/yourActionName)
  • Method: POST
  • Body: JSON object matching the input schema defined in the action’s metadata

Response

The response must be a JSON object with the following structure:

{
  "status": "success" | "failure" | "invalidInput",
  "data": {
    // Action-specific output as per the schema (for success)
  },
  "error": {
    "message": "string" // For failure or invalidInput
  }
}
  • status: Must be one of “success”, “failure”, or “invalidInput”.
  • data: Present only when status is “success”. Structure must match the output schema defined in the action’s metadata.
  • error: Present only when status is “failure” or “invalidInput”. Contains an error message explaining what went wrong.

Implementation Notes

  1. All endpoints must use HTTPS in production environments.
  2. Your module should handle errors gracefully and return appropriate status codes and error messages.
  3. Input and output schemas should be kept as simple as possible. Avoid complex nested structures or conditional logic.
  4. Ensure your module can handle concurrent requests if necessary.

Example

Here’s a minimal example of a metadata response for a module that provides random commit messages:

{
  "protocolVersion": 1,
  "moduleVersion": "1.0.0",
  "moduleName": "RandomCommitMessage",
  "description": "Provides random commit messages from whatthecommit.com",
  "actions": [
    {
      "name": "getRandomCommitMessage",
      "description": "Retrieves a random commit message",
      "riskLevel": "machineApprovalRequired",
      
      "route": "/action/getRandomCommitMessage",
      "input": {
        "type": "object",
        "properties": {}
      },
      "output": {
        "type": "object",
        "properties": {
          "message": {"type": "string"}
        },
        "required": ["message"]
      }
    }
  ]
}

How the Risk Evaluation Works

flowchart TB
    A[Start: Action Request] --> B{Check riskLevel}
    B -->|safe| C[Execute Action]
    B -->|machineApprovalRequired| D{Automated Check}
    B -->|humanApprovalRequired| E{Human Review}
    B -->|forbidden| F[Reject Action]
    
    D -->|Pass| C
    D -->|Fail| F
    
    E -->|Approved| C
    E -->|Rejected| F
    
    C --> G[Return Result]
    F --> H[Return Error]
    
    G --> I[End]
    H --> I

Example MSP Service

This is a super simple example service that implements the Module Service Protocol that should run/execute on almost any system with zero dependencies.

Save it to a file called example.py.

Then to run it:

chmod +x example.py
./example.py
#!/usr/bin/env python3
import json
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.request import urlopen

MODULE_METADATA = {
    "protocolVersion": 1,
    "moduleVersion": "1.0.0",
    "moduleName": "RandomCommitMessage",
    "description": "Provides random commit messages from whatthecommit.com",
    "actions": [
        {
            "name": "getRandomCommitMessage",
            "description": "Retrieves a random commit message",
            "route": "/action/getRandomCommitMessage",
            "riskLevel": "machineApprovalRequired",
            "input": {"type": "object", "properties": {}},
            "output": {"type": "object", "properties": {"message": {"type": "string"}}, "required": ["message"]},
        }
    ],
}


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/meta":
            self.send_response(200)
            self.send_header("Content-type", "application/json")
            self.end_headers()
            self.wfile.write(json.dumps(MODULE_METADATA).encode())
        else:
            self.send_error(404)

    def do_POST(self):
        if self.path == "/action/getRandomCommitMessage":
            try:
                content_length = int(self.headers["Content-Length"])
                post_data = self.rfile.read(content_length)
                json.loads(post_data.decode("utf-8"))  # Validate JSON

                with urlopen("http://whatthecommit.com/index.txt") as response:
                    commit_message = response.read().decode("utf-8").strip()

                self.send_response(200)
                self.send_header("Content-type", "application/json")
                self.end_headers()
                self.wfile.write(json.dumps({"status": "success", "data": {"message": commit_message}}).encode())
            except Exception as e:
                self.send_response(500)
                self.send_header("Content-type", "application/json")
                self.end_headers()
                self.wfile.write(json.dumps({"status": "failure", "error": {"message": str(e)}}).encode())
        else:
            self.send_error(404)


if __name__ == "__main__":
    HTTPServer(("", 9876), Handler).serve_forever()

You can check this is working by:

curl -X POST "localhost:9876/action/getRandomCommitMessage" -d '{}'

or

curl "http://127.0.0.1:9876/meta"