explainx.ai0k
TrendingAI News TodayPathwaysSkills
Pricing
explainx.ai

Upskill in AI — 16 free pathways, live workshops & bootcamps, and 50+ courses from practitioners. Plus the skills, tools, and MCP servers to practice on.

follow us

follow on google

Add explainx.ai as a preferred source

corporate training

support@explainx.ai

get started

Find your pathTake Free Evaluation

community

Join the community

learn

mind: share how you thinkpathways — start freeworkshopsbootcampscoursescompare Explainxcertificationsmock testsexplainx universitycorporate traininglearn skills & mcp

discover

skillsmcp serversexplainx mcptoolsmdx readeragentsllmsdesignsdictionarypeopleagi trackerfelony benchranks

company

aboutvisionmissionteaminstructorsteach on explainxpartnershipscommunityhackathonscareers

content

daily AI newsstate of AI — live resultsblogreleasespromptsgeneratorsresource libraryfor LLMsexplainx.ai kids

solutions

all solutionsdeveloper upskillingmarketing upskillingproduct manager upskillingleadership upskilling

newsletter · weekly

Get AI news, tools, and insights in your inbox.

supportcontactprivacytermsdata rightshow we create contentsubmission guidelines

© 2026 AISOLO Technologies Pvt Ltd

explainx.ai

On this page

  • TL;DR — what people are asking
  • Where mods work, and where they do not
  • Method 1: Install a mod from the plugin browser
  • Method 2: Have Claude build the mod
  • Method 3: Load your own folder in the desktop app
  • Build a desktop-aware mod: Usage Ring
  • Questions from the thread
  • Safety on the desktop
  • Troubleshooting on the desktop
  • Honest limitations
  • Bottom line
  • Related on explainx.ai
← Back to blog

explainx / blog

Claude Code Mods in the Desktop App: How to Install, Build and Test One (Step by Step)

Claude Code, Claude Code Mods, Desktop App, Tutorials, Plugins

A desktop-app guide to Claude Code mods: install from the plugin browser, have Claude build one, load a local folder, and build a limits-ring mod with SVG.

Oct 3, 2026·12 min read·Yash Thakker
add explainx.ai
go deep
Claude Code Mods in the Desktop App: How to Install, Build and Test One (Step by Step)

A post from a Claude Code desktop user this week showed a first mod running in the app and drew questions in the replies: can I share it, can I get the prompt, and what is it for if the usage circle is already at the bottom of the screen? Those are good questions, and the answers depend on a few things that are specific to the desktop app.

Mods are small TypeScript programs that ship inside plugins. They can watch what happens in a session, change what Claude Code does, and draw their own interface, such as a band above the prompt or a side pane. Anthropic's ClaudeDevs account announced them on October 1, 2026, noting you can write one in a few lines of TypeScript or have Claude build it, and install them with /plugin in the CLI or desktop app.

This guide is the desktop version of the story: how to install a mod from the Code tab, how to have Claude build one, how to load your own folder (where the desktop has a gap), and how to build and test a mod that uses a desktop-only element, an SVG ring, to show your usage limits. For the terminal path, our step-by-step build tutorial is the companion.

A note on what we verified. We cannot run the desktop app from our tooling. The install steps come from Anthropic's documentation, the mod code is validated and tested with the plugin test kit on both the terminal and desktop surfaces, and we say where something is unverified. The test kit checks hooks and element trees, not how the app paints them.

TL;DR — what people are asking

table · 2 cols
QuestionAnswer
Do mods work in the desktop app?Yes, in the Code tab for local and SSH sessions
Minimum version?Claude Code 2.1.287 or later; update the desktop app too
Install from the UI?+ next to the prompt box, then Plugins, then Add plugin
Manage or remove?+, then Plugins, then Manage plugins
Community marketplaces?Add them once from a terminal; the desktop app reads the same settings
Local folder in the UI?Not supported; workaround below
Where do mods not show up?Cloud sessions, WSL sessions, the VS Code panel (hooks run, no panes)
Can I share a mod?Yes, as a plugin in a marketplace on GitHub
Safety?Same access as Claude Code; read the source first
Weekly digest3.5k readers

Catch up on AI

Curated AI updates on agents, skills, and MCP — delivered to your inbox. Unsubscribe anytime.

Where mods work, and where they do not

The desktop app has three tabs: Chat, Cowork and Code. Mods live in the Code tab. Anthropic's documentation says plugins can be installed from the app without using the terminal, with these limits:

  • Local and SSH sessions have the plugin browser. Cloud sessions do not, and plugins you install from the desktop app are not available in cloud sessions.
  • Plugins are not available in WSL sessions.
  • Some plugins that an organization manages may be withheld by the app under an admin setting.
  • A mod's visual elements, panes and bands, are drawn in the terminal and the desktop Code tab. The VS Code extension and headless runs execute a mod's hooks but do not draw its interface.

The terminal, the desktop app's local sessions, and the VS Code extension on one computer read the same settings files. A plugin you install at user scope in any of them is available in the other two. That detail is the key to a few workarounds below.

Update the desktop app before you start. The plugin API distinguishes desktop builds that can place panes from older ones that place nothing, and a mod's pane waits until a build that can seat it is attached. If a pane never appears, check for an app update first.

Method 1: Install a mod from the plugin browser

This is the point-and-click path, taken from Anthropic's desktop documentation.

  1. Open the Code tab and start or open a local or SSH session.
  2. Click the + button next to the prompt box and choose Plugins.
  3. Choose Add plugin. The plugin browser opens with plugins from your configured marketplaces, including Anthropic's official marketplace.
  4. Select the plugin, then choose a scope: your user account, this project, or local only.
  5. To turn a plugin off or remove it later, use +, Plugins, Manage plugins.

Scope matters. User scope enables the plugin in every project on the machine; project scope records it in the repository's .claude/settings.json for collaborators, who still need to install it once; local scope enables it for you in this repository only. When a plugin is set at several scopes, local overrides project, and project overrides user.

Mods from community marketplaces

Most community mods live in GitHub repositories that act as marketplaces. The plugin browser shows plugins from marketplaces you have already configured, so add the marketplace once. The reliable way is from a terminal, which writes to the same settings the desktop app reads:

bash
claude plugin marketplace add owner/repo
claude plugin install some-mod@marketplace-name

Then open the plugin browser in the desktop app, or restart the app, and the plugin should be available. Some plugin READMEs say you can also type the same /plugin commands into the desktop prompt box, but we could not confirm that in the app, so treat the terminal route as the dependable one.

Examples you can try this way include our own awesome-claude-code-mods collection of 50 mods, which includes Context Meter and Quota Watch, and community usage-limit mods such as usage-band, which shows the five-hour and weekly limits above the prompt on the desktop app and terminal, and limit-bars, whose README says the desktop app gets simplified pie glyphs while the terminal gets animated rings. Read the source before installing any of them.

Method 2: Have Claude build the mod

You do not have to write code. Anthropic's announcement says you can write a mod or have Claude build it, and community guides describe the loop: start a session, describe the mod, and allow hot reload when prompted. The mod appears when Claude's turn ends. Mods created this way are temporary to that session unless you copy the folder out and install it as a plugin.

Here is a prompt that produces a usage-limits mod like the one in the post that sparked this guide. Paste it into a session, adjust the details, and review what Claude writes:

text
Build a Claude Code mod called usage-ring. Show a compact band above the
prompt with my context window percent and my plan rate limits (five-hour and
weekly) from the session usage API. On the desktop app, draw each as a small
SVG ring with the percent inside and turn it amber at 70 percent and red at
90 percent. In the terminal, draw a text bar instead. Refresh it after every
turn. Add tests for both surfaces, then run claude plugin validate and claude
plugin test and show me the output.

Three habits make this reliable. Ask for tests and validation in the same prompt. Read the generated code for anything that reads files or runs commands you did not ask for. And save the folder somewhere permanent before the session ends.

Method 3: Load your own folder in the desktop app

The desktop plugin browser has no field for a local folder. A GitHub issue requesting one (opened April 22, 2026) was closed as not planned, and its reported workaround was to add the folder as a marketplace from the terminal and restart the desktop app. For active development there is a better option.

The plugin reference documents an environment variable for sessions that a host starts, which includes the desktop app:

  • CLAUDE_CODE_PLUGIN_DIRS names folders to load exactly as the --plugin-dir flag would, one or more absolute paths separated by the platform's path separator.
  • It can be set in the process environment or in the env block of ~/.claude/settings.json. It is never read from a project's settings.
  • A long-lived host session watches the folder for changes when CLAUDE_CODE_PLUGIN_DIR_WATCH=1 is set the same way, so saving a file reloads the module.

A settings snippet for a development folder looks like this:

json
{
  "env": {
    "CLAUDE_CODE_PLUGIN_DIRS": "/Users/you/mods/usage-ring",
    "CLAUDE_CODE_PLUGIN_DIR_WATCH": "1"
  }
}

We could not verify this end to end in the desktop app, so treat it as documented behavior to try, and fall back to the marketplace route if it does not load. Either way, you can develop and verify a mod entirely from a terminal with claude --plugin-dir and the test kit, then use the desktop app only to use it.

Build a desktop-aware mod: Usage Ring

Now the concrete example. Usage Ring shows your context window and plan limits above the prompt: SVG rings on the desktop, a text bar in the terminal. It uses one feature that is specific to desktop, the Svg element.

Why the desktop needs different code

Each surface draws a different set of elements. From the plugin API's types:

table · 3 cols
ElementTerminalDesktop
Box, Text, Button, Input, Select, Link, Code, MarkdownYesYes
SvgNoYes
Raster, ImageYesNo
Client (custom surface module)YesYes

On the terminal a Button draws as [ label ]; on the desktop it is a native button. The surface is available as e.surface, and $.ui.resolve(e) returns the right constructors, so the same hook can branch: draw an SVG ring on desktop, draw text on terminal. A mod that assumes Svg exists will fail on the terminal, which is why the code branches.

The data: $.session.usage()

The session usage call returns the live context window, with tokens, window size and percent, and rateLimits, the windows the last API response reported, such as five_hour and seven_day, each with a percentUsed and an optional reset time. Rate limits populate only after the first reply and need a subscription account, and the list is empty when none has a reading, so the mod must handle nothing to show.

The files

.claude-plugin/plugin.json:

json
{
  "name": "limits-ring",
  "version": "0.1.0",
  "description": "Always-visible usage rings above the prompt: context window and plan limits, drawn as SVG on the desktop app and as a text bar in the terminal.",
  "author": { "name": "explainx.ai" }
}

hooks/hooks.json:

json
{ "modules": ["./register.tsx"] }

hooks/register.tsx:

tsx
import type { Register } from 'claude-code'

type Reading = { label: string; percent: number }

const LABELS: Record<string, string> = {
  five_hour: '5h',
  seven_day: 'week',
  spend_limit: 'spend',
}

const color = (percent: number) =>
  percent >= 90 ? '#e5484d' : percent >= 70 ? '#f5a524' : '#30a46c'

const ring = ({ label, percent }: Reading) => {
  const radius = 14
  const circumference = 2 * Math.PI * radius
  const filled = (Math.min(percent, 100) / 100) * circumference

  return (
    '<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">' +
    '<circle cx="20" cy="20" r="14" fill="none" stroke="#8884" stroke-width="4"/>' +
    `<circle cx="20" cy="20" r="14" fill="none" stroke="${color(percent)}" stroke-width="4"` +
    ` stroke-linecap="round" stroke-dasharray="${filled.toFixed(1)} ${circumference.toFixed(1)}"` +
    ' transform="rotate(-90 20 20)"/>' +
    `<text x="20" y="24" text-anchor="middle" font-size="10" fill="currentColor">${Math.round(percent)}</text>` +
    '</svg>'
  )
}

const bar = (percent: number) => {
  const filled = Math.round((Math.min(percent, 100) / 100) * 10)

  return '#'.repeat(filled) + '-'.repeat(10 - filled)
}

export const register: Register = on => {
  on('turn.complete', async ($, e, next) => {
    $.ui.invalidate('ui.render')

    return next(e)
  })

  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
    if (e.props.hasSurvey) {
      return next(e)
    }

    const usage = await $.session.usage()
    const readings: Reading[] = []

    if (usage.context.percent !== undefined) {
      readings.push({ label: 'context', percent: usage.context.percent })
    }

    for (const limit of usage.rateLimits) {
      readings.push({
        label: LABELS[limit.kind] ?? limit.kind,
        percent: limit.percentUsed,
      })
    }

    if (readings.length === 0) {
      return next(e)
    }

    const ui = $.ui.resolve(e)

    if (e.surface === 'desktop') {
      const { Box, Text, Svg } = ui as ReturnType<typeof $.ui.resolve> & {
        Svg: typeof ui.Box
      }

      return (
        <Box>
          {readings.map(one => (
            <Box key={one.label}>
              <Svg
                source={ring(one)}
                alt={`${one.label} used: ${Math.round(one.percent)} percent`}
                width={40}
                height={40}
              />
              <Text dimColor>{one.label}</Text>
            </Box>
          ))}
        </Box>
      )
    }

    const { Box, Text } = ui

    return (
      <Box>
        <Text dimColor>
          {readings
            .map(one => `${one.label} ${bar(one.percent)} ${Math.round(one.percent)}%`)
            .join('   ')}
        </Text>
      </Box>
    )
  })
}

How it works

  • turn.complete invalidates the render, so the rings refresh after every turn instead of showing stale numbers.
  • The AbovePrompt hook reads usage, builds a list of readings, and returns nothing when there is nothing to show by calling next(e), which leaves the slot alone.
  • The branch on e.surface draws SVG on the desktop and a text bar elsewhere. The SVG source is a string, and alt is required, because a surface without the element draws nothing else of it, so the alt text doubles as the accessible description.
  • Colors turn amber at 70 percent and red at 90 percent, using fixed hex values that read on both light and dark themes.

Validate and test

Run strict validation and the tests:

bash
claude plugin validate limits-ring --strict
claude plugin test limits-ring

Validation passed strict mode, listing the hooks turn.complete and ui.render on AbovePrompt and the calls $.session.usage, $.ui.invalidate and $.ui.resolve. The tests stand in for the engine's usage data, mount the band on each surface, and assert what appears:

tsx
import { expect, test } from 'claude-code/testing'

const usage = {
  startedAt: 0,
  context: { tokens: 84000, window: 200000, percent: 42 },
  rateLimits: [
    { kind: 'five_hour', percentUsed: 63.5 },
    { kind: 'seven_day', percentUsed: 12 },
  ],
}

for (const surface of ['terminal', 'desktop'] as const) {
  test(`the band shows context and plan limits on ${surface}`, async ($, on) => {
    on('session.usage', () => ({ value: usage }) as never)

    const band = await $.ui.mount({
      plugin: 'limits-ring',
      surface,
      component: 'AbovePrompt',
      props: { hasSurvey: false },
    })

    const found = await band.find({ type: surface === 'desktop' ? 'Svg' : 'Text' })

    expect(found).toBeDefined()

    await band.unmount()
  })
}

test('the band stays out of the way when nothing is reported', async ($, on) => {
  on('session.usage', () => ({ value: { startedAt: 0, context: { window: 200000 }, rateLimits: [] } }) as never)
  on('ui.render', ($, e) => {
    const { Text } = $.ui.resolve(e)

    return <Text>engine band</Text>
  })

  const band = await $.ui.mount({
    plugin: 'limits-ring',
    surface: 'desktop',
    component: 'AbovePrompt',
    props: { hasSurvey: false },
  })

  expect(await band.find({ type: 'Svg' })).toBeUndefined()
  expect(await band.find({ type: 'Text', text: /engine band/ })).toBeDefined()

  await band.unmount()
})

All three pass: the terminal finds its text, the desktop finds its Svg, and an empty reading leaves the slot to the engine. Two details cost us time: a test must stand in for the engine's session.usage with a hook that returns { value }, and a stand-in for ui.render must return a real element, which is why the last test is a .tsx file.

What these tests do not prove is how the desktop app paints the ring. The kit verifies hooks and element trees, never a surface's paint. Open the mod in the real app and look at it.

Questions from the thread

"Can we share mods?"

Yes. Publish the folder in a GitHub repository with a .claude-plugin/marketplace.json, and others add it with claude plugin marketplace add owner/repo and install by name. Our official TypeScript plugin guide covers distribution, and the Claude Code commands reference lists the plugin commands.

"I could already see limits by clicking the usage circle. What is the use?"

A fair question. The built-in indicator needs a click; a mod can keep numbers always visible, color them as you approach a limit, or add behavior such as a toast at 90 percent. Whether that is worth installing is a taste call, and a mod you do not use is clutter.

"Can you send the prompt?"

The prompt in Method 2 above is a working template. Tailor the layout and thresholds to your habits.

Safety on the desktop

A mod runs with the same access Claude Code has on your machine. It can read and write files your account can, see environment variables and settings, see every prompt and tool call, and spend your usage. Reports of the documentation say a mod can restyle much of the interface but not the permission prompt, which is a deliberate guardrail.

Practical steps: read the source, prefer mods from authors you can verify, run claude plugin validate to see what a mod hooks and calls, and know the off switches. Claude Code has a safe mode that disables all mods for a session and a setting that turns off all hooks, and organizations can restrict which mods load. Our permission modes explainer covers the controls that actually gate actions.

Troubleshooting on the desktop

table · 2 cols
SymptomLikely cause and fix
Plugin browser missingYou are in a cloud or WSL session; open a local or SSH session
Marketplace not listedAdd it from a terminal with claude plugin marketplace add, then reopen the browser
Installed but not activeRun /reload-plugins, or restart the app
Pane never appearsUpdate the desktop app; panes need a build that can place them
Band missingRate limits appear only after the first reply and need a subscription account
Local folder not loadingUse CLAUDE_CODE_PLUGIN_DIRS, or add the folder as a marketplace from the terminal
Mod misbehavesOpen the Errors tab in the /plugin panel and run claude plugin list in a terminal

Honest limitations

  • We did not run the desktop app. Install steps come from Anthropic's documentation, and UI labels may differ by version.
  • The CLAUDE_CODE_PLUGIN_DIRS route is documented in the plugin reference but unverified by us in the desktop app.
  • The Usage Ring tests check hooks and element trees on both surfaces, not painted pixels.
  • Community mod behaviors are as described in their READMEs.
  • The mods API is new and may change.

Bottom line

On the desktop, mods are one tab away: install from +, Plugins, Add plugin, add community marketplaces once from a terminal, and ask Claude to build the rest. Use the desktop's Svg element for richer visuals, branch on e.surface, and keep a text fallback. Test on both surfaces, then look at it in the real app.

Related on explainx.ai

  • How to build a Claude Code mod: a step-by-step tutorial
  • Claude Code mods: official TypeScript plugin guide
  • Awesome Claude Code Mods: 50 open-source mods
  • Claude Code mods: Anthropic opens a community extension layer
  • Claude Code permission modes explained
  • Claude Code commands: complete reference
  • Opus 5.5 task cost in Claude Code
  • Top 25 Claude plugins

Sources: Anthropic: Desktop application · Anthropic: Install and manage plugins · GitHub issue 52147: local plugin installation in the desktop app

Built and verified with the plugin test kit on Claude Code 2.1.288 on October 3, 2026. Desktop UI labels and the mods API may change.

Spotted something out of date? Let us know.
Yash Thakker

Written by

Yash Thakker

Yash is an AI expert with over 300K learners. Join his workshops →

View Yash Thakker in People in AI →

Related posts

Oct 3, 2026

Awesome Claude Code Mods: 50 Open-Source, MIT-Licensed Mods You Can Install Today

Anthropic opened Claude Code to mods on October 1, 2026. Within days we published awesome-claude-code-mods: 50 independent, MIT-licensed mods for session diagnostics, Git, repository viewing, workspace notes, utilities and workflow control. Here is how to install them, which ones are worth trying first, and what they can and cannot do.

Oct 3, 2026

How to Build a Claude Code Mod: A Step-by-Step Tutorial With Screenshots

Claude Code mods are plugins of function hooks that can draw UI, add commands and react to tool calls. This tutorial builds one from scratch, a Touched Files mod that shows how many files Claude has edited, and shows real screenshots of it running, plus the validate and test output.

Oct 2, 2026

Claude Code Mods: Official TypeScript Plugin Guide

On October 1, 2026 Addy Osmani published Anthropic's official getting-started guide for Claude Code mods on claude.dev. This is the GA how-to: version 2.1.287+, JavaScript or TypeScript modules inside plugins, observe/rewrite/answer hooks, the $ API, Token Weather, Blast Radius, Replay Theater, and how you share a mod with /plugin.