Skip to content

Instantly share code, notes, and snippets.

@lucis
Created October 3, 2025 12:49
Show Gist options
  • Save lucis/e11ec18d39b569df60c7f27bf0107401 to your computer and use it in GitHub Desktop.
Save lucis/e11ec18d39b569df60c7f27bf0107401 to your computer and use it in GitHub Desktop.

Integração com Deconfig via MCP – Guia Completo de Plumbing

1. Fundamentos e Arquitetura

Objetivo

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

Camadas da Stack

┌─────────────────────────────────────────────────────────────┐
│ 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)                   │
└─────────────────────────────────────────────────────────────┘

2. Endpoints e Autenticação

Workspace MCP

https://api.decocms.com/<root>/<slug>/mcp
  • <root>: users/<userId> ou shared/<teamSlug>
  • <slug>: nome do workspace (ex.: default)

Com grupo (virtual integration)

https://api.decocms.com/<root>/<slug>/mcp?group=<well-known-group>

Grupos (WellKnownMcpGroups):

  • integration-management: lista/chama integrations
  • deconfig-management: branch/file ops
  • workflows-management: CRUD de workflows
  • tools-management: CRUD de custom tools

Headers obrigatórios

Accept: application/json, text/event-stream
Content-Type: application/json
MCP-Protocol-Version: 2025-06-18
Cookie: sb-auth-auth-token.0=base64-<jwt>...

Auth via Cookie (para scripts)

# 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)" ...

3. Descoberta de Ferramentas

Listar todas as integrações e tools

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..."}
    ]
  }
]}}}

Chamar tool de uma integração

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
}

4. Deconfig Core Tools (File/Branch Ops)

Expostas em PROJECT_TOOLS via ...deconfigAPI.DECONFIG_TOOLS:

Branch operations

  • createBranch: cria uma branch
  • listBranches: lista branches
  • deleteBranch: remove branch
  • mergeBranch: merge de branches
  • diffBranch: diff entre branches

File operations

  • PUT_FILE: escreve arquivo (cria ou atualiza)
  • READ_FILE: lê conteúdo e metadata
  • DELETE_FILE: deleta arquivo
  • LIST_FILES: lista com prefixo e metadata

Exemplo: escrever workflow via PUT_FILE

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}"}]}}

Deconfig Integration – Plumbing Detalhado (parte 2)

5. Resources 2.0 – O Padrão Workflow

Anatomia completa

5.1. Schema (packages/sdk/src/mcp/workflows/schemas.ts)

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"),
});

5.2. Resource Definition (packages/sdk/src/mcp/workflows/api.ts)

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_SEARCH
  • DECO_RESOURCE_WORKFLOW_READ
  • DECO_RESOURCE_WORKFLOW_CREATE
  • DECO_RESOURCE_WORKFLOW_UPDATE
  • DECO_RESOURCE_WORKFLOW_DELETE

5.3. Registro no servidor (apps/api/src/api.ts)

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];
}));

6. Fluxo de Dados Completo: Criar e Executar Workflow

6.1. Frontend: criar workflow

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,
            };
          }`,
        },
      },
    ],
  },
});

6.2. SDK: hook implementation

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);
    },
  });
}

6.3. HTTP: POST via MCPClient

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
}

6.4. Backend: tool handler

// 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 };
}

6.5. Deconfig: persistir no Durable Object

// 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 };
}

7. Executar Workflow

7.1. Frontend: iniciar execução

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..." }

7.2. Backend: DECO_WORKFLOW_START handler

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 };
}

7.3. Cloudflare Workflows: execução

// 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];
  }
}

7.4. Frontend: polling status

const { data: status } = useWorkflowStatus(runId, {
  refetchInterval: 1000, // Poll a cada 1s
});

// status: { 
//   status: "completed", 
//   finalResult: { messageId: "msg_xyz", status: "sent" }, 
//   logs: [...] 
// }

8. Criar Novo Recurso (Ex.: Document)

8.1. Schema

// 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"),
});

8.2. Resource Definition

// 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..." },
  },
});

8.3. Registrar no servidor

// 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];

8.4. Frontend hook

// 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 });
    },
  });
}

9. Exposição para Apps

Apps consomem tools via:

  1. HTTP MCP: https://api.decocms.com/<workspace>/mcp?group=<group>
  2. Descoberta: INTEGRATIONS_LIST retorna virtual integrations com tools
  3. Chamada: INTEGRATIONS_CALL_TOOL com connection.url e params

Tools expostas (em PROJECT_TOOLS):

  • Deconfig: DECONFIG_TOOLS (branches/files)
  • Resources 2.0: CRUD automático via DeconfigResourceV2.define
  • Custom: qualquer tool registrada no servidor

Views (menu/admin):

// 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}`,
      }),
    }),
  ],
});

10. Exemplo Completo: Workflow "Sync Database"

Definição JSON

{
  "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 }; }"
      }
    }
  ]
}

Criar via curl

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}'

Executar

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\"}"}]}}

11. Checklist de Integração

Para apps consumindo Deconfig:

  • 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_FILE para persistir config
  • Polling de status para workflows (DECO_WORKFLOW_GET_STATUS)

Para criar novo recurso (Resources 2.0):

  • 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)

12. Script de Apoio

list-tools.sh

#!/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}"

Uso:

# 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment