Smart UI MCP Server

A Model Context Protocol server that gives any AI coding assistant deep, accurate knowledge of the Smart UI Web Components library — 110+ components, runnable examples for 5 frameworks, zero hallucinations.

v1.0.0 110 components 5 frameworks Node 18+

01What this is and why you want it#

smart-ui-mcp-server is a Model Context Protocol server that gives any MCP-aware coding assistant (Claude Code, Claude Desktop, Cursor, GitHub Copilot Chat in agent mode, Continue, Windsurf, Zed…) deep, accurate knowledge of the Smart UI Web Components library.

It exposes:

  • 110+ Smart UI components with the full API surface — every property, method, event, CSS variable, and allowed-value enum, sourced from the official component metadata.
  • The exact module import and theme stylesheet stack for any component — no more guessing whether the property is editing or editable, or which theme files to load and in what order.
  • Demo browsing — the assistant can list and read every demo your repo ships, per framework (Web Components, React, Angular, Vue).
  • Documentation browsing — the same for /documentation topics (overview.htm, api.htm, accessibility.htm, plus framework-specific guides for Angular, Blazor, React, Vue).
  • A code generator that emits a complete runnable file for Web Components, React, Angular, Vue, and Blazor, with the right imports, theme stylesheet, attribute markup, and post-mount property assignments for complex options.

Why this beats freehand prompting

Without the MCP, an assistant has to guess Smart UI API names from training data. With the MCP, the assistant queries the actual schema before writing code. Concretely:

  • It writes editing (the real property name) instead of hallucinating editable.
  • It emits the exact paths that exist in your node_modulessmart.default.css + fluent/light/smart.fluent.css for the theme stack, and a single smart.grid.js import that already bundles every sub-module — not a guess.
  • It can crib a working pattern from demos/grid/grouping/ instead of inventing one.

0260-second quick start#

# 1. Install Claude Code (skip if you already have it)
npm install -g @anthropic-ai/claude-code

# 2. Register the Smart UI MCP server
claude mcp add smart-ui npx smart-ui-mcp-server

# 3. Open any project and ask:
claude
> Use the smart-ui MCP. Generate a Web Components page with a smart-grid
> bound to a 10-row JSON array of employees (id, name, position, salary),
> with sorting and paging enabled. Theme fluent.

You'll get a complete runnable index.html with the right tag, imports, and config.

03Installation#

From npm (recommended)

# global install
npm install -g smart-ui-mcp-server

# or call directly with npx (no global install needed)
npx smart-ui-mcp-server

The package bundles a snapshot of the Smart UI API metadata (110 components, ~4 MB unpacked) so it works zero-config straight after install. No live Smart UI checkout is required for the core tools.

From a tarball

If you received smart-ui-mcp-server-1.0.0.tgz directly:

npm install -g ./smart-ui-mcp-server-1.0.0.tgz

From source

git clone <repo-url>
cd smart-ui-mcp-server
npm install
npm test          # 45 smoke tests should pass
npm start         # run the server over stdio (for manual probing)

Requirements

  • Node.js 18 or newer (uses ESM and the modern fs promises API).
  • Any MCP-capable assistant. Each one has its own config format — see the next section.

04Connecting your assistant#

The Smart UI MCP server speaks MCP over stdio. Every assistant below uses the same underlying command (npx smart-ui-mcp-server) — only the config file differs.

Claude Code (CLI)

The cleanest path. From any directory:

claude mcp add smart-ui npx smart-ui-mcp-server

To verify: claude mcp list. To remove later: claude mcp remove smart-ui.

Add it project-locally instead by passing --scope project, which writes to .mcp.json in the project root and is auto-picked-up by anyone who clones the repo.

Claude Desktop

Edit claude_desktop_config.json:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "smart-ui": {
      "command": "npx",
      "args": ["smart-ui-mcp-server"]
    }
  }
}

Restart Claude Desktop. The tools should appear under the 🔨 hammer icon in the message input.

VS Code — GitHub Copilot Chat (agent mode)

Create .vscode/mcp.json in your workspace root:

{
  "servers": {
    "smart-ui": {
      "type": "stdio",
      "command": "npx",
      "args": ["smart-ui-mcp-server"]
    }
  }
}

Open Copilot Chat, switch to agent mode, and ask a Smart UI question. The tools appear automatically.

Cursor

Cursor uses ~/.cursor/mcp.json (or <workspace>/.cursor/mcp.json for project scope):

{
  "mcpServers": {
    "smart-ui": {
      "command": "npx",
      "args": ["smart-ui-mcp-server"]
    }
  }
}

Continue

Edit ~/.continue/config.json:

{
  "experimental": {
    "modelContextProtocolServers": [
      {
        "transport": {
          "type": "stdio",
          "command": "npx",
          "args": ["smart-ui-mcp-server"]
        }
      }
    ]
  }
}

Windsurf

Edit ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "smart-ui": {
      "command": "npx",
      "args": ["smart-ui-mcp-server"]
    }
  }
}

Zed

Add to ~/.config/zed/settings.json:

{
  "context_servers": {
    "smart-ui": {
      "command": {
        "path": "npx",
        "args": ["smart-ui-mcp-server"]
      }
    }
  }
}

05Verifying the connection#

After restarting your assistant, ask:

Try this prompt: What Smart UI components do you know about?

A working setup will respond with a list of 100+ smart-* tags. If it says “I don't have access to that information” or makes up tag names that don't start with smart-, the MCP isn't wired in — check your config file path and restart the assistant.

You can also test directly from a shell:

echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | npx smart-ui-mcp-server

(Press Ctrl+C after a second.) You should see a JSON response listing all 13 tools.

06The 13 tools at a glance#

ToolWhen the assistant calls it
list_widgets“What components are there?” / fuzzy lookup by keyword.
get_widgetFirst call for any new component — gets counts, tag, class, module file, features.
get_widget_propertiesBefore writing config — finds the exact property name and its allowed values.
get_widget_methodsWhen the prompt mentions an imperative action (export, addRow, refresh).
get_widget_eventsWhen wiring event handlers (onChange, onClick, onCellEditEnd).
get_widget_css_variablesCustom theming / CSS variable overrides.
get_widget_typesWhen a property has an enum type and the assistant needs the legal values.
get_required_scriptsAlways called before generating code — gives the exact module URLs and the theme link.
list_demos“What demos do you ship for X?” — returns folder names.
get_demo_codeRead a specific demo folder (returns every source file in it).
list_documentationLists /documentation/<topic>/*.htm files for a component.
get_documentationRead a specific doc file (overview.htm, api.htm, accessibility.htm).
generate_exampleEmits the final runnable file for the chosen framework.
Tip: You don't need to memorise these — the assistant chooses for you. But knowing they exist lets you nudge it with prompts like “first read the existing grid grouping demo, then…”.

07How to refer to a component#

Every Smart UI component has three equivalent identifiers; the MCP accepts any of them in every tool call:

  • bare slugbutton, dropdownlist, ganttchart
  • custom-element tagsmart-button, smart-drop-down-list, smart-gantt-chart
  • class nameButton, DropDownList, GanttChart
button smart-button Button

All three resolve to the same component record. The generator always emits the canonical Smart UI form in the output code — <smart-grid> for HTML / Vue, <Grid> for React / Blazor, and <smart-grid> with GridComponent for Angular.

08Worked tutorial — Grid (Web Components)#

Prompt: Use the smart-ui MCP. Build a Web Components page with a smart-grid of employees (id, name, position, salary). Enable sorting, paging, and filtering. Theme fluent.

What happens under the hood

  1. The assistant calls get_widget("grid"). It learns the tag is smart-grid, the class is Grid, the available feature modules include sort, filter, pager, edit, group, export, reorder, select, tree, and the shorthand attributes include sortable, filterable, pageable, editable, groupable, selectable, columnResize, columnReorder.
  2. Because the prompt only asks to switch features on, the assistant picks the shorthand attributes (cleaner config) over the configurable object properties.
  3. It calls get_required_scripts("grid", { theme: "fluent" }) and gets two stylesheet links — smart.default.css (base) plus fluent/light/smart.fluent.css (overlay) — and exactly one module URL: smart.grid.js. That single file already imports every sub-module (sort, filter, pager, edit, …); no extra script tags needed.
  4. It calls generate_example with the merged options.

What you get

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Grid example</title>
    <!-- smart.default.css is the base; smart.fluent.css overlays it -->
    <link rel="stylesheet" type="text/css" href="../../../source/styles/smart.default.css" />
    <link rel="stylesheet" type="text/css" href="../../../source/styles/fluent/light/smart.fluent.css" />
    <!-- smart.grid.js already bundles sort/filter/pager/edit/group/… -->
    <script type="module" src="../../../source/modules/smart.grid.js"></script>
</head>
<body>
    <div id="grid"></div>
    <script type="module">
      window.addEventListener('load', () => {
        new Smart.Grid({
            dataSource: new Smart.DataAdapter({
                dataSource: [
                    { id: 1, name: 'Nancy Davolio', position: 'Sales Rep', salary: 60000 },
                    { id: 2, name: 'Andrew Fuller', position: 'VP Sales',   salary: 95000 },
                ],
                dataFields: [
                    'id: number',
                    'name: string',
                    'position: string',
                    'salary: number',
                ],
            }),
            sortable: true,
            filterable: true,
            pageable: true,
            columns: [
                { label: 'ID',       dataField: 'id' },
                { label: 'Name',     dataField: 'name' },
                { label: 'Position', dataField: 'position' },
                { label: 'Salary',   dataField: 'salary', cellsFormat: 'c0' },
            ],
            appendTo: '#grid',
        });
      });
    </script>
</body>
</html>

Theme application — always layer the overlay on the default base

Required: every Smart UI page must include smart.default.css. It carries the shared variables, layout primitives, and base component styles that all themes build on. Any non-default theme is layered on top, in this order:
<link rel="stylesheet" href="…/source/styles/smart.default.css" />                <!-- base -->
<link rel="stylesheet" href="…/source/styles/fluent/light/smart.fluent.css" />    <!-- overlay -->

Theme paths in the source repo are nested. Each non-default theme has its own subfolder under source/styles/<theme>/ with separate light/ and dark/ variants:

StylesheetSource-repo path
Base (required)source/styles/smart.default.css
Fluent lightsource/styles/fluent/light/smart.fluent.css
Fluent darksource/styles/fluent/dark/smart.fluent-dark.css
Material lightsource/styles/material/light/smart.material.css
Material darksource/styles/material/dark/smart.material-dark.css
Bootstrapsource/styles/bootstrap/light/smart.bootstrap.css
Strata / Tabulasource/styles/<theme>/light/smart.<theme>.css

get_required_scripts(..., { theme: 'fluent' }) returns both URLs already ordered correctly. For dark mode pass dark: true and the overlay flips to the corresponding *-dark.css variant.

The npm-packaged React and Angular distributions flatten the most common themes to the top level (so React's generated code uses smart-webcomponents-react/source/styles/smart.fluent.css directly), but the Vue (smart-webcomponents) package and the source repo both keep the nested layout.

Do NOT set theme="fluent" as an attribute on the component — themes are applied via the CSS stack, not via element attributes. The theme property on a component exists for runtime color-variant switching only.

One script tag per component — sub-modules are bundled

Smart UI's top-level smart.<component>.js ES module already imports every sub-module the component needs. For example, smart.grid.js imports smart.grid.sort.js, smart.grid.filter.js, smart.grid.edit.js, smart.grid.pager.js, smart.grid.group.js, and 20+ others — you load the grid with a single <script type="module"> tag.

The MCP's bundledSubmodules field on get_required_scripts and features field on get_widget show what's included; they're for documentation, not for additional <script> tags.

Shorthand attributes vs full configuration

Smart Grid (and many other components) accepts two parallel ways of toggling behaviour. Prefer the shorthand attributes whenever you only need to switch a feature on or off. They produce cleaner, more declarative HTML.

Shorthand boolean attrFull-config property
sortablesorting = { enabled, mode, … }
filterablefiltering = { enabled, filterRow, … }
pageablepaging = { enabled, pageSize, … }
editableediting = { enabled, mode, … }
groupablegrouping = { enabled, mode, … }
selectableselection = { enabled, mode, … }
columnResizeper-column allowResize plus options
columnReorderper-column allowReorder plus options

Switch to the object-form property only when you need to configure how the feature behaves (filter mode, page size, batch-edit, multi-sort, etc.). The MCP surfaces the available shorthand attrs for each component via get_widget (the shorthandAttributes field) — so a prompt like “add sorting and filtering to the grid” reliably produces sortable filterable rather than the longer object form.

Adjust the path prefix if you ship Smart UI from a different folder:
SMART_UI_SCRIPTS_PREFIX="/assets/smart-webcomponents/" npx smart-ui-mcp-server
Or pass prefix: "/assets/..." directly in the generate_example call when you're prompting.

09Worked tutorial — Scheduler (React)#

Prompt: Use the smart-ui MCP. Build a React App.tsx with a <Scheduler> showing Day, Week, and Month views. Use the fluent theme.

Behind the scenes

  1. get_widget("scheduler") → tag smart-scheduler, class Scheduler.
  2. get_widget_properties("scheduler", { nameFilter: "view" }) → confirms view, viewType, views, plus an enum of allowed view-type strings.
  3. get_widget_types("scheduler") → returns the View enum (day, week, month, agenda, timelineDay…).
  4. generate_example with framework: "react".

Result

import React from 'react';
import { createRoot } from 'react-dom/client';
// Theme stack: base first, overlay second.
import 'smart-webcomponents-react/source/styles/smart.default.css';
import 'smart-webcomponents-react/source/styles/smart.fluent.css';
import { Scheduler } from 'smart-webcomponents-react/scheduler';

function App() {
    return (
        <Scheduler
            view="week"
            views={['day', 'week', 'month']}
        />
    );
}

const root = createRoot(document.getElementById('root'));
root.render(<App />);

Grid-family components — use Smart.DataAdapter

For data-driven components (Grid, Chart, Scheduler, CardView, Kanban, GanttChart, PivotTable, QueryBuilder), the React import becomes { Smart, <Component> } and the data source is wrapped in new Smart.DataAdapter({ dataSource, dataFields }):

import { Smart, Grid } from 'smart-webcomponents-react/grid';

function App() {
    const dataSource = new Smart.DataAdapter({
        dataSource: [
            { id: 1, name: 'Alice', salary: 60000 },
            { id: 2, name: 'Bob',   salary: 75000 },
        ],
        dataFields: ['id: number', 'name: string', 'salary: number'],
    });

    return (
        <Grid
            dataSource={dataSource}
            sortable
            filterable
            pageable
            columns={[
                { label: 'ID',     dataField: 'id' },
                { label: 'Name',   dataField: 'name' },
                { label: 'Salary', dataField: 'salary', cellsFormat: 'c0' },
            ]}
        />
    );
}
Follow-up prompt: Add a sample appointment that recurs weekly on Mondays. The assistant will call get_widget_properties("scheduler", { nameFilter: "data" }) to find dataSource, then get_demo_code("scheduler", "recurring") to see the recurring-event shape your repo ships, and update the code accordingly.

10Worked tutorial — Chart with annotations (Angular)#

Prompt: Use the smart-ui MCP. Generate an Angular component with a Smart Chart (line series, sample sales data, 12 months) and a horizontal threshold line at y=5000.

The MCP path

  1. get_widget("chart") → features include annotations.
  2. get_required_scripts("chart", { features: ["annotations"], theme: "fluent" }) adds smart.chart.annotations.js to the module list.
  3. get_widget_properties("chart", { nameFilter: "annotation" }) → confirms the annotations property and its shape.
  4. generate_example with framework: "angular".
import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { ChartComponent, ChartModule } from '@smart-webcomponents-angular/chart';

// angular.json -> "styles": [
//   "./node_modules/@smart-webcomponents-angular/chart/styles/smart.base.css",
//   "./node_modules/@smart-webcomponents-angular/chart/styles/smart.chart.css",
//   "./node_modules/@smart-webcomponents-angular/chart/styles/smart.common.css",
//   "./node_modules/@smart-webcomponents-angular/chart/styles/smart.fluent.css"  // theme overlay
// ]

@Component({
    selector: 'app-root',
    standalone: true,
    imports: [ChartModule],
    template: `<smart-chart #chart></smart-chart>`,
})
export class AppComponent implements AfterViewInit {
    @ViewChild('chart', { read: ChartComponent, static: false })
    chart!: ChartComponent;

    ngAfterViewInit(): void {
        this.chart.caption = 'Monthly Sales';
        this.chart.seriesGroups = [{
            type: 'line',
            series: [{ dataField: 'sales', displayText: 'Sales' }],
        }];
        this.chart.annotations = [{
            type: 'line',
            xAxis:    { value: 0  }, yAxis:    { value: 5000 },
            xAxisEnd: { value: 11 }, yAxisEnd: { value: 5000 },
            lineColor: '#d33', lineWidth: 2,
        }];
    }
}
Angular pattern: bare <smart-*> element with a template-ref, then configure imperatively in ngAfterViewInit(). This matches every shipped Angular demo and avoids two-way binding pitfalls with object properties.
Package layout: Smart UI for Angular ships as a scoped multi-package on npm — @smart-webcomponents-angular/grid, @smart-webcomponents-angular/chart, etc. Each per-component package ships its own self-contained styles/ folder (smart.base.css + smart.<component>.css + smart.common.css); add all three to angular.json "styles", plus optionally smart.<theme>.css from the same folder for the theme overlay.

11Worked tutorial — Tree + context menu (composition)#

This is the most ambitious type of prompt — the assistant composes two components.

Prompt: Use the smart-ui MCP. Build a Web Components page with a smart-tree showing folders. Wire a smart-menu as a right-click context menu with Add / Rename / Delete actions.

The MCP path

  1. get_widget("tree") — confirms smart-tree with allowDrag, filterable, selectionMode.
  2. get_widget("menu") — confirms smart-menu with mode="dropDown" and a select event.
  3. get_widget_events("tree", { nameFilter: "context" }) — finds an event the assistant can use to position the menu, or falls back to the native contextmenu DOM event.
  4. list_demos("tree") — looks for any shipped right-click pattern; if one exists, get_demo_code reads it.
  5. generate_example is called twice — once for tree, once for menu — and the assistant manually composes them in one HTML file.

The output combines the two generated stubs with a contextmenu listener on the tree that opens the menu at the click coordinates and dispatches the selected action.

12Prompt patterns — a cheat sheet#

The MCP works best when your prompt includes:

  1. An explicit “Use the smart-ui MCP” — primes the assistant to call the tools instead of guessing.
  2. A target framework — webcomponents, react, angular, vue, or blazor.
  3. A theme name if you care — default, fluent, material, bootstrap, strata, tabula.
  4. The features you want by name — e.g. “sorting, paging, filtering, row grouping”. The assistant will resolve these to the right feature modules via get_required_scripts.

Templates

Generate from scratch

Use the smart-ui MCP. Generate a {framework} example of a {component} with {feature list}. Theme {theme}. Data: {description or sample}.

Adapt a shipped demo

Use the smart-ui MCP. List the demos for {component} in {framework}. Pick the {keyword} one, read its source, then adapt it to {what you actually want}.

API exploration

Use the smart-ui MCP. What properties does smart-{component} have for {area}? Show me the allowed values for each.

Migration / fix

Use the smart-ui MCP. Here's my current {component} config — check each property name against the real API and fix any that are wrong.

13Configuration reference#

All env vars are optional.

VariableDefaultPurpose
SMART_UI_REPO_ROOTunsetPath to a local Smart UI checkout. Enables list_demos, get_demo_code, list_documentation, get_documentation, and live feature/theme discovery.
SMART_UI_SCRIPTS_PREFIX../../../source/Prefix injected into generated <script> / <link> URLs.
SMART_UI_REACT_PACKAGEsmart-webcomponents-reactnpm package name used in React imports.
SMART_UI_ANGULAR_PACKAGEsmart-webcomponents-angularnpm package name used in Angular imports.
SMART_UI_VUE_PACKAGEsmart-webcomponentsnpm package name used in Vue imports.

Setting env vars per-client

Most MCP clients pass through env vars from your shell. To set them per-server (instead of globally), use the env field in the client config:

{
  "mcpServers": {
    "smart-ui": {
      "command": "npx",
      "args": ["smart-ui-mcp-server"],
      "env": {
        "SMART_UI_REPO_ROOT": "/Users/me/code/smart-ui-repo",
        "SMART_UI_SCRIPTS_PREFIX": "/assets/smart/"
      }
    }
  }
}

14Local development & contributing#

git clone <repo>
cd smart-ui-mcp-server
npm install

# rebuild the bundled snapshot from a Smart UI checkout
SMART_UI_REPO_ROOT=/path/to/Team npm run build-data

# run the 45-test smoke suite
SMART_UI_REPO_ROOT=/path/to/Team npm test

# run the server interactively (for hand-rolled JSON-RPC probing)
npm start

Adding a new tool

  1. Implement the logic in src/<area>.js (or a new module).
  2. Register it in src/server.js with server.registerTool(...). Use Zod to declare the input schema.
  3. Add a smoke test in test/smoke.js.
  4. Update README.md and this tutorial's tools table.

Updating the bundled API metadata

Whenever Smart UI ships new components or new properties:

SMART_UI_REPO_ROOT=/path/to/Team npm run build-data

This refreshes data/api/*.json, data/features.json, data/themes.json, data/widget-scripts.json, and data/snapshot.json.

15Troubleshooting#

“Tool not found” / the assistant doesn't see Smart UI

  • Restart the assistant after editing the MCP config.
  • Confirm the config file path is the one your assistant actually reads (Claude Desktop on Windows is %APPDATA%\Claude\, not %LOCALAPPDATA%).
  • Run npx smart-ui-mcp-server from a terminal — if it starts without errors, the binary is fine and the issue is in your client config.

“Smart UI API metadata not found”

The bundled snapshot lives in data/api/. If you installed via npm install and still see this, your install was incomplete. Reinstall:

npm uninstall -g smart-ui-mcp-server
npm install -g smart-ui-mcp-server

If you're running from source, run npm run build-data once.

Generated examples reference modules that don't exist on disk

The default SMART_UI_SCRIPTS_PREFIX (../../../source/) assumes a folder layout like <repo>/demos/<widget>/<example>/index.html with Smart UI sources at <repo>/source/. If your project layout differs, override the prefix — see Configuration reference.

list_demos returns an empty array

The demos browser only works against a local Smart UI checkout. Set SMART_UI_REPO_ROOT to a folder that contains a demos/ (and optionally react/demos/, smart-angular/demos/, vue/vue-3/demos/) subfolder.

The assistant emits the wrong tag name

If the generated code uses an unfamiliar prefix or a tag that doesn't start with smart-, the assistant is guessing instead of calling the MCP. Re-prompt with:

Use the smart-ui MCP first. Call get_widget for each component you reference and use the exact tag field it returns.

“Unknown widget” errors

Smart UI uses kebab-case multi-word tags. If you call getWidget("dropdown-list") or "DropDownList" it resolves fine; if you misspell ("dropdownllist") it returns an error. Run list_widgets({ filter: "drop" }) to find the right id.

16FAQ#

Does this require an Anthropic API key / Claude subscription?

No. The MCP server itself is plain Node.js with no API calls. It only provides knowledge to whatever assistant you've connected — that assistant provides its own model.

Does it work offline?

Yes. After npm install, all metadata is on disk. No network calls at runtime.

Can I use it with my own private Smart UI fork?

Yes. Set SMART_UI_REPO_ROOT to your fork's root, and either run npm run build-data to refresh the bundled snapshot, or leave it pointed at the live fork — the server reads JSON / source / demos / docs directly. You can also override the npm package names emitted in generated React / Angular / Vue code via SMART_UI_REACT_PACKAGE etc.

Does it cover Smart UI for Blazor too?

Yes — generate_example with framework: "blazor" emits Razor markup using @using Smart.Blazor and PascalCased element tags (<Button Value="…" />).

Can I add my own custom-component metadata?

Yes. Drop additional <slug>.json files into the data folder your SMART_UI_REPO_ROOT points at and rerun npm run build-data. The server picks them up alongside the official components — they're available through every tool just like the built-ins.

How big is the bundled snapshot?

~4 MB unpacked, ~720 KB packed. The bulk is the per-component API JSON.

How do I report a bug?

Open an issue at github.com/HTMLElements/smart-webcomponents/issues with: the prompt you used, the tool call(s) the assistant made, the actual vs expected output, and your assistant + MCP server versions.