mcpbeat Sign in

Actonos API Dev Agent Skill

Skill for developing REST API endpoints in internal/server/. Covers routing, request validation, WebSocket streaming, and API conventions.

2k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
139
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/actonos/actonos --skill actonos-api-dev

The instruction itself

19 sections, as written by the author

ActonOS API Development Skill

Use this skill when creating or modifying REST API endpoints in the internal/server/ package.


1. Package Overview

internal/server/
├── router.go             # Chi router setup, global & auth middlewares, route tree
├── api_auth.go           # Setup, login, logout, password change, auth status
├── api_dashboard.go      # Dashboard aggregate metrics & summaries
├── api_agent.go          # Agent CRUD, start/stop, chat, soul, memory-md, cron
├── api_tasks.go          # Autonomous Task matrix CRUD, Heartbeat config & manual pulse triggers
├── api_conversations.go  # Chat conversations and message history
├── api_plugins.go        # WASM plugin upload, enable/disable, logs, configuration & vault secrets
├── api_vault.go          # Hardware-bound vault secret management
├── api_integrations.go   # Channel accounts, pairing codes, sender authorization
├── api_tools.go          # MCP servers, skills, tool execution, hub marketplace
├── api_workspace.go      # Workspace file browser, read/write/mkdir/upload
├── api_system.go         # Metrics, token usage ledger history, keys, identity, HAL
├── api_setup.go          # Legacy/standalone setup endpoints
├── layered_fs.go         # Layered filesystem (/data/overrides/ → go:embed fallback)
├── static.go             # Embedded static asset server
└── server_test.go        # Comprehensive endpoint test suite

2. HTTP Framework & Conventions

Base URL & Versioning

  • Current Base URL: /api (all routes are prefixed with /api)
  • Do not use /api/v1/ prefix until v1.0.0 is officially released.

Router Engine

ActonOS uses Chi v5 with standard library net/http:

import (
    "github.com/go-chi/chi/v5"
    "github.com/go-chi/chi/v5/middleware"
)

Standard Response Envelope

Success (s.respondJSON(w, http.StatusOK, data)):

{
  "data": { ... }
}

Error (s.respondError(w, http.StatusBadRequest, "INVALID_REQUEST", "description")):

{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Human-readable explanation of error"
  }
}

Common Helper Methods in Server

// 1. Respond with JSON data wrapped in {"data": ...}
func (s *Server) respondJSON(w http.ResponseWriter, status int, data any)

// 2. Respond with standard error envelope wrapped in {"error": {"code": ..., "message": ...}}
func (s *Server) respondError(w http.ResponseWriter, status int, code, message string)

// 3. Decode request body with 1MB safety limit
func (s *Server) decodeJSON(r *http.Request, v any) error

3. Route Organization & Authentication

Public Routes

  • GET /api/health
  • GET /api/models
  • GET /api/notifications/push/vapid-key
  • GET /api/auth/status
  • POST /api/auth/setup
  • POST /api/auth/login
  • POST /api/auth/logout

Protected Subsystems

All other routes are nested inside r.Group with r.Use(s.RequireAuthMiddleware). Requests must include Authorization: Bearer <token> in the HTTP headers when authentication is initialized.


4. Implementing a New API Endpoint

Step 1: Add Request & Response Structs

type UpdateSoulRequest struct {
    SoulContent string `json:"soul_content"`
}

type SoulResponse struct {
    AgentID   string `json:"agent_id"`
    Content   string `json:"content"`
    UpdatedAt string `json:"updated_at"`
}

Step 2: Implement Handler Method on *Server

func (s *Server) handleSaveSoul(w http.ResponseWriter, r *http.Request) {
    agentID := chi.URLParam(r, "agentID")
    if agentID == "" {
        agentID = agent.DefaultSystemAgentID
    }

    var req UpdateSoulRequest
    if err := s.decodeJSON(r, &req); err != nil {
        s.respondError(w, http.StatusBadRequest, "INVALID_BODY", "failed to decode json body")
        return
    }

    if err := s.profileMgr.SaveSoul(r.Context(), agentID, req.SoulContent); err != nil {
        s.respondError(w, http.StatusInternalServerError, "SAVE_FAILED", err.Error())
        return
    }

    s.respondJSON(w, http.StatusOK, SoulResponse{
        AgentID:   agentID,
        Content:   req.SoulContent,
        UpdatedAt: time.Now().UTC().Format(time.RFC3339),
    })
}

Step 3: Register Route in internal/server/router.go

r.Route("/agents", func(r chi.Router) {
    // ...
    r.Route("/{agentID}", func(r chi.Router) {
        // ...
        r.Put("/soul", s.handleSaveSoul)
    })
})

Step 4: Update Documentation and TypeScript Types

  • Add endpoint entry to docs/API.md
  • Add route and handler to .agents/rules/source-registry.md
  • Add client method to web/src/lib/api.ts and interfaces to web/src/lib/types.ts

5. Streaming Endpoints (SSE / Server-Sent Events)

For streaming LLM tokens, reasoning thoughts, and tool call progress:

func (s *Server) handleChatStream(w http.ResponseWriter, r *http.Request) {
    flusher, ok := w.(http.Flusher)
    if !ok {
        s.respondError(w, http.StatusInternalServerError, "STREAMING_UNSUPPORTED", "streaming not supported")
        return
    }

    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")

    eventChan := make(chan agent.AgentStreamEvent, 64)
    go func() {
        _, _ = s.engine.ExecuteStepStreamWithHistory(
            r.Context(), agentID, msg, history, eventChan,
        )
    }()

    for ev := range eventChan {
        data, _ := json.Marshal(ev)
        fmt.Fprintf(w, "event: %s\ndata: %s\n\n", ev.Type, data)
        flusher.Flush()
    }
}

The stream endpoint must flush live thought, token, tool_call,

tool_result, audit, done, and error events. It must not proxy to the

non-streaming JSON handler. Conversation messages are persisted before and

after the stream.

Realtime Operations WebSocket

  • GET /api/realtime is a protected, same-origin WebSocket.
  • Browser authentication uses the HttpOnly actonos_token cookie set by setup/login; never put bearer tokens in WebSocket query strings.
  • Snapshots include hardware/Docker metrics, durable runs, pending approvals and token summaries.
  • The stream is observation-only and must not expose an interactive shell or mutation channel.

6. Verification Checklist for API Changes

  • [ ] Route is registered inside router.go
  • [ ] Authentication middleware requirement is verified
  • [ ] Error responses use standard s.respondError with appropriate HTTP status
  • [ ] TypeScript types in web/src/lib/types.ts and api.ts are synced
  • [ ] docs/API.md and .agents/rules/source-registry.md are updated
  • [ ] go test ./internal/server/... passes

7. Approval and Run APIs

  • api_approvals.go owns durable exact-action approval decisions.
  • api_runs.go exposes durable run summaries and ordered execution events.
  • Authentication is not tool authorization. Execution handlers MUST call

ToolRegistry.Execute and return HTTP 202 for ApprovalRequiredError.

  • MCP connection is a High-risk administrative action and requires approval.
  • Preserve trace IDs through handler, engine, registry, approval, and audit calls.
  • Workspace, skill, WASM, Hub, and restart mutations must use

requestAdminApproval and exact dispatch from api_approvals.go.

  • GET /api/system/audit/verify is the canonical audit-chain integrity check.
  • Checkpointed approvals resume the same durable run; do not create a replacement.
  • All server filesystem paths must derive from Config.DataDir,

Config.WorkspaceDir, Config.SkillsDir, or Config.WASMDir; handlers must

not assume the process working directory is the data root.

  • Provider credentials must use Server.vault; never write API keys into JSON

or .key files. Preserve automatic legacy migration and fail closed without

Vault.

  • SQLite backups must use VACUUM INTO through the live database connection;

never copy the main database file while WAL mode is active.

How to use it

Copy the folder

Take actonos/actonos-api-dev from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.