Agent Specification
Complete guide to creating, configuring, and deploying Ploinky agents.
Manifest Structure
Every agent is defined by a manifest.json
file that specifies its container, dependencies, and behavior:
Complete Manifest Schema
{
// Required fields
"container": "node:18-alpine", // Docker/Podman image
// Lifecycle commands
"install": "npm install", // Run once when agent is first created
"update": "npm update", // Run when agent needs updating
// Execution modes
"cli": "node repl.js", // Interactive CLI command (cli)
"agent": "node server.js", // Long-running service (start)
// Optional WebChat auto-configuration (runs on host before starting this agent)
"webchat": "ploinky webchat MyAgent",
// Metadata
"about": "Express API server", // Description shown in listings
// Environment configuration
"env": ["API_KEY", "DATABASE_URL"], // Required environment variables
// Auto-configuration (optional)
"enable": ["other-agent"], // Auto-enable other agents
"repos": { // Auto-add repositories
"repo1": "https://github.com/org/repo.git"
}
}
Field Descriptions
Field | Required | Description |
---|---|---|
container |
Yes | Base container image from Docker Hub or other registry |
install |
No | One-time setup command for dependencies |
update |
No | Command to update agent dependencies |
cli |
No | Interactive command for ploinky cli (executed inside the container via the WebChat wrapper) |
agent |
No | Service command for ploinky start |
about |
No | Human-readable description |
env |
No | List of required environment variables |
enable |
No | Agents to auto-enable when this agent is enabled |
repos |
No | Repositories to auto-add when this agent is enabled |
webchat |
No | Bash command executed on the host before the agent container is started. Typical usage: auto-configure WebChat for this agent (e.g., ploinky webchat MyAgent or ploinky webchat ./scripts/menu.sh ). |
Agent Lifecycle
1. Creation
# Create new agent
new agent myrepo MyAgent node:20
# Creates:
.ploinky/repos/myrepo/MyAgent/
├── manifest.json
└── (agent files)
2. Installation
When an agent is first enabled, Ploinky runs the install
command:
# manifest.json
"install": "npm install express body-parser"
# Executed in container:
docker run -v $PWD:$PWD node:18-alpine sh -c "npm install express body-parser"
3. Enablement
# Register agent in workspace
enable agent MyAgent
# Creates entry in .ploinky/agents
{
"ploinky_project_abc123_agent_MyAgent": {
"agentName": "MyAgent",
"containerImage": "node:18-alpine",
"createdAt": "2024-01-01T00:00:00Z",
...
}
}
4. Startup
# Start all enabled agents
start
If the manifest defines
webchat
, Ploinky executes that command on the host before starting the agent container. This lets you automatically bind WebChat to the agent's CLI or to a local script/program.
5. Runtime
During runtime, agents can be in different states:
- Running: Container active, service responding
- Stopped: Container exists but not running
- Exited: Container terminated (check exit code)
- Removed: Container deleted
Command Types
CLI Command
Interactive command for direct user interaction:
# Usage
cli MyAgent
Agent Command
Long-running service for API endpoints:
# manifest.json
"agent": "node server.js"
# server.js
const express = require('express');
app.get('/mcp/status', (req, res) => {
res.json({ status: 'running' });
});
app.listen(7000);
Supervisor Mode
If no agent
command is specified, Ploinky uses the default supervisor:
# /Agent/AgentServer.mjs provides:
- HTTP server on port 7000
- Health check at /mcp/status
- Process management
- Automatic restarts
Environment Setup
Container Environment
Agents run with these environment variables:
AGENT_NAME=MyAgent # Agent name
AGENT_REPO=myrepo # Repository name
WORKSPACE_PATH=/workspace # Mounted workspace
CODE_PATH=/code # Agent code directory
PORT=7000 # Default service port
Volume Mounts
Host Path | Container Path | Purpose |
---|---|---|
$(pwd) |
$(pwd) |
Workspace access |
/Agent |
/Agent |
Supervisor runtime |
.ploinky/repos/X/Y |
/code |
Agent code |
Exposing Variables
# Set variable in workspace
ploinky var DATABASE_URL postgres://localhost/mydb
# Expose to agent
ploinky expose DATABASE_URL $DATABASE_URL MyAgent
# Agent can now access:
process.env.DATABASE_URL
API Development
Basic HTTP Server
// server.js
const http = require('http');
if (req.url === '/mcp/status') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok' }));
} else if (req.url.startsWith('/mcp/')) {
// Handle API routes
const path = req.url.substring(5);
res.writeHead(200);
res.end(`API path: ${path}`);
} else {
res.writeHead(404);
res.end('Not found');
}
});
server.listen(7000, () => {
console.log('Agent server running on port 7000');
});
Express.js API
// api.js
const express = require('express');
const app = express();
app.use(express.json());
app.get('/mcp/status', (req, res) => {
res.json({
status: 'healthy',
agent: process.env.AGENT_NAME,
uptime: process.uptime()
});
});
// Custom endpoints
app.post('/mcp/process', (req, res) => {
const { data } = req.body;
// Process data
res.json({
result: `Processed: ${data}`,
timestamp: new Date()
});
});
app.listen(7000);
Python Flask API
# api.py
from flask import Flask, jsonify, request
import os
app = Flask(__name__)
@app.route('/mcp/status')
def status():
return jsonify({
'status': 'healthy',
'agent': os.environ.get('AGENT_NAME'),
'language': 'python'
})
@app.route('/mcp/process', methods=['POST'])
def process():
data = request.json
return jsonify({
'result': f"Processed: {data}",
'method': 'python'
})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7000)
Accessing Your API
Once deployed, access your agent's API through the routing server:
# Local development
http://localhost:8088/mcps/MyAgent/status
http://localhost:8088/mcps/MyAgent/process
# From client
client status MyAgent
client task MyAgent
Example Agents
Simple Shell Agent
{
"container": "alpine:latest",
"install": "apk add curl jq",
"cli": "/bin/sh",
"about": "Alpine Linux shell with curl and jq"
}
Node.js Development Agent
{
"container": "node:20",
"install": "npm install -g nodemon typescript @types/node",
"update": "npm update -g",
"cli": "node",
"agent": "nodemon --watch /workspace server.js",
"about": "Node.js development environment with hot reload"
}
Python AI Assistant
{
"container": "python:3.11",
"install": "pip install openai numpy pandas flask",
"update": "pip install --upgrade openai",
"cli": "python -i",
"agent": "python api_server.py",
"env": ["OPENAI_API_KEY"],
"about": "Python AI assistant with OpenAI integration"
}
Database Client Agent
{
"container": "postgres:15",
"install": "echo 'PostgreSQL client ready'",
"cli": "psql -U postgres",
"env": ["POSTGRES_PASSWORD"],
"about": "PostgreSQL client for database operations"
}
Multi-Agent System
{
"container": "node:18-alpine",
"install": "npm install",
"agent": "node orchestrator.js",
"about": "Orchestrator agent",
"enable": ["worker1", "worker2", "database"],
"repos": {
"workers": "https://github.com/myorg/worker-agents.git"
}
}
Best Practices
Container Selection
- Use Alpine-based images for smaller size
- Pin specific versions (node:18.19.0 vs node:18)
- Consider multi-stage builds for complex agents
- Minimize layers in install commands
Security
- Never hardcode secrets in manifest.json
- Use environment variables for sensitive data
- Run processes as non-root user when possible
- Validate all input in API endpoints
Performance
- Keep install commands minimal
- Cache dependencies in agent directory
- Use health checks for monitoring
- Implement graceful shutdown handlers
Development
- Test locally with
shell
first - Use
cli
for interactive debugging - Check logs with container runtime directly
- Version control your agent code separately
Troubleshooting
Common Issues
Problem | Cause | Solution |
---|---|---|
Container exits immediately | No long-running process | Add agent command or use supervisor |
Port 7000 not accessible | Service not binding correctly | Bind to 0.0.0.0:7000, not localhost |
Install command fails | Missing dependencies in base image | Use fuller base image or add apt/apk commands |
Environment variables not set | Not exposed to agent | Use expose command |
API returns 404 | Routing misconfiguration | Check path starts with /mcp/ |
Debugging Commands
# Check agent status
status
Health Checks
Implement health endpoints for monitoring:
// Health check endpoint
app.get('/mcp/status', (req, res) => {
const health = {
status: 'healthy',
checks: {
database: checkDatabase(),
memory: process.memoryUsage(),
uptime: process.uptime()
}
};
const isHealthy = Object.values(health.checks)
.every(check => check !== false);
res.status(isHealthy ? 200 : 503).json(health);
});
Advanced Features
Auto-Configuration
Agents can automatically configure their environment:
# manifest.json
{
"container": "node:18",
"agent": "node server.js",
"enable": ["database", "cache"], // Enable required agents
"repos": { // Add required repos
"utils": "https://github.com/org/utils.git"
}
}
# When this agent is enabled:
1. Adds 'utils' repository
2. Enables 'database' agent
3. Enables 'cache' agent
Custom Supervisor
Override the default supervisor with custom logic:
// custom-supervisor.js
const { spawn } = require('child_process');
const http = require('http');
// Start main process
const main = spawn('node', ['app.js']);
// Health check server
http.createServer((req, res) => {
if (req.url === '/mcp/status') {
res.writeHead(200);
res.end(JSON.stringify({
status: main.exitCode === null ? 'running' : 'stopped',
pid: main.pid
}));
}
}).listen(7000);
// Restart on crash
main.on('exit', (code) => {
if (code !== 0) {
console.log('Restarting after crash...');
// Restart logic
}
});