Habilitar apps externos a:
- Ler/escrever configuração versionada (branches/arquivos) no Deconfig (Durable Objects)
- Expor recursos tipados (Resources 2.0) via MCP, idêntico ao padrão
workflow - Consumir ferramentas do workspace via HTTP/SSE/Websocket
┌─────────────────────────────────────────────────────────────┐
│ Frontend (React + React Query) │
│ useWorkflows(), useCreateWorkflow(), useWorkflowStatus() │
└─────────────────────────────────────────────────────────────┘
↓ HTTP POST
┌─────────────────────────────────────────────────────────────┐
│ API Server (apps/api/src/api.ts) │
│ Hono routes → createMCPHandlerFor(tools) │
│ /mcp, /:root/:slug/mcp?group=<grupo> │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ MCP Server (@modelcontextprotocol/sdk) │
│ server.registerTool(name, schema, handler) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Tool Handler (packages/sdk/src/mcp/<resource>/api.ts) │
│ 1) assertWorkspaceResourceAccess(c) │
│ 2) c.resourceAccess.grant() │
│ 3) deconfig.PUT_FILE / READ_FILE / LIST_FILES │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Deconfig Client (Cloudflare Durable Object RPC) │
│ - Branches (git-like) │
│ - Files (JSON versionado) │
│ - Metadata (ctime, mtime, resourceType) │
└─────────────────────────────────────────────────────────────┘
https://api.decocms.com/<root>/<slug>/mcp
<root>:users/<userId>oushared/<teamSlug><slug>: nome do workspace (ex.:default)
https://api.decocms.com/<root>/<slug>/mcp?group=<well-known-group>
Grupos (WellKnownMcpGroups):
integration-management: lista/chama integrationsdeconfig-management: branch/file opsworkflows-management: CRUD de workflowstools-management: CRUD de custom tools
Accept: application/json, text/event-stream
Content-Type: application/json
MCP-Protocol-Version: 2025-06-18
Cookie: sb-auth-auth-token.0=base64-<jwt>...# Salve o cookie do browser em ~/.decocookie.txt
cat ~/.decocookie.txt
# sb-auth-auth-token.0=base64-eyJhY2Nlc3NfdG9rZW4iOi...
# Use no curl:
curl -H "cookie: $(tr -d '\n' ~/.decocookie.txt)" ...POST https://api.decocms.com/users/lucis/default/mcp
{
"method": "tools/call",
"params": {
"name": "INTEGRATIONS_LIST",
"arguments": {}
},
"jsonrpc": "2.0",
"id": 1
}Resposta (SSE):
event: message
data: {"result":{"isError":false,"structuredContent":{"items":[
{
"id":"i:workflows-management",
"name":"Workflows Management",
"connection":{"type":"HTTP","url":"https://api.decocms.com/users/lucis/default/mcp?group=workflows-management"},
"tools":[
{"name":"DECO_RESOURCE_WORKFLOW_SEARCH","description":"Search workflows..."},
{"name":"DECO_RESOURCE_WORKFLOW_READ","description":"Read a specific workflow by URI..."},
{"name":"DECO_RESOURCE_WORKFLOW_CREATE","description":"Create a new workflow..."},
{"name":"DECO_RESOURCE_WORKFLOW_UPDATE","description":"Update an existing workflow..."},
{"name":"DECO_RESOURCE_WORKFLOW_DELETE","description":"Delete a workflow..."},
{"name":"DECO_WORKFLOW_START","description":"Execute a workflow by URI..."},
{"name":"DECO_WORKFLOW_GET_STATUS","description":"Get workflow run status..."}
]
}
]}}}
POST https://api.decocms.com/users/lucis/default/mcp
{
"method": "tools/call",
"params": {
"name": "INTEGRATIONS_CALL_TOOL",
"arguments": {
"connection": {
"type": "HTTP",
"url": "https://api.decocms.com/users/lucis/default/mcp?group=workflows-management"
},
"params": {
"name": "DECO_RESOURCE_WORKFLOW_SEARCH",
"arguments": { "term": "send-email", "page": 1, "pageSize": 10 }
}
}
},
"jsonrpc": "2.0",
"id": 2
}Expostas em PROJECT_TOOLS via ...deconfigAPI.DECONFIG_TOOLS:
createBranch: cria uma branchlistBranches: lista branchesdeleteBranch: remove branchmergeBranch: merge de branchesdiffBranch: diff entre branches
PUT_FILE: escreve arquivo (cria ou atualiza)READ_FILE: lê conteúdo e metadataDELETE_FILE: deleta arquivoLIST_FILES: lista com prefixo e metadata
curl -sS -X POST "https://api.decocms.com/users/lucis/default/mcp?group=deconfig-management" \
-H "accept: application/json, text/event-stream" \
-H "content-type: application/json" \
-H "mcp-protocol-version: 2025-06-18" \
-H "cookie: $(tr -d '\n' ~/.decocookie.txt)" \
--data '{
"method": "tools/call",
"params": {
"name": "PUT_FILE",
"arguments": {
"branch": "main",
"path": "/src/workflows/send-email.json",
"content": "{\"name\":\"send-email\",\"description\":\"Sends email\",\"inputSchema\":{},\"outputSchema\":{},\"steps\":[]}",
"metadata": {
"resourceType": "workflow",
"resourceId": "send-email"
}
}
},
"jsonrpc": "2.0",
"id": 1
}'Resposta:
{"result":{"isError":false,"content":[{"type":"text","text":"{\"conflict\":false}"}]}}export const WorkflowDefinitionSchema = z.object({
name: z.string().min(1).describe("Unique workflow name"),
description: z.string().describe("What this workflow accomplishes"),
inputSchema: z.object({}).passthrough().describe("JSON Schema for input"),
outputSchema: z.object({}).passthrough().describe("JSON Schema for output"),
steps: z.array(WorkflowStepDefinitionSchema).min(1).describe("Sequential steps"),
});
export const WorkflowStepDefinitionSchema = z.object({
type: z.enum(["code", "tool_call"]),
def: z.union([CodeStepDefinitionSchema, ToolCallStepDefinitionSchema]),
});
export const ToolCallStepDefinitionSchema = z.object({
name: z.string(),
description: z.string(),
tool_name: z.string(),
integration: z.string().describe("Integration ID (format: i:<uuid>)"),
options: z.object({}).passthrough().optional(),
});
export const CodeStepDefinitionSchema = z.object({
name: z.string(),
description: z.string(),
execute: z.string().describe("ES module code with default async function"),
});export const WorkflowResourceV2 = DeconfigResourceV2.define({
directory: "/src/workflows", // Deconfig path
resourceName: "workflow", // Resource type
group: WellKnownMcpGroups.Workflows, // Tool group
dataSchema: WorkflowDefinitionSchema, // Zod schema
enhancements: {
DECO_RESOURCE_WORKFLOW_SEARCH: { description: WORKFLOW_SEARCH_PROMPT },
DECO_RESOURCE_WORKFLOW_READ: { description: WORKFLOW_READ_PROMPT },
DECO_RESOURCE_WORKFLOW_CREATE: { description: WORKFLOW_CREATE_PROMPT },
DECO_RESOURCE_WORKFLOW_UPDATE: { description: WORKFLOW_UPDATE_PROMPT },
DECO_RESOURCE_WORKFLOW_DELETE: { description: WORKFLOW_DELETE_PROMPT },
},
});Tools geradas automaticamente:
DECO_RESOURCE_WORKFLOW_SEARCHDECO_RESOURCE_WORKFLOW_READDECO_RESOURCE_WORKFLOW_CREATEDECO_RESOURCE_WORKFLOW_UPDATEDECO_RESOURCE_WORKFLOW_DELETE
const createContextBasedTools = (ctx: Context) => {
const appCtx = honoCtxToAppCtx(ctx);
const client = createDeconfigClientForContext(appCtx);
// 1. Criar resource implementation
const workflowResourceV2 = createWorkflowResourceV2Implementation(
client,
WellKnownMcpGroups.Workflows,
);
// 2. Criar stub para uso interno (opcional)
const resourcesClient = createMCPToolsStub({
tools: workflowResourceV2,
context: appCtx,
});
// 3. Criar execution tools (START, GET_STATUS)
const resourceWorkflowRead = (uri: string) =>
State.run(appCtx, async () =>
await resourcesClient.DECO_RESOURCE_WORKFLOW_READ({ uri })
);
const workflowBinding = createWorkflowBindingImpl({
resourceWorkflowRead,
});
// 4. Criar views
const workflowViewsV2 = createWorkflowViewsV2();
// 5. Agrupar todas as tools
const workflowTools = [
...workflowResourceV2, // CRUD
...workflowBinding, // START, GET_STATUS
...workflowViewsV2, // Views 2.0
].map((tool) => ({ ...tool, group: WellKnownMcpGroups.Workflows }));
return { workflowTools, /* ... */ };
};
// Registrar no MCP handler
app.all("/:root/:slug/mcp", createMCPHandlerFor(async (c) => {
const { workflowTools } = createContextBasedTools(c);
return [...workflowTools, ...PROJECT_TOOLS];
}));import { useCreateWorkflow } from "@deco/sdk/hooks/resources-workflow";
const { mutateAsync: createWorkflow } = useCreateWorkflow();
await createWorkflow({
data: {
name: "send-welcome-email",
description: "Send welcome email to new users",
inputSchema: {
type: "object",
properties: {
email: { type: "string", format: "email" },
name: { type: "string" },
},
required: ["email", "name"],
},
outputSchema: {
type: "object",
properties: {
messageId: { type: "string" },
status: { type: "string" },
},
},
steps: [
{
type: "tool_call",
def: {
name: "send-email",
description: "Send email via integration",
tool_name: "EMAIL_SEND",
integration: "i:email-management",
},
},
{
type: "code",
def: {
name: "format-response",
description: "Format final output",
execute: `export default async function(ctx) {
const emailResult = await ctx.readStepResult('send-email');
return {
messageId: emailResult.id,
status: emailResult.status,
};
}`,
},
},
],
},
});export function useCreateWorkflow() {
const { workspace } = useSDK();
const client = useQueryClient();
return useMutation({
mutationFn: async ({ data }: { data: WorkflowDefinition }) => {
const mcpClient = MCPClient.forWorkspace(workspace);
return await mcpClient.DECO_RESOURCE_WORKFLOW_CREATE({ data });
},
onSuccess: (result) => {
client.invalidateQueries({ queryKey: ["workflows", workspace] });
client.setQueryData(["workflow", workspace, result.uri], result);
},
});
}POST /users/lucis/default/mcp?group=workflows-management
{
"method": "tools/call",
"params": {
"name": "DECO_RESOURCE_WORKFLOW_CREATE",
"arguments": { "data": { /* workflow definition */ } }
},
"jsonrpc": "2.0",
"id": 1
}
// DECO_RESOURCE_WORKFLOW_CREATE handler (packages/sdk/src/mcp/deconfig-v2/index.ts)
handler: async ({ data }, c) => {
assertHasWorkspace(c);
await assertWorkspaceResourceAccess(c, "PUT_FILE");
// 1. Validar schema
const validatedData = dataSchema.parse(data);
// 2. Gerar resource ID
const resourceId = validatedData.name.replace(/[^a-zA-Z0-9-_]/g, "-");
const uri = `rsc://${integrationId}/${resourceName}/${resourceId}`;
const filePath = `/src/workflows/${resourceId}.json`;
// 3. Preparar dados com metadata
const resourceData = {
...validatedData,
id: resourceId,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
created_by: c.user?.id,
};
// 4. Escrever no Deconfig
await deconfig.PUT_FILE({
path: filePath,
content: JSON.stringify(resourceData, null, 2),
metadata: {
resourceType: "workflow",
resourceId,
name: validatedData.name,
description: validatedData.description,
},
});
return { uri, data: validatedData, created_at, updated_at };
}// Deconfig RPC (Cloudflare Durable Object)
async PUT_FILE({ path, content, metadata }) {
const file = {
content,
ctime: Date.now(),
mtime: Date.now(),
metadata,
};
await this.storage.put(path, file);
return { conflict: false };
}const { mutateAsync: startWorkflow } = useStartWorkflow();
const result = await startWorkflow({
uri: "rsc://workflows-management/workflow/send-welcome-email",
input: {
email: "[email protected]",
name: "John Doe",
},
});
// { runId: "wf_abc123..." }handler: async ({ uri, input }, c) => {
// 1. Ler workflow definition
const { data: workflow } = await resourceWorkflowRead(uri);
// 2. Validar input
const inputValidation = validate(input, workflow.inputSchema);
if (!inputValidation.valid) {
return { error: "Input validation failed" };
}
// 3. Criar instância no Cloudflare Workflows
const workflowInstance = await c.workflowRunner.create({
params: {
input,
steps: workflow.steps,
name: workflow.name,
context: { workspace: c.workspace, locator: c.locator },
},
});
return { runId: workflowInstance.id };
}// Cloudflare Workflows engine (automático)
class WorkflowRunner {
async execute(params) {
const state = {};
for (const step of params.steps) {
if (step.type === "tool_call") {
const result = await callIntegrationTool(step.def.tool_name, params.input);
state[step.def.name] = result;
} else if (step.type === "code") {
const ctx = {
readWorkflowInput: () => params.input,
readStepResult: (name) => state[name],
};
const fn = evalCode(step.def.execute);
state[step.def.name] = await fn(ctx);
}
}
return state[params.steps[params.steps.length - 1].def.name];
}
}const { data: status } = useWorkflowStatus(runId, {
refetchInterval: 1000, // Poll a cada 1s
});
// status: {
// status: "completed",
// finalResult: { messageId: "msg_xyz", status: "sent" },
// logs: [...]
// }// packages/sdk/src/mcp/documents/schemas.ts
export const DocumentDefinitionSchema = z.object({
name: z.string().min(1),
description: z.string(),
content: z.string(),
tags: z.array(z.string()).optional(),
status: z.enum(["draft", "published", "archived"]).default("draft"),
});// packages/sdk/src/mcp/documents/api.ts
export const DocumentResourceV2 = DeconfigResourceV2.define({
directory: "/src/documents",
resourceName: "document",
group: "documents-management",
dataSchema: DocumentDefinitionSchema,
enhancements: {
DECO_RESOURCE_DOCUMENT_SEARCH: { description: "Search documents..." },
DECO_RESOURCE_DOCUMENT_READ: { description: "Read a document..." },
DECO_RESOURCE_DOCUMENT_CREATE: { description: "Create a document..." },
DECO_RESOURCE_DOCUMENT_UPDATE: { description: "Update a document..." },
DECO_RESOURCE_DOCUMENT_DELETE: { description: "Delete a document..." },
},
});// apps/api/src/api.ts
const documentResourceV2 = createDocumentResourceV2Implementation(
client,
"documents-management",
);
const documentTools = [...documentResourceV2].map((tool) => ({
...tool,
group: "documents-management",
}));
return [...workflowTools, ...documentTools, ...PROJECT_TOOLS];// packages/sdk/src/hooks/resources-document.ts
export function useCreateDocument() {
const { workspace } = useSDK();
return useMutation({
mutationFn: async ({ data }: { data: DocumentDefinition }) => {
const mcpClient = MCPClient.forWorkspace(workspace);
return await mcpClient.DECO_RESOURCE_DOCUMENT_CREATE({ data });
},
});
}- HTTP MCP:
https://api.decocms.com/<workspace>/mcp?group=<group> - Descoberta:
INTEGRATIONS_LISTretorna virtual integrations com tools - Chamada:
INTEGRATIONS_CALL_TOOLcomconnection.urleparams
- Deconfig:
DECONFIG_TOOLS(branches/files) - Resources 2.0: CRUD automático via
DeconfigResourceV2.define - Custom: qualquer tool registrada no servidor
// Views 2.0 discovery
export const workflowViewsV2 = createViewImplementation({
renderers: [
createViewRenderer({
name: "workflow_detail",
title: "Workflow Detail",
tools: ["DECO_RESOURCE_WORKFLOW_READ", "DECO_WORKFLOW_START"],
handler: (input) => ({
url: `internal://resources/workflow?view=detail&resource=${input.resource}`,
}),
}),
],
});{
"name": "sync-database",
"description": "Sync data from API to database",
"inputSchema": {
"type": "object",
"properties": {
"apiUrl": { "type": "string", "format": "uri" },
"tableName": { "type": "string" }
},
"required": ["apiUrl", "tableName"]
},
"outputSchema": {
"type": "object",
"properties": {
"recordsInserted": { "type": "number" },
"recordsUpdated": { "type": "number" }
}
},
"steps": [
{
"type": "tool_call",
"def": {
"name": "fetch-data",
"description": "Fetch data from API",
"tool_name": "HTTP_GET",
"integration": "i:http-client"
}
},
{
"type": "code",
"def": {
"name": "transform-data",
"description": "Transform API response",
"execute": "export default async function(ctx) { const input = await ctx.readWorkflowInput(); const data = await ctx.readStepResult('fetch-data'); return data.items.map(i => ({ id: i.id, table: input.tableName })); }"
}
},
{
"type": "tool_call",
"def": {
"name": "insert-records",
"description": "Insert into database",
"tool_name": "DATABASES_RUN_SQL",
"integration": "i:databases-management"
}
},
{
"type": "code",
"def": {
"name": "format-result",
"description": "Format output",
"execute": "export default async function(ctx) { const r = await ctx.readStepResult('insert-records'); return { recordsInserted: r.inserted, recordsUpdated: r.updated }; }"
}
}
]
}curl -X POST "https://api.decocms.com/users/lucis/default/mcp?group=workflows-management" \
-H "cookie: $(tr -d '\n' ~/.decocookie.txt)" \
-H "accept: application/json, text/event-stream" \
-H "content-type: application/json" \
-H "mcp-protocol-version: 2025-06-18" \
--data '{"method":"tools/call","params":{"name":"DECO_RESOURCE_WORKFLOW_CREATE","arguments":{"data":{/* JSON acima */}}},"jsonrpc":"2.0","id":1}'curl -X POST "https://api.decocms.com/users/lucis/default/mcp?group=workflows-management" \
-H "cookie: $(tr -d '\n' ~/.decocookie.txt)" \
-H "accept: application/json, text/event-stream" \
-H "content-type: application/json" \
-H "mcp-protocol-version: 2025-06-18" \
--data '{
"method":"tools/call",
"params":{
"name":"DECO_WORKFLOW_START",
"arguments":{
"uri":"rsc://workflows-management/workflow/sync-database",
"input":{"apiUrl":"https://api.example.com/data","tableName":"users"}
}
},
"jsonrpc":"2.0",
"id":2
}'Resposta:
{"result":{"isError":false,"content":[{"type":"text","text":"{\"runId\":\"wf_12345\"}"}]}}- Obter cookie ou JWT token (
~/.decocookie.txt) - Descobrir integrações:
INTEGRATIONS_LIST - Mapear tools do grupo desejado (ex.:
workflows-management) - Implementar client HTTP/SSE para
INTEGRATIONS_CALL_TOOL - Usar
PUT_FILE/READ_FILEpara persistir config - Polling de status para workflows (
DECO_WORKFLOW_GET_STATUS)
- Definir schema Zod (
<Resource>DefinitionSchema) - Criar resource definition (
DeconfigResourceV2.define) - Registrar no servidor (
apps/api/src/api.ts) - Criar hooks React (
use<Resource>,useCreate<Resource>) - Adicionar views ao menu (Views 2.0)
- Documentar prompts customizados (enhancements)
#!/usr/bin/env bash
# scripts/deconfig/list-tools.sh
COOKIE_FILE="${DECO_COOKIE_FILE:-$HOME/.decocookie.txt}"
API_BASE="${DECO_API_BASE:-https://api.decocms.com}"
WORKSPACE="${DECO_WORKSPACE:-lucis/default}"
TOOL="${DECO_TOOL:-INTEGRATIONS_LIST}"
COOKIE_HEADER=$(tr -d '\n' < "$COOKIE_FILE")
curl -sS -X POST "${API_BASE}/${WORKSPACE}/mcp" \
-H "accept: application/json, text/event-stream" \
-H "content-type: application/json" \
-H "mcp-protocol-version: 2025-06-18" \
-H "cookie: ${COOKIE_HEADER}" \
--data "{\"method\":\"tools/call\",\"params\":{\"name\":\"${TOOL}\",\"arguments\":{}},\"jsonrpc\":\"2.0\",\"id\":1}"# Listar integrações
DECO_WORKSPACE="users/lucis/default" bash scripts/deconfig/list-tools.sh
# Listar workflows
DECO_TOOL="DECO_RESOURCE_WORKFLOW_SEARCH" bash scripts/deconfig/list-tools.sh