[{"data":1,"prerenderedAt":822},["ShallowReactive",2],{"\u002Fblog\u002Fhow-i-use-claude-code-inside-my-wsl-setup":3},{"id":4,"title":5,"body":6,"date":806,"description":807,"draft":808,"extension":809,"meta":810,"navigation":811,"ogImage":812,"path":813,"seo":814,"stem":815,"tags":816,"__hash__":821},"blog\u002Fblog\u002Fhow-i-use-claude-code-inside-my-wsl-setup.md","How I use Claude Code inside my WSL setup",{"type":7,"value":8,"toc":791},"minimark",[9,13,18,21,24,32,39,48,78,85,88,110,113,129,132,269,274,286,289,307,313,571,578,582,591,602,639,653,661,683,694,697,701,716,719,722,768,771,777,781,784,787],[10,11,12],"p",{},"I've been using Claude Code as my main AI coding assistant for a while now, and getting it to work properly inside WSL took more than I expected. This is the setup I landed on: part tutorial, part honest review.",[14,15,17],"h2",{"id":16},"why-wsl-makes-things-complicated","Why WSL makes things complicated",[10,19,20],{},"Claude Code runs natively in WSL, which is great. The problem starts when you want MCP servers that need to talk to Windows-side tools: browsers, design apps, anything with a GUI. WSL and Windows don't share the same network stack by default, so callbacks and port forwarding become your problem.",[10,22,23],{},"My two global MCP servers are Chrome DevTools and Figma. Both run on the Windows side, which means WSL can't reach them out of the box.",[14,25,27,28],{"id":26},"chrome-devtools-mcp-via-chrome-wsl","Chrome DevTools MCP via ",[29,30,31],"code",{},"chrome-wsl",[10,33,34,35,38],{},"The Chrome DevTools MCP lets Claude Code inspect live browser state (DOM, network requests, console) directly from the terminal. On a normal Linux machine it just works. On WSL, Chrome runs on Windows and exposes its remote debugging port on ",[29,36,37],{},"127.0.0.1:9222"," from Windows' perspective, not WSL's.",[10,40,41,47],{},[42,43,31],"a",{"href":44,"rel":45},"https:\u002F\u002Fgithub.com\u002Fdbalabka\u002Fchrome-wsl",[46],"nofollow"," bridges that gap. Here's what it does under the hood when you run it:",[49,50,51,55,62,72],"ol",{},[52,53,54],"li",{},"Detects the Windows host IP from inside WSL",[52,56,57,58,61],{},"Checks that a portproxy rule exists (",[29,59,60],{},"Windows IP:9222 -> 127.0.0.1:9222",") and that the firewall allows it. If either is missing, it prints the PowerShell admin commands you need to run once",[52,63,64,65,68,69,71],{},"Starts a ",[29,66,67],{},"socat"," process inside WSL that proxies the connection from ",[29,70,37],{}," (WSL) to the Windows Chrome debugging port",[52,73,74,75],{},"Launches Chrome on Windows with ",[29,76,77],{},"--remote-debugging-port=9222",[10,79,80,81,84],{},"The result: Claude Code sees ",[29,82,83],{},"http:\u002F\u002F127.0.0.1:9222"," as if Chrome was local.",[10,86,87],{},"Before each Claude Code session, run:",[89,90,95],"pre",{"className":91,"code":92,"language":93,"meta":94,"style":94},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","npx @dbalabka\u002Fchrome-wsl\n","bash","",[29,96,97],{"__ignoreMap":94},[98,99,102,106],"span",{"class":100,"line":101},"line",1,[98,103,105],{"class":104},"sbgvK","npx",[98,107,109],{"class":108},"s_sjI"," @dbalabka\u002Fchrome-wsl\n",[10,111,112],{},"And when you're done:",[89,114,116],{"className":91,"code":115,"language":93,"meta":94,"style":94},"npx @dbalabka\u002Fchrome-wsl --stop\n",[29,117,118],{"__ignoreMap":94},[98,119,120,122,125],{"class":100,"line":101},[98,121,105],{"class":104},[98,123,124],{"class":108}," @dbalabka\u002Fchrome-wsl",[98,126,128],{"class":127},"stzsN"," --stop\n",[10,130,131],{},"Then add the MCP server to your Claude Code config:",[89,133,138],{"className":134,"code":135,"filename":136,"language":137,"meta":94,"style":94},"language-json shiki shiki-themes material-theme-lighter github-light github-dark","{\n  \"mcpServers\": {\n    \"chrome\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"chrome-devtools-mcp@latest\", \"--browser-url=http:\u002F\u002F127.0.0.1:9222\"]\n    }\n  }\n}\n","~\u002F.claude.json","json",[29,139,140,146,166,182,207,251,257,263],{"__ignoreMap":94},[98,141,142],{"class":100,"line":101},[98,143,145],{"class":144},"sP7_E","{\n",[98,147,149,153,157,160,163],{"class":100,"line":148},2,[98,150,152],{"class":151},"s39Yj","  \"",[98,154,156],{"class":155},"sseR_","mcpServers",[98,158,159],{"class":151},"\"",[98,161,162],{"class":144},":",[98,164,165],{"class":144}," {\n",[98,167,169,172,176,178,180],{"class":100,"line":168},3,[98,170,171],{"class":151},"    \"",[98,173,175],{"class":174},"sZMiF","chrome",[98,177,159],{"class":151},[98,179,162],{"class":144},[98,181,165],{"class":144},[98,183,185,188,192,194,196,200,202,204],{"class":100,"line":184},4,[98,186,187],{"class":151},"      \"",[98,189,191],{"class":190},"srdBf","command",[98,193,159],{"class":151},[98,195,162],{"class":144},[98,197,199],{"class":198},"sjJ54"," \"",[98,201,105],{"class":108},[98,203,159],{"class":198},[98,205,206],{"class":144},",\n",[98,208,210,212,215,217,219,222,224,227,229,232,234,237,239,241,243,246,248],{"class":100,"line":209},5,[98,211,187],{"class":151},[98,213,214],{"class":190},"args",[98,216,159],{"class":151},[98,218,162],{"class":144},[98,220,221],{"class":144}," [",[98,223,159],{"class":198},[98,225,226],{"class":108},"-y",[98,228,159],{"class":198},[98,230,231],{"class":144},",",[98,233,199],{"class":198},[98,235,236],{"class":108},"chrome-devtools-mcp@latest",[98,238,159],{"class":198},[98,240,231],{"class":144},[98,242,199],{"class":198},[98,244,245],{"class":108},"--browser-url=http:\u002F\u002F127.0.0.1:9222",[98,247,159],{"class":198},[98,249,250],{"class":144},"]\n",[98,252,254],{"class":100,"line":253},6,[98,255,256],{"class":144},"    }\n",[98,258,260],{"class":100,"line":259},7,[98,261,262],{"class":144},"  }\n",[98,264,266],{"class":100,"line":265},8,[98,267,268],{"class":144},"}\n",[270,271,273],"h3",{"id":272},"automating-the-startstop","Automating the start\u002Fstop",[10,275,276,277,279,280,285],{},"Running ",[29,278,31],{}," manually before every session gets old fast. ",[42,281,284],{"href":282,"rel":283},"https:\u002F\u002Fdocs.anthropic.com\u002Fen\u002Fdocs\u002Fclaude-code\u002Fhooks",[46],"Claude Code hooks"," handle it automatically instead.",[10,287,288],{},"Install the package globally so the binary is available:",[89,290,292],{"className":91,"code":291,"language":93,"meta":94,"style":94},"npm install -g @dbalabka\u002Fchrome-wsl\n",[29,293,294],{"__ignoreMap":94},[98,295,296,299,302,305],{"class":100,"line":101},[98,297,298],{"class":104},"npm",[98,300,301],{"class":108}," install",[98,303,304],{"class":127}," -g",[98,306,109],{"class":108},[10,308,309,310,162],{},"Then add these hooks to ",[29,311,312],{},"~\u002F.claude\u002Fsettings.json",[89,314,316],{"className":134,"code":315,"filename":312,"language":137,"meta":94,"style":94},"{\n  \"hooks\": {\n    \"SessionStart\": [\n      {\n        \"matcher\": \"\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"chrome-wsl\"\n          }\n        ]\n      }\n    ],\n    \"SessionEnd\": [\n      {\n        \"matcher\": \"\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"chrome-wsl --stop\"\n          }\n        ]\n      }\n    ]\n  }\n}\n",[29,317,318,322,335,349,354,371,383,388,409,427,433,439,445,451,465,470,485,498,503,522,540,545,550,555,561,566],{"__ignoreMap":94},[98,319,320],{"class":100,"line":101},[98,321,145],{"class":144},[98,323,324,326,329,331,333],{"class":100,"line":148},[98,325,152],{"class":151},[98,327,328],{"class":155},"hooks",[98,330,159],{"class":151},[98,332,162],{"class":144},[98,334,165],{"class":144},[98,336,337,339,342,344,346],{"class":100,"line":168},[98,338,171],{"class":151},[98,340,341],{"class":174},"SessionStart",[98,343,159],{"class":151},[98,345,162],{"class":144},[98,347,348],{"class":144}," [\n",[98,350,351],{"class":100,"line":184},[98,352,353],{"class":144},"      {\n",[98,355,356,359,362,364,366,369],{"class":100,"line":209},[98,357,358],{"class":151},"        \"",[98,360,361],{"class":190},"matcher",[98,363,159],{"class":151},[98,365,162],{"class":144},[98,367,368],{"class":198}," \"\"",[98,370,206],{"class":144},[98,372,373,375,377,379,381],{"class":100,"line":253},[98,374,358],{"class":151},[98,376,328],{"class":190},[98,378,159],{"class":151},[98,380,162],{"class":144},[98,382,348],{"class":144},[98,384,385],{"class":100,"line":259},[98,386,387],{"class":144},"          {\n",[98,389,390,393,397,399,401,403,405,407],{"class":100,"line":265},[98,391,392],{"class":151},"            \"",[98,394,396],{"class":395},"swQdS","type",[98,398,159],{"class":151},[98,400,162],{"class":144},[98,402,199],{"class":198},[98,404,191],{"class":108},[98,406,159],{"class":198},[98,408,206],{"class":144},[98,410,412,414,416,418,420,422,424],{"class":100,"line":411},9,[98,413,392],{"class":151},[98,415,191],{"class":395},[98,417,159],{"class":151},[98,419,162],{"class":144},[98,421,199],{"class":198},[98,423,31],{"class":108},[98,425,426],{"class":198},"\"\n",[98,428,430],{"class":100,"line":429},10,[98,431,432],{"class":144},"          }\n",[98,434,436],{"class":100,"line":435},11,[98,437,438],{"class":144},"        ]\n",[98,440,442],{"class":100,"line":441},12,[98,443,444],{"class":144},"      }\n",[98,446,448],{"class":100,"line":447},13,[98,449,450],{"class":144},"    ],\n",[98,452,454,456,459,461,463],{"class":100,"line":453},14,[98,455,171],{"class":151},[98,457,458],{"class":174},"SessionEnd",[98,460,159],{"class":151},[98,462,162],{"class":144},[98,464,348],{"class":144},[98,466,468],{"class":100,"line":467},15,[98,469,353],{"class":144},[98,471,473,475,477,479,481,483],{"class":100,"line":472},16,[98,474,358],{"class":151},[98,476,361],{"class":190},[98,478,159],{"class":151},[98,480,162],{"class":144},[98,482,368],{"class":198},[98,484,206],{"class":144},[98,486,488,490,492,494,496],{"class":100,"line":487},17,[98,489,358],{"class":151},[98,491,328],{"class":190},[98,493,159],{"class":151},[98,495,162],{"class":144},[98,497,348],{"class":144},[98,499,501],{"class":100,"line":500},18,[98,502,387],{"class":144},[98,504,506,508,510,512,514,516,518,520],{"class":100,"line":505},19,[98,507,392],{"class":151},[98,509,396],{"class":395},[98,511,159],{"class":151},[98,513,162],{"class":144},[98,515,199],{"class":198},[98,517,191],{"class":108},[98,519,159],{"class":198},[98,521,206],{"class":144},[98,523,525,527,529,531,533,535,538],{"class":100,"line":524},20,[98,526,392],{"class":151},[98,528,191],{"class":395},[98,530,159],{"class":151},[98,532,162],{"class":144},[98,534,199],{"class":198},[98,536,537],{"class":108},"chrome-wsl --stop",[98,539,426],{"class":198},[98,541,543],{"class":100,"line":542},21,[98,544,432],{"class":144},[98,546,548],{"class":100,"line":547},22,[98,549,438],{"class":144},[98,551,553],{"class":100,"line":552},23,[98,554,444],{"class":144},[98,556,558],{"class":100,"line":557},24,[98,559,560],{"class":144},"    ]\n",[98,562,564],{"class":100,"line":563},25,[98,565,262],{"class":144},[98,567,569],{"class":100,"line":568},26,[98,570,268],{"class":144},[10,572,573,574,577],{},"Chrome starts automatically when Claude Code opens, and shuts down cleanly when you exit with ",[29,575,576],{},"\u002Fexit",".",[14,579,581],{"id":580},"giving-claude-code-more-context-with-skills","Giving Claude Code more context with skills",[10,583,584,585,590],{},"Claude Code works better when it knows what stack you're using. ",[42,586,589],{"href":587,"rel":588},"https:\u002F\u002Fskills.sh",[46],"skills.sh"," is a CLI tool that lets you install curated context files (called skills) that get injected into your Claude Code sessions automatically. Think of them as structured documentation that Claude reads before touching your code.",[10,592,593,594,597,598,601],{},"Install them globally with ",[29,595,596],{},"npx skills add -g \u003Cskill>"," and they live in ",[29,599,600],{},"~\u002F.agents\u002Fskills\u002F",". My current list:",[10,603,604,608,611,612,611,615,611,618,611,621,611,624,611,627,611,630,611,633,611,636],{},[605,606,607],"strong",{},"Nuxt \u002F Vue ecosystem",[29,609,610],{},"nuxt",", ",[29,613,614],{},"nuxt-ui",[29,616,617],{},"nuxt-content",[29,619,620],{},"nuxt-modules",[29,622,623],{},"nuxthub",[29,625,626],{},"nuxt-better-auth",[29,628,629],{},"vue",[29,631,632],{},"vueuse",[29,634,635],{},"vueuse-functions",[29,637,638],{},"reka-ui",[10,640,641,644,611,647,611,650],{},[605,642,643],{},"Frontend & design",[29,645,646],{},"frontend-design",[29,648,649],{},"motion",[29,651,652],{},"superdesign",[10,654,655,658],{},[605,656,657],{},"TypeScript",[29,659,660],{},"ts-library",[10,662,663,666,667,671,611,674,611,677,611,680],{},[605,664,665],{},"SEO & marketing"," ",[668,669,670],"em",{},"(useful for the portfolio)",[29,672,673],{},"seo-audit",[29,675,676],{},"schema-markup",[29,678,679],{},"programmatic-seo",[29,681,682],{},"social-content",[10,684,685,688,611,691],{},[605,686,687],{},"Other",[29,689,690],{},"document-writer",[29,692,693],{},"skill-creator",[10,695,696],{},"The Nuxt ones in particular make a real difference: Claude stops guessing about composables, module conventions, and Nuxt UI component APIs.",[14,698,700],{"id":699},"how-i-actually-use-claude-code-day-to-day","How I actually use Claude Code day to day",[10,702,703,704,709,710,715],{},"My workflow goes beyond Plan Mode. I've adopted a spec-driven development approach, inspired by ",[42,705,708],{"href":706,"rel":707},"https:\u002F\u002Fboristane.com\u002Fblog\u002Fhow-i-use-claude-code\u002F",[46],"Boris Tane's post"," and the ",[42,711,714],{"href":712,"rel":713},"https:\u002F\u002Fgithub.blog\u002Fai-and-ml\u002Fgenerative-ai\u002Fspec-driven-development-with-ai-get-started-with-a-new-open-source-toolkit\u002F",[46],"Spec Kit"," from GitHub: never let Claude write code until there's a written, reviewed plan.",[10,717,718],{},"The core idea is to separate thinking from typing. Claude executes well, but decides less reliably without clear direction upfront.",[10,720,721],{},"Here's the flow for any non-trivial task:",[723,724,725,729,736,740,747,751,761,765],"steps",{},[270,726,728],{"id":727},"research","Research",[10,730,731,732,735],{},"Ask Claude to read the relevant parts of the codebase deeply before doing anything. The findings go into a ",[29,733,734],{},"research.md"," file, not just a chat response. If the research is wrong, everything downstream will be wrong.",[270,737,739],{"id":738},"plan","Plan",[10,741,742,743,746],{},"Claude writes a ",[29,744,745],{},"plan.md"," with a detailed implementation approach, code snippets, and the files it intends to touch. I use my own markdown file rather than Claude Code's built-in plan mode — it persists, I can edit it directly, and I stay in control of the architecture decisions.",[270,748,750],{"id":749},"annotate","Annotate",[10,752,753,754,756,757,760],{},"Open ",[29,755,745],{}," in your editor and add inline notes: corrections, constraints, rejected approaches, domain knowledge Claude doesn't have. Then send it back: ",[668,758,759],{},"\"I added notes to the document, address them and update the plan, no code yet.\""," This cycle repeats until the plan is right.",[270,762,764],{"id":763},"implement","Implement",[10,766,767],{},"Once the plan is approved, let Claude run without interruption. By this point every decision has been made. Implementation should be boring.",[10,769,770],{},"The Chrome DevTools MCP fits naturally into the implementation phase: if something looks wrong in the browser, you can ask Claude to inspect the actual DOM state rather than describe it from memory.",[772,773,774],"tip",{},[10,775,776],{},"Don't use Claude Code for anything you can write faster yourself: simple refactors, config files, one-liners. It earns its place on tasks where the context is large and keeping everything in your head is the hard part.",[14,778,780],{"id":779},"is-it-worth-it-on-wsl","Is it worth it on WSL?",[10,782,783],{},"Yes, but the tooling around it matters. WSL is where I do all my development: the Linux toolchain, Bash, Unix conventions. Claude Code fits naturally into that environment. The friction of managing MCP servers that live on the Windows side is real, but it's a one-time setup cost. chrome-wsl solves the Chrome side cleanly, and wrapping everything in a start script removes the last bit of manual overhead.",[10,785,786],{},"If you're on a similar setup, start with the wrapper script. The two minutes it takes to write will save you the context switch every time you open a new session.",[788,789,790],"style",{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .swQdS, html code.shiki .swQdS{--shiki-light:#E53935;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":94,"searchDepth":148,"depth":148,"links":792},[793,794,798,799,805],{"id":16,"depth":148,"text":17},{"id":26,"depth":148,"text":795,"children":796},"Chrome DevTools MCP via chrome-wsl",[797],{"id":272,"depth":168,"text":273},{"id":580,"depth":148,"text":581},{"id":699,"depth":148,"text":700,"children":800},[801,802,803,804],{"id":727,"depth":168,"text":728},{"id":738,"depth":168,"text":739},{"id":749,"depth":168,"text":750},{"id":763,"depth":168,"text":764},{"id":779,"depth":148,"text":780},"2026-03-24","Getting Claude Code's MCP servers to talk to Windows-side tools, plus the spec-driven workflow I use daily.",false,"md",{},true,null,"\u002Fblog\u002Fhow-i-use-claude-code-inside-my-wsl-setup",{"title":5,"description":807},"blog\u002Fhow-i-use-claude-code-inside-my-wsl-setup",[817,818,819,820],"claude","wsl","workflow","mcp","RMD9IWGEZThAbJ9JTocwyk-aR2tibjsx5cz72rEQMrI",1775478689383]