Ploinky Architecture
Technical architecture and implementation details of the Ploinky AI agent deployment system.
System Overview
Ploinky separates the host lifecycle boundary from the services and agent runtimes that operate inside it:
graph TB
HOST["Host user / ploinky CLI\nBox Supervisor (host-side lifecycle)"] --> BOX
subgraph BOX["Ploinky Box\nrootless outer container"]
direction TB
ENTRY["Box Entrypoint\nvalidate mounts and initialize nested runtime"]
LOCAL["ploinky-local\ninner CLI and workspace supervisor"]
ROUTER["Routing Server\nHTTP / SSE / WebSocket / MCP edge"]
WATCHDOG["Watchdog\nhealth and restart supervisor"]
AGENT1["Isolated Agent 1"]
AGENT2["Isolated Agent 2"]
AGENT3["Isolated Agent 3"]
FLOW((" "))
ENTRY --> LOCAL
LOCAL --> ROUTER
WATCHDOG --> ROUTER
ROUTER -->|"CLI operations, API calls, etc."| FLOW
FLOW --> AGENT1
FLOW --> AGENT2
FLOW --> AGENT3
end
Ploinky Box architecture
Ploinky Box
The Ploinky Box is the rootless outer container that provides the runtime boundary for one workspace. A host user starts it with ploinky start; the Box Supervisor initializes the boundary and launches ploinky-local inside it. The Box exposes the host loopback Router surface while keeping the inner control and agent network private to the Box.
The Ploinky Box remains the active runtime boundary while the user works through interactive Ploinky commands. Core REPL operations such as stop and destroy stop or clean workspace services and agent runtimes inside the Box without removing the Box itself. The Box remains in place until the user explicitly runs ploinky destroy, which removes the exact outer workspace instance.
Routing Server and Watchdog
The Routing Server is the Box's single HTTP, SSE, WebSocket, and MCP edge. It authenticates and routes browser, CLI, and service requests to the selected agent runtime, while keeping private Router listeners inside the Box network.
The Watchdog supervises the Routing Server and managed agent containers. It performs health and readiness checks, applies bounded restart and backoff behavior, and inactivates stale routes when an agent no longer satisfies its runtime contract.
Containerized agent runtimes
Each configured agent runs in its own nested runtime represented by the three agent rectangles in the diagram. Inside a marked Ploinky Box, these runtimes use nested rootless Podman; each agent receives its own container identity, process space, mounts, and service route. The separate runtimes keep agent processes and files isolated from one another while allowing the Routing Server and Watchdog to manage them as one workspace.
Key Design Principles
- Isolation First: Every agent runs in its own container
- Workspace Scoped: All configuration is local to the project directory
- Zero Global State: No system-wide installation or configuration
- Git-Friendly: Configuration stored in .ploinky folder, can be gitignored
- Runtime Agnostic: Supports Docker, Podman, bubblewrap (Linux), and seatbelt (macOS) transparently
ploinky-local
ploinky-local is the inner Ploinky runtime that coordinates one workspace inside the Ploinky Box. Its components receive commands and workspace state through a shared lifecycle boundary, and each component sends its work into the local coordinator.
flowchart TB
CLI["CLI command layer"] --> LOCAL
AGENTS["Agent service"] --> LOCAL
WORKSPACE["Workspace and bootstrap"] --> LOCAL
ROUTER["Routing Server"] --> LOCAL
WATCHDOG["Watchdog"] --> LOCAL
DEPS["Repository and dependency services"] --> LOCAL
subgraph LOCAL["ploinky-local"]
direction TB
end
ploinky-local components
CLI command layer
The CLI command layer gives a workspace user one consistent interface for managing Ploinky without exposing the internal service layout. It defines the REPL vocabulary, command completion, command history, and one-shot command handling, then validates the user's command before any workspace state changes. The CLI Reference describes the available command forms and their arguments.
After parsing a command, the layer delegates work to the component that owns it. It sends repository, update, default-skill, and dependency work to repository and dependency services; sends agent lifecycle work to the agent service; starts the coordinated workspace path for start; and forwards interactive shell or agent CLI sessions to the selected runtime. Read-only commands such as status and logs take dedicated observational paths so they can inspect an existing workspace without initializing or repairing it.
Agent service
The agent service manages the lifecycle of one named agent or alias because each running agent needs an exact workspace identity, manifest, profile, and runtime record. It resolves an agent reference against installed repositories and enabled registry records, detects ambiguous names, and prevents an operation from selecting a different workspace instance.
The service enables an agent, creates or starts its runtime, stops it, restarts it, reinstalls it from a clean runtime, removes it, and reports its current status. Before it asks the agent runtime service to act, it combines the selected manifest with the configured run mode, alias, authentication choice, profile, dependency graph, and network contract. The resulting lifecycle record preserves the container identity, port mappings, and readiness evidence needed by later commands and the Watchdog.
Workspace and bootstrap
The workspace and bootstrap component establishes the local state that every other component shares. It creates and reads the workspace directory structure, enabled-agent registry, secrets, active profile, managed repository records, routing inputs, and persisted authorization-generation data so commands operate on one exact workspace rather than on host-global state.
When a workspace starts, bootstrap prepares the required repositories and applies the selected branch policy before agents are resolved. The workspace coordinator then orders manifest processing, dependency preparation, agent launch, route activation, and readiness checks. It refuses to activate a partial or stale state because the Router and agent service require the same exact registry, manifests, routing, policy, and desired-state inputs.
Routing Server
The Routing Server is the workspace edge that makes selected agent services reachable through HTTP, Server-Sent Events, WebSocket, and Model Context Protocol (MCP) requests. It exists so browser clients, command-line clients, and agent integrations use one authenticated entry point instead of connecting directly to arbitrary agent containers.
For every request, the server resolves the listener and host, applies the active immutable routing and policy generation, and verifies the selected agent instance before it opens an upstream connection. It then proxies the admitted request to that agent's private target or aggregates the declared MCP tools and resources. This design keeps private agent listeners inside the Box and ensures that a route cannot outlive the runtime identity and policy that authorized it.
Watchdog
The Watchdog keeps the workspace reliable after its initial launch by supervising the Routing Server process and the active agent-container fleet. It exists because a successful startup does not prove that the Router or an agent will remain responsive while the workspace is running.
The Watchdog starts the Router, records its output, checks its private health socket, and applies bounded restart backoff with a circuit breaker when the process fails repeatedly. It also polls the managed container inventory, runs the applicable continuous health checks, respects maintenance locks during explicit lifecycle operations, and writes structured events that identify failures and supervision decisions. The workspace coordinator remains the owner of configuration and activation; the Watchdog only supervises the runtimes that the coordinator has admitted.
Repository and dependency services
The repository and dependency services make agent source code and runtime packages available before an agent is launched. They install, remove, update, and inspect managed repositories; discover agents and manifests; apply repository branch policy; and refresh the default skill repositories into eligible project repositories.
For startup, these services resolve the dependency graph declared by agent manifests and prepare reusable library dependency caches from the global and agent package requirements. The agent service receives a resolved repository, manifest, dependency order, and cache identity instead of attempting to construct them during container launch. This separation keeps repository updates, dependency installation, and runtime admission coordinated while allowing the same cache to be reused when the resolved inputs have not changed.
Routing Server
Purpose
The Router is the network entry point for a Ploinky Box. It receives HTTP, server-sent event, WebSocket, and MCP requests, identifies the requested service, and decides whether Ploinky itself handles the request or forwards it to an enabled agent. This gives clients one stable entry point while agent runtimes remain private inside the Box.
Before forwarding an agent request, the router checks the listener, Host, path, caller authorization, and route policy. It selects the target only from the active route-and-policy generation and checks that generation again immediately before connecting to the agent. A partially written or obsolete routing configuration therefore cannot direct a request to an agent runtime.
The router listens on public/control port 8080, on box-private port 8081, and on an unmounted Unix health socket. The outer wrapper publishes exactly 127.0.0.1:<selected-router-host-port>:8080/tcp and 0.0.0.0:7882:7882/udp. The host-side --port option changes only the host port mapped to router port 8080; --publish, --expose, and --listen-lan cannot add another published TCP port.
Port 8081 is used only for private communication inside the Box. In a rootless Box, it binds the Box namespace wildcard so nested Podman runtimes can reach it through host.containers.internal:host-gateway; the outer runtime never publishes it on the host. Outside a Box, the private listener uses exact loopback or managed-address binds.
flowchart TB
subgraph WEB["Routing Server architecture"]
direction TB
ROUTER["Routing Server"]
WEBCHAT["WebChat"]
STATUS["Status and metrics"]
DATA["Workspace files and blobs"]
APPS["Agent web applications"]
ROUTER --> WEBCHAT
ROUTER --> STATUS
ROUTER --> DATA
ROUTER --> APPS
end
Exposed APIs
The RoutingServer owns the following route families. Agent-prefixed routes are forwarded only after their selected route has been authorized; the other route families are handled by the router or their dedicated router handler.
| Route family | Purpose |
|---|---|
/auth/* | Authentication routes establish and manage the browser session used by protected router and agent surfaces. |
/api/agents/* | Agent administration routes expose the user and agent management control surface. |
/api/marketplace/* | Marketplace routes expose the router-owned marketplace control surface. |
/agent-card, /mcp, and /<agent>/mcp | Agent discovery and Model Context Protocol requests are served through the aggregate or agent-specific MCP handlers. |
/webchat/* and /status/* | WebChat and status handlers provide the authenticated interactive and workspace-observation surfaces. |
/upload, /blobs/*, and /workspace-files/* | Workspace file upload, blob streaming, and router-owned workspace-file access are handled without forwarding to an agent. |
/policy/command, /health, and /admin/* | Protected router control, health-summary, and administrative surfaces remain router-owned and require their applicable authorization checks. |
/<agent>/* | The selected agent's HTTP, server-sent event, or WebSocket service receives the request after the router removes the agent mount prefix and validates the active route generation. |
Request Flow
sequenceDiagram
participant Client
participant Router as Routing Server
participant Generation as Active route and policy generation
participant Agent as Isolated agent runtime
Client->>Router: Request
Router->>Generation: Validate route and policy
Generation-->>Router: Authorized private target
Router->>Agent: Forward agent request
Agent-->>Router: Response or stream
Router-->>Client: Response or stream
- The client sends an HTTP, server-sent event, or WebSocket request through the loopback edge or an authenticated Cloudflare hostname.
- The RoutingServer identifies the listener class and exact Host, canonicalizes the path, and resolves the selected service.
- The routing provider and effective route policy admit the request against one active immutable authorization generation. For a guest ingestion route, the router also removes spoofable source and identity headers and creates a route-scoped opaque transport-source HMAC for throttling; that value is not authorization or user identity.
- Router-owned API routes are completed by their dedicated handlers. For an agent-prefixed route, the router captures the authorized private target and revalidates the generation lease immediately before creating the upstream connection.
- The router removes the agent mount prefix, proxies the request to the isolated agent runtime, and returns the response or stream to the client.
WebChat
WebChat is the browser interface for an interactive session with an enabled agent. After the router session is established, it serves the chat interface, resolves the selected agent, and creates the corresponding terminal session. The browser submits input, control, and interaction requests over HTTP, while WebChat sends terminal output back as a server-sent event stream. This keeps browser interaction responsive without granting the browser direct access to the agent process.
WebChat also supports task views, workspace file suggestions, uploads, and directory operations for the workspace selected by the session. These operations stay within the resolved workspace context, so a chat session can work with its intended files without treating an arbitrary host path as part of the web API.
Agent Static Serving
An enabled agent can provide its own web application behind its agent-prefixed route. The router owns workspace-global routes such as /workspace-files and the management surfaces, then proxies agent-prefixed requests to the selected agent. The agent receives the path with the mount prefix removed and is responsible for serving its own application files, HTTP behavior, server-sent events, and WebSocket behavior without exposing its own host port.
GET /index.html → 302 /<static-agent>/index.html
GET /explorer/index.html → explorer upstream /index.html
GET /explorer/assets/app.js → explorer upstream /assets/app.js
GET /workspace-files/doc.md → router workspace-file handler
The shared AgentServer serves static files from PLOINKY_CODE_DIR or /code after its built-in endpoints. Agents that use a custom manifest.agent command must implement equivalent static serving themselves.
Blob Storage API
The blob storage API transfers files and other raw byte payloads without placing them in an agent's source repository. A caller sends the payload as the request body, so the API can carry text, images, documents, archives, or another binary format. Content-Type describes the payload type; X-Mime-Type can override it, and X-File-Name records the original filename as metadata.
Each stored blob has a generated identifier and a JSON metadata sidecar that records its size, MIME type, creation time, optional filename, and owning agent when applicable. The bytes are streamed during upload and download. Download responses include the stored content type and length, support HEAD, and accept byte ranges for partial downloads.
Storage Scope
A request to the shared blob endpoints stores bytes in .ploinky/shared. These blobs are not associated with one agent and can be addressed through their generated identifier. A request that names an enabled agent stores bytes in that agent's workspace at <agent-workspace>/blobs. This lets an agent create or consume files that belong to its own work without exposing the file as a workspace-global resource.
The router resolves an agent name from the enabled-agent registry before accepting an agent-scoped upload or download. When the same agent name exists in more than one repository and the records have different workspaces, callers use <repository>:<agent> to identify the intended blob store.
Endpoints
| Endpoint | Behavior |
|---|---|
POST /blobs | Streams a raw payload into the shared .ploinky/shared blob store and returns its generated identifier and download URL. |
GET or HEAD /blobs/<id> | Streams or inspects a shared blob. |
POST /blobs/<agent> | Streams a raw payload into the selected enabled agent's blobs directory and returns its generated identifier and download URL. |
GET or HEAD /blobs/<agent>/<id> | Streams or inspects a blob owned by the selected enabled agent. |
The blob API is separate from POST or PUT /upload?path=..., which writes a replacement file at a selected workspace-relative path. Blob endpoints always generate the storage identifier and do not accept a caller-selected destination path.
Status and Metrics
The status service is an administrator-only observation surface. It returns the known server states, the selected static application information, and the latest workspace runtime metrics as JSON. A client can request a continuing NDJSON stream of metric snapshots, allowing a monitoring consumer to follow workspace changes without receiving command-execution or runtime-control capabilities.
Watchdog Supervisor
The Watchdog (cli/server/Watchdog.js) is the supervisor for the RoutingServer process and for enabled agent runtimes. It starts the router, observes whether it remains healthy, and starts the container monitor that tracks the runtime associated with each enabled agent. This supervision keeps the router and agent runtimes available without treating an intentional shutdown or a configuration error as a crash that should be retried.
When a runtime needs recovery, the Watchdog uses the manifest and enabled-agent record that identify that exact runtime. It waits for the replacement runtime to satisfy its health contract before activating its route. If an agent becomes unhealthy after activation, the Watchdog removes its active route and prepares a new replacement rather than continuing to direct requests to a runtime that only passed an earlier startup check.
| Responsibility | How the Watchdog performs it |
|---|---|
| RoutingServer process supervision | The Watchdog starts the RoutingServer, probes its supervisor-only Unix health socket, and restarts it after three consecutive failed health checks or an unexpected exit. A clean exit, an intentional termination signal, a port or permission error, and a fatal exit do not trigger an automatic restart. |
| Restart containment | The Watchdog waits with exponential backoff between restart attempts, beginning at one second and doubling up to thirty seconds. It stops automatic recovery after five restarts within sixty seconds, so a persistent failure cannot create an endless restart loop. |
| Agent runtime monitoring | The container monitor runs every five seconds and checks all OCI targets from one shared runtime inventory. When that inventory cannot be read, it defers the tick instead of issuing separate runtime calls or restarting agents from incomplete state. |
| Health and route admission | The Watchdog runs the health probes declared by an agent manifest before activating a replacement route and repeats continuous probes for running agents. A failed runtime probe inactivates routing for that runtime and schedules recovery through a fresh exact-generation replacement. |
| Restart coordination | The Watchdog defers an automatic restart while a workspace-start or per-container maintenance lock is active. It rechecks the manifest and enabled-agent record during recovery so a restart cannot apply stale inputs over a concurrent lifecycle operation. |
| Operational record | The Watchdog writes structured events for router lifecycle, health checks, container monitoring, and recovery to .ploinky/logs/watchdog.log. |
Workspace System
Directory Structure
.ploinky/
├── agents.json # Enabled agents registry (+ _config key)
├── .secrets # Environment variables and secrets
├── profile # Active profile name (dev/qa/prod)
├── ploinky_history # CLI command history
├── repos/ # Cloned agent repositories
│ ├── basic/
│ ├── demo/
│ └── ...
├── agents/ # Per-agent work directories
├── code/ # Symlinks to agent code
├── skills/ # Symlinks to agent skills
├── logs/ # Router and watchdog logs
├── shared/ # Shared data between agents
├── running/ # PID files
├── routing.json # Candidate route input; inert until coordinated apply
├── edge-generations/ # Immutable validated route-and-policy generations
├── topology/ # Box-owned non-secret topology generations
├── servers.json # Web surface config (ports, tokens)
└── deps/ # Dependency caches
├── global/ # Global node_modules per runtime key
└── agents/ # Per-agent node_modules per runtime key
Agent Registry (agents/)
JSON file storing enabled agents and their configuration:
{
"ploinky_project_abc123_agent_demo": {
"agentName": "demo",
"repoName": "demo",
"containerImage": "node:18-alpine",
"createdAt": "2024-01-01T00:00:00Z",
"projectPath": "<workspace>/.data/demo",
"type": "agent",
"config": {
"binds": [...],
"env": [...],
"ports": [{"containerPort": 7000}]
}
}
}
Configuration Management
Workspace configuration persists across sessions:
// Stored in agents/_config
{
"static": {
"agent": "demo",
"port": 8088
}
}
Security Model
Container Isolation
Filesystem access
An agent does not receive access to the complete host filesystem or to every workspace directory. Ploinky builds the agent's view from the mounts declared in its manifest after resolving the active profile. If an agent needs writable code or a host path, that mount is an explicit trusted capability: the agent can modify what it can see there, so the manifest must request it deliberately.
Network access
A bridge-networked agent starts with a controlled hosts configuration and one fixed mapping, host.containers.internal:host-gateway. That mapping gives the agent a transport path to its approved private Router endpoint; it does not authenticate the agent or let it reach arbitrary host services. Host-network mode has a stricter requirement: it needs the exact effective-instance and current-generation capability, and only the owner of the current capability can bind reserved UDP port 7882.
Process boundary
Container, bubblewrap, and Seatbelt runtimes limit how a launched agent process interacts with the surrounding system. They reduce accidental exposure between an agent and the host, but they are not a hostile multi-tenant security boundary because the same workspace operator controls the workloads and their declared capabilities.
Public and private network edge
Ploinky does not expose agent or router TCP ports directly on the physical network. The outer host accepts TCP only through loopback, and public HTTP reaches the RoutingServer through an outbound Cloudflare tunnel. Private Router and support listeners remain inside the Box, so another host cannot connect to them directly.
Secret Management
Environment variables stored in .ploinky/.secrets with aliasing support:
API_KEY=sk-123456789
PROD_KEY=$API_KEY # Alias reference
DATABASE_URL=postgres://localhost/db
Authentication Modes
Each agent can be independently configured with one of three auth modes via enable agent --auth none|pwd|sso:
- none: No authentication (default).
- pwd (local): Username/password auth with HMAC-signed JWT sessions. Cookie:
ploinky_jwt. Session TTL: 4 hours. - sso (OIDC): Delegates to the configured SSO provider agent marked with
"ssoProvider": true. Supports PKCE flow. Cookie:ploinky_sso.
Request-Bound Identity and Policy
Ploinky derives a distinct HMAC secret for each agent. User Sessions, Agent Assertions, Router Requests, and the distinct private-service assertion profile are direction-typed, short-lived, replay-protected, and bound to method, canonical path, and request content. A private assertion additionally binds the exact effective instance and current enable generation, but is never a user or administrator credential.
Exact Host and listener class select a closed route surface before path dispatch. All HTTP, SSE, and WebSocket authorization uses one immutable route-and-policy authorization generation and revalidates its lease immediately before dialing. Every TCP admin/control/status handler requires a real local-admin session, and mutations additionally require exact Origin/CSRF validation.
Cloudflare, Topology, and TURN
The pinned in-box cloudflared process exists only in complete Cloudflare mode and always uses http://127.0.0.1:8080 as origin. Local-only mode starts no connector. A coordinated apply already selects credential-absent state as local-ready, so the publication supervisor adopts that exact generation without a duplicate route apply; a previous Cloudflare ownership journal still requires fail-closed teardown and coordinated commit. Ploinky creates neither quick tunnels nor tunnels, and keeps connector/API credentials out of argv, status, diagnostics, and ordinary agent environments.
The box-owned topology carries only non-secret logical locators and distinguishes the immutable route-and-policy authorization generation, a content-derived configuration generation, and a monotonic readiness/publication generation. The authenticated no-store browser projection returns one active locator plus configuration/publication ids, never the authorization id or inventory. TURN long-term credentials remain in core; exact current-generation consumers receive only short-lived brokered credentials and their expiry.
Data Flow Examples
Starting an Agent
1. User: enable agent demo
→ Find manifest in repos/demo/demo/manifest.json
→ Register in .ploinky/agents.json
→ Generate container name
2. Host user: ploinky --port 8088 start demo
→ Select physical loopback 8088; forward fixed inner Router port 8080
→ Bind private 8081 to the Box namespace wildcard (exact addresses outside a Box), then start public/control 8080
→ Prepare recursive manifest repositories and resolve the planning graph without enabling processes
→ Capture an early inactive generation with exact identities and every retained route targetless
→ Run fatal static preinstall, then startup config providers
→ Abort the early lease, reload registry, re-evaluate retained runtime hashes, and rotate newly stale tuples
→ Capture the final inactive targetless generation and exact launch lease
→ Start blocking waves while preserving the final exact identities
→ Create/reuse private mappings for primary and explicit TCP service targets
→ Apply each blocking wave's resolved targets before readiness authorizes dependents
→ Start additional enabled agents outside the graph
→ Spawn detached no-wait helpers last without waiting for their completion
3. Container startup:
→ Pull image if needed
→ Mount volumes (workspace, code, Agent)
→ Set environment variables
→ Run agent command or supervisor
API Request Routing
1. Client: GET http://simulator.localhost:8088/apis/simulator/monty-hall
2. RoutingServer:
→ Resolve public listener plus exact host surface
→ Resolve and authorize against the captured immutable authorization generation
→ Revalidate that authorization generation immediately before dialing
→ Proxy to the private target selected by the service route
3. Agent Container:
→ Process request
→ Return response
4. RoutingServer:
→ Forward response to client
WebChat Session
1. User: webchat secret python bot.py
2. Router WebChat handler:
→ Start PTY for the selected agent CLI
→ Serve chat.html through the router
3. Browser connects:
→ Open the server-sent event stream
→ Authenticate through the router login flow
→ Reuse the workspace/agent runtime across browser tabs
→ Receive agent-owned conversation snapshots through session-state events
4. Message flow:
→ User types in chat
→ HTTP POST → Server → TTY stdin
→ Program output → TTY stdout → SSE → Browser
→ Display as chat bubble
Agent MCP Bridge
The Model Context Protocol (MCP) bridge gives authenticated callers one Router-controlled way to discover and invoke capabilities supplied by active agents. The bridge keeps agent listeners private to the Box: the Router reaches an agent over its registered loopback target and does not publish the agent's MCP port as an outer Box service.
Router-owned MCP endpoints
/mcp is the aggregate MCP endpoint. It requires Router authentication, accepts JSON-RPC over POST, and creates a Router MCP session during initialize. The endpoint supports tool and resource discovery, tool calls, resource reads, and ping; GET event streams are not supported, and DELETE ends the Router session.
For each request, the Router resolves routes from the captured active generation and revalidates its lease before it connects upstream. cli/server/AgentClient.js encapsulates the Streamable HTTP client transport used for that loopback connection. Discovery fans out only to active registered routes, annotates each returned tool or resource with its source agent, and returns upstream failures as bounded per-agent errors.
The aggregate endpoint applies MCP policy before it advertises or invokes a tool. Tool access is fail-closed and is classified as authenticated, admin, or internal; resources are available only to Router-authenticated user or guest sessions. A tool name shared by multiple agents must be qualified with its agent route, preventing an ambiguous call from being sent to an arbitrary provider.
/<agent>/mcp is the corresponding single-agent proxy. Browser callers use the Router authentication flow. Agent-to-agent calls carry an Agent Assertion and may invoke only tools/call; the Router verifies the assertion, the target permission, caller ACL, and MCP policy before forwarding the request.
Request-bound agent invocation
Before the Router invokes a tool or reads a resource, it mints a target-specific Router Request token. The token is bound to the target agent, active generation, HTTP method, /mcp path, requested operation, and canonical arguments. AgentServer verifies that token and replay state before it exposes invocation metadata to a command. A token for one tool, resource, or argument set therefore cannot authorize another request.
Agent MCP server and configuration
The AgentServer runs a Streamable HTTP MCP server at /mcp on port 7000 by default. Each MCP initialization creates an isolated server and transport session. The server also provides the private agent health endpoint, an optional agent card endpoint, and protected task status and cancellation endpoints.
An agent declares its MCP surface in its mcp-config.json. During runtime preparation, Ploinky stages that file in the agent work directory and supplies the runtime configuration path through PLOINKY_MCP_CONFIG_PATH. The configuration can define tools, resources, resource templates, and prompts. A missing configuration produces an empty MCP surface; invalid or incomplete individual entries are skipped.
A configured tool or resource runs its command with a JSON payload on standard input. Tool payloads contain the tool name, input, and verified invocation metadata; resource payloads contain the resource name, URI, and template parameters. Standard output becomes the MCP result. A non-zero command result is returned as an MCP error.
Asynchronous tools
A tool marked async is queued by Agent/server/TaskQueue.mjs and returns task metadata immediately. The queue stores its state in .tasksQueue, runs up to ten tasks concurrently by default, and accepts maxParallelTasks from the MCP configuration. Pending tasks resume after restart, while tasks that were running are returned to the pending state.
Each queued command receives a unique task identifier in its payload. Tool-specific timeoutMs limits execution time. The Router can obtain task state through /task or /getTaskStatus, and can cancel a task through /task/cancel; each operation requires a Router Request token bound to that exact task operation.