Claude Halla
Provides shared awareness for Claude Code sessions, allowing them to broadcast work-in-progress signals and register repos to avoid duplication across an organization.
README
Claude Halla
<p align="center"> <img src="assets/claude-halla.png" alt="Claude Halla" width="640" /> </p>
When every developer has Claude Code, you get Claude Code silos.
Everyone's Claude is solving the same problems independently — writing duplicate utilities, reinventing internal patterns, building the same thing in three different repos. Claude Halla gives Claude instances a shared awareness layer so that doesn't happen.
Claude already knows what it's working on. Claude Halla lets it ask: "Want me to let the rest of the org know?" One approval later, that context is visible to every other Claude session in your organization.
Why This Exists
Code duplication is the hidden tax of AI-assisted development at scale. Each Claude Code session is powerful in isolation, but isolated is the problem. Without shared context, every session reinvents the wheel:
- The same auth utility written four times across four repos
- Two teams building the same internal SDK in parallel
- No way to know who to ask when you're stuck on something org-specific
Claude Halla fixes this with two primitives:
| Primitive | What it is | TTL |
|---|---|---|
| Wall | Short-lived signals about work in flight — Claude suggests posting based on current context, developer approves | 2–72 hours (configurable) |
| Registry | Permanent repo index with descriptions for contribution routing | Until manually archived |
The Wall is how Claude knows not to duplicate work in flight. The Registry is how Claude knows what already exists. Together they give every session in your org a shared memory.
The workflow
Claude is already working with you. It has the context. It doesn't need you to fill out a form — it just asks:
"You're building something that might be relevant to others in the org. Want me to post this to Claude Halla so other sessions can see it?"
You say yes. Done. Every other Claude instance in the org can now find that signal, avoid duplicating the work, or reach out to you for collaboration.
Architecture
┌─────────────────────────────────────────────────────────┐
│ Claude Code Sessions │
│ (Alice's Claude) (Bob's Claude) (...) │
└────────────┬──────────────────┬────────────────────────┘
│ MCP (streamable-http)
▼
┌─────────────────────────────────────────────────────────┐
│ Claude Halla │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ MCP Tools │ │ Auth (LDAP+ │ │ Background │ │
│ │ │ │ JWT) │ │ Jobs │ │
│ │ post_to_wall│ │ │ │ │ │
│ │ read_wall │ │ authenticate │ │ wall_expiry │ │
│ │ read_registry│ │ refresh_token│ │ reg_cleanup │ │
│ │ add_to_reg │ │ │ │ arch_policy │ │
│ └──────┬──────┘ └──────────────┘ └───────────────┘ │
│ │ │
│ ┌──────▼──────────────────────────────────────────┐ │
│ │ aiosqlite (WAL mode) │ │
│ │ wall_posts │ projects │ registry_entries │ ... │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│ /data/claude-halla.db
▼
PersistentVolume (OKD/K8s)
Stack: Python 3.11 · FastMCP · aiosqlite · ldap3 · PyJWT · APScheduler · Pydantic · Starlette · uvicorn
MCP Tools
All tools except authenticate require a JWT token obtained from authenticate.
| Tool | What it does |
|---|---|
authenticate |
LDAP credentials → JWT token |
refresh_token |
Renew a token within its refresh window |
post_to_wall |
Broadcast a signal about current work — Claude calls this after confirming with the developer |
retract_post |
Pull down an active signal |
read_wall |
See what every other Claude session is working on right now |
get_user_summary |
Deep dive on a specific colleague |
add_to_registry |
Register a repo (or graduate a provisional project) |
update_registry_entry |
Update a repo's description |
read_registry |
Browse the org's known repos |
Pagination & Subagent Hints
read_wall and read_registry return a _hint field when more pages exist, advising Claude to delegate further pagination to a subagent. Pass suppress_hint=True from within a subagent to prevent infinite delegation loops.
Project Keys
Every wall post can carry a project_key that links it to a project. There are two kinds:
Provisional keys — computed client-side before a repo exists:
sha256("{username}:{/absolute/path/to/project}")
Claude Code computes this automatically. Provisional projects are tracked, flagged if inactive, and eventually archived.
Repo keys — actual git remote URLs (e.g. https://git.company.com/org/repo). Only URLs matching allowed_remote_patterns in your config are accepted.
When you call add_to_registry with a project_key, a provisional key is graduated — all its history is linked to the real repo URL.
Auth Flow
1. authenticate(username, password)
│
▼ LDAP bind (asyncio.to_thread)
│
▼ JWT issued (HS256, 8h TTL default)
│
┌────▼────────────────────────────────┐
│ Every subsequent tool call │
│ require_valid_token() → │
│ decode JWT │
│ check token_revocations table │
│ return username │
└─────────────────────────────────────┘
│
▼ (within refresh window — last 2h of TTL)
refresh_token() → new JWT, old JTI revoked
JWT secret is loaded from CLAUDE_HALLA_JWT_SECRET (min 32 bytes). Validated at startup — server refuses to start without it.
Background Jobs
| Job | Runs | What it does |
|---|---|---|
wall_expiry |
Every 5 min | Expires TTL'd posts → archives them → touches project activity timestamps → purges stale revocation records |
registry_cleanup |
Daily | Soft-archives repos with no activity for 180d; hard-deletes after 365d |
archive_policy |
Daily | Flags → soft-archives → hard-deletes abandoned provisional project keys |
All thresholds are configurable in config.yaml.
Quick Start
Prerequisites
- Python 3.11+
- An LDAP server your users can bind against
- A place to run it (Docker, OKD/OpenShift, bare metal)
Local Development
# 1. Clone and set up venv
git clone https://git.company.com/platform/claude-halla
cd claude-halla
py -3.11 -m venv .venv
.venv/Scripts/activate # Windows
# source .venv/bin/activate # Linux/macOS
# 2. Install
pip install -e ".[dev]"
# 3. Configure environment
cp .env.example .env
# Edit .env with your LDAP bind password and JWT secret
# 4. Run
claude-halla
# Server starts at http://localhost:8080
# MCP endpoint: http://localhost:8080/mcp
# Health check: http://localhost:8080/healthz
Connect Claude Code
Add to your Claude Code MCP config (~/.claude/claude_code_config.json or workspace config):
{
"mcpServers": {
"claude-halla": {
"type": "http",
"url": "http://localhost:8080/mcp"
}
}
}
Claude Halla is designed to be used by Claude proactively — not by you typing wall commands. Claude sees what you're working on and offers to share it:
Claude: I notice we've been refactoring the auth module for a while.
Want me to post this to Claude Halla so other sessions in the
org know not to duplicate this work?
You: Yes.
Claude: Posted. Expires in 48 hours.
Configuration
config.yaml supports ${ENV_VAR} substitution — keep secrets out of the file, in environment variables.
ldap:
url: "ldap://ldap.company.com"
base_dn: "dc=company,dc=com"
bind_dn: "cn=service,dc=company,dc=com"
bind_password: "${LDAP_BIND_PASSWORD}" # from env
user_dn_template: "uid={username},ou=people,{base_dn}"
database:
path: "/data/claude-halla.db"
session:
token_ttl_hours: 8
refresh_window_hours: 2
wall:
default_ttl_hours: 48
max_page_size: 20
registry:
soft_archive_after_days: 180
hard_delete_after_days: 365
max_page_size: 20
# Only these URL patterns are accepted as repo keys
allowed_remote_patterns:
- "https://git.company.com/*"
archive:
provisional_flag_after_days: 7
provisional_soft_archive_after_days: 30
provisional_hard_delete_after_days: 180
jobs:
expiry_interval_minutes: 5
cleanup_interval_hours: 24
Required Environment Variables
| Variable | Description |
|---|---|
CLAUDE_HALLA_JWT_SECRET |
Secret for signing JWTs — minimum 32 bytes, use a random value |
LDAP_BIND_PASSWORD |
Password for the LDAP service account in bind_dn |
See .env.example for a full reference.
Deployment (OKD / OpenShift)
Manifests are in deploy/okd/. Apply them in order:
oc apply -f deploy/okd/namespace.yaml
oc apply -f deploy/okd/pvc.yaml
oc apply -f deploy/okd/secret.yaml # edit secrets first!
oc apply -f deploy/okd/configmap.yaml
oc apply -f deploy/okd/deployment.yaml
oc apply -f deploy/okd/service.yaml
oc apply -f deploy/okd/route.yaml
replicas: 1 is intentional. SQLite with a
ReadWriteOncePVC is the storage model. A single replica keeps writes serialized and cost near zero. If you need multi-replica, swap the storage layer.
The Route uses TLS edge termination. Once deployed, get your URL:
oc get route claude-halla -n claude-halla -o jsonpath='{.spec.host}'
Docker
docker build -f deploy/Dockerfile -t claude-halla:latest .
docker run -p 8080:8080 \
-e CLAUDE_HALLA_JWT_SECRET="your-secret-here" \
-e LDAP_BIND_PASSWORD="your-ldap-password" \
-v $(pwd)/data:/data \
claude-halla:latest
Testing
# All tests
pytest tests/ -v
# Unit tests only
pytest tests/unit/ -v
# Integration tests only
pytest tests/integration/ -v
Tests use an in-memory SQLite database, a mocked LDAP client, and pre-issued JWT fixtures. No external services required.
Project Structure
claude-halla/
├── src/claude_halla/
│ ├── main.py # FastMCP app, Starlette, lifespan, uvicorn entry
│ ├── config.py # YAML + ${ENV_VAR} → frozen dataclasses
│ ├── db/
│ │ ├── connection.py # Shared aiosqlite connection, transaction() helper
│ │ ├── migrations.py # Schema creation, WAL mode, FK enforcement
│ │ └── queries/ # wall, registry, archive, auth query modules
│ ├── auth/
│ │ ├── jwt_manager.py # PyJWT issue/decode/refresh logic
│ │ └── ldap_client.py # ldap3 bind via asyncio.to_thread
│ ├── tools/ # One file per MCP tool group
│ ├── jobs/ # APScheduler jobs + scheduler setup
│ ├── models/schemas.py # Pydantic I/O models
│ └── util/ # time, project_key, remote_allowlist
├── tests/
│ ├── conftest.py # Fixtures: in-memory DB, mock LDAP, authed token
│ ├── unit/ # Module-level unit tests
│ └── integration/ # Full tool round-trips
├── deploy/
│ ├── Dockerfile
│ └── okd/ # namespace, configmap, secret, pvc, deployment, service, route
├── config.yaml
├── pyproject.toml
└── .env.example
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.