AI Chat Assistant (Claude + GitHub)
Configure the Claude-powered AI chat assistant — proxy setup, streaming, and GitHub issue/PR actions — safely for GitHub Pages.
AI Chat Assistant
A floating chat assistant powered by the Claude Messages API, grounded in the current page’s content. Responses stream token-by-token, and the assistant can take GitHub actions on your repository: file an issue when a visitor reports a problem, or open a pull request that improves the page’s content or UI/UX.
Why Proxy Mode
GitHub Pages cannot run server-side code. If you call the Anthropic API directly from browser JavaScript, your key is exposed in the page source.
The recommended approach is:
- Keep the site static on GitHub Pages.
- Send chat requests to your own proxy endpoint.
- Let the proxy hold the Anthropic key (and GitHub token) server-side.
A ready-to-deploy Cloudflare Worker that does all of this — streaming chat passthrough plus GitHub issue/PR routes — ships with the theme in templates/deploy/chat-proxy/.
Which Anthropic credential does the proxy use?
The proxy can authenticate to Claude two ways (auto-detected from which secrets you set):
- Claude Code connector (OAuth) — use your Claude Code / Claude.ai login
token (
sk-ant-oat…) instead of an API key. Best for a private/personal chat: the proxy sends a Bearer token, refreshes it automatically (KV-cached), and must sit behind Cloudflare Access so only you can use it — the token spends your personal account. See the chat-proxy README for the OAuth setup. - API key — set
ANTHROPIC_API_KEYon the proxy instead. Best for a public site (use a workspace-scoped key with a spend cap).
Configuration
Add this to your production config:
ai_chat:
enabled: true
auth_mode: 'proxy'
proxy_ready: true # widget renders only when this is true
endpoint: '/api/chat' # your proxy's chat route
model: 'claude-opus-4-8'
max_tokens: 1024
strict_context: true
out_of_scope_message: "I can only answer from the content on this page."
github:
enabled: true
mode: 'url' # or 'proxy' — see GitHub Actions below
Important Defaults
auth_mode: 'proxy'is the recommended mode.proxy_ready: falsekeeps the widget hidden unless your proxy is deployed.strict_context: truegrounds answers to the current page; the GitHub tools still work because grounding only restricts how questions are answered.model: 'claude-opus-4-8'— any current Claude model ID works; the proxy template can pin the model server-side so clients cannot change it.
GitHub Actions from the Chat
When ai_chat.github.enabled is true, the assistant gains Claude tools:
| Tool | What it does | Needs |
|---|---|---|
get_page_source |
Reads the page’s raw source from raw.githubusercontent.com so proposed edits are based on the real file |
Public repo, no token |
create_github_issue |
Files an issue when a visitor reports a bug/typo or requests an enhancement | url mode: nothing; proxy mode: server-side token |
create_pull_request |
Opens a PR that updates one source file with improved content or UI/UX | proxy mode only |
Two modes:
mode: 'url'(default, zero-config) — the assistant drafts the issue, the visitor confirms in-chat, and a pre-filledgithub.com/…/issues/newform opens in a new tab. The visitor submits it under their own GitHub account. No token exists anywhere.mode: 'proxy'— the widget calls your proxy’s/api/githubroutes, which use a fine-grained server-side token to create the issue or the branch + commit + pull request directly. The chat shows a link card to the created issue/PR.
Every creation is gated by an explicit confirmation card in the chat — the model can never file anything silently.
ai_chat:
github:
enabled: true
mode: 'proxy'
endpoint: '/api/github'
base_branch: 'main'
default_labels: ['from-chat']
pr_branch_prefix: 'chat/'
GitHub Pages Compatible Deployment Flow
- Deploy the proxy first (see the chat-proxy README).
- Set
proxy_ready: trueandendpointto that proxy URL. - Build and publish your Jekyll site as usual.
jekyll build --config _config.yml
No client-side Anthropic key is required in proxy mode.
Local Development
A static Jekyll site can’t hold a secret, so local dev runs a small dev proxy that executes the same Worker logic on Node and reads your credential from .env:
- Get a long-lived Claude Code token (Claude Pro/Max):
claude setup-token. - Add it to
.env(git-ignored):CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-…(or setANTHROPIC_API_KEYinstead). -
Run it alongside
docker-compose up:node --env-file=.env templates/deploy/chat-proxy/dev-proxy.mjs
_config_dev.yml already points the widget at http://localhost:8787/api/chat, so the chat works at http://localhost:4000 with no Cloudflare or API key in the page. See the chat-proxy README.
How to verify the chat works locally
With docker-compose up and the dev proxy both running:
- Open
http://localhost:4000— a floating button with thebi-roboticon appears bottom-right. (The widget renders only whenai_chat.enabledand a usable auth path exist; in dev that isproxy_ready: truefrom_config_dev.yml.) -
Confirm the proxy is listening:
curl -i http://localhost:8787/api/chat
You should get an HTTP response (a 4xx for the empty/GET body is fine — it proves the route is reachable; only a connection error means the proxy is down).
- Send a message and watch it stream in token-by-token (SSE). If
ai_chat.github.enabledis true, the issue/PR confirmation chips appear in the panel.
Edit the current page from the chat (dev only)
In local development (ai_chat.local_edit: true, set in _config_dev.yml) the assistant can edit the current page’s source file directly: ask it to fix a typo or reword a section, review the change in the confirmation card, and the dev server rebuilds the page live. The dev proxy writes the file through a sandboxed local route — only content files (.md/.html) inside the repo can be edited, and only existing pages (it never creates files). This is off in production: the published site can’t and doesn’t write files. For changes to a deployed site, use the GitHub issue/PR actions above instead.
Optional Direct Mode (browser → Anthropic)
For a quick test without the dev proxy, direct mode sends requests from the browser straight to https://api.anthropic.com/v1/messages using the anthropic-dangerous-direct-browser-access header. The key is visible in the page source — never publish a build with one embedded, and note OAuth tokens are not usable this way (use an API key).
ai_chat:
auth_mode: 'direct'
api_key: 'sk-ant-...'
Put this in _config_secrets_local.yml (git-ignored). It is not loaded by default — the Docker dev loop builds with --config '_config.yml,_config_dev.yml' only. Add the overlay explicitly when you want direct mode locally:
bundle exec jekyll serve \
--config '_config.yml,_config_dev.yml,_config_secrets_local.yml'
Response Quality Features
- Streaming: responses render token-by-token over SSE.
- Strict grounding: answers are constrained to page metadata and content.
- Out-of-scope fallback: a configured fallback message is used when context is missing.
- Safe markdown rendering: assistant output supports a small markdown subset without unsafe HTML execution.
Troubleshooting
Widget does not appear
ai_chat.enabledistrue.- If using proxy mode,
proxy_readyistrue. - Your
endpointis reachable from the browser.
Requests fail in browser
- Proxy endpoint URL is correct and returns CORS headers for your origin
(
ALLOWED_ORIGINSin the worker). - Proxy holds a valid
ANTHROPIC_API_KEY. - Direct mode: the key is valid and the model ID exists (e.g.
claude-opus-4-8).
Issue/PR actions don’t work
ai_chat.github.enabledistrue(and the chips appear in the panel).proxymode: the worker hasGITHUB_TOKEN+GITHUB_REPOSITORYset and the token has Issues/Contents/Pull requests read-write on the repo.urlmode: pop-ups are allowed for your site (the pre-filled form opens in a new tab).
Replies are too generic
strict_contextistrue.context_max_lengthis high enough for your page content.system_promptstill emphasizes page-only grounding.