Skip to content

Server Plugins ​

Server plugins extend a Medipal instance with new actions: sending an email, calling an external API, evaluating a chart, exporting data, or anything else a workflow or an administrator may need to trigger. Plugins are written in Python against a small, stable SDK and run inside the instance.

What a plugin can do ​

  • Expose one or more named actions with declared input and output schemas.
  • Be configured per instance of the plugin, so the same plugin can be installed once and configured several times (for example one email plugin instance per department).
  • Read secrets from the Vault through references in its configuration, without ever handling raw secrets itself.
  • Read and change resources on the instance (patients, enrollments, charts, and more) through the host API, using a scoped API key bound to the plugin instance.
  • Be invoked from Workflows, from the API, and from the command line for testing.

Plugins shipped with the platform illustrate the range:

PluginWhat it does
Email senderSends transactional email through an SMTP relay configured per instance.
OpenAPI clientCalls any REST API described by an OpenAPI document supplied in the instance configuration, validating requests against it first.
Resource accessGives workflows structured read and write access to instance resources through the host API.

Lifecycle ​

  1. Install — the plugin package is included in the instance image by the deploying partner.
  2. Register — on start-up and periodically, the instance discovers installed plugins and records their definitions: name, version, actions and schemas. Registration can also be triggered on demand.
  3. Configure — an administrator creates one or more plugin instances in the web application, filling in the configuration form generated from the plugin's configuration schema. See Plugins.
  4. Invoke — workflows and API calls run actions on a plugin instance. Each invocation receives the validated payload, the instance configuration with secrets resolved, and a context describing who triggered it.
  5. Upgrade — when a new version is installed and registered, existing plugin instances are moved to the new definition and the previous version is retired.

The SDK contract ​

PartWhat the plugin provides
MetadataA unique plugin name and a version.
ActionsThe list of action names the plugin supports.
Action schemasJSON Schema for the input (and optionally output) of each action. Used to validate payloads and to build editor forms in the workflow editor.
Configuration schemaJSON Schema for the per-instance configuration. Fields can be marked as secret references so administrators pick a Vault entry instead of typing a value.
InvokeThe function that runs an action: it receives the action name, the request payload, the resolved instance configuration and the invocation context, and returns a response payload.
Entry pointA package entry point in the mp_server.plugins group, which is how the instance discovers the plugin.

Action schemas can also be computed per plugin instance, so a plugin whose actions depend on its configuration (such as the OpenAPI client, whose actions are the operations of the supplied document) presents exactly the right form in the workflow editor.

Errors and retries ​

Error classMeaningEffect
User errorThe input is invalid or a precondition is not met.Reported to the caller; the action is not retried.
Permission errorThe caller lacks a scope the action needs.Reported as forbidden; not retried.
Transient errorA temporary failure, such as an unreachable upstream.Reported as a server-side failure; workflows may retry.

Any other exception is treated as an unexpected failure and surfaced with its message.

Host API ​

When a plugin needs data from the instance, it calls named commands on the host API rather than making HTTP calls. Commands are discoverable at runtime with their descriptions, schemas and required scopes. The host checks scopes for every command against the context of the invocation.

Scopes come from an API key bound to the plugin instance: the administrator selects a Vault entry containing a key when configuring the instance, and the platform resolves it, derives the permissions, and removes it from the configuration before the plugin runs. Plugin instances without a bound key have no host access.

Building a plugin ​

  1. Start from the template. The plugin template project, available from the private registry, scaffolds the package, entry point, tests and release tooling. One command renames the template to your plugin name.
  2. Implement the contract. Fill in the metadata, actions, schemas and the invoke function. A minimal plugin fits in a few dozen lines:
python
class HelloPlugin(BackendPlugin):
    name = "hello"
    version = "1.0.0"

    def actions(self):
        return ["greet"]

    def action_schemas(self):
        return {"greet": {"input_schema": {"type": "object",
                                            "properties": {"name": {"type": "string"}}}}}

    def config_schema(self):
        return PluginConfigSchema.model_validate({"type": "object", "properties": {}})

    def invoke(self, *, action, request, instance_config, ctx):
        return PluginActionResponse(payload={"greeting": f"Hello, {request.payload.get('name', 'world')}!"})
  1. Test locally. The template ships a test suite and a watch mode. Actions can also be invoked directly through the instance's command line against a local instance, which exercises schema validation and secret resolution exactly as production does.
  2. Version and publish. Bump the version with the template's release commands and publish the package to the private registry.
  3. Install. Ask the deploying partner to add the package to the instance image and roll the instance; registration happens automatically on start-up.
  4. Configure. Create a plugin instance in the web application and use it from a workflow.

Guidelines ​

  • Keep actions small and composable; workflows combine them.
  • Validate inputs with schemas rather than in code, so the workflow editor can help authors.
  • Never log secrets or patient data; the invocation context carries a request identifier for correlation.
  • Raise the transient error class for failures that are worth retrying and the user error class for everything the caller must fix.
  • Keep the plugin name stable across versions: it is the identity that instance configurations follow through upgrades.