Ir para o conteúdo
decodecodeveloper docs
Studio → MCP Apps e extensões → Criar MCP Apps

Estrutura do Projeto

Entenda a arquitetura TypeScript full-stack dos MCP Apps na deco

Este guia segue o template de MCP App no commit 3fc9bd15. O servidor MCP e a interface React são separados do framework Blocks para websites.

Para usar o template revisado:

git clone https://github.com/decocms/mcp-app.git my-mcp-app
cd my-mcp-app
git checkout 3fc9bd15cb28a66361d5c99730962307d3fd77e0
bun install --frozen-lockfile
bun run dev

Estrutura de Diretórios

├── api/                        # Servidor MCP (Bun)
│   ├── app.ts                  # Runtime, tools, resources e middleware
│   ├── main.bun.ts             # Entrada do servidor HTTP Bun
│   ├── tools/
│   │   ├── index.ts            # Registro de tools
│   │   └── hello.ts            # Tool de exemplo (hello_world)
│   ├── resources/
│   │   └── hello.ts            # Resource de MCP App (serve HTML)
│   └── types/
│       └── env.ts              # StateSchema + tipo Env
├── web/                        # UI React (MCP App)
│   ├── app.tsx                 # Ponto de entrada do React
│   ├── context.tsx             # Gerenciamento de estado do MCP
│   ├── router.tsx              # TanStack Router (baseado em hash)
│   ├── types.ts                # Tipos McpState, McpStatus
│   ├── tools/                  # Uma pasta por UI de tool
│   │   └── hello/
│   │       └── index.tsx       # UI do tool hello_world
│   ├── components/ui/          # Componentes shadcn/ui
│   ├── lib/utils.ts            # Helper cn()
│   └── globals.css             # Estilos base do Tailwind
├── index.html                  # HTML de entrada do Vite
├── package.json
├── tsconfig.json
├── biome.json
├── vite.config.ts              # Vite + React Compiler + singlefile
├── components.json             # Configuração shadcn/ui
└── app.json                    # Identidade do app e conexão implantada

Backend (api/)

app.ts

O núcleo da aplicação configura o runtime MCP. Registre tools e resources aqui:

import { withRuntime } from "@decocms/runtime";
import { helloAppResource } from "./resources/hello.ts";
import { tools } from "./tools/index.ts";
import { type Env, StateSchema } from "./types/env.ts";
 
const runtime = withRuntime<Env, typeof StateSchema>({
  configuration: {
    state: StateSchema,
  },
  tools,
  resources: [helloAppResource],
});
 

O app factory existente envolve runtime.fetch com logs e withMcpApiRoute(). Preserve esse adaptador: ele serve o endpoint público em /api/mcp e rejeita /mcp.

main.bun.ts

A entrada Bun importa o núcleo da aplicação e inicia o servidor HTTP:

import { app } from "./app.ts";
 
Bun.serve({
  hostname: "0.0.0.0",
  port: Number(process.env.PORT) || 3001,
  fetch: app.fetch,
});

Outras plataformas precisam de uma entrada própria e armazenamento de resources compatível; a entrada do template revisado roda no Bun.

tools/

Definições de tools organizadas por domínio. Cada arquivo exporta uma função criadora de tool que recebe o environment.

  • tools/index.ts exporta o array tools — todas as tool creators registradas aqui
  • Veja Construindo Tools para detalhes

resources/

Resources de MCP App que servem bundles HTML de arquivo único como UIs ricas para resultados de tools.

  • Cada resource mapeia uma URI (ex: ui://mcp-app/hello) para o arquivo HTML buildado
  • Veja Resources para detalhes

types/

import type { DefaultEnv } from "@decocms/runtime";
import { z } from "zod";
 
export const StateSchema = z.object({});
 
export type Env = DefaultEnv<typeof StateSchema>;
  • StateSchema — schema Zod que define a configuração do seu app (mostrada a quem instala)
  • Env — environment tipado que estende DefaultEnv, disponível nos handlers de tools

Frontend (web/)

Ponto de entrada (web/app.tsx)

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { McpProvider } from "./context.tsx";
import { AppRouter } from "./router.tsx";
 
createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <McpProvider>
      <AppRouter />
    </McpProvider>
  </StrictMode>,
);

O app envolve tudo em McpProvider (conecta ao host MCP) e AppRouter (roteia para a UI de tool correta).

Contexto MCP (web/context.tsx)

Gerencia a conexão com o host MCP usando @modelcontextprotocol/ext-apps:

  • McpProvider — conecta via useApp(), trata eventos (ontoolinput, ontoolresult, ontoolcancelled, onerror, onhostcontextchanged), aplica estilos do host
  • useMcpState<TInput, TResult>() — acessa o estado MCP atual (status, input/result do tool)
  • useMcpApp() — acessa a instância do MCP App (ex: para app.sendMessage())
  • useMcpHostContext() — acessa o contexto do host (safe area insets, informações do tool)

Router (web/router.tsx)

Usa TanStack Router com histórico baseado em hash:

const TOOL_PAGES: Record<string, React.ComponentType> = {
  hello_world: HelloPage,
};

O componente ToolRouter lê toolName do estado MCP e renderiza o componente de página correspondente. RootLayout aplica os safe area insets do contexto do host.

UIs de tools (web/tools/<nome>/)

A UI de cada tool vive em sua própria pasta. O componente usa useMcpState<TInput, TResult>() para tratar todos os estados do ciclo de vida: initializing, connected, tool-input, tool-result, tool-cancelled e error.

Build system

  • Vite com vite-plugin-singlefile — builda em um único dist/client/index.html
  • React 19 com React Compiler (babel-plugin-react-compiler)
  • Todo o CSS e JS são inlineados em um único HTML para servir como resource de MCP App

Configuração

app.json

{
  "scopeName": "deco",
  "name": "mcp-app",
  "friendlyName": "MCP App Template",
  "description": "Starter template for building MCP Apps on deco.",
  "connection": {
    "type": "HTTP",
    "url": "https://mcp.example.com/api/mcp",
    "configSchema": {
      "type": "object",
      "properties": {},
      "required": []
    }
  }
}
  • scopeName + name — identificador único do seu app na plataforma deco
  • friendlyName — nome de exibição mostrado aos usuários
  • connection.url — seu endpoint MCP implantado
  • configSchema — JSON Schema que define as opções de configuração mostradas a quem instala

.mcp.json

Para um client MCP local, crie esta configuração no client que suporte esse formato:

{
  "mcpServers": {
    "mcp-app": {
      "type": "http",
      "url": "http://localhost:3001/api/mcp"
    }
  }
}

O Studio hospedado precisa de uma URL HTTPS acessível, e não do localhost da sua máquina. Veja Testando ferramentas para o fluxo de conexão.

Fluxo de Desenvolvimento

  1. Crie um tool em api/tools/my-tool.ts
  2. Registre-o em api/tools/index.ts
  3. Crie um componente de UI em web/tools/my-tool/index.tsx
  4. Registre a página em web/router.tsx (map TOOL_PAGES)
  5. Crie um resource em api/resources/my-tool.ts
  6. Registre o resource em api/app.ts (array resources)
  7. Rode bun run dev e teste