> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mcp-use.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build your first MCP App

> Create, run, and verify an MCP App with a view-bound tool.

Create a TypeScript MCP App, run it locally, and verify that a tool returns a React view in the Inspector.

## What you'll build

This guide creates a local server with:

* an MCP endpoint at `http://localhost:3000/mcp`
* an Inspector at `http://localhost:3000/mcp/inspector`
* a `search-fruits` tool that returns a view
* a React view at `views/product-search-result/view.tsx`

## Prerequisites

* Node.js 22.22.2 or higher
* Basic TypeScript and React knowledge

## Create the project

Run the MCP Apps template:

```bash theme={null}
npx create-mcp-use-app my-widget-server --template mcp-apps
cd my-widget-server
```

The template creates more files for assets, view components, and local tooling. These are the files you edit most often:

```text theme={null}
my-widget-server/
├── index.ts
├── mcp-env.d.ts
├── views/
│   └── product-search-result/
│       ├── view.tsx
│       ├── components/
│       └── hooks/
├── public/
│   ├── favicon.ico
│   ├── icon.svg
│   └── fruits/
├── package.json
└── tsconfig.json
```

## Run the server

Start the development server:

```bash theme={null}
npm run dev
```

This starts the MCP server, view build pipeline, and Inspector. Keep the process running while you test the app.

## Inspect the view tool

Open `index.ts` and find the `search-fruits` tool. The template includes server branding, 16 fruits, and a second `get-fruit-details` tool. This excerpt shows the view-bound path you need to understand first.

```typescript theme={null}
// index.ts excerpt; server, fruits, and z are defined above.
export const searchFruits = server.tool(
  {
    name: "search-fruits",
    title: "Search fruits",
    description: "Search for fruits and display the results in a view",
    inputSchema: z.object({
      query: z.string().optional().describe("Search query to filter fruits"),
    }),
    outputSchema: z.object({
      query: z.string(),
      results: z.array(
        z.object({
          fruit: z.string(),
          color: z.string(),
        }),
      ),
    }),
    annotations: {
      readOnlyHint: true,
      destructiveHint: false,
      openWorldHint: false,
    },
    view: {
      name: "product-search-result",
      description: "Product search results with carousel and details",
      prefersBorder: true,
    },
  },
  ({ query = "" }) => {
    const results = fruits.filter(
      (fruit) =>
        query === "" || fruit.fruit.toLowerCase().includes(query.toLowerCase()),
    );

    const data = { query, results };

    return {
      content: [
        {
          type: "text",
          text: `Found ${results.length} fruits matching "${query || "all"}"`,
        },
      ],
      structuredContent: data,
    };
  },
);

export default server;
```

`view.name` must match the folder under `views/`. In this template, `product-search-result` maps to `views/product-search-result/view.tsx`.

The `structuredContent` object becomes view rendering data via `useToolContext()`. The `content` array is the short text result the model can read.

## Inspect the React view

Open `views/product-search-result/view.tsx`. The view reads the tool result with `useToolContext()`.

The generated view includes a carousel, detail panel, display-mode controls, favorites, and tool calls. This excerpt shows the core data access pattern.

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

export default function ProductSearchResult() {
  const { status, error, toolOutput } = useToolContext<"search-fruits">();

  if (status === "pending") {
    return <div className="p-4">Searching…</div>;
  }

  if (status === "error") {
    return <div className="p-4">{error.message}</div>;
  }

  const { query, results } = toolOutput;

  return (
    <div className="p-4">
      <h2>
        Results for "{query || "all"}" ({results.length})
      </h2>
      {/* carousel, details, accordion … */}
    </div>
  );
}
```

Views can render before the tool finishes. Check `status === "pending"` before reading `toolOutput`.

## Verify in the Inspector

Use the Inspector to confirm that the server and view work.

1. Open `http://localhost:3000/mcp/inspector`.
2. Go to the **Tools** tab.
3. Select `search-fruits`.
4. Run the tool with `{ "query": "a" }` or `{}`.
5. Confirm that the view renders below the tool result.

If the tool runs but no view appears, check that `view.name` exactly matches the folder under `views/` and that the handler returns `structuredContent`.

## Next steps

* Use [`useToolContext()`](https://mcp-use-typescript-api-reference.vercel.app/modules/mcp-use.react.html#usetoolcontext) to read tool input, output, and lifecycle status.
* Use [`useCallTool()`](https://mcp-use-typescript-api-reference.vercel.app/modules/mcp-use.react.html#usecalltool) when a view needs to call another MCP tool.
* Configure [Content Security Policy](/v2/typescript/mcp-apps/content-security-policy) before loading external APIs, images, scripts, embeds, or static assets.
