| name | mcon-agent |
| description | Manage personal mCon dashboards on behalf of your human owner. Create accounts, dashboards, projects, and steps via the mCon REST API, authenticated with your Alien Agent ID over RFC 9449 DPoP. |
| license | MIT |
| compatibility | Any AI agent with shell access, Node.js 18+, and an Alien Agent ID v3+ (DPoP) |
| metadata | {"author":"mCon","version":"2.1.0"} |
| allowed-tools | Bash(node *agent-id-core/bin/cli.mjs:*) Bash(node *agent-id-auth/bin/cli.mjs:*) Bash(curl:*) Bash(jq:*) |
mCon — Agent-Managed Dashboards for Your Human Owner
You are an AI agent. mCon is a multi-tenant dashboard service where every
project card and checklist item is created and updated by an agent on
behalf of its human owner. Humans only read; you write.
This skill teaches you how to:
- Authenticate to mCon with your Alien Agent ID using RFC 9449 DPoP
- Register your account and dashboards (created on first authenticated call)
- Create, update, and delete projects and steps
- Respect the per-agent and per-owner quotas
1) Prerequisites
You must have an Alien Agent ID (v3 or later — DPoP-enabled) already
bound to a human owner. If you don't, run the /agent-id-core
skill first. The minimum requirement is:
node /absolute/path/to/plugins/agent-id-core/bin/cli.mjs status
If bound is false, stop here and complete the Alien Agent ID setup
(/agent-id-core bootstrap). mCon rejects requests without a valid
DPoP-bound access token with 401.
You also need:
- Node.js 18+ (for the Alien Agent ID plugin CLIs)
curl and jq (or any HTTP client of your choice)
- The mCon base URL — the same origin you fetched this skill from.
All API paths below are relative to it. (For a local dev install with
no public origin, default to
http://localhost:8765.)
Resolve the CLI paths
Alien Agent ID ships as a marketplace of focused plugins. This skill uses
two of them:
agent-id-core for status and refresh (session lifecycle).
agent-id-auth for header (per-request DPoP headers).
AGENT_ID_CLI="node /absolute/path/to/plugins/agent-id-core/bin/cli.mjs"
AGENT_ID_AUTH_CLI="node /absolute/path/to/plugins/agent-id-auth/bin/cli.mjs"
If you installed via the Claude Code plugin marketplace, both binaries
live under ~/.claude/plugins/<source>/agent-id-<name>/bin/cli.mjs —
substitute the absolute path your installation chose. All examples
below assume both variables are set.
2) The auth headers (RFC 9449 DPoP)
mCon authenticates every write call using the two-header DPoP form from
RFC 9449. Each request carries:
Authorization: DPoP <access_token>
DPoP: <proof JWT>
- The access token is an Alien-SSO-issued
at+jwt (RFC 9068) that names
your owner (sub), pins your agent key (cnf.jkt), and has a short
lifetime (~5 minutes). It's reusable across many requests until it expires.
- The DPoP proof is a fresh per-request JWT signed by your agent key,
binding that specific request to that specific access token via the
request method (
htm), URL (htu), and access-token hash (ath).
Because the proof is bound to the target URL, you must regenerate it for
every API call. The CLI does both halves in one shot:
AGENT_ID_CLI="<see above>"
AGENT_ID_AUTH_CLI="<see above>"
MCON="<the origin you fetched this skill from>"
auth() {
local method="$1" url="$2"
local pair
pair=$($AGENT_ID_AUTH_CLI header --method "$method" --url "$url") || return 1
AUTH_HEADER=$(printf '%s' "$pair" | jq -r .authorization)
DPOP_HEADER=$(printf '%s' "$pair" | jq -r .dpop)
}
auth GET "$MCON/api/me"
curl -s -H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" "$MCON/api/me"
The CLI also accepts --raw to print two Header: value lines instead
of JSON. Prefer the JSON form above — it's robust to whitespace, doesn't
need eval, and gives you the two header values as separate variables.
Shortcut for one-shot calls. agent-id-auth also has a call
subcommand that signs and sends in one step, parses the response, and
returns { ok, status, body, ... } as JSON. The auth + curl pattern
above is what you want when you need to drive curl (or another HTTP
client) yourself — e.g. when you want to inspect non-2xx responses or
set additional headers. For the simple read path, $AGENT_ID_AUTH_CLI call --url "$MCON/api/me" --method GET is equivalent.
Read endpoints (GET) are public — no auth required.
Write endpoints (POST, PATCH, PUT, DELETE) require both headers.
Don't precompute headers and reuse them. Each proof's jti is
single-use; mCon's verifier rejects replays. Re-run auth for every
request, even back-to-back ones against the same URL.
3) Quotas
mCon enforces these limits server-side:
| Limit | Cap | Returned status when exceeded |
|---|
| Dashboards per agent | 5 | 409 Conflict |
| Agents per human owner | 2 | 409 Conflict (on first call from the third agent) |
You don't pre-register. The first authenticated call you make implicitly
registers you (subject to the per-owner cap). If your owner already has
two agents, all of your write calls will fail until the human deletes
one of the older agents via that agent's DELETE /api/me call.
4) Account routes
auth GET "$MCON/api/me"
curl -s -H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" "$MCON/api/me" | jq
auth DELETE "$MCON/api/me"
curl -X DELETE -H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" "$MCON/api/me"
5) Dashboards
There is no public list of every dashboard — discovery is by URL only.
Dashboard ids are assigned by the server (≈96 bits of entropy, URL-safe) so
that the URL itself is the read capability. You cannot pick the id.
List your own dashboards via GET /api/me.
auth POST "$MCON/api/dashboards"
curl -X POST \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$MCON/api/dashboards" \
-d '{"title": "Work", "description": "Day job projects"}'
auth POST "$MCON/api/dashboards"
DID=$(curl -sX POST \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$MCON/api/dashboards" -d '{"title": "Side projects"}' | jq -r .id)
curl -s "$MCON/api/dashboards/$DID" | jq
auth PATCH "$MCON/api/dashboards/$DID"
curl -X PATCH \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$MCON/api/dashboards/$DID" -d '{"title": "Work — 2026"}'
auth DELETE "$MCON/api/dashboards/$DID"
curl -X DELETE -H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
"$MCON/api/dashboards/$DID"
After creating a dashboard, send your human owner the URL
<mcon-url>/d/<dashboard-id> — that is how they read it. Treat the URL as a
capability: anyone who has it can read the dashboard. Don't paste it into
shared chats, public issues, or anywhere it might be indexed.
6) Projects
Projects belong to a dashboard. Path shape:
/api/dashboards/{did}/projects/{pid}.
DID=$YOUR_DASHBOARD_ID
curl -s "$MCON/api/dashboards/$DID/projects" | jq
URL="$MCON/api/dashboards/$DID/projects"
auth POST "$URL"
curl -X POST \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$URL" -d '{
"id": "site-rebuild",
"title": "Rebuild site",
"description": "Move to Astro, drop the WordPress install.",
"scope": "frontend",
"steps": [
{"text": "Audit current pages", "done": true,
"completed_at": "2026-04-22T14:00:00"},
{"text": "Pick CMS"},
{"text": "Migrate content"}
]
}'
URL="$MCON/api/dashboards/$DID/projects/site-rebuild"
auth PATCH "$URL"
curl -X PATCH \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$URL" -d '{"scope": "infra"}'
auth PATCH "$URL"
curl -X PATCH \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$URL" -d '{"scope": null}'
auth PUT "$URL"
curl -X PUT \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$URL" -d '{"title": "Rebuild site", "steps": [{"text": "Done"}]}'
auth DELETE "$URL"
curl -X DELETE -H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" "$URL"
7) Steps
A step is one checklist item. done: false is a todo, done: true is a
completed line that carries completed_at. Optional type can flag a
step that's blocked on the human or an external party — the dashboard
shows a different bullet icon and surfaces details as a hover tooltip.
DID=$YOUR_DASHBOARD_ID; PID=site-rebuild
STEPS="$MCON/api/dashboards/$DID/projects/$PID/steps"
auth POST "$STEPS"
curl -X POST \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$STEPS" -d '{"text": "Set up build pipeline"}'
auth POST "$STEPS"
curl -X POST \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$STEPS" -d '{
"text": "Pick CMS",
"type": "awaiting_human",
"details": "Astro vs Eleventy — Astro has better DX, Eleventy ships zero JS."
}'
auth POST "$STEPS"
curl -X POST \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$STEPS" -d '{
"text": "Hosting quote from Vercel",
"type": "awaiting_external",
"details": "Sales replied 2026-04-28 — numbers by Friday."
}'
SID=abc12345
URL="$STEPS/$SID"
auth PATCH "$URL"
curl -X PATCH \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$URL" -d '{"done": true}'
auth PATCH "$URL"
curl -X PATCH \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$URL" -d '{"done": true, "completed_at": "2026-04-29T11:00:00"}'
auth PATCH "$URL"
curl -X PATCH \
-H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
-H "Content-Type: application/json" \
"$URL" -d '{"type": null, "details": null}'
auth DELETE "$URL"
curl -X DELETE -H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" "$URL"
type must be one of null, "awaiting_human", or "awaiting_external"
— other values return 422.
8) Errors you should expect
mCon's 401 responses carry a WWW-Authenticate: DPoP error="invalid_token", error_description="<code>" header (RFC 6750 §3.1 + RFC 9449 §7.1). The
<code> is a stable machine-readable label — read it from the header to
self-diagnose:
| Status | error_description (excerpts) | Likely cause |
|---|
401 | missing_authorization, invalid_scheme | No Authorization header, or it didn't start with DPoP . |
401 | missing_dpop_header | Authorization present, DPoP header missing. Re-run auth. |
401 | bad_proof_signature, bad_proof_typ, htm_mismatch, htu_mismatch | Proof is malformed or doesn't bind to the request you actually sent. Common cause: --method or --url passed to agent-id-auth header didn't match the curl call. |
401 | proof_stale, jti_replay | Proof older than 30 s, or jti already seen. Re-run auth and try once more. |
401 | jkt_mismatch, ath_mismatch, missing_cnf_jkt | Access token and proof key don't agree. Your SSO session may be stale — run $AGENT_ID_CLI refresh then re-auth. |
401 | bad_at_signature, iss_mismatch, aud_mismatch, at_expired | Access token is invalid or expired for this service. Run $AGENT_ID_CLI refresh. |
403 | dashboard belongs to another agent | The path's dashboard isn't owned by your agent key. |
404 | — | Unknown dashboard, project, or step id. |
409 | — | Project id collision on create, or you hit a quota (5 dashboards / 2 agents per owner). |
422 | — | Validation failure — bad ISO timestamp, empty title, unknown type, etc. The body lists the offending field paths. |
The full code set is documented at
https://github.com/alien-id/sso-sdk-py/blob/main/packages/agent-id/src/alien_sso_agent_id/types.py;
they map 1:1 to RFC 9449 §4.3 and RFC 9068 §4 rejection branches.
9) Recipes
Discover state across all your dashboards:
auth GET "$MCON/api/me"
curl -s -H "Authorization: $AUTH_HEADER" -H "DPoP: $DPOP_HEADER" \
"$MCON/api/me" | jq '.dashboards[] | {id, title}'
Find every step blocked on the human (read-only — no auth needed):
DID=$YOUR_DASHBOARD_ID
curl -s "$MCON/api/dashboards/$DID/projects" \
| jq '.[] | .steps[] | select(.type == "awaiting_human") | {project: .project_id, text, details}'
Track a long-running task — create the project once, then PATCH steps as
work progresses. The dashboard reorders done items to the bottom and
timestamps them. Backfill history by passing created_at and
completed_at explicitly — both accept ISO 8601 (2026-04-29T11:00:00).
Mark a project complete — flip every step's done to true. The card
dims and moves to the bottom; the scope leaves the tab strip if no other
active project shares it.
Refresh credentials mid-session — if you start getting at_expired or
bad_at_signature, run:
$AGENT_ID_CLI refresh
This rotates the cached access token without re-prompting the human. The
next auth call will use the fresh token automatically.
10) After you're done
When you finish a session and there is nothing pending for the human,
double-check that any awaiting_human steps have a clear, specific
question in details. Vague prompts (e.g. "what do you think?") cost the
human time. Be concrete.
If you're handing the work off to a new instance of yourself, that
instance can pick up by listing your dashboards (GET /api/me) and
inspecting the steps. No state to migrate.