Back

mcp-builder

Build, modify, debug, migrate, review, or verify TypeScript MCP servers and MCP Apps with mcp-use. Use for tools, resources, prompts, middleware, Views, authentication, Skills over MCP, scaffolding, and advanced features.

Skyll
91.29,620 installsmcp-use/mcp-use

Add to your agent

curl "https://api.skyll.app/skill/mcp-builder"
SKILL.md
# Build with mcp-use

Treat the installed `mcp-use` package, its exported types, generated declarations, and the project's existing code as the source of truth. Inspect the installed version before choosing APIs or changing code.

## Workflow

1. Inspect `package.json`, the server entry, exported tool refs, `mcp-env.d.ts`, `views/`, `skills/`, and the installed `mcp-use` version.
2. Scaffold a new stable project with `create-mcp-use-app@latest` and the appropriate template. Match the package version or dist-tag when working on beta, canary, or an existing versioned project.
3. Read only the references needed for the task:
   - [Server](references/server.md) for tools, resources, prompts, MCP middleware, request context, and result envelopes.
   - [Views](references/views.md) for interactive MCP Apps, React hooks, model context, host capabilities, assets, and CSP.
   - [Authentication](references/auth.md) for OAuth providers, verified identity, scopes, permissions, and authorization.
   - [Skills over MCP](references/skills-over-mcp.md) when a server should ship reusable workflows alongside its tools.
   - [Advanced features](references/advanced-features.md) for OpenAPI, proxying, notifications, subscriptions, and elicitation.
   - [Migration](references/migration.md) only when retired or compatibility-only imports, helpers, registration shapes, UI patterns, or session assumptions are present.
   - [Verification](references/verification.md) before reporting implementation work complete.
4. Implement against installed types. Prefer the framework's current conventions over copied examples or historical changelogs.
5. Validate the smallest real lifecycle that proves the changed behavior, then expand checks in proportion to risk.

## Core invariants

- Import server APIs from `mcp-use`, React APIs from `mcp-use/react`, and OAuth provider adapters from their `mcp-use/oauth/*` subpaths.
- Define tool arguments with `inputSchema`. Add `outputSchema` for structured results and every View-bound tool.
- Return raw MCP result envelopes. A successful schema-backed tool must include matching `structuredContent`; an expected failure may return `isError: true` with model-readable `content`.
- Put each View at `views/<name>/view.tsx` and bind it with `view: { name: "<name>" }`.
- Export every statically declared tool ref consumed by a View. Default-export the server entry used by `mcp-use dev`, `build`, and `start`.
- Keep identity and mutable workflow state request-scoped or in an external store. Treat client-reported metadata as unverified.
- Consider Skills over MCP when a server exposes a repeatable, multi-step workflow that would otherwise inflate tool descriptions.

## Guardrails

- Do not invent exports, configuration fields, or callback shapes. Confirm uncertain details in installed declarations or source.
- Do not preserve APIs that are absent from the installed version merely because they appear in an existing project.
- Do not return a plain domain object from a tool callback.
- Do not bind a View without a matching `outputSchema` and `structuredContent` result.
- Do not use module globals for cross-request identity, elicitation continuity, or durable business state.
- Do not claim success from a source build alone when types, package exports, authentication, or interactive behavior changed.
- Do not deploy or mutate external systems unless the user explicitly requests it.

References (7)

📎 advanced-features.md
# Advanced features

Use these features only when the task requires them. Confirm exact options and current limitations against the installed package.

## OpenAPI-generated tools

Use `MCPServer.fromOpenAPI()` with a parsed, bundled OpenAPI 3.x document. Supply `baseUrl` when the document has no usable server URL. Use `tags` and `exclude` to limit exposed operations.

Bundle external `$ref` targets before creating the server. Generated inputs cover path, query, header, and JSON-compatible request bodies; cookie parameters and non-JSON bodies are not exposed. Generated tools do not derive `outputSchema` from response definitions.

## Proxy MCP servers

`server.proxy()` requires the optional `@mcp-use/client` package. Call it before `listen()` or the first `server.fetch` request. Config-map keys namespace upstream tools, static resources, and prompts.

Provide bearer tokens or headers explicitly; proxy startup does not run interactive OAuth. A config-created connection is owned by the server, while the application must close an explicitly supplied `MCPConnection`.

Do not assume every capability is forwarded. Confirm current support for resource templates, completions, subscriptions, and upstream list resynchronization before designing around them.

## Request-scoped notifications

Send status only while the originating callback is active:

```ts
await ctx.sendNotification("com.example/import-status", { status: "started" });
await ctx.reportProgress(50, 100, "Halfway");
await ctx.sendLog("info", { imported: 42 }, "import-worker");
```

Await notifications before returning. They are not a post-response broadcast channel. `reportProgress()` returns `false` when the caller supplied no progress token.

## List and resource invalidations

Publish cross-request invalidations only to clients with an active subscription listener:

```ts
await server.notifyToolsChanged();
await server.notifyPromptsChanged();
await server.notifyResourcesChanged();
await server.notifyResourceUpdated("config://settings");
```

Treat these as non-durable cache invalidations. Keep the resource or registry authoritative, make reads repeatable, and never depend on delivery of every event.

## Elicitation

Use `ctx.elicit(key, message, schemaOrUrl)` when a capable client must collect structured input or complete an external flow. Return `required.result` directly, then handle `accept`, `decline`, and `cancel` when the callback reruns.

```ts
const approval = await ctx.elicit("publish-approval", "Publish now?", schema);
if (approval.status === "required") return approval.result;
if (approval.status !== "accept" || !approval.data.approve) {
  return { isError: true, content: [{ type: "text", text: "Not approved" }] };
}
```

Callbacks rerun for input-required rounds. Perform irreversible side effects only after accepted input, use a distinct stable key for each question, validate any bare input responses, and use verified request state when continuity affects authorization or business logic. Never collect passwords, API keys, payment details, or OAuth secrets in a form elicitation.
📎 auth.md
# Authentication

## Choose an integration

Configure OAuth when a server must identify callers or authorize access by user, organization, role, scope, or permission.

- Use a built-in provider adapter when the identity provider supports the expected resource-server and Dynamic Client Registration flow.
- Use `oauthCustomProvider` from `mcp-use/oauth` for another compatible provider.
- Use the OAuth proxy and verifier helpers when the upstream authorization server uses preregistered credentials rather than Dynamic Client Registration.
- Inspect the installed adapter declarations and current provider documentation for required options and environment variables.

Import adapters from explicit subpaths:

```ts
import { MCPServer } from "mcp-use";
import { oauthAuth0Provider } from "mcp-use/oauth/auth0";

const server = new MCPServer({
  name: "secure-server",
  version: "1.0.0",
  oauth: oauthAuth0Provider({ domain: process.env.AUTH0_DOMAIN! }),
});
```

Validate configuration at startup in production instead of relying on non-null assertions.

## Use verified request identity

With OAuth configured, callbacks receive:

- `ctx.auth.user`: provider-normalized verified identity. Built-in users have `id`; optional fields vary.
- `ctx.auth.scopes`: grants from verified auth information.
- `ctx.auth.permissions`: provider-mapped application permissions.
- `ctx.auth.payload`: verified claims or introspection data.
- `ctx.auth.accessToken`: the bearer token, for intentional downstream delegation only.
- `ctx.auth.clientId`, `expiresAt`, and `resource` when available.

Authorize the specific action rather than checking only that a user exists:

```ts
async ({ documentId }, ctx) => {
  if (!ctx.auth.permissions.includes("documents:delete")) {
    return {
      isError: true,
      content: [{ type: "text", text: "Forbidden" }],
    };
  }

  await deleteOwnedDocument(documentId, ctx.auth.user.id);
  return { content: [{ type: "text", text: "Document deleted" }] };
};
```

Prefer normalized user fields. Read raw payload claims only when the provider does not map a required verified value.

## Security boundaries

- Never treat `ctx.client.user()`, locale, location, subject, conversation ID, or other request metadata as authenticated identity.
- Keep authorization next to sensitive operations or in narrowly scoped MCP middleware.
- Forward an access token only to the intended upstream resource and never log or return it.
- Do not include client secrets, tokens, full claims, or provider payloads in examples, logs, tool content, structured output, `_meta`, or View state.
- Keep multi-request state in a trusted external store. Use verified request state for sensitive elicitation flows; do not infer continuity from module globals or transport details.
📎 migration.md
# Migrate older mcp-use patterns

Read this reference only when the project contains retired or compatibility-only patterns that are absent from or no longer preferred by the installed package. Migrate behavior, not names alone, and verify every changed boundary against installed types.

## Replace retired patterns

| Older or compatibility pattern | Current replacement |
| --- | --- |
| Server imports from `mcp-use/server` | Import public server APIs from `mcp-use` |
| Tool `schema` | Tool `inputSchema`; prompt arguments still use `schema` |
| Inline callback fields | Pass the callback as the registration method's second argument |
| Chained `server.tool(...).tool(...)` | Register separately; `tool()` returns a `ToolRef` |
| Nested resource-template configuration | Top-level `uriTemplate` and `complete` fields |
| Response helpers as the default result path | Raw tool, resource, or prompt protocol envelopes |
| `resources/<name>/widget.tsx` | `views/<name>/view.tsx` |
| Tool `widget: { name }` | Tool `view: { name }` |
| `widget({ props, output })` | `{ content, structuredContent, _meta? }` |
| `useWidget()` or `useWidgetProps()` | Destructured `useToolContext<"tool-name">()` |
| Aggregate provider wrapper | Runtime bootstrap plus focused React hooks and components |
| Aggregate widget state and host methods | `useViewState`, `useHostContext`, `useDisplayMode`, and focused hooks |

Do not mechanically rename every `schema`: prompts intentionally retain that field. Confirm resource and prompt callback signatures independently from tools.

## Rebuild interactive UI

For each rendering tool:

1. Move the entry to `views/<name>/view.tsx`.
2. Export the tool ref from the server entry.
3. Add `outputSchema` and `view: { name }`.
4. Put model-readable text in `content`, typed render data in `structuredContent`, and View-only invocation data in `_meta`.
5. Destructure `useToolContext()` and handle pending, error, and ready states before reading `toolOutput`.
6. Replace aggregate UI methods with focused hooks.
7. Move CSP to `view.csp` and resolve public assets through the framework base.

## Remove transport and session assumptions

Treat callbacks as request-scoped and potentially concurrent. Replace active-session registries, session affinity, in-memory user identity, and post-response client targeting with request context or an external store. Use `ctx.client` only for self-reported capabilities and `ctx.auth` for verified identity.

Use `basePath` for the MCP endpoint path and `MCP_URL` for the externally visible public origin. Do not reconstruct public URLs from localhost assumptions.

## Migration checklist

1. Search source, tests, examples, and project documentation for the retired identifiers above.
2. Confirm public imports and registration signatures against the installed package.
3. Run type generation and typechecking before interpreting downstream errors.
4. Exercise every migrated tool, resource template, and prompt through a real client.
5. Render each migrated View and test pending, error, ready, interaction, asset, and CSP behavior.
6. Test authentication, notifications, and elicitation through supported clients when those boundaries changed.
7. Pack and import from a clean consumer when package exports or dependencies changed.
📎 server.md
# Server

## Project and lifecycle

Import `MCPServer` from `mcp-use`. Default-export the server entry so `mcp-use dev`, `mcp-use build`, and `mcp-use start` can own the listener and View pipeline. Call `server.listen()` only in an explicitly standalone program.

Use the project's package manager and existing scripts. For a new stable project, scaffold instead of recreating framework boilerplate:

```bash
npx create-mcp-use-app@latest my-server --template mcp-server
```

## Tools

Use a Standard Schema-compatible validator such as Zod, ArkType, or Valibot. Describe fields when the description helps a client or model choose valid input.

```ts
import { MCPServer } from "mcp-use";
import { z } from "zod";

const server = new MCPServer({ name: "inventory", version: "1.0.0" });

export const lookupInventory = server.tool(
  {
    name: "lookup-inventory",
    description: "Return available inventory for one SKU",
    inputSchema: z.object({ sku: z.string().describe("Inventory SKU") }),
    outputSchema: z.object({ sku: z.string(), available: z.number().int() }),
    annotations: { readOnlyHint: true, openWorldHint: false },
  },
  async ({ sku }) => {
    const data = { sku, available: await inventory.count(sku) };
    return {
      content: [{ type: "text", text: JSON.stringify(data) }],
      structuredContent: data,
    };
  },
);

export default server;
```

Use `visibility: "app"` for helper tools intended for Views but hidden from the model. Treat annotations as behavioral hints, not authorization controls.

## Result channels

- Put concise model-readable output in `content`.
- Put schema-validated JSON in `structuredContent` when `outputSchema` exists.
- Put invocation-specific, View-only data in result `_meta`; validate or narrow it in the View.
- Return `isError: true` with useful `content` for expected operational failures.
- Throw only for unexpected failures that should surface as protocol errors.

## Resources and prompts

Use `server.resource()` for one stable URI and `server.resourceTemplate()` for a URI family. Return `{ contents: [...] }`; each entry must include its URI and either text or a base64 blob. A template callback receives `(uri, params, ctx)`, and template values may be `string | string[]`.

Use `server.prompt()` for model-ready messages. Prompt arguments use `schema`, not a tool's `inputSchema`, and the callback returns `{ messages: [...] }`. Wrap a string field with `completable()` when clients should receive suggestions without restricting other valid strings.

## MCP middleware and request context

Register protocol middleware with `server.use("mcp:<method>", handler)`. Use the narrowest operation, call `next()`, and return its result unless intentionally replacing it.

```ts
server.use("mcp:tools/call", async (ctx, next) => {
  const startedAt = Date.now();
  const result = await next();
  console.log(`${ctx.params.name}: ${Date.now() - startedAt}ms`);
  return result;
});
```

Tool, resource, and prompt callbacks receive request-scoped context. Use `ctx.signal` for cancellation, `ctx.client` for self-reported capabilities, and `ctx.auth` only when OAuth is configured. Never use client metadata for authorization.

Register static capabilities while constructing the server. Use the notification helpers described in [Advanced features](advanced-features.md) when discoverable lists or resource content change.
📎 skills-over-mcp.md
# Skills over MCP

Skills let a server ship reusable operating instructions alongside its tools. Prefer a Skill when a task requires a repeatable multi-step workflow, policies, reference material, templates, or scripts that would otherwise bloat tool descriptions.

Do not replace an executable capability with prose: tools perform actions, resources expose content, prompts return model-ready messages, and Skills teach an agent how to combine them safely.

## Add a Skill

Create a conventional `skills/` directory beside the server entry. Its presence enables discovery automatically.

```text
skills/
  process-refund/
    SKILL.md
    references/
      policy.md
    templates/
      confirmation.md
```

Every skill needs a directory-matching `SKILL.md` with concise trigger metadata and task instructions:

```md
---
name: process-refund
description: Check refund eligibility and process approved customer refunds
---

# Process refunds

Read `references/policy.md` before deciding eligibility.
Use `templates/confirmation.md` after a successful refund.
```

Keep `SKILL.md` procedural and small. Put detailed policies and domain knowledge in references, deterministic operations in scripts, and output templates or binary material in supporting files. Link every supporting file directly from `SKILL.md` and state when to read or use it.

## Configure discovery

Normally omit `skills` from `MCPServer` configuration. Use an explicit option only when behavior must differ:

```ts
new MCPServer({ name: "shop", version: "1.0.0", skills: true });
new MCPServer({ name: "shop", version: "1.0.0", skills: false });
new MCPServer({
  name: "shop",
  version: "1.0.0",
  skills: { directory: "server-skills" },
});
```

- `true` requires the conventional directory.
- `false` ignores a conventional directory.
- A custom directory is project-relative.
- With `--mcp-dir`, automatic discovery follows the MCP source directory; an explicit custom directory remains project-relative.

## Understand discovery and validation

The server advertises the draft Skills over MCP extension. Hosts inspect the catalog with `skills/list`, retrieve a Skill with `skills/get`, and read supporting content through resource directory and file reads. The SDK does not inject Skill text into server instructions or tool descriptions; activation remains a host decision.

Serve only trusted Skills. A host should require appropriate user approval before activating a Skill or its allowed tools.

During `mcp-use dev`, invalid Skills are logged and omitted until fixed. `mcp-use build` is strict and fails for an invalid catalog, then embeds the validated snapshot into the production build.
📎 verification.md
# Verification

Run the smallest checks that prove the requested behavior, then expand for changes involving generated types, authentication, Views, package boundaries, or concurrency.

## Static checks

Use the project's package manager and scripts. For a typical project:

```bash
npx mcp-use typecheck
npm run typecheck
npm run build
```

Do not assume every project defines both typecheck commands. `mcp-use build` bundles and transpiles; it does not replace typechecking. Resolve new type, lint, package-boundary, and generated-registry failures before handoff.

## Server and capability checks

Start the actual development server, connect through its public MCP endpoint, and drive the changed capability:

```bash
npm run dev
npx mcp-use client connect dev http://localhost:3000/mcp
npx mcp-use client dev tools list
npx mcp-use client dev tools call lookup-inventory sku=item-1
```

For tools, test valid input, schema rejection, expected failures, and matching `structuredContent`. For resources, read static and templated URIs and exercise completion. For prompts, inspect the exact generated messages and suggestions. Exercise authorization and cancellation paths when changed.

## View checks

Render every affected View through its bound tool in the Inspector. Verify:

- Pending, ready, and error rendering.
- View-to-tool calls and host actions.
- Model-visible state versus ephemeral UI state.
- Theme, sizing, supported display modes, and accessibility.
- Public assets, external requests, CORS, runtime errors, and CSP.

Capture the real View with the screenshot command, not a `tools call` flag:

```bash
npx mcp-use screenshot --server dev --tool show-product id=item-1
```

## Advanced and packaging checks

- For Skills over MCP, verify the catalog, retrieve the Skill, read supporting files, and run a strict production build.
- For notifications, keep a listener active and confirm non-durable invalidation behavior.
- For elicitation, test required, accept, decline, cancel, invalid input, callback replay, and side-effect ordering.
- For proxying or OpenAPI, verify representative generated capabilities and documented unsupported boundaries.
- For export, dependency, or packaging changes, pack the package and install it in an empty temporary consumer; a workspace build cannot prove the published boundary.

Do not deploy merely to validate source changes. If deployment was not requested, verify local build artifacts and state the untested external boundary.
📎 views.md
# Views

## Bind a View

Create `views/<name>/view.tsx`. Export the rendering tool ref, declare its `outputSchema`, bind `view: { name }`, and return matching `structuredContent`. The directory name and `view.name` must match exactly.

Use result `content` for a concise model-readable summary, `structuredContent` for typed render data, and `_meta` for invocation-specific data that should be visible only to the View.

## Read the rendering call

Destructure `useToolContext()` and narrow its discriminated lifecycle before reading `toolOutput`:

```tsx
import { useToolContext } from "mcp-use/react";

export default function ProductResults() {
  const { status, toolInput, toolOutput, error, meta } =
    useToolContext<"search-products">();

  if (status === "pending") {
    return <SearchSkeleton query={toolInput?.query} />;
  }

  if (status === "error") {
    return <ErrorBanner message={error.message} />;
  }

  const source = typeof meta?.source === "string" ? meta.source : undefined;
  return (
    <Results
      items={toolOutput.items}
      source={source}
    />
  );
}
```

`toolInput` may be partial while pending. Treat `meta` as untyped external data and validate or narrow it before use.

## Choose the interaction channel

- `useCallTool("tool-name")`: call an exported server tool with inferred types.
- `useDynamicTool`: call a runtime-generated tool when no static ref exists.
- `useSendFollowUp`: request a new model turn.
- `useOpenExternal`: ask the host to open a URL outside the sandbox.
- `useDisplayMode`: inspect and request a supported presentation mode.
- `useViewTool`: expose a temporary action that operates on the mounted UI.
- `useFiles`: use host file capabilities after checking support.

Guard host actions with `useHostContext()` capability signals. A host may reject or modify a request, so render pending and failure states and read the resulting host state.

## State and model context

- Use React state for ephemeral UI details the model does not need.
- Use `useViewState(objectDefault)` for JSON-serializable selections, filters, drafts, or progress that future model turns should understand.
- Use `<ModelContext content="...">` to describe currently visible UI declaratively.
- Store durable business data in the backend, not View state.

Do not put secrets or large render-only payloads into model-visible state. Keep `_uiContext` reserved for the runtime.

## Presentation, assets, and CSP

Use `ThemeProvider`, `ViewControls`, `useViewTheme`, or `viewConfig` only when their behavior is needed; the runtime bootstraps the host bridge and enables automatic resizing by default. A named `viewConfig` may restrict supported display modes or disable automatic resize.

Keep View code and CSS under its View folder. Put shared public files in `public/` and resolve them through the framework's public asset base rather than a hard-coded localhost URL.

Declare exact external origins in `view.csp`:

- `connectDomains` for fetch, EventSource, and WebSocket.
- `resourceDomains` for scripts, styles, images, fonts, and media.
- `frameDomains` for embedded frames.
- `baseUriDomains` only for an intentional external base URI.