touch-grass
This server acts as a context-aware outdoor break companion for AI coding agents, helping encourage users to step away from their screens by tracking conditions, suggesting activities, and logging habits.
check_grass_conditions– Fetch live weather (temperature, conditions), minutes until sunset, golden-hour status, and streak state using IP-based geolocation (cached 24h) and open-meteo.com data.suggest_activity– Get a situation-appropriate outdoor activity recommendation (e.g., "☀️ short walk" or "🌅 catch the sunset") based on current weather, temperature, and time until sunset.log_touch_grass– Record a confirmed outdoor break, updating the user's streak, total touch count, and session history in~/.touch-grass/state.json. Accepts an optional activity label.get_stats– Read raw local stats (current streak, longest streak, total touches, last touched date, session count) without any network calls.
🌿 touch-grass
The Claude Code plugin that reminds you to go outside.
Weather-aware · sunset-aware · session-aware · never interrupts your flow.
Install · How it works · Tools · Privacy · Site
Why this exists
Pomodoro timers interrupt your flow. Calendar blocks get ignored. The best time to step away from your editor is a moving target that depends on the weather, the time of day, your streak, and what you're in the middle of — none of which a dumb interval timer knows.
Your AI coding agent already knows when you hit a natural pause. It finished the feature. It's waiting for you to answer a question. The tests went green. That's the right moment to nudge — not every 25 minutes on the dot.
touch-grass turns your agent into a context-aware break buddy. A SessionStart hook feeds it live weather and sunset timing at the top of every session. A skill teaches it tiger-mom tone (warm, specific, never preachy). An MCP server lets it log when you actually went outside. You keep your flow. It keeps you honest.
Related MCP server: Apple Health MCP
What it does
Like a Pomodoro timer, but it knows the weather, your sunset time, and your coding streak — and it talks to your AI agent instead of interrupting you.
SessionStart hook injects live weather, sunset timing, and streak state into your agent's context every time a Claude Code session starts.
MCP server exposes four tools your agent (Claude Code, Cursor, Claude Desktop, Codex) can call on demand.
Skill teaches the agent when to nudge, when to stay quiet, and what tone to use (tiger mom, not preachy).
It's a Claude Code pomodoro replacement for people who'd rather have their coding agent tell them to go outside than have a screen-blocking timer break their flow.
Install
Claude Code (full experience — hook + MCP + skill):
/plugin install nalediym/touch-grassThat's it. Open a new session — the hook fires, the context drops in, your agent takes it from there.
git clone https://github.com/nalediym/touch-grass
cd touch-grass/plugin/mcp-server && npm installThen add to your client's MCP config:
{
"mcpServers": {
"touch-grass": {
"command": "node",
"args": ["/absolute/path/to/touch-grass/plugin/mcp-server/index.mjs"]
}
}
}You get the four MCP tools but lose the SessionStart hook, which is Claude Code specific. Your agent will only bring up grass when you explicitly ask about it.
How it works
flowchart LR
A[Claude Code<br/>session starts] --> B[SessionStart hook fires]
B --> C[ip-api.com<br/>location]
B --> D[open-meteo.com<br/>weather + sunset]
B --> E[~/.touch-grass/state.json<br/>streak + sessions]
C --> F[Context injection]
D --> F
E --> F
F --> G[Agent<br/>decides when to nudge]
G -.calls.-> H[MCP tools]
H --> EOn session start, a hook script runs. It detects your location from your public IP (cached 24h), fetches current weather and sunset time from open-meteo, reads your local streak file, and synthesises a short context block for the agent. The agent reads it, sits on it, and at a natural pause — feature done, bug fixed, waiting for input — nudges you outside with language that matches the actual conditions.
When you confirm you went outside, the agent calls log_touch_grass via the MCP server, which increments your streak in the local state file.
MCP tools
Tool | Purpose | Returns |
| Weather, temperature, minutes until sunset, and the user's streak state. Decision context. | JSON block with |
| Random activity recommendation, weighted by time of day. Golden hour gets sunset-specific suggestions. | Plain text like |
| Records that the user went outside. Updates their streak. Call only when confirmed. | Confirmation with new streak count |
| Raw session telemetry and streak history. | JSON block |
All four are callable from any MCP-compatible agent. In Claude Code, the agent mostly uses them via the context the hook injects — you rarely need to call them manually.
Example prompts
The plugin works ambiently, but these phrasings work well if you want to bring it up yourself:
"Should I touch grass right now?"
"What's my streak?"
"Remind me to go outside before sunset."
"I just went for a walk, log it."
"Is it nice out?"
Privacy
Everything is local-first.
Stored on your machine:
~/.touch-grass/state.json(streak, session counts, last touched date) and~/.touch-grass/config.json(cached location, weather threshold).Leaves your machine: your public IP is sent to
ip-api.comonce every 24 hours to resolve city coordinates, and those coordinates are sent toapi.open-meteo.comon each session start to get weather and sunset.Never leaves your machine: your streak, your activity notes, your coding schedule, your prompts, anything from your Claude Code session.
No accounts. No API keys. No telemetry. No analytics. No auth. If you want to disable network access entirely, pin "location" manually in ~/.touch-grass/config.json and the IP lookup never fires.
Configuration
{
"location": {
"lat": 40.7128,
"lon": -74.0060,
"city": "New York",
"timezone": "America/New_York",
"fetchedAt": 9999999999999
},
"niceWeatherThresholdC": 15,
"breakIntervalHours": 2,
"enabled": true,
"customActivities": [
{ "label": "walk to the corner store", "emoji": "🛒" },
{ "label": "sit on the fire escape", "emoji": "🪜" }
]
}Key | Default | Description |
| auto (ip-api) | Pin to a specific location. Set |
|
| Temperature (°C) below which weather isn't considered "nice." |
|
| After this many hours of continuous coding, nudges get firmer. |
|
| Set to |
|
| Replace the default activity list with your own. Each entry is |
Troubleshooting
Run the hook by hand to confirm it works:
node plugin/hooks/session-start.mjsYou should see a JSON object with hookSpecificOutput.additionalContext containing the nudge. If additionalContext is an empty string, the hook couldn't reach ip-api.com or open-meteo.com — check your network. If the hook is fine but Claude Code never calls it, the plugin isn't wired up — re-run /plugin install nalediym/touch-grass in a fresh session.
Check ~/.touch-grass/state.json. The sessionStart, lastSessionStart, and totalCodingSessions fields update every time the hook runs. If sessionStart is older than your most recent claude invocation, the hook isn't running.
Yes. Pin your location manually in ~/.touch-grass/config.json:
{
"location": {
"lat": 40.7128,
"lon": -74.0060,
"city": "New York",
"timezone": "America/New_York",
"fetchedAt": 9999999999999
}
}The high fetchedAt timestamp prevents the 24h cache from expiring, so the IP lookup never fires. Weather will still be fetched from open-meteo.com.
Set "enabled": false in ~/.touch-grass/config.json. The hook still writes session telemetry but emits empty context, so the agent stops seeing grass reminders.
Delete or edit ~/.touch-grass/state.json. The file will be recreated on the next session with defaults.
/plugin uninstall touch-grass
rm -rf ~/.touch-grassThe ~/.touch-grass directory only holds your state and cached location — safe to delete.
Development
git clone https://github.com/nalediym/touch-grass
cd touch-grass/plugin/mcp-server && npm install
# Test the hook directly (outputs JSON context)
node ../hooks/session-start.mjs
# Test the MCP server with the inspector
npx @modelcontextprotocol/inspector node ./index.mjsThree moving parts:
plugin/hooks/session-start.mjs— zero-dep Node script. Called by Claude Code on session start. Outputs JSON to stdout.plugin/mcp-server/index.mjs— MCP server. Uses@modelcontextprotocol/sdk. stdio transport.plugin/lib/grass.mjsandplugin/lib/nudge.mjs— shared logic (weather, sunset, state, nudge text).
The hook and the MCP server read from the same state file, so they stay in sync.
Related
open-meteo.com — free, keyless weather API
ip-api.com — free, keyless IP geolocation
License
MIT · Built in the shade.
Support
touch-grass is free and MIT-licensed. If it's helped you step away from the screen, consider supporting it:
Support on Polar → — $9 one-time. Your name goes in the supporters list, you get priority issues, and you unlock custom activity lists in config.
Supporters
Be the first. Support on Polar →
Available Tools
4 toolscheck_grass_conditionsARead-onlyIdempotent
Returns the user's current outdoor context: approximate city/region (IP-cached 24h), live weather code + temperature, minutes until sunset, golden-hour flag, and current streak. Latency ~200–600ms.
Side effects: outbound HTTPS to ip-api.com (location, cached to ~/.touch-grass/state.json) and open-meteo.com (weather + sunset, no key). Reads state.json; never mutates streak fields.
When to use: once per session before deciding whether to nudge the user outside. The plugin's SessionStart hook already injects this context — read that block first.
When NOT to use: don't poll repeatedly; conditions change on the order of minutes. For streak-only data without a network call, use get_stats.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses side effects: outbound HTTPS to ip-api.com and open-meteo.com, caching, latency 200-600ms, and that it never mutates streak fields. This adds value beyond annotations (readOnlyHint, idempotentHint) which already indicate safe behavior. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Description is concise, well-structured, and front-loaded. Every sentence provides value: main output, side effects, latency, usage guidance, and alternatives.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although there is no output schema, the description details the return fields. Combined with annotations covering safety, the description is complete for a read-only tool with no parameters.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Input schema has 0 parameters, so schema coverage is 100%. Description adds no parameter info, which is acceptable since there are none. Baseline 4 for zero parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns the user's outdoor context including city/region, weather, sunset, golden-hour, and streak. It uses specific verbs and lists resources, distinguishing itself from sibling get_stats which is for streak-only without network calls.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly provides when to use ('once per session before deciding whether to nudge the user outside') and when not to use ('don't poll repeatedly; conditions change on the order of minutes'). References alternative tool get_stats for streak-only data and mentions the SessionStart hook.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_statsARead-onlyIdempotent
Read-only. Returns the raw contents of ~/.touch-grass/state.json: streak, longestStreak, totalTouches, lastTouchedDate, sessionCount, and the cached location/weather block. No network calls, no streak mutation. No auth required.
When to use: when the user asks about their streak/totals, or when you need stats but explicitly want to skip the weather lookup.
When NOT to use: if you also need current weather/sunset, prefer check_grass_conditions — it returns the same streak fields plus live conditions in one call.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds context beyond annotations: no network calls, no mutation, no auth required, and specifics about the file source and returned fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Concise three sentences with clear structure: purpose, return contents, and usage guidance. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given zero parameters and no output schema, the description fully explains what the tool returns and when to use it, with sibling references for context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
No parameters exist; description adds no param info but correctly describes the return value, fulfilling the lack of schema parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states it returns the raw contents of a specific file with listed fields, and distinguishes from sibling check_grass_conditions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly provides 'When to use' and 'When NOT to use' sections, naming an alternative tool for different needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
log_touch_grassA
Writes to local state. Records that the user went outside, increments total touches, and either extends or resets the daily streak based on the gap since the last entry. Mutates ~/.touch-grass/state.json (streak, longestStreak, totalTouches, history). NOT idempotent — each call adds an entry. Errors (e.g., disk full, permission denied on ~/.touch-grass) surface as structured tool errors with isError=true; the streak is not partially updated on failure.
Side effects: append-only file write. No network calls. No auth required. The mutation is local-only and persists across sessions.
When to use: ONLY after the user explicitly confirms they went outside (e.g., 'I just got back from a walk', 'done — touched grass'). Never on speculation.
When NOT to use: never call to 'test' the tool — every invocation permanently inflates the user's streak/totals and there is no built-in undo. Don't call when the user is merely planning to go outside; wait for confirmation.
| Name | Required | Description | Default |
|---|---|---|---|
| activity | No | Free-form label describing what the user just did outside (e.g., 'walk', 'coffee on porch', 'stretched'). Stored verbatim as the `activity` field of this streak entry in ~/.touch-grass/state.json — it becomes the human-readable record of THIS recording action. Optional; defaults to 'outside time'. Has no effect on streak math; purely descriptive metadata for history readback. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes beyond annotations by detailing non-idempotency, error handling, side effects (append-only local file write, no network), and persistence. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well-structured with sections for action, side effects, and usage guidelines. Every sentence adds value, though could be slightly more compact without losing clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers all essential aspects: what it does, side effects, error handling, usage context, and file mutation details. No output schema but return values are implied by history mutation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers the single optional parameter fully with description of its role as descriptive metadata. Tool description does not add extra parameter context beyond schema, which has 100% coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool logs a grass-touching session, increments total touches, and adjusts streak. It distinguishes itself from siblings like check_grass_conditions and get_stats by focusing on recording user action.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use (only after user confirms) and when not to use (never for testing, planning, or speculation). Provides clear context for appropriate invocation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
suggest_activityARead-only
Read-only. Returns a single context-appropriate outdoor activity (e.g., '☀️ short walk', '🌅 catch the sunset') filtered by current weather, temperature, and time until sunset. Picks deterministically per call from a small curated list — calls in quick succession may return the same suggestion.
Side effects: internally calls check_grass_conditions, so the same outbound HTTPS calls (ip-api, open-meteo) and the same 24h location cache write apply. No streak mutation. No auth required.
When to use: after you've decided to nudge the user — gives you something concrete to suggest instead of a vague 'go outside'.
When NOT to use: don't call before deciding to nudge (wasted network round-trip). Don't re-suggest an activity the user already declined this session — the tool has no memory of prior suggestions.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (readOnlyHint=true), the description reveals internal side effects: it calls check_grass_conditions, triggering HTTPS calls and cache writes. It also notes determinism and potential repetition in quick succession.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with front-loaded purpose, side effects, and usage rules. Every sentence adds value without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (no params, no output schema), the description fully covers inputs, behavior, side effects, and usage context, leaving no gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
No parameters exist, but the description explains the implicit inputs (weather, temperature, sunset) and the deterministic behavior, adding value beyond the empty schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns a single context-appropriate outdoor activity filtered by weather, temperature, and sunset time. It distinguishes from siblings like check_grass_conditions, get_stats, and log_touch_grass.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance is provided: when to use (after deciding to nudge) and when not to use (before deciding, or re-suggesting declined activities). This helps the agent decide correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
1 tool update
v0.1.2- Changed
log_touch_grass1 field changed- changed
Input schema / properties / activity / descriptionPrevious value: -"What the user did outside (e.g., 'walk', 'coffee on porch', 'stretched'). Optional."New value: +"Free-form label describing what the user just did outside (e.g., 'walk', 'coffee on porch', 'stretched'). Stored verbatim as the `activity` field of this streak entry in ~/.touch-grass/state.json — it becomes the human-readable record of THIS recording action. Optional; defaults to 'outside time'. Has no effect on streak math; purely descriptive metadata for history readback."
4 tool updates
v0.1.0- First observed
check_grass_conditions - First observed
get_stats - First observed
log_touch_grass - First observed
suggest_activity
TDQS
Each tool has a clearly distinct purpose: check_grass_conditions provides live weather and streak; get_stats returns cached streak data without network calls; log_touch_grass records outdoor time; suggest_activity gives a context-aware suggestion. No overlapping functionality.
All tool names follow a consistent verb_noun pattern using snake_case: check_grass_conditions, get_stats, log_touch_grass, suggest_activity. The convention is uniform and predictable.
The server has 4 tools, which is well-suited for the narrow domain of tracking outdoor activity. While there is slight overlap between check_grass_conditions and get_stats, each tool earns its place. The count is not excessive or insufficient.
The tool set covers the core workflow: checking conditions, retrieving stats, logging an outdoor touch, and suggesting activities. Missing features like undo or streak reset are intentionally omitted. Overall, it's complete for its purpose.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer
WHOOP recovery, strain, sleep and workouts in Claude via official WHOOP OAuth. Free, open source.
Garmin data in Claude: 135 tools — activities, sleep, HRV, training, workouts. Free, open source.
Use AI models for chat, image, and video generation from Claude Code and other MCP hosts.
Related MCP Servers
- AlicenseAqualityBmaintenanceAn MCP server that provides access to Cronometer nutrition data, enabling users to pull food logs, macro and micronutrient summaries, and biometric data into Claude or Cursor. It supports daily nutrition tracking and raw CSV exports by interfacing with the Cronometer web protocol.2717MIT
- AlicenseBqualityCmaintenanceEnables users to query Apple Health metrics, workouts, and trends from CSV files exported via the Health Auto Export app. It allows MCP clients to analyze health data such as heart rate, sleep stages, and activity levels directly from local iCloud Drive storage.3211MIT
- FlicenseNot gradedqualityNot gradedmaintenanceA minimal Python MCP server that enables Claude Code to call local Ollama models (e.g., gemma3) as a tool, routing low-stakes work off the API and onto a homelab.-
- AlicenseNot gradedqualityDmaintenanceAn MCP server that wraps the Ambient Weather REST API. Query your personal weather stations conversationally from Claude Code, Claude.ai, or any MCP-compatible client.1MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/nalediym/touch-grass'
If you have feedback or need assistance with the MCP directory API, please join our Discord server