Skip to main content
In this guide, you’ll set up Devctrl to govern an AI agent’s access to GitHub. The agent needs to triage issues on a specific repo — but it shouldn’t be able to create pull requests, delete branches, or access repos it’s not assigned to. You’ll connect a GitHub MCP server, make tool calls, add policies, see them fail (on purpose), and learn how everything fits together.

Prerequisites

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

1

Create a project

Log in to the Devctrl Console and create a new project.
  • Name: engineering
  • MCP Enabled: Yes
  • Default policy action: ALLOW
We’re starting with ALLOW so everything works out of the box. You’ll lock it down with policies in Part 2.
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.
3

Create an identity for the agent

Go to Identities and create a new identity.
  • Name: triage-bot
Go to Credentials and click Generate Credential. Copy the secret — this is the agent’s bearer token.
You can add labels like team: devops or role: triage from the identity’s settings page. Labels are used in policy expressions.
The credential is shown only once. Save it somewhere safe.
4

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
Click Connect. The Inspector handles the MCP protocol handshake automatically.
5

List available tools

In the Inspector, go to the Tools tab. You should see all GitHub tools:
  • github__list_issues
  • github__issue_read
  • github__issue_write
  • github__create_pull_request
  • github__search_code
  • github__get_file_contents
6

Make tool calls — everything is open

In the Inspector, call github__list_issues with arguments:
{ "owner": "YOUR_OWNER", "repo": "YOUR_REPO" }
Works. Now try something the triage agent shouldn’t be doing — call github__create_pull_request:
{
  "owner": "YOUR_OWNER",
  "repo": "YOUR_REPO",
  "title": "Untested changes",
  "head": "some-branch",
  "base": "main"
}
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 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.
1

Write a policy

Go to Policies and create a new rule.
  • Name: read-only-github
  • Effect: Allow
  • Expression:
request.tool.name in [
  "github__list_issues",
  "github__issue_read",
  "github__search_issues",
  "github__get_file_contents",
  "github__list_commits",
  "github__search_code"
]
  • Message on deny: This agent is restricted to read-only GitHub tools.
The expression evaluates to 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.
2

Try creating a PR — still works

In the Inspector, call github__create_pull_request again:
{
  "owner": "YOUR_OWNER",
  "repo": "YOUR_REPO",
  "title": "Should this be blocked?",
  "head": "some-branch",
  "base": "main"
}
It still works. Why? Because policies only take effect after you publish a release. Your policy is still a draft.
This is intentional. The draft → publish workflow lets you write and review policies safely before they affect live traffic.
3

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.
4

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.
1

Define a task

Go to Tasks and create a new task.
  • Name: triage-issues
  • Context schema:
{
  "type": "object",
  "properties": {
    "owner": {
      "type": "string",
      "description": "The GitHub owner (user or org)"
    },
    "repo": {
      "type": "string",
      "description": "The repository name"
    }
  },
  "required": ["owner", "repo"]
}
This requires the agent to specify which owner and repo it’s triaging when it starts the task.
2

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:
request.tool.args.owner == task.context.owner
  && request.tool.args.repo == task.context.repo
  • Message on deny: Tool call targets a repo outside the assigned task scope.
Your project now has two policies. Both must pass for a call to be allowed:
  1. read-only-github — is this a read-only tool?
  2. scope-to-assigned-repo — is the agent accessing the owner/repo from its task context?
Publish a new release from the Releases tab to include both policies.
3

Create a task session

Assign the agent to triage a specific repo. Task sessions use a REST API, so you can use curl:
curl -s -X POST https://gateway.devctrl.ai/v1/tasks/sessions \
  -H "Authorization: Bearer YOUR_CREDENTIAL" \
  -H "Content-Type: application/json" \
  -d '{
    "taskId": "YOUR_TASK_ID",
    "context": {
      "owner": "YOUR_OWNER",
      "repo": "YOUR_REPO"
    },
    "ttl": 3600
  }' | jq
Save the taskToken from the response. This token is scoped to this task and expires in 1 hour.
4

Test with the task token

In the Inspector, add the task token as an additional header:
  • Header: X-Task-Token: YOUR_TASK_TOKEN
Now call github__list_issues with:
{ "owner": "YOUR_OWNER", "repo": "YOUR_REPO" }
Allowed. The tool is read-only and the owner/repo matches the task context.
5

Access a different repo — denied

Still in the Inspector with the same task token, call github__list_issues with a different repo:
{ "owner": "YOUR_OWNER", "repo": "some-other-repo" }
Denied. The task context says 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

  1. Without policies, the agent can do anything — list issues, create PRs, access any repo
  2. Policies are drafts until you publish a release — a common gotcha
  3. Multiple policies work together — read-only-github restricts tools, scope-to-assigned-repo restricts data
  4. Task context scopes access dynamically — the agent only sees the repo it’s assigned to
  5. Every decision is logged in the audit trail with full context
This is the core of Devctrl: agents get exactly the access they need, for exactly the task they’re performing, and every action is logged.

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.