Borrowing it
Nothing to install: this file belongs to alpacax/alpacon-mcp. Take a copy, put it at the same path in your own repository, and replace the rules that are about this project with yours.
curl -O https://raw.githubusercontent.com/alpacax/alpacon-mcp/main/AGENTS.mdgit clone --depth 1 https://github.com/alpacax/alpacon-mcpWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/instructions/alpacax/alpacon-mcp/agents-md)<a href="https://agentmods.dev/instructions/alpacax/alpacon-mcp/agents-md"><img src="https://agentmods.dev/badge/instructions/alpacax/alpacon-mcp/agents-md/github.svg" alt="Measured on agentmods" height="20"></a>Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.
<a href="https://agentmods.dev/instructions/alpacax/alpacon-mcp/agents-md"><img src="https://agentmods.dev/badge/instructions/alpacax/alpacon-mcp/agents-md.svg" alt="Reviewed on agentmods" width="80" height="20"></a>What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5.1 | $0.03649 | $0.03649 |
| Opus 5.5 | $0.01460 | $0.01460 |
| Sonnet 5.5 | $0.00730 | $0.00730 |
| Haiku 4.5 | $0.00365 | $0.00365 |
Grade A, and why
alpacon-mcp AGENTS.md scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 2d ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 229 lines — stays where its author put it; the contents beside it link to each section on GitHub.
AGENTS.md
Alpacon MCP Server: an MCP server that bridges Alpacon's zero-trust infrastructure access to AI assistants over plain HTTP. No alpacon CLI, no subprocess, no external binaries in the runtime path, except that a local stdio server raises desktop notifications through the platform tool, best effort.
Red lines
- Reach Alpacon only through
mcp__alpacon__*tools. Never thealpaconCLI, neverssh, never a shell fallback when an MCP call fails. On failure: report the exact error, propose another MCP route, ask for the missing auth/config. This constrains how you talk to Alpacon—it does not restrict ordinary local work in this repo (pytest,git,uv). server_idis always a UUID, never a server name.list_serversmaps a name to its UUID. A name fails at the input validator, before the API call.- Approve/reject is human-only. No
approve_requesttool exists and the server answers agent/token channels with 403. When a tool returnsstatus="pending_approval", surface it to a human who decides out-of-band in the web console or Slack, then retry.
Development commands
Python 3.12 or higher and uv >=0.13,<0.14 are required (pyproject.toml).
uv venv && source .venv/bin/activate
uv sync --extra dev # runtime + dev dependencies
python main.py # stdio transport (default MCP mode)
python main_http.py # streamable-http; needs AUTH0_ALPACON_* configured
pytest # test suite
ruff check . # lint
ruff format --check . # formatting check
mypy . # type check
python -c "from server import mcp; print('ok')" # import smoke check
Non-obvious invariants
The legacy Auth0 environment variable names still work as fallbacks and will be removed in a later release; see docs/configuration.md for the mapping.
Things the code will not tell you at a glance:
- Regions are
ap1andus1only (the internaldevis not a served region), except whereALPACON_MCP_ENVisstagingordev: there the JWT namesdev, so tool schemas accept it too. The setting is read when the tools are decorated, at import. An omittedregionis resolved from the JWT in remote mode and fromtoken.jsonin local mode, then validated; resolution fails when no unique region can be determined. - Toolset selection crosses module boundaries.
--toolsets/ALPACON_MCP_TOOLSETSgates imports, and registration is an import-time side effect.alpacon://servers/.../overviewis backed bysystem_info_tools, so--toolsets serversalone silently drops it. Remote mode is gated onALPACON_MCP_AUTH_ENABLED=true, not on the transport name, and ignores the setting entirely. @mcp_tool_handlerowns validation, token injection, error shape, and logging. Never write a try/except around an HTTP call in a tool.region,workspace,server_id,server_ids,servers,session_id,limit, andpage_sizeare validated before the local API token lookup; remote mode reads the JWT after workspace validation and before region resolution and the remaining field checks. Other explicitly declared*_idarguments are checked as single path segments, exceptwork_session_id, whose padding is stripped separately. File paths are not checked—callvalidate_file_path()inline in any tool taking a path.requires_workspace=Falseis for a tool that answers before a workspace is known: it drops the workspace checks, the workspace-keyed region resolution, the JWT workspace authorization, and the stdio token injection.list_workspacesis the only tool using it. Whatever a tool declares, the decorator publishesregionasap1,us1or null (plusdevon a staging or dev deployment), so the SDK refuses anything else,''included. The decorator also rewrites the published signature, dropping the**kwargsit injects the token into: the SDK reads that catch-all as an ordinary required field and would publish it to clients. Two rules follow: a tool must declare**kwargsor decoration raises, and no tool may forward its own**kwargson to another tool, because by then it holds the resolved credential.with_loggingbinds the published signature strictly, so a forwarded catch-all raises aTypeError—but only while INFO is enabled, since the bind sits inside that guard.TestCatchAllForwardingpins the direct form—a call handing its own**kwargsstraight to another tool—at any log level; an indirect forward, such as**{**kwargs}or a singletoken=kwargs.get('token'), is still caught by the bind alone and so only at INFO.health_checksits outside the decorator entirely because it must answer before any JWT exists, and borrows@with_error_handlingalone for the error shape.AlpaconHTTPClientnever caches a response, deliberately. Every read worth caching—the server list, process info, IAM users and groups—comes back filtered by the calling token's permissions, and nothing in this process sees a grant revoked in the web console, in Slack, or by another MCP process. Do not reintroduce a response cache in the shared client: a hit would answer with access the caller has lost. The auth-path caches stay: the JWKS keys inutils/auth.pyand the renamed-slug lookup inutils/workspace_resolver.pyare not permission-filtered tool data.AlpaconHTTPClientnever resends a POST or PATCH that may have reached the server (#392). Only the errors in_NEVER_SENT_ERRORS, raised before the request goes out, and the 5xx codes in_REJECTED_BEFORE_VIEW_CODESretry them; a read timeout, a broken connection or any other 5xx returns at once withoutcome_unknown: true, and such an error gets no recovery hints. The upstream API takes no idempotency key, so a resent/api/events/commands/POST runs the command twice.- The WorkSession gate applies to OAuth/browser callers only. Static API and
service tokens bypass it, so a flow that works in stdio mode can be blocked in
remote mode.
ALPACON_WORK_SESSIONsupplies a default session id; an explicitwork_session_idargument wins. - API token tools reject static API tokens. List/get receive an upstream
403 for
source='api'tokens. Create/update/delete/duplicate/rotate use@require_jwt_authand return a local error for non-JWT tokens before any HTTP call. The scopes and presets catalogs are exempt. - MFA re-authentication exists only in remote/streamable-http mode, with a 60-second cooldown against re-auth loops.
- A remote
auth_mfa_requiredanswers in the tool result, not as a 403._handle_upstream_401records the step-up inutils/mfa_step_up.py's per-call holder and raisesMfaStepUpRequired, never writing torequest_signal(any entry there replaces the response with a re-auth challenge). The outermostwith_error_handlingreturnsstatus="mfa_required"with Alpacon'smfa_url, even when a fan-out tool swallowed the exception; a tool that is not read-only and returned normally keeps its result aspartial_result(it may hold no successful part), and the message asks for a retry of the refused parts only. A tool whose refusal propagated after a POST or PATCH of the same call succeeded lists those requests inalready_sentand never asks for the whole call again; one with no such request does. A read-only tool always asks for the whole call again. Once recorded, every laterAlpaconHTTPClient.requestthe tool makes raises before sending (themfa_urlfetch runs after the holder is released), so a refusal is not followed by more side effects. There is no MFA pre-check: Alpacon decides.auth_token_mfa_missingkeeps the 403 re-login. - A 2026-07-28 request declaring
elicitation.urlgets the whole-retrymfa_requiredas anInputRequiredResult(#427). The outermostwith_error_handlingbuilds it, so a tool calling a tool still gets the link. The retry'srequestStateandinputResponsesreach it throughutils/client_surfaces.py'sbind_tool_call_round, and it answers with a freshly fetched link, without calling the tool, until Alpacon's completion poll stops saying MFA is not done; the link's ticket redeems once, so a sealed one is never reused. TherequestStateis sealed under a grant-secret key so any replica opens it, or per process when no grant or client secret is set. - A client that renders MCP Apps is pointed at
show_mfa(tools/mfa_tools.py, remote mode only, #427). Itsmfa_requiredmessage names the tool and still carriesmfa_url;show_mfaputs its own fresh link only in result_meta, never in the text the model reads; a client of unknown support gets it in both, since its host may render the panel. Apps support means theio.modelcontextprotocol/uiextension liststext/html;profile=mcp-app, andhide_ui_tools(utils/ui_tools.py) drops every_meta.uitool fromtools/listfor a client that declares capabilities without it. - The code and refresh token a remote client holds are sealed by this
server, carrying the device id minted for that grant. Rotating
ALPACON_MCP_GRANT_SECRET(orAUTH0_ALPACON_LOGIN_CLIENT_SECRET, which it is derived from by default) logs every remote session out. - A remote access token may not be an Auth0 JWT. With
ALPACON_MCP_REMOTE_LEDGER=true,/oauth/tokenwraps a command pre-approval ledger token into an encryptedamcpl1.value (utils/ledger_grant.py), andAuth0TokenVerifierunwraps it before verifying, soAccessToken.tokenis always the bare JWT. That credential is the only place a remote ledger token lives; never cache one in the process. - Desktop notifications fire only inside
server.run()'s stdio run.utils/local_signal.pyruns the OS notifier on the MCP server's own host, so streamable-http and the hostedmcp.alpacon.io(including throughmcp-remoteor thealpaconplugin) never notify. A stdio server in a container or launched over SSH attempts delivery on that host, not on the user's desktop. Never enablestdio_transport()outside that run; a remote equivalent belongs in a client-side hook. - Transport settings live at the entry point, not on the constructor. SDK 2.x
takes
host,json_responseandstateless_httponmcp.streamable_http_app(), socreate_streamable_http_app(host=...)carries them.hostdecides DNS rebinding protection—the SDK enables it for loopback addresses only—and must be the address uvicorn actually binds, or a deployment on0.0.0.0rejects its own public Host header with a 421. server.run()refuses streamable-http. It serves stdio only;main_http.pycomposes that transport itself, wrappingcreate_streamable_http_app()inUpstreamAuthErrorMiddleware. An app built through any other path is unguarded—no 401 is ever turned into a re-auth challenge.- The re-auth signal is a per-request mutable object in a ContextVar. The
middleware plants an empty dict (
utils/request_signal.py) and the tool handler mutates it; the handler runs in a task the transport spawned, so rebinding the ContextVar there never reaches the middleware. Only a JWT-carrying request ever signals, andUpstreamAuthErrordoes not cross the ASGI boundary: the SDK turns a handler exception into a wire response. - The agent's
clientInfotravels the other way, so a plain ContextVar works.utils/declared_agent.pybinds it in an SDKServerMiddleware, which wraps the handler call, andhttp_clientforwards it as twoX-Alpacon-Declared-Agent-*headers. It is self-reported, and the server composes and sanitizes it. A stateless streamable-http server never sees a pre-2026 client's handshake at tool time, so nothing is sent there (#317). Over stdio only (prepare()turns it on), a request whoseclientInfonames no agent is declared from the CLI's environment markers instead (utils/agent_environment.py).
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 2d ago Changed · +8 lines · +143 tokens per session 78c8ecf99eeb
- 3d ago Changed · +1 lines · +146 tokens per session cc97afc0294a
- 5d ago First seen · 220 lines · 3,360 tokens per session scan A 217b6259977e
alpacon-mcp AGENTS.md is an instructions file published in the GitHub repository alpacax/alpacon-mcp (1 stars, last pushed yesterday), licensed MIT. It adds 3,649 tokens to every session, about $0.0146 per session on Opus 5.5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-11-03.
alpacon-mcp AGENTS.md compared with instructions from other repositories
| Mod | Type | Stars | Last push | Licence | Scan | Tokens | Works with |
|---|---|---|---|---|---|---|---|
| alpacon-mcp AGENTS.mdalpacax/alpacon-mcp | Instructions file | 1 | yesterday | MIT | A | 3,649 | Codex, OpenCode |
| vibe-coding-prompt-template backend.instructions.mdKhazP/vibe-coding-prompt-template | Instructions file | 3.2k | 1mo ago | MIT | A | 139 | GitHub Copilot |
| plan-forge naming.instructions.mdsrnichols/plan-forge | Instructions file | 6 | 3d ago | MIT | A | 1,453 | GitHub Copilot |
| vscode buildNext.instructions.mdmicrosoft/vscode | Instructions file | 195k | today | MIT | A | 9,719 | GitHub Copilot |
| vscode oss-third-party-notices.instructions.mdmicrosoft/vscode | Instructions file | 195k | today | MIT | A | 5,516 | GitHub Copilot |
| next.js AGENTS.mdvercel/next.js | Instructions file | 143k | yesterday | MIT | A | 7,461 | Codex, OpenCode |
| langchain AGENTS.mdlangchain-ai/langchain | Instructions file | 149k | today | MIT | A | 4,680 | Codex, OpenCode |