Prerequisites
- A Devctrl account at console.devctrl.ai
- A GitHub account with at least two repositories you own (one to allow, one to test denial)
- A GitHub Personal Access Token (classic) with these scopes:
repo,read:org,read:user - MCP Inspector for testing tool calls
curlfor creating task sessions
Throughout this guide, replace
YOUR_OWNER with your GitHub username or organization, and YOUR_REPO with an actual repository name you own.Part 1: Connect GitHub and make your first calls
Create a project
Log in to the Devctrl Console and create a new project.
- Name:
engineering - MCP Enabled:
Yes - Default policy action:
ALLOW
ALLOW so everything works out of the box. You’ll lock it down with policies in Part 2.Register the GitHub MCP server
Go to Tools in the top header and click Register MCP server. Select GitHub from the catalog.Enter a name (or keep the default) and paste your GitHub Personal Access Token in the token field.After saving, Devctrl connects to GitHub and discovers all available tools —
github__list_issues, github__issue_read, github__issue_write, github__create_pull_request, github__search_code, and more.Create an identity for the agent
Go to Identities and create a new identity.
- Name:
triage-bot
Connect with MCP Inspector
Open MCP Inspector and connect to the gateway:
- Transport Type: Streamable HTTP
- URL:
https://gateway.devctrl.ai/mcp - Header:
Authorization: Bearer YOUR_CREDENTIAL
List available tools
In the Inspector, go to the Tools tab. You should see all GitHub tools:
github__list_issuesgithub__issue_readgithub__issue_writegithub__create_pull_requestgithub__search_codegithub__get_file_contents- …
Make tool calls — everything is open
In the Inspector, call Works. Now try something the triage agent shouldn’t be doing — call That works too (or returns a GitHub error if the branch doesn’t exist — but Devctrl allowed the call). Right now every tool call is allowed because the default policy action is
github__list_issues with arguments:github__create_pull_request:ALLOW and there are no policies.Go to Executions in the console — you’ll see both calls logged with decision ALLOW.Part 2: Lock it down with a policy
The triage agent should only read issues and repos — not create PRs or write anything. Let’s add a policy.Write a policy
Go to Policies and create a new rule.
- Name:
read-only-github - Effect:
Allow - Expression:
- Message on deny:
This agent is restricted to read-only GitHub tools.
true only for read-only tools. If the agent tries github__create_pull_request or github__issue_write, the expression returns false and the call is denied. The message on deny is shown in the audit log and returned to the agent when a call is blocked.Try creating a PR — still works
In the Inspector, call It still works. Why? Because policies only take effect after you publish a release. Your policy is still a draft.
github__create_pull_request again:This is intentional. The draft → publish workflow lets you write and review policies safely before they affect live traffic.
Publish a release
Back in the console, go to the Releases tab under Policies and click Publish Release. This creates a versioned snapshot of your current policies and makes it the active release.The gateway picks up the new policies within about a minute.
Try again — now it's enforced
Wait a moment, then call
github__create_pull_request in the Inspector again.Denied. The policy blocked it because github__create_pull_request isn’t in the allowed tools list.Now call github__list_issues with { "owner": "YOUR_OWNER", "repo": "YOUR_REPO" } — it still works.Check Executions — you’ll see the denied github__create_pull_request with the exact policy rule that blocked it, and the allowed github__list_issues call.Part 3: Scope the agent to one repo
The agent can now only read — but it can read from any repo. For issue triage, it should only access the repo it’s assigned to. This is where tasks come in.Define a task
Go to Tasks and create a new task.This requires the agent to specify which owner and repo it’s triaging when it starts the task.
- Name:
triage-issues - Context schema:
Create a second policy
Keep
read-only-github as is. Now create a second policy that scopes access to the task’s repo.- Name:
scope-to-assigned-repo - Effect:
Allow - Expression:
- Message on deny:
Tool call targets a repo outside the assigned task scope.
read-only-github— is this a read-only tool?scope-to-assigned-repo— is the agent accessing the owner/repo from its task context?
Create a task session
Assign the agent to triage a specific repo. Task sessions use a REST API, so you can use curl:Save the
taskToken from the response. This token is scoped to this task and expires in 1 hour.Test with the task token
In the Inspector, add the task token as an additional header:Allowed. The tool is read-only and the owner/repo matches the task context.
- Header:
X-Task-Token: YOUR_TASK_TOKEN
github__list_issues with:Access a different repo — denied
Still in the Inspector with the same task token, call Denied. The task context says
github__list_issues with a different repo:repo: "YOUR_REPO", but the tool call targets some-other-repo. The scope-to-assigned-repo policy catches the mismatch.Check Executions to see both decisions with the full evaluation context — which policy passed, which failed, and why.What you’ve learned
- Without policies, the agent can do anything — list issues, create PRs, access any repo
- Policies are drafts until you publish a release — a common gotcha
- Multiple policies work together —
read-only-githubrestricts tools,scope-to-assigned-reporestricts data - Task context scopes access dynamically — the agent only sees the repo it’s assigned to
- Every decision is logged in the audit trail with full context
What’s next
Task-Based Access Control
Understand TBAC and how it differs from traditional RBAC.
Connect an agent
Point any MCP-compatible agent at the gateway.
CEL expressions
Full reference for writing policy expressions.
Policy examples
Real-world policy patterns you can copy and adapt.