Template

Progress rings widget for MCP Apps

Show how close each goal is in one look. Every ring fills to the share reached as it appears, with the percentage in the middle and the value against the target below.

Add a UI to your MCP server

We present to the board tomorrow. How are we doing against this quarter's goals?

Used show_progress_rings

The house design: black outline, butter yellow and a marker swipe under the title.

When to use progress rings

Rings answer "how far along are we?" for several goals at once, and they make the gap visible in a way a list of numbers does not. They fit any data that comes as a current value and a target:

  • Sales quotas and team targets, one ring per person or per region.
  • OKRs and quarterly goals, where a mix of units (revenue, customers, a score) sits side by side as percentages.
  • Budgets: spent against allocated, per department or per project.
  • Habits and personal goals: workouts this month, pages read, savings.
  • Fundraising or course completion, where the target is fixed and the value moves.

If what you have is one figure and how it changed, metrics fits better. If you need to show how a total splits into parts, use the donut chart.

How the model fills the rings

Each entry in rings needs a label, a value and a goal. The schema tells the model that value cannot be negative and goal must be greater than zero, which rules out a ring that divides by zero. Optional unit is shown after the target, and optional color takes any CSS color when a goal has a color of its own; leave it out and the ring takes the next color of the design palette.

The useful detail is what the model does not do: it does not compute percentages. It passes the raw value and target from your tool, and the Liquid template works out the share, rounds it and draws the arc. That removes a source of arithmetic mistakes and keeps the numbers in the ring identical to the ones in your data. A ring that reaches or passes its goal stops full at 100% and gets a done class you can style.

The tool description lists "targets, quotas, budgets or habits", which tells the model when to pick this widget over a chart.

What it does in the chat

Every ring is an SVG circle whose stroke animates from empty to its percentage over a little more than a second, one after another, so the eye follows them in order. The percentage sits in the middle in the design's display type and the "value of target" line sits under the label. The grid wraps to the width of the chat, so a narrow window stacks the rings and a wide one lines them up. Animation stops for people who prefer reduced motion.

There is no script and no library: the whole widget is SVG and CSS, self-contained, with no extra CSP domains. It declares no host actions.

Make it yours

The rings change character with the design: hairline rings with square ends in Nordic, gradient strokes that glow in Aurora, fat flat bands between black rims in Brutalist, thin rings with big serif figures in Editorial, glowing tubes on a tick dial in Neon. Brand-inspired designs and your organization's own work too. The word between value and target follows the language the host reports (English or Spanish) and lives in the markup if you want other wording. Add a deadline field and show it under each ring, or style done rings with a check.

Take it to your server

Link the published widget from your MCP server so changes reach it without a deploy, or download the HTML and manifest: add UI to your MCP server covers both paths. For the background on how widgets and tools fit together, start with what MCP Apps are.

The data it takes

FieldTypeWhat it is
titlerequiredstring
subtitlestring
ringsrequiredobject[]
└ labelrequiredstring
└ valuerequirednumber
└ goalrequirednumber
└ unitstring
└ colorstringAny CSS color. Leave it out to use the design palette.

Sample data

{
  "title": "Quarterly goals",
  "subtitle": "Q4 2026",
  "rings": [
    {
      "label": "Sales",
      "value": 84000,
      "goal": 120000,
      "unit": "€"
    },
    {
      "label": "New customers",
      "value": 18,
      "goal": 20
    },
    {
      "label": "NPS",
      "value": 61,
      "goal": 70
    },
    {
      "label": "Training sessions",
      "value": 9,
      "goal": 12
    }
  ]
}

The tool that shows it

A widget made from this template is shown by one tool. The model reads its description to decide when to call it and fills the input schema with the data. This is the definition Widgetry exports:

{
  "name": "show_progress_rings",
  "title": "Progress rings",
  "description": "Shows progress towards goals as animated rings: each ring has a label, the current value and the goal, and fills to the percentage reached. Use it for targets, quotas, budgets or habits.",
  "inputSchema": {
    "type": "object",
    "required": [
      "title",
      "rings"
    ],
    "properties": {
      "title": {
        "type": "string"
      },
      "subtitle": {
        "type": "string"
      },
      "rings": {
        "type": "array",
        "items": {
          "type": "object",
          "required": [
            "label",
            "value",
            "goal"
          ],
          "properties": {
            "label": {
              "type": "string"
            },
            "value": {
              "type": "number",
              "minimum": 0
            },
            "goal": {
              "type": "number",
              "exclusiveMinimum": 0
            },
            "unit": {
              "type": "string"
            },
            "color": {
              "type": "string",
              "description": "Any CSS color. Leave it out to use the design palette."
            }
          }
        }
      }
    }
  }
}

Your first widget, in the chat in minutes

Pick a template, drop in your data and see it as ChatGPT or Claude will show it. Then link it from your MCP server, or let your agent build the next one.