build-mcp-use-server
Skill13 starsChanged 13 days agoMITView on GitHub
Use if building, modifying, debugging, or verifying TypeScript MCP servers and ChatGPT Apps with mcp-use v2, MCPServer, Apps SDK UI, Views, and Plugin Extensions.
- Reads credentials
- Installs packages
We split this skill into the rules it gives an agent
11 rules. About 0 are common, 2 are distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 9
Treat the installed mcp-use package, its exported types, generated declarations (mcp-env.d.ts), and official documentation at …
- Very specific: line 17
Inspect Project: Check package.json, server entry, exported tool refs, views/, skills/, and the installed mcp-use version (Node.js >= …
- Distinctive: line 34
Implement Against Installed Types: Always export static tool references and default-export the server instance.
- Very specific: line 41
Root Server Import: Always import MCPServer and server primitives from "mcp-use" (the legacy "mcp-use/server" subpath is obsolete).
- Very specific: line 42
Export Static Tool Refs: Always export every tool reference (export const myTool = server.tool(...)).
- Very specific: line 43
Default Server Export: Always default-export the server instance (export default server;).
- Very specific: line 46
Views Architecture: Put each view at views/<name>/view.tsx.
- Very specific: line 47
OpenAI Apps SDK UI Compliance: Every ChatGPT app must install @openai/apps-sdk-ui, import @openai/apps-sdk-ui/css, configure Tailwind 4 …
- Very specific: line 263
All imported from @openai/apps-sdk-ui/components/<Component>:
- Distinctive: line 335
Read incoming URLs sent to global entrypoints:
- Very specific: line 468
Place skills under skills/<skill-name>/SKILL.md.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Build 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/viteIn 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 deployCanonical 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:
In this repository31 other files it gives its agents.
- AGENTS.mdAGENTS.md
- audit-agentic-cliSkill
- audit-agentic-mcpSkill
- audit-completionSkill
- audit-skill-by-derailmentSkill
- audit-ui-uxSkill
- build-chrome-extensionSkill
- build-cloudflare-access-ssoSkill
- build-cloudflare-email-sendingSkill
- build-kernel-ts-sdkSkill
- build-licenseseat-swiftSkill
- build-raycast-script-commandSkill
- build-skillSkill
- ci-cd-optimizeSkill
- convert-to-natural-writingSkill
- convert-url-to-nextjsSkill
- init-agent-configSkill
- init-makefilesSkill
- mobilerun-controlSkill
- optimize-nextjs-fluiditySkill
- publish-npm-packageSkill
- run-astro-auditSkill
- run-railwaySkill
- run-repo-cleanupSkill
- run-ts-cleanupSkill
- test-by-maestroSkill
- test-by-mcpc-cliSkill
- update-agent-configSkill
- use-chatgpt-by-applescriptSkill
- use-cloudflare-tunnelSkill
- use-linear-cliSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

