agentleFS
yigitkonuryigitkonur/skills-by-yigitkonur/skills/build-mcp-use-server/SKILL.md

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
  1. Very specific:

    Treat the installed mcp-use package, its exported types, generated declarations (mcp-env.d.ts), and official documentation at …

    line 9
  2. Very specific:

    Inspect Project: Check package.json, server entry, exported tool refs, views/, skills/, and the installed mcp-use version (Node.js >= …

    line 17
  3. Distinctive:

    Implement Against Installed Types: Always export static tool references and default-export the server instance.

    line 34
  4. Very specific:

    Root Server Import: Always import MCPServer and server primitives from "mcp-use" (the legacy "mcp-use/server" subpath is obsolete).

    line 41
  5. Very specific:

    Export Static Tool Refs: Always export every tool reference (export const myTool = server.tool(...)).

    line 42
  6. Very specific:

    Default Server Export: Always default-export the server instance (export default server;).

    line 43
  7. Very specific:

    Views Architecture: Put each view at views/<name>/view.tsx.

    line 46
  8. Very specific:

    OpenAI Apps SDK UI Compliance: Every ChatGPT app must install @openai/apps-sdk-ui, import @openai/apps-sdk-ui/css, configure Tailwind 4 …

    line 47
  9. Very specific:

    All imported from @openai/apps-sdk-ui/components/<Component>:

    line 263
  10. Distinctive:

    Read incoming URLs sent to global entrypoints:

    line 335
  11. Very specific:

    Place skills under skills/<skill-name>/SKILL.md.

    line 468

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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

  1. Inspect Project: Check package.json, server entry, exported tool refs, views/, skills/, and the installed mcp-use version (Node.js >= 22.22.2 required).
  2. Scaffold or Initialize: For greenfield projects, scaffold with the official CLI:
    npx create-mcp-use-app@latest my-app --template mcp-apps
    (Use --template mcp-server for headless tool servers).
  3. 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), native server.settings(), tool icons, useDeepLink(), and useModelContext().
    • Authentication: OAuth provider adapters, mixedAuth: true, tool-level securitySchemes, and verified identity on ctx.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, and mcp-use screenshot.
  4. Implement Against Installed Types: Always export static tool references and default-export the server instance.
  5. Verify Deterministically: Drive real tool calls through mcp-use client and capture view screenshots with mcp-use screenshot.

Core Invariants

  1. Root Server Import: Always import MCPServer and server primitives from "mcp-use" (the legacy "mcp-use/server" subpath is obsolete).
  2. Export Static Tool Refs: Always export every tool reference (export const myTool = server.tool(...)). The TypeScript compiler derives view and client contracts in mcp-env.d.ts from exported tool names.
  3. 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.
  4. Definition-First Tools & Standard Schema: Define tools with server.tool(definition, callback). Tool arguments use inputSchema; model prompts use schema. Always describe model-filled schema fields.
  5. Raw Result Envelopes: Tool callbacks must return raw CallToolResult envelopes ({ content: [...], structuredContent?: ... }). Do not use deprecated v1 helpers (text(), object(), widget()). Expected failures return { isError: true, content: [...] }; unexpected failures throw.
  6. Views Architecture: Put each view at views/<name>/view.tsx. Each view must have exactly one owning tool with matching view: { name: "<name>" } and a mandatory outputSchema. Return matching structuredContent.
  7. 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">.
  8. 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.requestState or an external database.
  9. Strict CLI Flag Syntax: Boolean CLI flags reject inline values in v2.7.2+ (--tunnel=false fails; use --tunnel or omit).

Guardrails & Common Anti-Patterns

  • Never import non-existent @openai/apps-sdk-ui components:
    • Card does 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).
    • AvatarGroup and CopyTooltip are official @openai/apps-sdk-ui components (from @openai/apps-sdk-ui/components/Avatar and @openai/apps-sdk-ui/components/Tooltip).
    • Cart and Checkmark do not exist in @openai/apps-sdk-ui/components/Icon. Use Check, CheckCircle, Search, Plus, Settings, etc.
  • Never omit linkComponent on <AppsSDKUIProvider>: The TypeScript contract requires linkComponent (e.g. <AppsSDKUIProvider linkComponent="a">).
  • Never read useToolContext().toolOutput un-narrowed: useToolContext() is a discriminated union. When status === "pending", toolOutput is undefined. Always handle status === "pending" and status === "error" before accessing toolOutput. Narrow error instanceof ToolCancelledError for cancelled actions.
  • Configure websiteUrl, icons, and logging at the top level of MCPServer:
    • icons?: Icon[], websiteUrl?: string, and logging?: LoggingOptions are top-level properties on ServerConfig. Do not nest them in a fictitious branding: { ... } object (server.branding is a read-only getter).
    • Server logging level is configured via logging: { enabled: true, level: "info" } or overridden by the MCP_USE_LOG_LEVEL environment 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, and acceptedContent.
  • 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].

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:

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>:

CategoryComponentsPurpose
Feedback & StatusAlert, Badge, Indicator, EmptyMessage, ShimmerTextStatus banners, live badges, empty states, and loading shimmers.
Identity & IconsAvatar, Image, IconUser profiles, image fallbacks, and 700+ monochrome SVGs.
ActionsButton, ButtonLink (plus CopyButton)Primary, secondary, soft, outline action buttons and copy controls.
Form InputsInput, Textarea, Checkbox, Switch, RadioGroup, Slider, SegmentedControl, Select, SelectControl, TagInputAccessible, validated inputs and grouped controls.
Date PickersDatePicker, DateRangePickerCalendar popups and date range selectors.
OverlaysMenu, Popover, TooltipDropdown menus, anchored popovers, and contextual tooltips.
Typography & ContentMarkdown, CodeBlock, TextLinkStyled markdown renderer, syntax-highlighted code blocks, and themed hyperlinks.
Motion & TransitionsTransition (Animate, AnimateLayout, SlotTransitionGroup, TransitionGroup)Smooth height, fade, and layout morph transitions for iframes.
ProvidersAppsSDKUIProviderRoot 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:

  1. Place skills under skills/<skill-name>/SKILL.md.
  2. Automatic discovery is enabled by default. Require with skills: true or disable with skills: false on MCPServer.
  3. Hosts retrieve skill metadata via skills/list and skills/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

Download now (ZIP)

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.