@loopstack/github-oauth-example
An example module for the Loopstack AI automation framework.
This module demonstrates how to build workflows that interact with the GitHub API using OAuth authentication. It includes two workflows: a structured overview that fetches repository data, and an interactive chat agent powered by Claude that can use all 25 GitHub tools.
Workflows
GitHub Repos Overview (gitHubReposOverview)
A multi-step workflow that fetches and displays a comprehensive overview of a GitHub repository. If the user is not authenticated, it launches the OAuth sub-workflow and retries automatically.
Inputs: owner (default: octocat), repo (default: Hello-World)
Flow:
start -> user_fetched -> orgs_fetched -> repo_fetched -> issues_prs_fetched
-> content_actions_fetched -> search_done -> endWith OAuth branching:
user_fetched -> (unauthorized via @Guard) -> awaiting_auth
|
auth_completed -> start (retry)Key patterns:
The workflow uses @Guard to check if authentication is needed and routes to the OAuth flow:
@Transition({ from: 'user_fetched', to: 'awaiting_auth', priority: 10 })
@Guard('needsAuth')
async authRequired() {
const result = await this.oAuth.run(
{ provider: 'github', scopes: ['repo', 'read:org', 'workflow'] },
{ alias: 'oAuth', callback: { transition: 'authCompleted' } },
);
await this.documentStore.save(
LinkDocument,
{
label: 'GitHub authentication required',
workflowId: result.workflowId,
embed: true,
expanded: true,
},
{ id: `link_${result.workflowId}` },
);
}
needsAuth(): boolean {
return !!this.requiresAuthentication;
}The auth callback uses wait: true with CallbackSchema to receive the OAuth completion signal:
@Transition({
from: 'awaiting_auth',
to: 'start',
wait: true,
schema: CallbackSchema,
})
async authCompleted(payload: { workflowId: string }) {
await this.documentStore.save(
LinkDocument,
{
status: 'success',
label: 'GitHub authentication completed',
workflowId: payload.workflowId,
embed: true,
expanded: false,
},
{ id: `link_${payload.workflowId}` },
);
}Tools exercised in the workflow:
| Category | Tool | Used in workflow |
|---|---|---|
| Users | gitHubGetAuthenticatedUser | Yes |
| Users | gitHubListUserOrgs | Yes |
| Repos | gitHubListRepos | No |
| Repos | gitHubGetRepo | Yes |
| Repos | gitHubCreateRepo | No |
| Repos | gitHubListBranches | Yes |
| Issues | gitHubListIssues | Yes |
| Issues | gitHubGetIssue | No |
| Issues | gitHubCreateIssue | No |
| Issues | gitHubCreateIssueComment | No |
| Pull Requests | gitHubListPullRequests | Yes |
| Pull Requests | gitHubGetPullRequest | No |
| Pull Requests | gitHubCreatePullRequest | No |
| Pull Requests | gitHubMergePullRequest | No |
| Pull Requests | gitHubListPrReviews | No |
| Content | gitHubGetFileContent | No |
| Content | gitHubCreateOrUpdateFile | No |
| Content | gitHubListDirectory | Yes |
| Content | gitHubGetCommit | No |
| Actions | gitHubListWorkflowRuns | Yes |
| Actions | gitHubTriggerWorkflow | No |
| Actions | gitHubGetWorkflowRun | No |
| Search | gitHubSearchCode | Yes |
| Search | gitHubSearchRepos | No |
| Search | gitHubSearchIssues | No |
All 25 tools are injected and available; 9 are called directly by the workflow transitions. The remaining 16 (including all write operations) are available for use but not exercised automatically.
GitHub Agent (gitHubAgent)
An interactive chat agent that gives Claude access to all 25 GitHub tools. The agent can manage repositories, issues, pull requests, browse code, check CI/CD status, search across GitHub, and handle OAuth automatically via the AuthenticateGitHubTask custom tool.
How it works:
- Sets up a hidden system message describing available GitHub capabilities
- Waits for user input via a
wait: truetransition - Sends the conversation to Claude with all 25 GitHub tools plus
authenticateGitHub - If
message.stopReason === 'tool_use', delegates tool calls viaLlmDelegateToolCallsTooland collects results withLlmUpdateToolResultTool - If a tool returns an auth error, the LLM calls
authenticateGitHubwhich launches OAuth - Loops back to wait for the next user message
Agent loop pattern:
@InjectTool({
provider: 'claude',
model: 'claude-sonnet-4-6',
system: `You are a helpful GitHub assistant with access to repository, issue, PR, code, actions,
and search tools. When a tool returns an unauthorized error, use authenticateGitHub
to let the user sign in, then retry. Be concise and format results using markdown.`,
tools: [
'gitHubListRepos', 'gitHubGetRepo', 'gitHubCreateRepo', 'gitHubListBranches',
'gitHubListIssues', 'gitHubGetIssue', 'gitHubCreateIssue', 'gitHubCreateIssueComment',
'gitHubListPullRequests', 'gitHubGetPullRequest', 'gitHubCreatePullRequest',
'gitHubMergePullRequest', 'gitHubListPrReviews',
'gitHubGetFileContent', 'gitHubCreateOrUpdateFile', 'gitHubListDirectory', 'gitHubGetCommit',
'gitHubListWorkflowRuns', 'gitHubTriggerWorkflow', 'gitHubGetWorkflowRun',
'gitHubSearchCode', 'gitHubSearchRepos', 'gitHubSearchIssues',
'gitHubGetAuthenticatedUser', 'gitHubListUserOrgs',
'authenticateGitHub',
],
})
llmGenerateText: LlmGenerateTextTool;
@InjectTool({ provider: 'claude' }) llmDelegateToolCalls: LlmDelegateToolCallsTool;
@Transition({ from: 'ready', to: 'prompt_executed' })
async llmTurn() {
const result: ToolResult<LlmGenerateTextResult> = await this.llmGenerateText.call();
this.llmResult = result.data;
}
@Transition({ from: 'prompt_executed', to: 'awaiting_tools', priority: 10 })
@Guard('hasToolCalls')
async executeToolCalls() {
const result: ToolResult<LlmDelegateResult> = await this.llmDelegateToolCalls.call({
message: this.llmResult!.message,
callback: { transition: 'toolResultReceived' },
});
this.delegateResult = result.data;
}
hasToolCalls(): boolean {
return this.llmResult?.message.stopReason === 'tool_use';
}This is the easiest way to interactively test every GitHub tool — just ask the agent to perform any GitHub operation.
Setup
Environment Variables
Create a GitHub OAuth App at https://github.com/settings/developers and configure:
GITHUB_CLIENT_ID=your-client-id
GITHUB_CLIENT_SECRET=your-client-secret
GITHUB_OAUTH_REDIRECT_URI=http://localhost:5173/oauth/callbackFor the GitHub Agent workflow, you also need an LLM API key:
ANTHROPIC_API_KEY=your-anthropic-api-keyDependencies
@loopstack/common- Core workflow/runtime APIs (BaseWorkflow,@Workflow,@Transition,@Guard,CallbackSchema,ToolResult,LinkDocument,MarkdownDocument,MessageDocument)@loopstack/llm-provider-module- LLM adapter tools (LlmGenerateTextTool,LlmMessageDocument,LlmDelegateToolCallsTool,LlmUpdateToolResultTool)@loopstack/oauth-module- OAuth infrastructure (OAuthWorkflow)@loopstack/github-module- All 25 GitHub tools
About
Author: Jakob Klippel
License: MIT
Additional Resources
- Loopstack Documentation
- Getting Started with Loopstack
- Find more Loopstack examples in the Loopstack Registry