Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,34 @@ Bun is the test runner throughout. Co-locate test files next to source as `*.tes
## Authentication & Authorization
Uses Better Auth for authentication (OAuth 2.1 + SSO + API keys). Authorization uses custom AccessControl layer with organization/project-level RBAC. Auth configuration is in `apps/mesh/auth-config.json` (example: `auth-config.example.json`).

## Documentation Requirements

**IMPORTANT**: Documentation must be updated alongside code changes. Before committing:

1. **Check for relevant docs** in `apps/docs/client/src/content/` (EN + PT-BR)
2. **Update affected docs** when changing:
- API behavior or endpoints
- Configuration options
- Authentication/authorization flows
- Connection types or proxy behavior
- New features or removed functionality
3. **Update README.md** for significant features
4. **Review docs diff** before committing to ensure accuracy

Documentation locations:
- `apps/docs/client/src/content/en/` - English docs
- `apps/docs/client/src/content/pt-br/` - Portuguese docs
- `README.md` - Project overview and quick start
- `packages/*/README.md` - Package-specific docs

Run `bun run docs:dev` to preview documentation changes locally.

## Commit & Pull Request Guidelines
Follow Conventional Commit-style history: `type(scope): message`, optionally wrapping the type in brackets for chores (e.g., `[chore]: update deps`). Reference issues with `(#1234)` when applicable. PRs should include:
- Succinct summary of changes
- Testing notes and affected areas
- Screenshots for UI changes
- **Documentation updates** for any behavior changes
- Confirm formatting (`bun run fmt`) and linting (`bun run lint`) pass
- Run tests (`bun test`) before requesting review
- Flag follow-up work with TODOs linked to issues
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,23 @@ Gateways are configurable and extensible. You can add new strategies and also cu

---

## STDIO Connections (Local MCPs)

Run npx packages or custom scripts as MCP servers. Mesh passes credentials via environment variables:

```bash
# Mesh spawns your MCP with these env vars:
MESH_TOKEN=<jwt> # Infinite-expiry JWT for mesh API calls
MESH_URL=<url> # Mesh instance URL
MESH_STATE=<json> # Binding values as JSON
```

Your MCP just reads `process.env.MESH_TOKEN` — no special configuration tools needed. This mirrors how HTTP connections receive `x-mesh-token` headers.

→ See [Building STDIO MCPs](https://docs.deco.page/en/mcp-mesh/mcp-servers) for examples in [decocms/mcps](https://github.com/decocms/mcps).

---

## Define Tools

Tools are first-class citizens. Type-safe, audited, observable, and callable via MCP.
Expand Down
12 changes: 6 additions & 6 deletions apps/docs/client/src/content/en/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ If MCP is the standard interface for tool access, deco CMS is the **production l
- **The MCP Mesh**: [decocms.com/mesh](https://www.decocms.com/mesh)
- **MCP Studio**: [decocms.com/mcp-studio](https://www.decocms.com/mcp-studio)

If you know us from before (as **deco.cx**) and youre looking for **headless CMS + storefront** capabilities, visit [deco.cx](https://www.decocms.com/use-case/deco-cx). See the deco.cx docs at [docs.deco.cx](https://docs.deco.cx/en/getting-started/overview).
If you know us from before (as **deco.cx**) and you're looking for **headless CMS + storefront** capabilities, visit [deco.cx](https://www.decocms.com/use-case/deco-cx). See the deco.cx docs at [docs.deco.cx](https://docs.deco.cx/en/getting-started/overview).

## Start with the MCP Mesh

Expand Down Expand Up @@ -92,7 +92,7 @@ The application will be available at `http://localhost:8080`.
- **[MCP Mesh Overview](/en/mcp-mesh/overview)**
- **[Quickstart](/en/mcp-mesh/quickstart)**
- **[Concepts](/en/mcp-mesh/concepts)**
- **[MCP Servers (Connections)](/en/mcp-mesh/mcp-servers)**
- **[Connections](/en/mcp-mesh/mcp-servers)**
- **[MCP Agents](/en/mcp-mesh/mcp-gateways)**
- **[API Keys](/en/mcp-mesh/api-keys)**
- **[Monitoring](/en/mcp-mesh/monitoring)**
Expand All @@ -101,9 +101,9 @@ The application will be available at `http://localhost:8080`.
**Docs split (quick guide):**

- **The MCP Mesh** → Self-hosting, deploying, and operating the Mesh (recommended).
- **Legacy Admin** → If youre using `admin.decocms.com` (still supported, **deprecated soon**).
- **Legacy Admin** → If you're using `admin.decocms.com` (still supported, **deprecated soon**).

Were launching **MCP Studio** (on top of the Mesh), which will bring the current SaaS admin capabilities to the Mesh (including a hosted option by us) and replace the legacy SaaS over time.
We're launching **MCP Studio** (on top of the Mesh), which will bring the current SaaS admin capabilities to the Mesh (including a hosted option by us) and replace the legacy SaaS over time.
</Callout>

## Problem: the production gap
Expand Down Expand Up @@ -139,7 +139,7 @@ A path to distribute reusable MCP-native capabilities across teams (and eventual

## Why MCP?

Were built on MCP because enterprises need interoperability at the tool layer — and a way to operate that tool access with the rigor of any production surface.
We're built on MCP because enterprises need interoperability at the tool layer — and a way to operate that tool access with the rigor of any production surface.

MCP gives you a standard interface. deco CMS provides the **control plane** around it:

Expand All @@ -150,7 +150,7 @@ MCP gives you a standard interface. deco CMS provides the **control plane** arou

## Legacy Admin quick start (admin.decocms.com)

If youre using the current SaaS admin at `admin.decocms.com` (still supported, deprecated soon), start here:
If you're using the current SaaS admin at `admin.decocms.com` (still supported, deprecated soon), start here:

- **[For AI Builders](/en/getting-started/ai-builders)**
- **[For Developers](/en/getting-started/developers)**
67 changes: 66 additions & 1 deletion apps/docs/client/src/content/en/mcp-mesh/mcp-servers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,72 @@ import Callout from "../../../components/ui/Callout.astro";

## What is a connection?

A **Connection** in the Mesh is a configured upstream MCP endpoint (typically HTTP). The Mesh stores its configuration and (optionally) credentials, and can then proxy MCP requests to it.
A **Connection** in the Mesh is a configured upstream MCP endpoint. The Mesh stores its configuration and (optionally) credentials, and can then proxy MCP requests to it.

## Connection Types

The Mesh supports two types of connections:

### HTTP Connections

HTTP connections are the most common type. They connect to remote MCP servers via HTTP/SSE endpoints.

- **Use cases**: Cloud-hosted MCP servers, SaaS integrations, production deployments
- **Token handling**: Short-lived tokens (5 minutes) issued per request

### STDIO Connections (Local Commands)

STDIO connections spawn a local process (like `npx` or custom scripts) and communicate via stdin/stdout.

- **Use cases**: Local tools, npx packages, development, private MCPs
- **Token handling**: Infinite-expiry tokens persisted locally by the MCP server

<Callout type="tip">
STDIO connections are perfect for running npm packages as MCP servers. Example: `npx @decocms/local-fs` runs a file system MCP locally.
</Callout>

## STDIO Credentials via Environment Variables

When mesh spawns an STDIO MCP process, it passes credentials as environment variables:

| Variable | Description |
|----------|-------------|
| `MESH_TOKEN` | JWT token for authenticating with Mesh API (infinite expiry) |
| `MESH_URL` | Base URL of the Mesh instance |
| `MESH_STATE` | JSON-encoded state with binding values |

This is analogous to how HTTP connections receive `x-mesh-token` headers.

<Callout type="tip">
**No special tools needed!** Just read `process.env.MESH_TOKEN` on startup. No need to implement `ON_MCP_CONFIGURATION` or any configuration protocol.
</Callout>

### Example (Node.js/Bun)

```typescript
// Read mesh credentials from env
const meshToken = process.env.MESH_TOKEN;
const meshUrl = process.env.MESH_URL;
const state = process.env.MESH_STATE ? JSON.parse(process.env.MESH_STATE) : {};

// Connection ID (get it in the UI under Connections, or via the API)
const connectionId = "<connection-id>";

// Use token for mesh API calls
const response = await fetch(`${meshUrl}/mcp/${connectionId}`, {
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The code example references connectionId which is never defined or explained. Consider adding a comment explaining where this value comes from, or defining it (perhaps from state).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/client/src/content/en/mcp-mesh/mcp-servers.mdx, line 60:

<comment>The code example references `connectionId` which is never defined or explained. Consider adding a comment explaining where this value comes from, or defining it (perhaps from `state`).</comment>

<file context>
@@ -8,7 +8,69 @@ import Callout from &quot;../../../components/ui/Callout.astro&quot;;
+const state = process.env.MESH_STATE ? JSON.parse(process.env.MESH_STATE) : {};
+
+// Use token for mesh API calls
+const response = await fetch(`${meshUrl}/mcp/${connectionId}`, {
+  headers: { Authorization: `Bearer ${meshToken}` },
+  // ...
</file context>

✅ Addressed in 0c31351

headers: { Authorization: `Bearer ${meshToken}` },
// ...
});
```

## Building STDIO-Compatible MCPs

See examples in the [decocms/mcps](https://github.com/decocms/mcps) repository:

- `template-minimal/` - Minimal MCP without view
- `template-with-view/` - MCP with web interface
- `local-fs/` - File system MCP (runs via npx)
- `perplexity/`, `openrouter/` - Production MCPs

## In the UI

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/client/src/content/pt-br/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ A aplicação ficará disponível em `http://localhost:8080`.
- **[Visão geral](/pt-br/mcp-mesh/overview)**
- **[Quickstart](/pt-br/mcp-mesh/quickstart)**
- **[Conceitos](/pt-br/mcp-mesh/concepts)**
- **[MCP Servers](/pt-br/mcp-mesh/mcp-servers)**
- **[Conexões](/pt-br/mcp-mesh/mcp-servers)**
- **[MCP Agents](/pt-br/mcp-mesh/mcp-gateways)**
- **[API Keys](/pt-br/mcp-mesh/api-keys)**
- **[Monitoring](/pt-br/mcp-mesh/monitoring)**
Expand Down
83 changes: 73 additions & 10 deletions apps/docs/client/src/content/pt-br/mcp-mesh/mcp-servers.mdx
Original file line number Diff line number Diff line change
@@ -1,27 +1,90 @@
---
title: Conexões
description: Conexões com MCP servers upstream (HTTP) que o Mesh faz proxy e observa
description: Conexões com MCPs upstream que o Mesh faz proxy e observa
icon: Server
---

## O que é uma Conexão (no Mesh)?
import Callout from "../../../components/ui/Callout.astro";

No Mesh, uma **Conexão** é uma **connection** para um MCP upstream (normalmente um endpoint MCP via HTTP).
## O que é uma conexão?

Cada connection guarda:
No Mesh, uma **Conexão** é a configuração de um MCP upstream.

- **URL do MCP**
Cada conexão guarda:

- **URL do MCP** (para HTTP) ou **comando** (para STDIO)
- **credenciais/config** (quando necessário, armazenadas no vault)
- metadados para operação (nome, status, etc.)

## Quando usar vs Agent
## Tipos de Conexão

### Conexões HTTP

Conexões HTTP conectam a servidores MCP remotos via endpoints HTTP/SSE.

- **Casos de uso**: MCPs em nuvem, integrações SaaS, produção
- **Tokens**: JWT com expiração curta (5 minutos)

### Conexões STDIO (Comandos Locais)

Conexões STDIO iniciam um processo local (como `npx` ou scripts) e comunicam via stdin/stdout.

- **Casos de uso**: Ferramentas locais, pacotes npx, desenvolvimento
- **Tokens**: JWT sem expiração, persistido localmente

<Callout type="tip">
Conexões STDIO são perfeitas para rodar pacotes npm como MCPs. Exemplo: `npx @decocms/local-fs` roda um MCP de sistema de arquivos localmente.
</Callout>

## Credenciais STDIO via Variáveis de Ambiente

Quando o mesh inicia um processo MCP STDIO, ele passa credenciais como variáveis de ambiente:

| Variável | Descrição |
|----------|-----------|
| `MESH_TOKEN` | Token JWT para autenticação com a API do Mesh (sem expiração) |
| `MESH_URL` | URL base da instância Mesh |
| `MESH_STATE` | Estado com valores de bindings em JSON |

Isso é análogo a como conexões HTTP recebem headers `x-mesh-token`.

- Use **connection** para isolar e depurar um upstream específico.
<Callout type="tip">
**Nenhuma ferramenta especial necessária!** Apenas leia `process.env.MESH_TOKEN` na inicialização. Não precisa implementar `ON_MCP_CONFIGURATION` ou qualquer protocolo de configuração.
</Callout>

### Exemplo (Node.js/Bun)

```typescript
// Ler credenciais mesh das env vars
const meshToken = process.env.MESH_TOKEN;
const meshUrl = process.env.MESH_URL;
const state = process.env.MESH_STATE ? JSON.parse(process.env.MESH_STATE) : {};

// ID da Conexão (você pega na UI em Connections, ou via API)
const connectionId = "<connection-id>";

// Usar token para chamadas à API mesh
const response = await fetch(`${meshUrl}/mcp/${connectionId}`, {
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The example uses connectionId but this variable is never defined. Consider adding a placeholder definition or a comment explaining where it comes from.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/client/src/content/pt-br/mcp-mesh/mcp-servers.mdx, line 64:

<comment>The example uses `connectionId` but this variable is never defined. Consider adding a placeholder definition or a comment explaining where it comes from.</comment>

<file context>
@@ -1,24 +1,86 @@
+const state = process.env.MESH_STATE ? JSON.parse(process.env.MESH_STATE) : {};
+
+// Usar token para chamadas à API mesh
+const response = await fetch(`${meshUrl}/mcp/${connectionId}`, {
+  headers: { Authorization: `Bearer ${meshToken}` },
+  // ...
</file context>

✅ Addressed in 4e7fe45

headers: { Authorization: `Bearer ${meshToken}` },
// ...
});
```

## Quando usar vs Agents

- Use **Conexão** para isolar e depurar um upstream específico.
- Use **Agent** para expor um surface agregado/curado para os clients.

## Boas práticas
## Construindo MCPs STDIO

- Mantenha URLs e credenciais por ambiente (dev/staging/prod).
- Evite expor uma connection diretamente em produção; prefira um Agent para governança.
Veja exemplos no repositório [decocms/mcps](https://github.com/decocms/mcps):

- `template-minimal/` - MCP mínimo sem view
- `template-with-view/` - MCP com interface web
- `local-fs/` - MCP de sistema de arquivos (roda via npx)
- `perplexity/`, `openrouter/` - MCPs em produção

## Boas práticas

- Mantenha URLs e credenciais por ambiente (dev/staging/prod).
- Evite expor uma Conexão diretamente em produção; prefira um Agent para governança.
8 changes: 8 additions & 0 deletions apps/mesh/src/api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ import {
import { MiddlewareHandler } from "hono/types";
import { getToolsByCategory, MANAGEMENT_TOOLS } from "../tools/registry";
import { Env } from "./env";
import { resetStdioConnectionPool } from "../stdio/stable-transport";
import { devLogger } from "./utils/dev-logger";

const getHandleOAuthProtectedResourceMetadata = () =>
oAuthProtectedResourceMetadata(auth);
const getHandleOAuthDiscoveryMetadata = () => oAuthDiscoveryMetadata(auth);
Expand Down Expand Up @@ -132,6 +134,12 @@ export interface CreateAppOptions {
export function createApp(options: CreateAppOptions = {}) {
const database = options.database ?? getDb();

// Kill and respawn STDIO connections on restart/HMR
// Old processes have stale credentials, need fresh spawn with new tokens
resetStdioConnectionPool().catch((err) => {
console.error("[StableStdio] Error resetting pool:", err);
});

// Stop any existing event bus worker (cleanup during HMR)
if (currentEventBus && currentEventBus.isRunning()) {
console.log("[EventBus] Stopping previous worker (HMR cleanup)");
Expand Down
Loading