Lattice MCP Server
Automates Lattice HR objectives (create, update, delete, list) via browser session replay. No API key required — authenticates through SSO and persists the session for headless Playwright reuse.
Prerequisites
- Python 3.10+
- Chrome/Chromium (for SSO login)
- A display environment (local machine or X-forwarded) for initial login
Installation
pip install lattice-mcp
playwright install chromium
Authentication
The server uses a saved Playwright browser session (~/.config/lattice/browser-state.json). You must log in once via SSO to create it.
Option A: Local machine with a display
lattice ui login [--hostname <your-company>.latticehq.com]
If --hostname is omitted, it falls back to the LATTICE_WEB_HOSTNAME environment variable (see Configuration).
A Chromium window opens — complete your SSO login. The window closes automatically once authenticated and the session is saved. The session is reusable until it expires on Lattice's side (typically days to weeks).
Option B: Headless server (attach to running Chrome)
# On a machine with a display, start Chrome with remote debugging:
google-chrome --remote-debugging-port=9223 --user-data-dir=/tmp/chrome-lattice --no-first-run &
# Complete SSO in that browser, then capture the session:
lattice ui login --cdp-url http://127.0.0.1:9223
Option C: Silent headless re-login (after first login)
lattice ui login --headless
Reuses the stored browser state: even if the Lattice session has expired, the saved IdP session cookies (e.g. Microsoft Entra) usually allow the SSO to complete silently — no window, no password, no MFA. Works until the IdP's own session expires (often weeks), then fall back to Option A/B. Whether the silent hop is permitted depends on your IdP tenant's conditional-access policies.
Session expiry
If tools return "Session expired", re-run lattice ui login — or let the agent call the lattice_ui_login MCP tool, which tries the silent headless refresh first and only opens a login window if that fails (you complete the SSO yourself).
Configuration
All configuration is via environment variables — no code changes needed to point at your own Lattice tenant:
| Variable | Purpose | Default |
|---|---|---|
LATTICE_WEB_HOSTNAME |
Your Lattice tenant, e.g. acme.latticehq.com |
c3.latticehq.com |
LATTICE_USER_ENTITY_ID |
Your Lattice user entity UUID — the default owner for lattice_objectives |
unset (tools require an explicit owner_id) |
LATTICE_CONFIG_DIR |
Where credentials and the browser session are stored | ~/.config/lattice |
lattice ui login also accepts --hostname directly (takes precedence over the env var). The hostname is not saved with the session — the MCP server re-reads LATTICE_WEB_HOSTNAME on every call, so set it wherever the server is launched (see below).
To find your user entity ID: open any Lattice page filtered to your objectives and copy the UUID from the URL (ownerEntityIdsFilter=...), or inspect a GraphQL response in your browser's devtools.
Notifications (ntfy)
Cron jobs and agents can push alerts to your phone via ntfy:
lattice notify --setup # generate your personal topic + show subscribe info (QR)
lattice notify --test # send a test push
lattice notify "message" --title "optional title"
lattice notify --show # re-print topic / URL / QR anytime
The first use generates a per-user topic like lattice-<user>-<random12> and stores it in ~/.config/lattice/config.json. The random suffix is the secret — on public ntfy servers the topic name is the only access control, so don't shorten it or share it.
A successful manual lattice ui login shows the subscription info (topic, URL, QR) automatically, so new users see it at onboarding without a separate step. The ASCII QR is also saved to ~/.config/lattice/ntfy-qr.txt for when the terminal output isn't usable (e.g. agent-mediated setup — open the file in any editor and scan it). If a send auto-generates a topic (nothing configured yet), it prints a warning that nobody is subscribed.
Overrides (env beats config file):
| env | config.json key | default | |
|---|---|---|---|
| Topic | LATTICE_NTFY_TOPIC |
ntfy_topic |
generated on first use |
| Server | LATTICE_NTFY_SERVER |
ntfy_server |
https://ntfy.sh |
Install the qr extra (pip install lattice-mcp[qr]) for a scannable terminal QR code during setup:
$ lattice notify --show
Topic: lattice-example-abc123xyz789
URL: https://ntfy.sh/lattice-example-abc123xyz789
█▀▀▀▀▀▀▀████▀▀▀██▀▀▀▀▀█▀▀██▀▀▀▀▀▀▀█
█ █▀▀▀█ █▀▄▀▄█ ▄ █ ▀█▄█▄▀▄█ █▀▀▀█ █
█ █ █ █ █ ▄ ▀▄ ▄█▀▄▄ ▄▄ █ █ █ █
█ ▀▀▀▀▀ █ ▄▀▄ █ ▄ ▄ █ █▀█▀█ ▀▀▀▀▀ █
█▀█▀▀▀▀▀█▄█ █▄▀▀ ▀▀███▄ ▀ █▀▀▀▀▀███
█ ▄█ ▄ ▀▄ ▀█ ██▀ █ ▄▄ █▄▄█ ▄▀▄ ▀▄█
█▄▄█▄█▀▀ ▀▄ █▄▄▄█▀█▄ ▄▀▄▀██ ▄▄██
█ ▄▀ ▄ ▀ █ ▀ ▀▀ ▄▄██▀▄▀▄▄ █▀█▄ ▄█
█▀██▀ ▀▀▀▄▄ █▄ █▄▀▀ ▄█▀▄ ▄ ▀▄▄█▀█
█▄█▀▄ ▄▀ ▀ █▄▄▀▀ ▄▄▄█▀▄▀█ ▀▄ ▀▄█
█▀█▄ ▄▀▀ ▄▄▀█ █ ▀▄▄█ ▀▀▄█ █▄ ▄ ▀█
█ █▄ ▀ ▀ ▀▄▀ ▄█ ▀▄▄█▀█▀▄▄ ▀▀▄▀▄█
█ █▀ ▀ ▀▀▄▄▄ ▀█ █▀██▀▄ ▄▀▀ ▀▀▄▀▀█
█▀▀▀▀▀▀▀█▄▀▄█ ▄▀▄█ ▄ █▀ █▀█ █ █▄█
█ █▀▀▀█ █ ▀███▄ ▄██▀██ ▄▄ ▀▀▀ ▄ ██
█ █ █ █ ▄▄█▀█▄▄██▀▄▀ ▀ ▀▀▄▀ █▄█
█ ▀▀▀▀▀ █▀ ██ ▀█ ▀█ ▄▄▀▄▀█ ▀▄▀▄▀██
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
Scan with your phone to subscribe in the ntfy app.
MCP Server Setup (Claude Code)
Add to your project's .mcp.json:
{
"mcpServers": {
"lattice": {
"command": "lattice-mcp",
"env": {
"LATTICE_WEB_HOSTNAME": "<your-company>.latticehq.com",
"LATTICE_USER_ENTITY_ID": "<your-user-entity-uuid>"
}
}
}
}
Restart Claude Code to load the server. The tools appear as lattice_* in your session.
Available Tools
| Tool | Description |
|---|---|
lattice_session_status |
Check if the browser session is active |
lattice_ui_login |
Open a browser window for SSO login (requires a display; the user completes the login) |
lattice_notify |
Send a push notification to the user via ntfy |
lattice_objectives |
List active objectives for a user (defaults to you) |
lattice_create_objective |
Create a new objective (title, optional priority/due date) |
lattice_update_objective |
Post a status update + comment to an objective; pass close=True to mark it complete |
lattice_delete_objective |
Delete an objective by entityId |
lattice_scrape |
Scrape any Lattice page and return visible text |
Example usage (via Claude Code)
> list my lattice objectives
> create a lattice objective titled "Ship feature X"
> update objective <entityId> status green comment "Merged PR, deploying tomorrow"
> delete objective <entityId>
Scheduled weekly updates (cron)
Write a prompt file (e.g. ~/.config/lattice/weekly-update.md):
Post my weekly Lattice objective updates (it's Friday).
1. Call lattice_objectives to list my active objectives.
2. For each objective, call lattice_scrape on /goals/<entityId> to read its current state.
3. For each objective, compose a brief weekly status comment (2-4 sentences) continuing the
narrative from the latest note. Keep the current status color.
Do NOT invent accomplishments or metrics not present in the existing notes.
4. Post each comment with lattice_update_objective.
5. Call lattice_notify with a summary of what was posted.
6. If any tool returns "Session expired": do NOT attempt login (nobody may be at the machine).
Instead call lattice_notify with "Lattice weekly update FAILED — session expired. Run: lattice ui login"
and stop.
Add a cron entry (crontab -e):
47 7 * * 5 cd /path/to/your/project && claude -p "$(cat ~/.config/lattice/weekly-update.md)" --allowedTools "mcp__lattice__*" >> ~/lattice-weekly.log 2>&1
See Scheduled Automation below for prerequisites, session maintenance, and the Jira cross-referencing variant.
Multi-server workflow (with Jira MCP)
If you also have a Jira MCP server in your session, you can chain them:
> fetch PLAT-0000 and PLAT-0001 from jira, then create lattice objectives from their summaries
> close any lattice objectives whose jira tickets are done
Claude calls the Jira server to check ticket status, then calls lattice_update_objective with close=True to mark completed objectives — no glue code needed.
Setting up the Jira MCP server
We use mcp-atlassian — an actively maintained MCP server covering Jira + Confluence (~60 tools including JQL search, issue CRUD, transitions, comments).
- Install:
pipx install mcp-atlassian
-
Generate a Jira API token at https://id.atlassian.com/manage-profile/security/api-tokens
-
Create a wrapper script (avoids putting the token in
.mcp.json):
cat > ~/.local/bin/jira-mcp << 'EOF'
#!/bin/bash
export JIRA_URL="https://<your-company>.atlassian.net"
export JIRA_USERNAME="<your-email>"
export JIRA_API_TOKEN="<your-api-token>"
exec mcp-atlassian "$@"
EOF
chmod +x ~/.local/bin/jira-mcp
- Add to your project's
.mcp.jsonalongside the lattice server:
{
"mcpServers": {
"lattice": {
"command": "lattice-mcp",
"env": {
"LATTICE_WEB_HOSTNAME": "<your-company>.latticehq.com",
"LATTICE_USER_ENTITY_ID": "<your-user-entity-uuid>"
}
},
"jira": {
"command": "jira-mcp"
}
}
}
- Restart Claude Code — the tools appear as
jira_*andlattice_*in your session.
Note: Pin
mcp-atlassianto a major version if you hit dependency issues (e.g.pipx runpip mcp-atlassian install "mcp>=1.0,<2.0"if themcp2.0 breaking change bites).
Scheduled Automation (cron)
You can run headless Claude on a schedule to automate recurring Lattice tasks — e.g. posting weekly objective updates sourced from Jira tickets every Friday.
Prerequisites
- Claude Code CLI installed and authenticated on the machine
lattice-mcpinstalled (pipx install lattice-mcp[qr]+playwright install chromium)- Initial login done once:
lattice ui login - ntfy set up:
lattice notify --setup(scan QR on your phone) - Your project's
.mcp.jsonincludes thelatticeserver (andjiraif you want cross-referencing)
Example: weekly objective update (with Jira cross-referencing)
- Write a prompt file (e.g.
~/.config/lattice/weekly-update.md):
Post my weekly Lattice objective updates (it's Friday).
1. Call lattice_objectives to list my active objectives.
2. For each objective, call lattice_scrape on /goals/<entityId> to read its current state.
3. Fetch my in-progress Jira tickets with jira_search (assignee = currentUser(), status = "In Progress").
4. For each objective, compose a brief status comment (2-4 sentences) referencing relevant Jira progress.
5. Post each comment with lattice_update_objective, keeping the current status color.
6. Call lattice_notify with a summary of what was posted.
7. If any tool returns "Session expired": do NOT attempt login (nobody may be at the machine).
Instead call lattice_notify with "Lattice weekly update FAILED — session expired. Run: lattice ui login"
and stop.
- Add a cron entry (
crontab -e):
47 7 * * 5 cd /path/to/your/project && claude -p "$(cat ~/.config/lattice/weekly-update.md)" --allowedTools "mcp__lattice__*,mcp__jira__*" >> ~/lattice-weekly.log 2>&1
cd /path/to/your/project— must contain the.mcp.jsonthat registers lattice (and jira)--allowedTools— restricts Claude to only MCP tools (no shell access needed)- Friday 7:47 AM — adjust to run before your company's bot checks for updates
Session maintenance
The Lattice session expires after hours/days, but silent headless re-login recovers it automatically whenever you (or an agent) next use a lattice tool interactively. The stored IdP cookies (e.g. Microsoft Entra, ~90-day rolling window) allow SSO to complete without interaction.
As long as you use Lattice at least once every ~80 days (the weekly cron counts — any interactive session triggers a re-login if needed), the chain stays warm indefinitely. If the IdP session eventually expires, you'll get an ntfy alert telling you to run lattice ui login once.
Without Jira
The standalone prompt and cron entry shown in Scheduled weekly updates above works without Jira — no extra setup needed.
How It Works
- Tools launch headless Chromium with the saved session cookies
- Navigate to the relevant Lattice page
- Interact with the UI (fill forms, click buttons) via Playwright
- GraphQL mutations fire as a side-effect of the UI interaction
- Cloudflare passes because the session includes valid clearance cookies
There is no direct API access — Lattice does not issue API keys to non-admins. See DESIGN.md for the full decision log.
Troubleshooting
| Problem | Fix |
|---|---|
| "No browser session" | Run lattice ui login |
| "Session expired" | Re-run lattice ui login |
| Cloudflare blocks (403) | Session stale — re-login to get fresh cf_clearance cookies |
| Tool times out | Lattice page may be slow; try again |
| Create succeeds but objective not visible | Check the "All time" filter on /goals — it may default to current quarter |
Metadata
Release files for lattice-mcp 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lattice_mcp-1.0.0.tar.gz | 29.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lattice_mcp-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 52.8 kB
Release files / lattice_mcp-1.0.0.tar.gz
| Download URL | lattice_mcp-1.0.0.tar.gz |
|---|---|
| Size | 29.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
399828811dd92e0605226f55e341b6f17377c70960667825fbab2981f1980ba2
|
|
BLAKE2b-256 checksum How to use checksums |
413b3d390a4682d3272e18750a8943717736887270c1e0c515fcc82de0b47c80
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / lattice_mcp-1.0.0-py3-none-any.whl
| Download URL | lattice_mcp-1.0.0-py3-none-any.whl |
|---|---|
| Size | 23.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
767f3289262baf3300557da45bf753cfb63d1ba5c6ce4d87fe127f25b5f7ae51
|
|
BLAKE2b-256 checksum How to use checksums |
b76fb26e37cd5243798848a94d607c1a25f92d41a9851be076bf54e9c9aa51f9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|