From zero to launched
This is the linear walkthrough. You take an application you already run, work through the five steps on the Applications screen, and end with it serving 200s to the right callers and 403s to everyone else.
About 10 minutes the first time. Step 4 is the only one with real decisions.
Before you start
- An application reachable over HTTPS. An MCP server, an AI agent endpoint, an API. The example below uses an MCP server at
https://mcp.acme.example/mcp; substitute your own URL anywhere you see it. (For local dev against a service on your laptop, AuthSec also accepts loopback addresses — see local development.) - Admin access to AuthSec. If you can see the Applications screen, you're good.
- A few minutes to drop the SDK into your codebase. Python, Go, and TypeScript all ship a one-line wrapper.
That's it. No OAuth knowledge required — the SDK and admin UI take care of the dance.
Step 1 — Register the application
Open Applications in the admin UI → click Install protection. The side panel asks for:
- Name — human readable, shows up across the UI.
- Public Base URL — the externally reachable origin of your application, e.g.
https://mcp.acme.example. - Protected Path — defaults to
/mcp. The Resource URI is composed as<Public Base URL><Protected Path>.
Submit. AuthSec creates the application, generates a one-time introspection secret, and lands you on the application detail view. The application starts in the Discovered state ("Not launched") — none of the five steps have been completed yet.
Copy the introspection secret now. It is shown once. If you lose it, you'll rotate it later from the same detail panel.
The secret and the Resource URI are the two values you need for step 2. Everything else is derivable from your AuthSec base URL.
More detail: Register an application.
Step 2 — Install protection
Drop the AuthSec SDK wrapper into your application's startup code. It's a ~5-line change.
Python
from fastapi import FastAPI
from authsec_sdk.runtime import Config, mount_mcp
cfg = Config(
issuer="https://api.authsec.dev",
resource_server_id="<id from step 1>",
introspection_client_secret="<introspection secret from step 1>",
resource_uri="https://your-app.example.com/mcp",
publish_manifest=True,
)
app = FastAPI()
mount_mcp(app, "/mcp", your_existing_handler, cfg)
Go
cfg := authsec.Config{
Issuer: "https://api.authsec.dev",
ResourceServerID: "<id from step 1>",
IntrospectionClientSecret: "<introspection secret from step 1>",
ResourceURI: "https://your-app.example.com/mcp",
PublishManifest: true,
}
mux := http.NewServeMux()
authsec.MountMCP(mux, "/mcp", yourExistingHandler, cfg)
http.ListenAndServe(":8000", mux)
TypeScript
import express from "express";
import { loadConfigFromEnv, mountMCP } from "@authsec/sdk";
const app = express();
app.use(express.json());
await mountMCP(app, { config: loadConfigFromEnv(), path: "/mcp" });
app.post("/mcp", yourExistingHandler);
app.listen(8080);
npm install @authsec/sdk express. Full guide at TypeScript SDK.
After restarting, your application is wired up. It still serves every call (the policy isn't live yet — that's step 5), but every request now flows through the SDK middleware.
Treat the secret like a password. Put it in your secrets manager / env var; never in source control.
More detail: Python SDK · Go SDK.
Step 3 — Publish the tool manifest
The tool manifest is the list of tools your application exposes. AuthSec needs it so the admin UI can show each tool, you can map each to a scope, and the SDK can enforce policy per tool at runtime. Two ways to land it:
From the SDK (recommended). With publish_manifest=True (Python) or PublishManifest: true (Go), the SDK pushes the inventory at startup. Restart your app and refresh the Tools tab — every tool is listed.
By scanning over the wire. If your tool list is dynamic or you'd rather not publish from the SDK, click Scan now on the Tools tab. AuthSec issues a synthetic JSON-RPC tools/list request against your Resource URI and stores whatever it gets back.
The Tools tab shows what AuthSec knows. The example screenshot here shows 101 tools discovered from a GitHub MCP server. Pretty common — manifests are usually long.
More detail: What is a tool manifest? · Tool inventory and discovery (operator view).
Step 4 — Review tool access
This is the slow step. Everything else is mechanical; this one is design work.
4a. Define access labels
A scope is a named permission a user grants to an AI client — the unit of agreement that lets the client do one specific kind of thing. Open the Scopes tab. AuthSec seeds starter labels per application:
rs-<id>:tools:readrs-<id>:tools:writers-<id>:admin
Accept the defaults if you're just starting; add custom scopes (e.g. rs-<id>:repos:read) once you see patterns in your tool surface. For the tradeoffs and the decision tree, see Scope design for MCP tools.
4b. Map each tool to a scope
Back on the Tools tab, for every tool pick one of:
- Map to a scope — the tool requires that scope on the caller's token. The common path.
- Mark Public — no auth required. Use sparingly. Health checks, capability discovery.
- Leave Unmapped — the tool stays in the inventory but no one can call it (denied at runtime). The safe default.
The header counters track mapped / public / unmapped in real time. Unmapped tools are launch blockers — Activate stays disabled until every tool is mapped or marked public.
For bulk patterns ("every actions_get_* is read scope"), use the API — see Map tools to scopes.
4c. Default access policy
Open the Access tab. Pick a default role (likely viewer, auto-created with no scopes) and enable the policy. Every new user who logs in for the first time gets this role.
Then add manual exceptions for yourself as admin — if you don't, you'll lock yourself out the first time you test. The exception table tags MANUAL_ADMIN for hand-added rows vs DEFAULT_POLICY for auto-grants, so your overrides survive policy changes.
More detail: Define scopes · Map tools to scopes · Access policy.
Step 5 — Launch
Open the Overview tab. The action queue tracks the setup work you just did. When every blocker is gone, the Activate action lights up.
Click it. The application flips from "Not launched" to live, and the SDK starts enforcing the policy on its next refresh (within ~5 minutes; restart your app to force it now).
Verify it works
Three calls. If all three behave correctly, you're done.
Unauthenticated → 401 with a working challenge.
curl -sv -X POST https://your-app/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Expected: 401, with header WWW-Authenticate: Bearer resource_metadata="https://your-app/.well-known/oauth-protected-resource/mcp".
Read-scoped token calling a write tool → 403 insufficient_scope. Acquire a token via the OAuth flow with only the read scope (Claude Desktop is the easiest path), then call a write tool. Expected: 403, body lists the missing scope.
Properly-scoped token → 200. Acquire a token with the write scope, call the same tool. Expected: 200 with the tool's response.
If anything is off, see Troubleshoot OAuth tokens — the four-claim checklist (aud, iss, exp, scope) handles the vast majority of cases.
More detail: Run the protection test · Launch the application.
What "launched" means on the Applications list
The Applications list updates the moment you activate. Per app you now see:
- Readiness — whether the setup is still internally consistent.
- Risk — surfaced from drift events and unmapped tools.
- Last Signal — the most recent runtime or policy event.
- Next Action — the one thing AuthSec thinks you should look at.
This is your operational view from here on. Open it daily; the Next Action column is the closest thing to a TODO list.
After launch
- Add scopes as your tool surface grows. The Tools tab makes per-tool updates a one-click affair.
- Watch for drift events. Anything that changes the live policy after launch (scope deleted, tool unmapped, default role disabled, secret rotated) shows up in the Monitor tab. See Monitor drift and runtime.
- Onboard end users. If your app accepts public traffic, end users self-onboard through OAuth consent. Manage them under End Users. See Manage end users.
- Add operators. Settings → Team. Invite, suspend, or remove workspace members (the people who can administer Applications). See Manage workspace members.
When you outgrow the basics
- More than one application sharing a tool? Read Preventing confused-deputy attacks — the
audclaim is what makes that safe. - Latency-sensitive? Compare JWT vs token introspection.
- Org-scale operator counts? Move from per-user role bindings to group-mediated bindings. See Groups and role bindings.