tidewave-integration
Tidewave MCP runtime tools — debugging, smoke testing, live state inspection, SQL queries, hex docs. Use when evaluating code in a running Phoenix app.
Install
npx skills add https://github.com/oliver-kriska/claude-elixir-phoenix/tree/main/plugins/elixir-phoenix/skills/tidewave-integration
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install oliver-kriska-claude-elixir-phoenix@llmmart
git clone https://github.com/oliver-kriska/claude-elixir-phoenix.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole oliver-kriska/claude-elixir-phoenix collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Tidewave MCP Integration
Runtime intelligence for Phoenix apps via MCP. Prefer Tidewave tools over Bash when available.
Iron Laws — Never Violate These
- DEV ONLY — Never use Tidewave tools in production contexts. Avoid on shared dev servers with production data copies
- PREFER TIDEWAVE OVER BASH —
mcp__tidewave__get_docs>web_fetch,execute_sql_query>psql - CHECK AVAILABILITY FIRST — Call Tidewave only when matching
mcp__tidewave__*tools are present - SQL IS READ-HEAVY — Use
execute_sql_queryfor SELECT, be careful with mutations - EXACT VERSIONS —
get_docsreturns docs for YOUR mix.lock versions, not latest
Quick Reference
| Task | Tidewave Tool | Fallback |
|---|---|---|
| Get docs | mcp__tidewave__get_docs Module.func/3 |
web_fetch hexdocs.pm/... |
| Run code | mcp__tidewave__project_eval |
mix run -e "code" |
| SQL query | mcp__tidewave__execute_sql_query |
psql $DATABASE_URL |
| Find source | mcp__tidewave__get_source_location |
grep -rn "defmodule" |
| Inspect DOM | mcp__Tidewave-Web__browser_eval |
Manual browser inspection |
| List schemas | mcp__tidewave__get_ecto_schemas |
Read lib/*/schemas/ |
| Read logs | mcp__tidewave__get_logs level: :error |
tail -f log/dev.log |
Detection
# Check endpoint
curl -s http://localhost:4000/tidewave/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"ping"}'
Or use /mcp in Claude Code to see connected servers.
Essential Patterns
Test Function Immediately
# mcp__tidewave__project_eval
MyApp.Accounts.create_user(%{email: "test@example.com"})
Verify Migration
-- mcp__tidewave__execute_sql_query
SELECT column_name, data_type FROM information_schema.columns
WHERE table_name = 'users';
Debug LiveView (with PID from browser)
# mcp__tidewave__project_eval
pid = pid("0.1234.0")
:sys.get_state(pid) |> Map.get(:socket) |> Map.get(:assigns) |> Map.keys()
Setup Requirements
# mix.exs
{:tidewave, "~> 0.6", only: :dev}
# endpoint.ex (in dev block)
plug Tidewave
# config/dev.exs (for LiveView source mapping)
config :phoenix_live_view,
debug_heex_annotations: true,
debug_attributes: true
The dependency and endpoint plug expose Tidewave's streamable HTTP server; they
do not register it with an MCP client. Configure the current runtime separately
with http://localhost:<port>/tidewave/mcp, then verify that Tidewave tools are
available before relying on this skill.
Reliability Guards
Worktree/port check (FIRST, in multi-worktree setups): multiple
worktrees = multiple dev servers on different ports. Before trusting any
Tidewave result, confirm the endpoint belongs to THIS checkout: grep
config/dev.exs for the configured port, and verify with
project_eval File.cwd!() — if it returns a different worktree path,
you're debugging the wrong server.
Schema introspection BEFORE SQL: never guess column names. Run
get_ecto_schemas (or query information_schema.columns) before writing
SQL against a table you haven't already introspected this session. A
guessed-column error costs more than the introspection.
Output-size guard: runtime output is unbounded. Always cap it —
LIMIT 20 in SQL, Enum.take(20) in evals, inspect(x, limit: 50, printable_limit: 500) for large structs. Re-query narrower rather than
dumping wide.
browser_eval fallback: if mcp__Tidewave-Web__browser_eval is absent
or errors, don't stall — inspect the same state server-side: LiveView
assigns via :sys.get_state(pid) in project_eval, rendered HTML via
Phoenix.LiveViewTest, or read the template source directly.
QA walkthrough pattern: after a feature completes, run a short
checklist through project_eval/browser_eval: create the record, fetch
it back, exercise the main event, check get_logs level: :error is clean.
Report each step's pass/fail — not just "smoke test passed".
Proactive Runtime Checks
Query runtime state at workflow checkpoints without waiting to be asked:
- After code edits:
get_logs level: :error(catch runtime crashes) - After features complete:
project_evalsmoke test (behavioral check) - Before planning:
get_ecto_schemas+ routes eval (concrete context) - When investigating: Auto-capture errors before asking user
- LiveView UI bugs:
browser_evalto inspect DOM state before editing components
See ${CLAUDE_SKILL_DIR}/references/proactive-patterns.md for full integration points.
References
For detailed patterns, see:
${CLAUDE_SKILL_DIR}/references/proactive-patterns.md- Push-like runtime patterns at workflow checkpoints${CLAUDE_SKILL_DIR}/references/tool-examples.md- Complete tool usage examples${CLAUDE_SKILL_DIR}/references/validation-checklist.md- Runtime validation patterns
Files (claude-elixir-phoenix)
-
references
-
proactive-patterns.md 3.8 KB
# Proactive Runtime Patterns Push-like patterns using Tidewave within MCP's pull constraints. Instead of waiting for the developer to ask, these patterns **automatically query runtime state at workflow checkpoints**. ## Philosophy From Jose Valim's vertical integration thesis: agents should understand the relationship between code AND running behavior. MCP is pull-only, but we simulate push by proactively querying at the right moments. ## When to Proactively Query ### During Work Phase (per-task runtime check) After editing `.ex` files, call `mcp__tidewave__get_logs level: :error` to catch runtime errors that compile-time checks miss (supervision tree failures, config issues, module loading problems). If errors found, investigate immediately -- don't wait for `mix test` to surface them. ### During Work Phase (per-feature smoke test) After completing all tasks for a domain feature: ```elixir # Ecto feature: create -> fetch -> verify (rolled back, no orphan records) mcp__tidewave__project_eval """ alias MyApp.{Accounts, Repo} Repo.transaction(fn -> {:ok, record} = Accounts.create_user(%{ email: "smoke-#{System.unique_integer()}@test.com", password: "valid_password_123" }) fetched = Accounts.get_user!(record.id) true = fetched.email == record.email Repo.rollback(:smoke_test_passed) end) # Returns {:error, :smoke_test_passed} = success """ ``` ```sql -- Schema verification after migration -- mcp__tidewave__execute_sql_query SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = 'target_table' ORDER BY ordinal_position; ``` ### During Planning Phase (context gathering) Before spawning research agents: ```elixir # Understand current data model mcp__tidewave__get_ecto_schemas # Understand current routes (auto-discovers router module) mcp__tidewave__project_eval """ router = :code.all_loaded() |> Enum.find(fn {mod, _} -> function_exported?(mod, :__routes__, 0) end) |> elem(0) Phoenix.Router.routes(router) |> Enum.map(& {&1.verb, &1.path, &1.plug}) """ # Check for existing warnings in planned area mcp__tidewave__get_logs level: :warning ``` Pass gathered context to research agent prompts so they work with concrete project state, not assumptions. ### During Investigation (auto-capture) When investigating a bug, auto-capture BEFORE asking the user: ```elixir # Step 1: Capture recent errors mcp__tidewave__get_logs level: :error # Step 2: Correlate with source mcp__tidewave__get_source_location ModuleName # Step 3: Inspect live state mcp__tidewave__project_eval """ # Check process state, ETS tables, or query results # relevant to the reported bug """ ``` Present pre-populated investigation context rather than asking the developer to copy-paste errors. ## Integration Points | Workflow Phase | Checkpoint | Tidewave Query | Purpose | |---------------|------------|----------------|---------| | Plan | Before agents | `get_ecto_schemas`, routes eval | Concrete project context | | Plan | Before agents | `get_logs :warning` | Existing issues in planned area | | Work | Per-task | `get_logs :error` | Runtime error detection | | Work | Per-feature | `project_eval` smoke test | Behavioral verification | | Work | Per-feature | `execute_sql_query` | Schema/data verification | | Investigate | Entry | `get_logs :error` | Auto-capture errors | | Investigate | Hypothesis | `project_eval` | Test fix before applying | | Review | Pre-review | `get_logs :error` | Catch runtime issues reviewers miss | ## Fallback Behavior When Tidewave is NOT available, all proactive checks silently skip. The workflow falls back to static analysis, `mix compile`, `mix test`. No functionality is lost. When Tidewave IS available but the app is not running, also silently skip. Tidewave tool calls will return errors that the agent can safely ignore. -
tool-examples.md 3.9 KB
# Tidewave Tool Examples ## project_eval - Execute Elixir Code Best for: Testing functions, inspecting state, quick experiments ```elixir # Test a context function MyApp.Accounts.get_user!(1) # Inspect process state pid = Process.whereis(MyApp.SomeGenServer) :sys.get_state(pid) # Check application config Application.get_env(:my_app, MyAppWeb.Endpoint) # Test changeset %MyApp.User{} |> MyApp.User.changeset(%{email: "test@example.com"}) |> Map.get(:valid?) # Inspect LiveView socket (requires PID from dev tools) :sys.get_state(lv_pid) |> Map.get(:socket) |> Map.get(:assigns) # Check module compiled Code.ensure_loaded?(MyApp.SomeModule) # Recompile if needed IEx.Helpers.recompile() ``` ## execute_sql_query - Database Operations Best for: Verifying data, checking migrations, debugging queries ```sql -- Check table structure SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = 'users'; -- Verify migration ran SELECT * FROM schema_migrations ORDER BY inserted_at DESC LIMIT 5; -- Check indexes SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'users'; -- Debug query results SELECT u.*, COUNT(p.id) as post_count FROM users u LEFT JOIN posts p ON p.user_id = u.id GROUP BY u.id; -- Check Oban jobs SELECT id, worker, args, state, scheduled_at FROM oban_jobs ORDER BY inserted_at DESC LIMIT 10; -- Table exists? SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_name = 'users' ); ``` ## get_docs - Fetch Documentation Best for: Looking up function signatures, module docs, exact API ``` # Module documentation Phoenix.LiveView # Specific function Ecto.Changeset.validate_required/3 # Callback documentation Phoenix.LiveView.handle_event/3 # Type specifications Ecto.Schema.belongs_to/3 ``` **Advantage**: Returns docs for exact versions in your mix.lock ## get_source_location - Find Code Best for: Locating modules, finding implementations ``` # Find module MyApp.Accounts # Find function MyApp.Accounts.create_user/1 # Find LiveView MyAppWeb.UserLive.Index # Find component MyAppWeb.CoreComponents.button/1 ``` Returns: `{:ok, %{file: "lib/my_app/accounts.ex", line: 15}}` ## get_ecto_schemas - Introspect Data Model Best for: Understanding existing schemas, checking fields/associations ``` # All schemas (no filter) # Filter by name User # Filter by context Accounts ``` Returns: Schema definitions including fields, types, associations, source table ## get_logs - Application Logs Best for: Debugging errors, tracing requests ``` # All recent logs (no filter) # Filter by level level: :error level: :warning ``` ## Workflow Integration ### When Planning Features 1. Understand existing patterns: `get_ecto_schemas` 2. Check documentation: `get_docs` for relevant modules 3. Find similar code: `get_source_location` ### When Implementing 1. Test as you go: `project_eval` after each function 2. Verify queries: `execute_sql_query` for Ecto queries 3. Check for errors: `get_logs level: :error` ### When Debugging 1. Find the code: `get_source_location` 2. Read logs: `get_logs` 3. Test fix: `project_eval` 4. Verify data: `execute_sql_query` ### When Investigating Memory Leaks Use `project_eval` to walk through a structured investigation: ```elixir # 1. Find processes sorted by memory usage Process.list() |> Enum.map(fn pid -> info = Process.info(pid, [:memory, :message_queue_len, :registered_name]) {pid, info} end) |> Enum.sort_by(fn {_, info} -> info[:memory] end, :desc) |> Enum.take(10) # 2. Inspect suspicious process (high memory or large message queue) Process.info(pid, [:memory, :message_queue_len, :current_function, :initial_call, :dictionary]) # 3. Check supervision tree for the process Process.info(pid, [:links, :monitors, :monitored_by]) # 4. If GenServer, inspect state size :sys.get_state(pid) |> :erts_debug.size() |> Kernel.*(8) # bytes ``` Flow: enumerate by memory → find outlier → check message queue → trace supervisor → propose fix -
validation-checklist.md 3.4 KB
# Runtime Validation Checklist Validate implementations before marking complete. Prefer Tidewave when available. ## Schema & Migration ### With Tidewave ```elixir # Verify schema loaded mcp__tidewave__get_ecto_schemas User # Check migration applied mcp__tidewave__execute_sql_query """ SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = 'users' ORDER BY ordinal_position """ # Verify indexes mcp__tidewave__execute_sql_query """ SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'users' """ # Test changeset mcp__tidewave__project_eval """ %MyApp.User{} |> MyApp.User.changeset(%{email: "test@example.com"}) |> Map.take([:valid?, :errors]) """ ``` ### Without Tidewave ```bash mix ecto.migrations mix ecto.migrate psql $DATABASE_URL -c "\\d users" ``` ## Context Functions ### With Tidewave ```elixir # Test create mcp__tidewave__project_eval """ MyApp.Accounts.create_user(%{ email: "test-#{System.unique_integer()}@example.com", password: "password123456" }) """ # Verify in DB mcp__tidewave__execute_sql_query """ SELECT id, email, inserted_at FROM users ORDER BY inserted_at DESC LIMIT 5 """ ``` ### Without Tidewave ```bash mix test test/my_app/accounts_test.exs ``` ## LiveView ### With Tidewave ```elixir # Find source mcp__tidewave__get_source_location MyAppWeb.UserLive.Index # Check errors mcp__tidewave__get_logs level: :error # Verify assigns (with PID) mcp__tidewave__project_eval """ pid = pid("0.1234.0") :sys.get_state(pid).socket.assigns |> Map.keys() """ ``` ### Without Tidewave ```bash mix phx.server mix test test/my_app_web/live/user_live_test.exs ``` ## Oban Jobs ### With Tidewave ```elixir # Check enqueued mcp__tidewave__execute_sql_query """ SELECT id, worker, args, state FROM oban_jobs ORDER BY inserted_at DESC LIMIT 10 """ # Test worker directly mcp__tidewave__project_eval """ job = %Oban.Job{args: %{"user_id" => 1}} MyApp.Workers.WelcomeEmailWorker.perform(job) """ # Check failures mcp__tidewave__execute_sql_query """ SELECT worker, args, errors FROM oban_jobs WHERE state IN ('retryable', 'discarded') """ ``` ## GenServer / Processes ### With Tidewave ```elixir # Check registered mcp__tidewave__project_eval """ Process.whereis(MyApp.CacheServer) |> is_pid() """ # Inspect state mcp__tidewave__project_eval """ pid = Process.whereis(MyApp.CacheServer) :sys.get_state(pid) """ # Check supervision tree mcp__tidewave__project_eval """ Supervisor.which_children(MyApp.Supervisor) |> Enum.map(fn {id, _, _, _} -> id end) """ ``` ## Quick Validation Template ```markdown ## Validation: [Feature Name] ### Schema/Data - [ ] Migration applied - [ ] Schema fields correct - [ ] Indexes created - [ ] Changeset validates ### Context Functions - [ ] create_* works - [ ] get_* works - [ ] list_* works - [ ] update_* works ### Web Layer - [ ] Routes configured - [ ] Controller/LiveView responds - [ ] Templates render ### Tests - [ ] Unit tests pass - [ ] No regressions ### Logs - [ ] No errors - [ ] No warnings ``` ## Troubleshooting ### Module Not Found ```elixir mcp__tidewave__project_eval """ Code.ensure_loaded?(MyApp.SomeModule) """ # Recompile mcp__tidewave__project_eval """ IEx.Helpers.recompile() """ ``` ### Table/Column Not Found ```sql SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_name = 'users' ); SELECT EXISTS ( SELECT FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'email' ); ```
-
-
SKILL.md 5.1 KB
--- name: tidewave-integration description: "Tidewave MCP runtime tools — debugging, smoke testing, live state inspection, SQL queries, hex docs. Use when evaluating code in a running Phoenix app." effort: low user-invocable: false --- # Tidewave MCP Integration Runtime intelligence for Phoenix apps via MCP. Prefer Tidewave tools over Bash when available. ## Iron Laws — Never Violate These 1. **DEV ONLY** — Never use Tidewave tools in production contexts. Avoid on shared dev servers with production data copies 2. **PREFER TIDEWAVE OVER BASH** — `mcp__tidewave__get_docs` > `web_fetch`, `execute_sql_query` > `psql` 3. **CHECK AVAILABILITY FIRST** — Call Tidewave only when matching `mcp__tidewave__*` tools are present 4. **SQL IS READ-HEAVY** — Use `execute_sql_query` for SELECT, be careful with mutations 5. **EXACT VERSIONS** — `get_docs` returns docs for YOUR mix.lock versions, not latest ## Quick Reference | Task | Tidewave Tool | Fallback | |------|---------------|----------| | Get docs | `mcp__tidewave__get_docs Module.func/3` | `web_fetch hexdocs.pm/...` | | Run code | `mcp__tidewave__project_eval` | `mix run -e "code"` | | SQL query | `mcp__tidewave__execute_sql_query` | `psql $DATABASE_URL` | | Find source | `mcp__tidewave__get_source_location` | `grep -rn "defmodule"` | | Inspect DOM | `mcp__Tidewave-Web__browser_eval` | Manual browser inspection | | List schemas | `mcp__tidewave__get_ecto_schemas` | Read `lib/*/schemas/` | | Read logs | `mcp__tidewave__get_logs level: :error` | `tail -f log/dev.log` | ## Detection ```bash # Check endpoint curl -s http://localhost:4000/tidewave/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"ping"}' ``` Or use `/mcp` in Claude Code to see connected servers. ## Essential Patterns ### Test Function Immediately ```elixir # mcp__tidewave__project_eval MyApp.Accounts.create_user(%{email: "test@example.com"}) ``` ### Verify Migration ```sql -- mcp__tidewave__execute_sql_query SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'users'; ``` ### Debug LiveView (with PID from browser) ```elixir # mcp__tidewave__project_eval pid = pid("0.1234.0") :sys.get_state(pid) |> Map.get(:socket) |> Map.get(:assigns) |> Map.keys() ``` ## Setup Requirements ```elixir # mix.exs {:tidewave, "~> 0.6", only: :dev} # endpoint.ex (in dev block) plug Tidewave # config/dev.exs (for LiveView source mapping) config :phoenix_live_view, debug_heex_annotations: true, debug_attributes: true ``` The dependency and endpoint plug expose Tidewave's streamable HTTP server; they do not register it with an MCP client. Configure the current runtime separately with `http://localhost:<port>/tidewave/mcp`, then verify that Tidewave tools are available before relying on this skill. ## Reliability Guards **Worktree/port check (FIRST, in multi-worktree setups)**: multiple worktrees = multiple dev servers on different ports. Before trusting any Tidewave result, confirm the endpoint belongs to THIS checkout: grep `config/dev.exs` for the configured port, and verify with `project_eval File.cwd!()` — if it returns a different worktree path, you're debugging the wrong server. **Schema introspection BEFORE SQL**: never guess column names. Run `get_ecto_schemas` (or query `information_schema.columns`) before writing SQL against a table you haven't already introspected this session. A guessed-column error costs more than the introspection. **Output-size guard**: runtime output is unbounded. Always cap it — `LIMIT 20` in SQL, `Enum.take(20)` in evals, `inspect(x, limit: 50, printable_limit: 500)` for large structs. Re-query narrower rather than dumping wide. **browser_eval fallback**: if `mcp__Tidewave-Web__browser_eval` is absent or errors, don't stall — inspect the same state server-side: LiveView assigns via `:sys.get_state(pid)` in `project_eval`, rendered HTML via `Phoenix.LiveViewTest`, or read the template source directly. **QA walkthrough pattern**: after a feature completes, run a short checklist through `project_eval`/`browser_eval`: create the record, fetch it back, exercise the main event, check `get_logs level: :error` is clean. Report each step's pass/fail — not just "smoke test passed". ## Proactive Runtime Checks **Query runtime state at workflow checkpoints** without waiting to be asked: - **After code edits**: `get_logs level: :error` (catch runtime crashes) - **After features complete**: `project_eval` smoke test (behavioral check) - **Before planning**: `get_ecto_schemas` + routes eval (concrete context) - **When investigating**: Auto-capture errors before asking user - **LiveView UI bugs**: `browser_eval` to inspect DOM state before editing components See `${CLAUDE_SKILL_DIR}/references/proactive-patterns.md` for full integration points. ## References For detailed patterns, see: - `${CLAUDE_SKILL_DIR}/references/proactive-patterns.md` - Push-like runtime patterns at workflow checkpoints - `${CLAUDE_SKILL_DIR}/references/tool-examples.md` - Complete tool usage examples - `${CLAUDE_SKILL_DIR}/references/validation-checklist.md` - Runtime validation patterns
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.