@ethisyscore/plugin-sdk
v1.0.0-alpha.12
Published
EthisysCore Plugin SDK for Node.js — build container-hosted plugins with TypeScript/JavaScript
Downloads
315
Maintainers
Readme
EthisysCore Node.js Plugin SDK
@ethisyscore/plugin-sdk — Build container-mode plugins with TypeScript/JavaScript.
Container plugins run as isolated Docker containers with Dapr sidecars, communicating with the EthisysCore platform via HTTP.
Installation
npm install @ethisyscore/plugin-sdkQuick Start
import { PluginBase, PluginHost } from '@ethisyscore/plugin-sdk';
import type { PluginManifest, PluginContext, ToolResult } from '@ethisyscore/plugin-sdk';
class GreeterPlugin extends PluginBase {
get manifest(): PluginManifest {
return {
id: 'greeter',
name: 'Greeter Plugin',
version: '1.0.0',
type: 'TenantPlugin',
owner: 'acme-corp',
compatibleCoreVersion: '>=1.0.0',
mcpVersion: '1.0',
executionMode: 'Container',
mcpTools: [{
name: 'greet',
description: 'Say hello',
inputSchemaJson: JSON.stringify({
type: 'object',
properties: { name: { type: 'string' } },
required: ['name']
})
}]
};
}
async onInvokeTool(toolName: string, args: unknown, context: PluginContext): Promise<ToolResult> {
if (toolName === 'greet') {
const { name } = args as { name: string };
return { success: true, resultJson: JSON.stringify({ greeting: `Hello, ${name}!` }) };
}
return { success: false, error: `Unknown tool: ${toolName}` };
}
}
new PluginHost(new GreeterPlugin()).start();Lifecycle Hooks
All hooks are optional — override only what you need:
async onInitialize(context: PluginContext): Promise<void> // After platform connects
async onEnable(context: PluginContext): Promise<void> // Extension enabled for tenant
async onDisable(context: PluginContext): Promise<void> // Extension disabled
async onGetResource(uri: string, context: PluginContext): Promise<ResourceResult>
async onCheckHealth(): Promise<HealthResult> // Health probePlatform Services
Available through PluginContext:
// Key-value persistence (per plugin per tenant)
const count = await context.dataStore.get<number>('counter') ?? 0;
await context.dataStore.set('counter', count + 1);
// Structured logging
context.logger.info('Processing request');
context.logger.error('Failed to process', new Error('...'));
// Invoke tools from other plugins
const result = await context.mcpClient.invokeTool('other-tool', '{"key":"value"}');
// Publish domain events
await context.publishEvent('ItemCreated', { id: '123' });HTTP Endpoints
PluginHost starts an Express server (port: DAPR_APP_PORT or 8080):
| Endpoint | Method | Purpose |
|----------|--------|---------|
| /plugin/initialize | POST | Platform connects, provides context |
| /plugin/enable | POST | Extension enabled for tenant |
| /plugin/disable | POST | Extension disabled |
| /plugin/invoke-tool | POST | MCP tool invocation |
| /plugin/get-resource | POST | MCP resource read |
| /plugin/health | GET | Health probe |
| /plugin/heartbeat | GET | Liveness check |
Environment Variables
| Variable | Purpose | Default |
|----------|---------|---------|
| DAPR_APP_PORT | Port the plugin listens on | 8080 |
| DAPR_HTTP_PORT | Dapr sidecar HTTP port | 3500 |
| DAPR_API_TOKEN | Dapr sidecar auth token | — |
v1 Limitations
Compared to the .NET SDK:
- Monolithic dispatch — all tools route through
onInvokeTool()(no handler-per-tool classes) - No middleware pipeline — no built-in error handling, logging, or validation middleware
- No DI container — plugins manage their own dependencies
Handler-per-tool, middleware, and DI are planned for v2.
Build & Test
npm ci
npm run build
npm test