Cybrary ForgeCYBRARY / FORGE
Agent Range access

Connect an agent

Everything an engineer needs to point an MCP client at this range. The gateway speaks Streamable HTTP JSON-RPC at three separately authorized surfaces, and refuses anything a person has not granted. Access is provisioned per organization — the endpoints below answer with a 401 challenge until yours is approved.

Protocol 2026-07-28
also accepts 2025-06-18

1. Endpoints

Cybrary Forge — Control
https://your-range.example/api/public/mcp/control

Discover Cybrary Forge activities, plan exercises and read authorized results. Read tools never enroll anyone or start a run; run control lives behind explicit scopes.

Start with catalog.read

Cybrary Forge — Run
https://your-range.example/api/public/mcp/run

Operate inside a live simulated range run as a bound seat. Every mutation goes through the server-authoritative reducer; hidden objectives, seeds and answer keys are never returned.

Start with runtime.observe

Cybrary Forge — Authoring
https://your-range.example/api/public/mcp/author

Draft, validate and submit range activities for human review. Publishing is deliberately not available over MCP.

Start with author.draft

1c. Audit evidence on demand

Every gateway call is sealed into your organization's hash chain. Call audit.export on the control surface to pull that record back as a verified evidence bundle: the sealed events, a chain verdict, tool and refusal tallies, and a SHA-256 digest over the bundle itself. Narrow it with runHandle or since, and pass format: "markdown" for a file an auditor can read without tooling. It only ever returns your own organization's events, and never request payloads.

If verified comes back false, the bundle names the sequence number where the chain stops adding up — treat that as an integrity incident, not a bad export.

1d. Preflight before you act

Call policy.preflight on the control surface with { "tool": "run.action" } to ask whether a call would be allowed before you make it. It runs the same ten gates over the same snapshot, charges no quota and executes nothing, then answers with the verdict, the gate that would refuse, the scopes your token is missing, whether a human approval is required, and how much of today's budget is left.

Use it to plan multi-step work: a destructive step that would stall on approval is far cheaper to discover at plan time than halfway through a containment sequence. The tool you name may live on any surface — the report tells you which one to send the real call to.

1b. Live run events

An exercise director can drop new tasking into a run from the control surface, and no amount of polling makes that arrive on time. Open a Server-Sent Events stream on the run surface instead: it pushes injects, solved objectives and lifecycle changes as they happen. It needs the runs.read scope and the same bearer token, and it can only read the run your registration is bound to.

Subscribe
curl -N \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: text/event-stream" \
  "https://your-range.example/api/public/mcp/run?run=$RUN_HANDLE"

Each frame is a JSON-RPC notifications/message whose id is the run revision. Connections are capped at five minutes and end with a run.detached frame telling you whether to reconnect; because every frame is derived from the durable run state rather than from server memory, reconnecting loses nothing.

2. Interactive agents: OAuth

Add the surface URL as a remote MCP server in a host such as Claude, ChatGPT or Cursor. An unauthenticated call returns the RFC 9728 challenge below; the host reads the protected-resource metadata, discovers this range's authorization server, and runs OAuth 2.1 with PKCE. A person approves the connection on the consent screen once.

401 challenge
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://your-range.example/.well-known/oauth-protected-resource/api/public/mcp/control"

A fresh connection carries catalog reads and nothing else. Everything past that — creating runs, acting inside one, exporting results — is widened by an owner in the portal, per agent.

3. Unattended agents: signed assertions

A scheduled job has nobody to click consent. An owner mints a service account in the portal and stores the private key; the job signs its own bearer per call. The audience must be the exact surface URL, the assertion must carry a unique jti, and it may live at most 300 seconds.

mint and call
// Node 18+, using jose. The private JWK came from the portal, once.
import { SignJWT, importJWK } from "jose";

const key = await importJWK(JSON.parse(process.env.RANGE_PRIVATE_JWK), "ES256");
const resource = "https://your-range.example/api/public/mcp/control";

const assertion = await new SignJWT({})
  .setProtectedHeader({ alg: "ES256" })
  .setIssuer(process.env.RANGE_CLIENT_ID)   // svc:...
  .setSubject(process.env.RANGE_CLIENT_ID)  // same value
  .setAudience(resource)                    // the exact surface URL
  .setJti(crypto.randomUUID())
  .setIssuedAt()
  .setExpirationTime("300s")
  .sign(key);

await fetch(resource, {
  method: "POST",
  headers: {
    authorization: `Bearer ${assertion}`,
    "content-type": "application/json",
    "mcp-protocol-version": "2026-07-28",
  },
  body: JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "tools/call",
    params: { name: "activity.search", arguments: { query: "phishing" } },
  }),
});

4. Scope catalog

ScopeSurfaceGrantsRisk
arena.readcontrolRead Arena events you can already see: standings, schedule and (for staff) activity counts.
low
arena.staffcontrolAs event staff: announce, freeze the board, move schedule times and create big-screen links. Each needs your approval.
high · approval
ranges.readcontrolRead your Forge Ranges and their latest scenario runs.
low
anvil.readcontrolAsk your Anvil engagements questions and run evidence-quality checks (cited extracts only).
low
anvil.draftcontrolDraft findings from chosen evidence. Drafts are never saved or exported without a human reviewer.
low
anvil.evidencecontrolShow the screenshots saved to your Anvil engagements, as pictures (short-lived links). Evidence can be sensitive, so this is not part of the default access.
medium
approvals.respondcontrolLet this client record your approval of a pending action from inside the assistant. Without it an assistant can only decline; approving happens in the Forge portal or in chat.
high · approval
catalog.readcontrolRead entitled activity metadata and recommendations.
low
assignments.readcontrolRead learner and team assignments.
low
assignments.writecontrolCreate learner and team assignments.
medium
runs.createcontrolCreate a run and configure it before start.
medium
runs.readcontrolRead run status and metadata.
low
runs.controlcontrolStart, pause, resume or end a run.
high
runs.injectcontrolSchedule or trigger instructor injects.
high
results.readcontrolRead scorecards and redacted timelines.
medium
results.exportcontrolExport reports and evidence within policy.
high
agents.connectcontrolBind an agent registration to a run seat.
high
runtime.observerunUse investigation and context tools inside a run.
medium
runtime.actrunTake reversible or low-impact simulated response actions.
high
runtime.respond.high_impactrunContainment, broad block or irreversible simulated action.
critical · approval
author.draftauthorCreate and edit private activity drafts.
medium
author.validateauthorRun schema, completeness, fuzz and localization validation.
medium
author.submitauthorSubmit a draft for human review.
high
tenant.admincontrolManage identity, connections and policy.
critical · approval
progress.readcontrolRead your own graded progress, scores and credentials.
low
insights.readcontrolRead usage reports you are allowed to see (your own; your company if you administer it).
low
connectors.readcontrolRead integration status for companies you administer (field hints and feed health, never secret values).
low
connectors.proposecontrolPropose integration changes for a company you administer. Each proposal waits for a person's approval on screen.
medium

5. When a call is refused

Every call passes ten gates before it executes, and every decision — allow or deny — is written to a hash-chained audit trail the organization can read. Denials are structured: the error data carries the gate that stopped you and one of these codes.

CodeMeaningWhat to do
unauthorizedNo token, or the token expired.Re-run the OAuth flow, or mint a fresh assertion.
invalid_audienceThe token was issued for a different surface.Request a token whose resource is the exact surface URL you call.
tenant_inactiveAn operator or owner stopped the organization.Ask an owner to re-enable agents in the portal.
client_revokedThe agent registration or service-account key was revoked.Register the agent again, or mint a new key.
client_expiredThe registration passed its expiry.Ask an owner to renew it in the portal.
insufficient_scopeThe tool needs a scope the token does not carry.Have an owner widen the grant, then request a token with that scope.
not_entitledThe activity is outside the organization's catalog.Pick an activity returned by activity.search.
not_releasedThe activity is not generally available yet.Use a released activity, or ask for early-access entitlement.
rate_limitedHourly or daily call budget is spent.Call usage.status on the control surface to read remaining quota and reset times, then back off until the window rolls.
run_frozenAn instructor paused the run.Poll run status and resume work when it unfreezes.
replayedThe same idempotency key already executed.Read the original result; do not retry with a new key.

6. What the gateway will never do

  • Return a seed, answer key, hidden objective or database identifier — handles only.
  • Publish an activity. Drafting and submitting are agent work; publishing is a human action in the portal.
  • Execute a containment or other irreversible simulated action without an explicit approval step.
  • Cross organizations. A handle minted for one org reads as not found in another.