About build-mcp-use-server
A development skill for building MCP servers and ChatGPT apps with mcp-use. It guides project inspection, implementation, and verification, with emphasis on installed API types and OpenAI Apps SDK UI compliance.
Key features
- 01Inspect project structure and mcp-use version
- 02Guide server and view implementation
- 03Cover ChatGPT extensions and authentication
- 04Enforce typed, stateless server patterns
- 05Verify tool calls and views with the CLI
Use cases
- 01Scaffold a mcp-use ChatGPT app
- 02Add interactive views to an MCP server
- 03Resolve legacy API and migration issues
MCP support
The skill guides developers in building MCP servers with mcp-use's MCPServer and validating implementations through real tool calls using mcp-use client.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ yigitkonur/ skills-by-yigitkonur --skill build-mcp-use-serverAfter installing, type/build-mcp-use-serverin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
README.mdagents/openai.yamlreferences/advanced-features.mdarchestra-and-multi-host.mdauth.mdchatgpt-apps-ui.mdchatgpt-extensions.mdmigration.mdserver.mdskills-over-mcp.mdverification.mdviews.mdscripts/audit-server-readiness.shaudit-server-readiness.sh.mdcheck-mcp-use-version.shcheck-mcp-use-version.sh.mdscaffold-mcp-use-server.shscaffold-mcp-use-server.sh.mdDetails
From the project READMEBuild with mcp-use & ChatGPT Apps
Treat the installed mcp-use package, its exported types, generated declarations (mcp-env.d.ts), and official documentation at docs.mcp-use.com as the authoritative sources of truth.
This skill grounds on the official MCPServer API surface, applies the lean architecture of the official mcp-use skills, and ensures that every ChatGPT app built with mcp-use is compliant with the OpenAI Apps SDK UI (@openai/apps-sdk-ui) by default.
Workflow
- Inspect Project: Check
package.json, server entry, exported tool refs,views/,skills/, and the installedmcp-useversion (Node.js >= 22.22.2 required). - Scaffold or Initialize: For greenfield projects, scaffold with the official CLI:
(Usenpx create-mcp-use-app@latest my-app --template mcp-apps--template mcp-serverfor headless tool servers). - Read Focused References:
- Server: Tools, resources, prompts, middleware, request context, result envelopes, and
ServerConfig. - Views: MCP Apps, React hooks, interaction channels, host capabilities, and CSP.
- OpenAI Apps SDK UI Guide: Full 29 components, hooks, Tailwind 4 setup, design tokens, and theme sync.
- ChatGPT Plugin Extensions: Launch entrypoints (
global,thread,file), nativeserver.settings(), tool icons,useDeepLink(), anduseModelContext(). - Authentication: OAuth provider adapters,
mixedAuth: true, tool-levelsecuritySchemes, and verified identity onctx.auth. - Skills over MCP: Reusable agent workflows and conventions under
skills/. - Advanced Features: OpenAPI (
fromOpenAPI), proxying, notifications, subscriptions, and interactive elicitation (inputRequired). - Multi-Host & Archestra: Deploying to ChatGPT, Claude Desktop, and Archestra (installed-server route vs owned apps).
- Migration: Upgrading legacy v1 patterns, deprecated imports, and obsolete helpers to v2.
- Verification: Verification loop with
mcp-use typecheck,mcp-use dev,mcp-use client, andmcp-use screenshot.
- Server: Tools, resources, prompts, middleware, request context, result envelopes, and
- Implement Against Installed Types: Always export static tool references and default-export the server instance.
- Verify Deterministically: Drive real tool calls through
mcp-use clientand capture view screenshots withmcp-use screenshot.
Core Invariants
- Root Server Import: Always import
MCPServerand server primitives from"mcp-use"(the legacy"mcp-use/server"subpath is obsolete). - Export Static Tool Refs: Always export every tool reference (
export const myTool = server.tool(...)). The TypeScript compiler derives view and client contracts inmcp-env.d.tsfrom exported tool names. - Default Server Export: Always default-export the server instance (
export default server;). The CLI runner (mcp-use dev,build,start) owns the HTTP listener and Vite view pipeline. - Definition-First Tools & Standard Schema: Define tools with
server.tool(definition, callback). Tool arguments useinputSchema; model prompts useschema. Always describe model-filled schema fields. - Raw Result Envelopes: Tool callbacks must return raw
CallToolResultenvelopes ({ content: [...], structuredContent?: ... }). Do not use deprecated v1 helpers (text(),object(),widget()). Expected failures return{ isError: true, content: [...] }; unexpected failures throw. - Views Architecture: Put each view at
views/<name>/view.tsx. Each view must have exactly one owning tool with matchingview: { name: "<name>" }and a mandatoryoutputSchema. Return matchingstructuredContent. - OpenAI Apps SDK UI Compliance: Every ChatGPT app must install
@openai/apps-sdk-ui, import@openai/apps-sdk-ui/css, configure Tailwind 4 with@source, and wrap interactive components with<AppsSDKUIProvider linkComponent="a">. - Stateless Request Model: Server instances are request-scoped. Never use module globals or in-memory session registries for multi-request state or authentication. Use
ctx.requestStateor an external database. - Strict CLI Flag Syntax: Boolean CLI flags reject inline values in v2.7.2+ (
--tunnel=falsefails; use--tunnelor omit).
Guardrails & Common Anti-Patterns
- Never import non-existent
@openai/apps-sdk-uicomponents:Carddoes not exist in@openai/apps-sdk-ui. Surface cards are standard<div>elements styled with Tailwind utility classes (rounded-2xl border border-default bg-surface p-4 shadow-sm).AvatarGroupandCopyTooltipare official@openai/apps-sdk-uicomponents (from@openai/apps-sdk-ui/components/Avatarand@openai/apps-sdk-ui/components/Tooltip).CartandCheckmarkdo not exist in@openai/apps-sdk-ui/components/Icon. UseCheck,CheckCircle,Search,Plus,Settings, etc.
- Never omit
linkComponenton<AppsSDKUIProvider>: The TypeScript contract requireslinkComponent(e.g.<AppsSDKUIProvider linkComponent="a">). - Never read
useToolContext().toolOutputun-narrowed:useToolContext()is a discriminated union. Whenstatus === "pending",toolOutputisundefined. Always handlestatus === "pending"andstatus === "error"before accessingtoolOutput. Narrowerror instanceof ToolCancelledErrorfor cancelled actions. - Configure
websiteUrl,icons, andloggingat the top level ofMCPServer:icons?: Icon[],websiteUrl?: string, andlogging?: LoggingOptionsare top-level properties onServerConfig. Do not nest them in a fictitiousbranding: { ... }object (server.brandingis a read-only getter).- Server logging level is configured via
logging: { enabled: true, level: "info" }or overridden by theMCP_USE_LOG_LEVELenvironment variable.
- Never use legacy v1
ctx.elicit(): Removed in v2.0.0 during the transition to the stateless request model. Use official protocol primitives:inputRequired,inputRequired.elicit,inputRequired.elicitUrl,inputResponse, andacceptedContent. - Never use wrong CLI commands:
- Client connect syntax:
mcp-use client connect <name> <url> [options](e.g.npx mcp-use client connect dev http://localhost:3000/mcp). - Screenshot syntax:
mcp-use screenshot (--server <name> | --mcp <url>) --tool <name> [args...] [options].
- Client connect syntax:
Base MCPServer Architecture
Ground on MCPServer and Server Documentation:
import { MCPServer } from "mcp-use";
const server = new MCPServer({
name: "catalog-server",
version: "1.0.0",
title: "Inventory & Catalog Manager",
description: "Manage product catalog, stock levels, and search items.",
instructions: "Use lookup-item before creating an order. Confirm stock availability.",
basePath: "/mcp", // default route
host: "127.0.0.1", // "0.0.0.0" for public hosting
port: 3000,
legacy: "stateless", // "stateless" (default) or "reject"
websiteUrl: "https://example.com",
icons: [
{ src: "icons/logo.svg", mimeType: "image/svg+xml", sizes: ["20x20"] }
],
logging: { enabled: true, level: "info" }, // Overridden by MCP_USE_LOG_LEVEL env var
skills: true, // Auto-discover conventional skills/ directory
cors: {
origin: "*",
credentials: true,
},
});
export default server;
Tool Definitions & Result Envelopes
Documentation: Server Tools
import { MCPServer } from "mcp-use";
import { z } from "zod";
const server = new MCPServer({ name: "catalog", version: "1.0.0" });
export const getProduct = server.tool(
{
name: "get-product",
title: "Get Product Details",
description: "Fetch product metadata, pricing, and live inventory by SKU.",
icons: [
{ src: "icons/product.svg", mimeType: "image/svg+xml", sizes: ["20x20"] }
],
inputSchema: z.object({
sku: z.string().describe("Stock Keeping Unit identifier (e.g. 'PROD-100')"),
}),
outputSchema: z.object({
sku: z.string(),
title: z.string(),
price: z.number(),
inStock: z.boolean(),
}),
annotations: {
readOnlyHint: true,
openWorldHint: false,
},
visibility: "model", // "model" (default) or "app" (hidden from LLM, visible to UI views)
view: {
name: "product-card", // binds to views/product-card/view.tsx
description: "Interactive product display card",
},
},
async ({ sku }, ctx) => {
// Expected operational failure
if (!sku.startsWith("PROD-")) {
return {
isError: true,
content: [{ type: "text", text: `Invalid SKU format: ${sku}` }],
};
}
const data = { sku, title: "Precision Screwdriver", price: 29.99, inStock: true };
return {
content: [{ type: "text", text: `Product ${data.title} (${sku}) is in stock at $${data.price}` }],
structuredContent: data,
_meta: { internalId: "db_98213" }, // Private to view, hidden from LLM
};
}
);
export default server;
ChatGPT Apps & OpenAI Apps SDK UI Compliance
Documentation:
- ChatGPT Plugin Extensions
- OpenAI Apps SDK UI Repository
- Apps SDK UI Storybook Docs
- ChatGPT Apps UI Reference
1. Installation & Styles Setup
npm install @openai/apps-sdk-ui tailwindcss @tailwindcss/vite
In views/main.css (or view-specific CSS):
@import "tailwindcss";
@import "@openai/apps-sdk-ui/css";
@source "../node_modules/@openai/apps-sdk-ui";
2. View Component with Apps SDK UI & Theme Sync
Create views/product-card/view.tsx:
import "./main.css";
import React, { useEffect } from "react";
import { useToolContext, useModelContext, useViewTheme, ToolCancelledError, type ViewConfig } from "mcp-use/react";
import { AppsSDKUIProvider } from "@openai/apps-sdk-ui/components/AppsSDKUIProvider";
import { Button } from "@openai/apps-sdk-ui/components/Button";
import { Badge } from "@openai/apps-sdk-ui/components/Badge";
import { applyDocumentTheme } from "@openai/apps-sdk-ui/theme";
import { Check, Plus } from "@openai/apps-sdk-ui/components/Icon";
export const viewConfig = {
autoResize: true,
displayModes: ["inline", "fullscreen"],
preferredDisplayMode: "inline",
} satisfies ViewConfig;
export default function ProductCardView() {
const { status, toolInput, toolOutput, error } = useToolContext<"get-product">();
const theme = useViewTheme();
const { add } = useModelContext();
// Synchronize host theme with Apps SDK UI data-theme attribute
useEffect(() => {
if (theme) applyDocumentTheme(theme);
}, [theme]);
if (status === "pending") {
return <div className="p-4 text-secondary text-sm">Loading SKU {toolInput?.sku}…</div>;
}
if (status === "error") {
if (error instanceof ToolCancelledError) {
return <div className="p-4 text-secondary text-sm">Action cancelled: {error.reason}</div>;
}
return <div className="p-4 text-danger text-sm">Error: {error.message}</div>;
}
return (
<AppsSDKUIProvider linkComponent="a">
{/* Standard surface card styled with semantic tokens */}
<div className="w-full max-w-md rounded-2xl border border-default bg-surface p-4 shadow-sm">
<div className="flex items-start justify-between">
<div>
<h2 className="heading-md">{toolOutput.title}</h2>
<p className="text-secondary text-sm">SKU: {toolOutput.sku}</p>
</div>
<Badge color={toolOutput.inStock ? "success" : "warning"}>
{toolOutput.inStock ? "In Stock" : "Backorder"}
</Badge>
</div>
<div className="mt-4 flex items-center justify-between border-t border-subtle pt-3">
<span className="text-lg font-semibold">${toolOutput.price.toFixed(2)}</span>
<Button
color="primary"
onClick={() => {
// Attach verified evidence to next ChatGPT model turn (synchronous void)
add("selected:product", {
type: "text",
text: `Selected SKU ${toolOutput.sku}: $${toolOutput.price}`,
});
}}
>
<Plus className="size-4" />
Add to Chat
</Button>
</div>
</div>
</AppsSDKUIProvider>
);
}
3. Complete Apps SDK UI Component Inventory (29 Components)
All imported from @openai/apps-sdk-ui/components/<Component>:
| Category | Components | Purpose |
|---|---|---|
| Feedback & Status | Alert, Badge, Indicator, EmptyMessage, ShimmerText | Status banners, live badges, empty states, and loading shimmers. |
| Identity & Icons | Avatar, Image, Icon | User profiles, image fallbacks, and 700+ monochrome SVGs. |
| Actions | Button, ButtonLink (plus CopyButton) | Primary, secondary, soft, outline action buttons and copy controls. |
| Form Inputs | Input, Textarea, Checkbox, Switch, RadioGroup, Slider, SegmentedControl, Select, SelectControl, TagInput | Accessible, validated inputs and grouped controls. |
| Date Pickers | DatePicker, DateRangePicker | Calendar popups and date range selectors. |
| Overlays | Menu, Popover, Tooltip | Dropdown menus, anchored popovers, and contextual tooltips. |
| Typography & Content | Markdown, CodeBlock, TextLink | Styled markdown renderer, syntax-highlighted code blocks, and themed hyperlinks. |
| Motion & Transitions | Transition (Animate, AnimateLayout, SlotTransitionGroup, TransitionGroup) | Smooth height, fade, and layout morph transitions for iframes. |
| Providers | AppsSDKUIProvider | Root context providing router linkComponent configuration. |
4. Official Hooks Suite (@openai/apps-sdk-ui/hooks)
useBreakpoint(size: BreakpointName): Responsive breakpoint match boolean (e.g.useBreakpoint("md")).useAutoGrowTextarea(): Dynamic auto-growing textarea heights.useAnimatedScrollTo(): Smooth programmatic scrolling with cancellation.useDocumentVisible(): Tracks iframe visibility.useIsMounted(),useLatestValue(),usePrevious(),useScrollable(),useSimulatedProgress(),useStableCallback(),useTextSelection(),useTrailingValue().
ChatGPT Plugin Extensions
Documentation: ChatGPT Plugin Extensions | Extensions Reference
1. Typed Launch Entrypoints
Declare launch surfaces on tool.view.entrypoints:
global: Launches app from ChatGPT top-level launcher (input schema must accept{}).thread: Launches app within conversation tab (input schema must accept{}).file: Launches app as file viewer for matching extensions (input schema must receive{ file: { name: string, resourceUri: string } }).
export const openCsvViewer = server.tool(
{
name: "open-csv",
title: "CSV Table Viewer",
inputSchema: z.object({
file: z.object({ name: z.string(), resourceUri: z.string() }),
}),
outputSchema: z.object({ file: z.object({ name: z.string(), resourceUri: z.string() }) }),
view: {
name: "csv-viewer",
entrypoints: [{ type: "file", extensions: [".csv", ".tsv"] }],
},
},
async ({ file }) => ({ content: [], structuredContent: { file } })
);
2. Native Plugin Settings (server.settings)
Register native ChatGPT plugin settings with typed fields and layout sections:
server.settings({
fields: {
theme: { type: "string", label: "Theme", default: "system" },
autoSync: { type: "boolean", label: "Automatic Sync", default: true },
},
layout: [
{
title: "Preferences",
fields: ["theme", "autoSync"],
},
],
read: async (ctx) => ({ theme: "system", autoSync: true }),
update: async (patch, ctx) => ({ theme: "system", autoSync: true, ...patch }),
});
3. Incoming Deep Links (useDeepLink)
Read incoming URLs sent to global entrypoints:
import { useDeepLink } from "mcp-use/react";
export function AppRouter() {
const { url } = useDeepLink(); // e.g. "/items?tag=hardware"
}
4. Selected Model Context (useModelContext)
Manage evidence attachments sent with the next user model turn (attachments is Record<string, ModelContextBlock>, add is synchronous void):
import { useModelContext } from "mcp-use/react";
const { attachments, add, remove, clearAttachments } = useModelContext();
// Attach text summary (synchronous void, no title)
add("summary", { type: "text", text: "Order Summary: Total $120.00" });
// Attach image with Base64 data (PNG/JPEG/GIF/WebP, max 10MB)
add("receipt", { type: "image", data: receiptBase64, mimeType: "image/png" });
// Enumerate attached blocks
Object.entries(attachments).map(([key, block]) => (
<span key={key}>{key}: {block.type}</span>
));
Authentication & Mixed Auth
Documentation: Authentication | Mixed Auth | Auth Reference
Configure mixedAuth: true on MCPServer to serve public discovery, optional auth, and protected tools from one endpoint:
import { MCPServer } from "mcp-use";
import { oauthAuth0Provider } from "mcp-use/oauth/auth0";
import { z } from "zod";
const server = new MCPServer({
name: "store",
version: "1.0.0",
oauth: oauthAuth0Provider({ domain: process.env.AUTH0_DOMAIN! }),
mixedAuth: true, // Enables unauthenticated tools/list discovery
});
// Public tool: Anyone may call without token
export const searchCatalog = server.tool(
{
name: "search-catalog",
inputSchema: z.object({ query: z.string() }),
securitySchemes: [{ type: "noauth" }],
},
async ({ query }, ctx) => {
return { content: [{ type: "text", text: `Results for ${query}` }] };
}
);
// Protected tool: Requires sign-in with specific scopes
export const checkout = server.tool(
{
name: "checkout",
inputSchema: z.object({ cartId: z.string() }),
securitySchemes: [{ type: "oauth2", scopes: ["orders:write"] }],
},
async ({ cartId }, ctx) => {
const userId = ctx.auth.user.id;
return { content: [{ type: "text", text: `Order placed for user ${userId}` }] };
}
);
Supported Providers: Clerk (mcp-use/oauth/clerk), Auth0 (mcp-use/oauth/auth0), WorkOS (mcp-use/oauth/workos), Supabase (mcp-use/oauth/supabase), Keycloak (mcp-use/oauth/keycloak), Better Auth (mcp-use/oauth/better-auth), Scalekit (mcp-use/oauth/scalekit), Convex (mcp-use/oauth/convex), and Custom (oauthCustomProvider).
Interactive Elicitation
Documentation: Elicitation | Advanced Features
ctx.elicit() was removed in v2.0.0 during the transition to the stateless request model. Use official protocol primitives:
import { MCPServer, inputRequired, inputResponse, acceptedContent } from "mcp-use";
import { z } from "zod";
const confirmSchema = z.object({ approve: z.boolean().describe("Confirm action") });
server.tool(
{
name: "delete-resource",
inputSchema: z.object({ id: z.string() }),
},
async ({ id }, ctx) => {
const response = inputResponse(ctx.inputResponses, "delete-approval");
if (response.kind === "elicit" && response.action !== "accept") {
return { isError: true, content: [{ type: "text", text: "Action cancelled" }] };
}
const confirmation = acceptedContent(ctx.inputResponses, "delete-approval", confirmSchema);
if (!confirmation) {
return inputRequired({
inputRequests: {
"delete-approval": inputRequired.elicit({
message: `Permanently delete resource ${id}?`,
requestedSchema: confirmSchema,
}),
},
});
}
if (!confirmation.approve) {
return { isError: true, content: [{ type: "text", text: "Action rejected" }] };
}
// Side-effects execute only after confirmed, validated input
await db.delete(id);
return { content: [{ type: "text", text: `Deleted ${id}` }] };
}
);
For external authorization: inputRequired.elicitUrl({ message: "...", url: authUrl }).
Skills over MCP (SEP-2640)
Documentation: Skills over MCP | Skills Reference
Serve reusable instruction workflows alongside tools:
- Place skills under
skills/<skill-name>/SKILL.md. - Automatic discovery is enabled by default. Require with
skills: trueor disable withskills: falseonMCPServer. - Hosts retrieve skill metadata via
skills/listandskills/get.
CLI & Tooling Reference
Documentation: CLI Reference | Client CLI | Verification
# Scaffold new v2 project
npx create-mcp-use-app@latest my-server --template mcp-apps
# Start development with hot reload, Vite views pipeline, and Inspector
mcp-use dev
# Start development with authenticated public tunnel for live ChatGPT testing
mcp-use dev --tunnel
# Typecheck server and views
mcp-use typecheck
# Build for production
mcp-use build
# Start production server
mcp-use start
# Connect live client session straight from terminal
mcp-use client connect dev http://localhost:3000/mcp
# List tools and call with arguments
mcp-use client dev tools list
mcp-use client dev tools call get-product sku=PROD-100
# Capture automated screenshot of view preview
mcp-use screenshot --server dev --tool get-product sku=PROD-100
# Deploy to mcp-use cloud (requires GitHub repo)
mcp-use deploy
Canonical Documentation Links
- API Base: MCPServer Class Reference
- Official Docs: docs.mcp-use.com
- Server Quickstart & Core
- Server Migration Guide
- Server Tools
- Server Examples
- MCP Apps Quickstart
- ChatGPT Plugin Extensions
- MCP Apps Interactivity
- Model Context Attachments
- Content Security Policy
- Authentication & Mixed Auth
- Interactive Elicitation
- Skills over MCP
- CLI Reference
- Client CLI Guide
- Changelog & Breaking Changes
- OpenAI Apps SDK UI: